PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/IBM_DB/ibm_db_django/ibm_db_django/operations.py

http://ibm-db.googlecode.com/
Python | 392 lines | 353 code | 9 blank | 30 comment | 22 complexity | 321af8aa45ae5588c56924e369e70a17 MD5 | raw file
Possible License(s): Apache-2.0
  1. # +--------------------------------------------------------------------------+
  2. # | Licensed Materials - Property of IBM |
  3. # | |
  4. # | (C) Copyright IBM Corporation 2009. |
  5. # +--------------------------------------------------------------------------+
  6. # | This module complies with Django 1.0 and is |
  7. # | Licensed under the Apache License, Version 2.0 (the "License"); |
  8. # | you may not use this file except in compliance with the License. |
  9. # | You may obtain a copy of the License at |
  10. # | http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable |
  11. # | law or agreed to in writing, software distributed under the License is |
  12. # | distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
  13. # | KIND, either express or implied. See the License for the specific |
  14. # | language governing permissions and limitations under the License. |
  15. # +--------------------------------------------------------------------------+
  16. # | Authors: Ambrish Bhargava, Tarun Pasrija, Rahul Priyadarshi |
  17. # +--------------------------------------------------------------------------+
  18. from django.db.backends import BaseDatabaseOperations
  19. from ibm_db_django import query
  20. from string import upper
  21. from django import VERSION as djangoVersion
  22. import sys
  23. _IS_JYTHON = sys.platform.startswith( 'java' )
  24. if( djangoVersion[0:2] >= ( 1, 4 ) ):
  25. from django.utils.timezone import is_aware, is_naive, utc
  26. from django.conf import settings
  27. class DatabaseOperations ( BaseDatabaseOperations ):
  28. def __init__( self, connection ):
  29. if( djangoVersion[0:2] >= ( 1, 4 ) ):
  30. super( DatabaseOperations, self ).__init__(self)
  31. else:
  32. super( DatabaseOperations, self ).__init__()
  33. self.connection = connection
  34. if( djangoVersion[0:2] >= ( 1, 2 ) ):
  35. compiler_module = "ibm_db_django.compiler"
  36. def check_aggregate_support( self, aggregate ):
  37. # In DB2 data type of the result is the same as the data type of the argument values for AVG aggregation
  38. # But Django aspect in Float regardless of data types of argument value
  39. # http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/index.jsp?topic=/com.ibm.db2.luw.apdv.cli.doc/doc/c0007645.html
  40. if aggregate.sql_function == 'AVG':
  41. aggregate.sql_template = '%(function)s(DOUBLE(%(field)s))'
  42. #In DB2 equivalent sql function of STDDEV_POP is STDDEV
  43. elif aggregate.sql_function == 'STDDEV_POP':
  44. aggregate.sql_function = 'STDDEV'
  45. #In DB2 equivalent sql function of VAR_SAMP is VARIENCE
  46. elif aggregate.sql_function == 'VAR_POP':
  47. aggregate.sql_function = 'VARIANCE'
  48. #DB2 doesn't have sample standard deviation function
  49. elif aggregate.sql_function == 'STDDEV_SAMP':
  50. raise NotImplementedError
  51. #DB2 doesn't have sample variance function
  52. elif aggregate.sql_function == 'VAR_SAMP':
  53. raise NotImplementedError
  54. def combine_expression( self, operator, sub_expressions ):
  55. if operator == '%%':
  56. return 'MOD(%s, %s)' % ( sub_expressions[0], sub_expressions[1] )
  57. elif operator == '&':
  58. return 'BITAND(%s, %s)' % ( sub_expressions[0], sub_expressions[1] )
  59. elif operator == '|':
  60. return 'BITOR(%s, %s)' % ( sub_expressions[0], sub_expressions[1] )
  61. else:
  62. return super( DatabaseOperations, self ).combine_expression( operator, sub_expressions )
  63. def convert_values( self, value, field ):
  64. field_type = field.get_internal_type()
  65. if field_type in ( 'BooleanField', 'NullBooleanField' ):
  66. if value in ( 0, 1 ):
  67. return bool( value )
  68. else:
  69. return value
  70. # Function to extract day, month or year from the date.
  71. # Reference: http://publib.boulder.ibm.com/infocenter/db2luw/v9r5/topic/com.ibm.db2.luw.sql.ref.doc/doc/r0023457.html
  72. def date_extract_sql( self, lookup_type, field_name ):
  73. if upper( lookup_type ) == 'WEEK_DAY':
  74. return " DAYOFWEEK(%s) " % ( field_name )
  75. else:
  76. return " %s(%s) " % ( upper( lookup_type ), field_name )
  77. # Rounding of date on the basic of looktype.
  78. # e.g If input is 2008-12-04 and month then output will be 2008-12-01 00:00:00
  79. # Reference: http://www.ibm.com/developerworks/data/library/samples/db2/0205udfs/index.html
  80. def date_trunc_sql( self, lookup_type, field_name ):
  81. sql = "TIMESTAMP(DATE(SUBSTR(CHAR(%s), 1, %d) || '%s'), TIME('00:00:00'))"
  82. if upper( lookup_type ) == 'DAY':
  83. sql = sql % ( field_name, 10, '' )
  84. elif upper( lookup_type ) == 'MONTH':
  85. sql = sql % ( field_name, 7, '-01' )
  86. elif upper( lookup_type ) == 'YEAR':
  87. sql = sql % ( field_name, 4, '-01-01' )
  88. return sql
  89. # Function to Implements the date interval functionality for expressions
  90. def date_interval_sql( self, sql, connector, timedelta ):
  91. date_interval_token = []
  92. date_interval_token.append( sql )
  93. date_interval_token.append( str( timedelta.days ) + " DAYS" )
  94. if timedelta.seconds > 0:
  95. date_interval_token.append( str( timedelta.seconds ) + " SECONDS" )
  96. if timedelta.microseconds > 0:
  97. date_interval_token.append( str( timedelta.microseconds ) + " MICROSECONDS" )
  98. sql = "( %s )" % connector.join( date_interval_token )
  99. return sql
  100. #As casting is not required, so nothing is required to do in this function.
  101. def datetime_cast_sql( self ):
  102. return "%s"
  103. # Function to return SQL from dropping foreign key.
  104. def drop_foreignkey_sql( self ):
  105. return "DROP FOREIGN KEY"
  106. # Dropping auto generated property of the identity column.
  107. def drop_sequence_sql( self, table ):
  108. return "ALTER TABLE %s ALTER COLUMN ID DROP IDENTITY" % ( self.quote_name( table ) )
  109. #This function casts the field and returns it for use in the where clause
  110. def field_cast_sql( self, db_type ):
  111. if db_type == 'CLOB':
  112. return "VARCHAR(%s, 4096)"
  113. else:
  114. return " %s"
  115. def fulltext_search_sql( self, field_name ):
  116. sql = "WHERE %s = ?" % field_name
  117. return sql
  118. # Function to return value of auto-generated field of last executed insert query.
  119. def last_insert_id( self, cursor, table_name, pk_name ):
  120. if not _IS_JYTHON:
  121. return cursor.last_identity_val
  122. else:
  123. operation = 'SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1'
  124. cursor.execute( operation )
  125. row = cursor.fetchone()
  126. last_identity_val = None
  127. if row is not None:
  128. last_identity_val = int( row[0] )
  129. return last_identity_val
  130. # In case of WHERE clause, if the search is required to be case insensitive then converting
  131. # left hand side field to upper.
  132. def lookup_cast( self, lookup_type ):
  133. if lookup_type in ( 'iexact', 'icontains', 'istartswith', 'iendswith' ):
  134. return "UPPER(%s)"
  135. return "%s"
  136. # As DB2 v91 specifications,
  137. # Maximum length of a table name and Maximum length of a column name is 128
  138. # http://publib.boulder.ibm.com/infocenter/db2e/v9r1/index.jsp?topic=/com.ibm.db2e.doc/db2elimits.html
  139. def max_name_length( self ):
  140. return 128
  141. # As DB2 v97 specifications,
  142. # Maximum length of a database name is 8
  143. #http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.sql.ref.doc/doc/r0001029.html
  144. def max_db_name_length( self ):
  145. return 8
  146. def no_limit_value( self ):
  147. return None
  148. # Method to point custom query class implementation.
  149. def query_class( self, DefaultQueryClass ):
  150. return query.query_class( DefaultQueryClass )
  151. # Function to quote the name of schema, table and column.
  152. def quote_name( self, name ):
  153. name = upper( name )
  154. if( name.startswith( "\"" ) & name.endswith( "\"" ) ):
  155. return name
  156. if( name.startswith( "\"" ) ):
  157. return "%s\"" % name
  158. if( name.endswith( "\"" ) ):
  159. return "\"%s" % name
  160. return "\"%s\"" % name
  161. # SQL to return RANDOM number.
  162. # Reference: http://publib.boulder.ibm.com/infocenter/db2luw/v8/topic/com.ibm.db2.udb.doc/admin/r0000840.htm
  163. def random_function_sql( self ):
  164. return "SYSFUN.RAND()"
  165. # As save-point is supported by DB2, following function will return SQL to create savepoint.
  166. def savepoint_create_sql( self, sid ):
  167. return "SAVEPOINT %s ON ROLLBACK RETAIN CURSORS" % sid
  168. # Function to commit savepoint.
  169. def savepoint_commit_sql( self, sid ):
  170. return "RELEASE TO SAVEPOINT %s" % sid
  171. # Function to rollback savepoint.
  172. def savepoint_rollback_sql( self, sid ):
  173. return "ROLLBACK TO SAVEPOINT %s" % sid
  174. # Deleting all the rows from the list of tables provided and resetting all the
  175. # sequences.
  176. def sql_flush( self, style, tables, sequences ):
  177. curr_schema = self.connection.connection.get_current_schema().upper()
  178. sqls = []
  179. if tables:
  180. sqls.append( '''CREATE PROCEDURE FKEY_ALT_CONST(django_tabname VARCHAR(128), curr_schema VARCHAR(128))
  181. LANGUAGE SQL
  182. P1: BEGIN
  183. DECLARE fktable varchar(128);
  184. DECLARE fkconst varchar(128);
  185. DECLARE row_count integer;
  186. DECLARE alter_fkey_sql varchar(350);
  187. DECLARE cur1 CURSOR for SELECT tabname, constname FROM syscat.tabconst WHERE type = 'F' and tabschema = curr_schema and ENFORCED = 'N';
  188. DECLARE cur2 CURSOR for SELECT tabname, constname FROM syscat.tabconst WHERE type = 'F' and tabname = django_tabname and tabschema = curr_schema and ENFORCED = 'Y';
  189. IF ( django_tabname = '' ) THEN
  190. SET row_count = 0;
  191. SELECT count( * ) INTO row_count FROM syscat.tabconst WHERE type = 'F' and tabschema = curr_schema and ENFORCED = 'N';
  192. IF ( row_count > 0 ) THEN
  193. OPEN cur1;
  194. WHILE( row_count > 0 ) DO
  195. FETCH cur1 INTO fktable, fkconst;
  196. IF ( LOCATE( ' ', fktable ) > 0 ) THEN
  197. SET alter_fkey_sql = 'ALTER TABLE ' || '\"' || fktable || '\"' ||' ALTER FOREIGN KEY ';
  198. ELSE
  199. SET alter_fkey_sql = 'ALTER TABLE ' || fktable || ' ALTER FOREIGN KEY ';
  200. END IF;
  201. IF ( LOCATE( ' ', fkconst ) > 0) THEN
  202. SET alter_fkey_sql = alter_fkey_sql || '\"' || fkconst || '\"' || ' ENFORCED';
  203. ELSE
  204. SET alter_fkey_sql = alter_fkey_sql || fkconst || ' ENFORCED';
  205. END IF;
  206. execute immediate alter_fkey_sql;
  207. SET row_count = row_count - 1;
  208. END WHILE;
  209. CLOSE cur1;
  210. END IF;
  211. ELSE
  212. SET row_count = 0;
  213. SELECT count( * ) INTO row_count FROM syscat.tabconst WHERE type = 'F' and tabname = django_tabname and tabschema = curr_schema and ENFORCED = 'Y';
  214. IF ( row_count > 0 ) THEN
  215. OPEN cur2;
  216. WHILE( row_count > 0 ) DO
  217. FETCH cur2 INTO fktable, fkconst;
  218. IF ( LOCATE( ' ', fktable ) > 0 ) THEN
  219. SET alter_fkey_sql = 'ALTER TABLE ' || '\"' || fktable || '\"' ||' ALTER FOREIGN KEY ';
  220. ELSE
  221. SET alter_fkey_sql = 'ALTER TABLE ' || fktable || ' ALTER FOREIGN KEY ';
  222. END IF;
  223. IF ( LOCATE( ' ', fkconst ) > 0) THEN
  224. SET alter_fkey_sql = alter_fkey_sql || '\"' || fkconst || '\"' || ' NOT ENFORCED';
  225. ELSE
  226. SET alter_fkey_sql = alter_fkey_sql || fkconst || ' NOT ENFORCED';
  227. END IF;
  228. execute immediate alter_fkey_sql;
  229. SET row_count = row_count - 1;
  230. END WHILE;
  231. CLOSE cur2;
  232. END IF;
  233. END IF;
  234. END P1''' )
  235. for table in tables:
  236. sqls.append( "CALL FKEY_ALT_CONST( '%s', '%s' );" % ( table.upper(), curr_schema ) )
  237. for table in tables:
  238. sqls.append( style.SQL_KEYWORD( "DELETE" ) + " " +
  239. style.SQL_KEYWORD( "FROM" ) + " " +
  240. style.SQL_TABLE( "%s" % self.quote_name( table ) ) )
  241. sqls.append( "CALL FKEY_ALT_CONST( '' , '%s' );" % ( curr_schema, ) )
  242. sqls.append( "DROP PROCEDURE FKEY_ALT_CONST;" )
  243. for sequence in sequences:
  244. if( sequence['column'] != None ):
  245. sqls.append( style.SQL_KEYWORD( "ALTER TABLE" ) + " " +
  246. style.SQL_TABLE( "%s" % self.quote_name( sequence['table'] ) ) +
  247. " " + style.SQL_KEYWORD( "ALTER COLUMN" ) + " %s "
  248. % self.quote_name( sequence['column'] ) +
  249. style.SQL_KEYWORD( "RESTART WITH 1" ) )
  250. return sqls
  251. else:
  252. return []
  253. # Table many contains rows when this is get called, hence resetting sequence
  254. # to a large value (10000).
  255. def sequence_reset_sql( self, style, model_list ):
  256. from django.db import models
  257. cursor = self.connection.cursor()
  258. sqls = []
  259. for model in model_list:
  260. table = model._meta.db_table
  261. for field in model._meta.local_fields:
  262. if isinstance( field, models.AutoField ):
  263. max_sql = "SELECT MAX(%s) FROM %s" % ( self.quote_name( field.column ), self.quote_name( table ) )
  264. cursor.execute( max_sql )
  265. max_id = [row[0] for row in cursor.fetchall()]
  266. if max_id[0] == None:
  267. max_id[0] = 0
  268. sqls.append( style.SQL_KEYWORD( "ALTER TABLE" ) + " " +
  269. style.SQL_TABLE( "%s" % self.quote_name( table ) ) +
  270. " " + style.SQL_KEYWORD( "ALTER COLUMN" ) + " %s "
  271. % self.quote_name( field.column ) +
  272. style.SQL_KEYWORD( "RESTART WITH %s" % ( max_id[0] + 1 ) ) )
  273. break
  274. for field in model._meta.many_to_many:
  275. m2m_table = field.m2m_db_table()
  276. if not field.rel.through:
  277. max_sql = "SELECT MAX(%s) FROM %s" % ( self.quote_name( 'ID' ), self.quote_name( table ) )
  278. cursor.execute( max_sql )
  279. max_id = [row[0] for row in cursor.fetchall()]
  280. if max_id[0] == None:
  281. max_id[0] = 0
  282. sqls.append( style.SQL_KEYWORD( "ALTER TABLE" ) + " " +
  283. style.SQL_TABLE( "%s" % self.quote_name( m2m_table ) ) +
  284. " " + style.SQL_KEYWORD( "ALTER COLUMN" ) + " %s "
  285. % self.quote_name( 'ID' ) +
  286. style.SQL_KEYWORD( "RESTART WITH %s" % ( max_id[0] + 1 ) ) )
  287. if cursor:
  288. cursor.close()
  289. return sqls
  290. def tablespace_sql( self, tablespace, inline = False ):
  291. # inline is used for column indexes defined in-line with column definition, like:
  292. # CREATE TABLE "TABLE1" ("ID_OTHER" VARCHAR(20) NOT NULL UNIQUE) IN "TABLESPACE1";
  293. # couldn't find support for this in create table
  294. # (http://publib.boulder.ibm.com/infocenter/db2luw/v9/topic/com.ibm.db2.udb.admin.doc/doc/r0000927.htm)
  295. if inline:
  296. sql = ""
  297. else:
  298. sql = "IN %s" % self.quote_name( tablespace )
  299. return sql
  300. def value_to_db_datetime( self, value ):
  301. if value is None:
  302. return None
  303. if( djangoVersion[0:2] <= ( 1, 3 ) ):
  304. #DB2 doesn't support time zone aware datetime
  305. if ( value.tzinfo is not None ):
  306. raise ValueError( "Timezone aware datetime not supported" )
  307. else:
  308. return value
  309. else:
  310. if is_aware(value):
  311. if settings.USE_TZ:
  312. value = value.astimezone( utc ).replace( tzinfo=None )
  313. else:
  314. raise ValueError( "Timezone aware datetime not supported" )
  315. return unicode( value )
  316. def value_to_db_time( self, value ):
  317. if value is None:
  318. return None
  319. if( djangoVersion[0:2] <= ( 1, 3 ) ):
  320. #DB2 doesn't support time zone aware time
  321. if ( value.tzinfo is not None ):
  322. raise ValueError( "Timezone aware time not supported" )
  323. else:
  324. return value
  325. else:
  326. if is_aware(value):
  327. raise ValueError( "Timezone aware time not supported" )
  328. else:
  329. return value
  330. def year_lookup_bounds_for_date_field( self, value ):
  331. lower_bound = "%s-01-01"
  332. upper_bound = "%s-12-31"
  333. return [lower_bound % value, upper_bound % value]
  334. def bulk_insert_sql(self, fields, num_values):
  335. values_sql = "( %s )" %(", ".join( ["%s"] * len(fields)))
  336. bulk_values_sql = "VALUES " + ", ".join([values_sql] * num_values )
  337. return bulk_values_sql
  338. def for_update_sql(self, nowait=False):
  339. #DB2 doesn't support nowait select for update
  340. if nowait:
  341. return ValueError( "Nowait Select for update not supported " )
  342. else:
  343. return 'WITH RS USE AND KEEP UPDATE LOCKS'
  344. def distinct_sql(self, fields):
  345. if fields:
  346. return ValueError( "distinct_on_fields not supported" )
  347. else:
  348. return 'DISTINCT'