PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/IBM_DB/ibm_db_django/ibm_db_django/creation.py

http://ibm-db.googlecode.com/
Python | 343 lines | 315 code | 7 blank | 21 comment | 7 complexity | 3daeafad9494b29e3873ff232ff73cc1 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. import sys
  19. _IS_JYTHON = sys.platform.startswith( 'java' )
  20. # Importing IBM_DB wrapper ibm_db_dbi, if not running on jython
  21. if not _IS_JYTHON:
  22. try:
  23. import ibm_db_dbi as Database
  24. except ImportError, e:
  25. raise ImportError( "ibm_db module not found. Install ibm_db module from http://code.google.com/p/ibm-db/. Error: %s" % e )
  26. from django.db.backends.creation import BaseDatabaseCreation
  27. from django.conf import settings
  28. from django.core.management import call_command
  29. from django import VERSION as djangoVersion
  30. from django.db.backends.util import truncate_name
  31. TEST_DBNAME_PREFIX = 'test_'
  32. class DatabaseCreation ( BaseDatabaseCreation ):
  33. psudo_column_prefix = 'psudo_'
  34. data_types = {
  35. 'AutoField': 'INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1, CACHE 10 ORDER)', # DB2 Specific
  36. 'BooleanField': 'SMALLINT CHECK (%(attname)s IN (0,1))',
  37. 'CharField': 'VARCHAR(%(max_length)s)',
  38. 'CommaSeparatedIntegerField': 'VARCHAR(%(max_length)s)',
  39. 'DateField': 'DATE',
  40. 'DateTimeField': 'TIMESTAMP',
  41. 'DecimalField': 'DECIMAL(%(max_digits)s, %(decimal_places)s)',
  42. 'FileField': 'VARCHAR(%(max_length)s)',
  43. 'FilePathField': 'VARCHAR(%(max_length)s)',
  44. 'FloatField': 'DOUBLE',
  45. 'ImageField': 'VARCHAR(%(max_length)s)',
  46. 'IntegerField': 'INTEGER',
  47. 'BigIntegerField': 'BIGINT',
  48. 'IPAddressField': 'VARCHAR(15)',
  49. 'GenericIPAddressField': 'VARCHAR(39)',
  50. 'ManyToManyField': 'VARCHAR(%(max_length)s)',
  51. 'NullBooleanField': 'SMALLINT CHECK (%(attname)s IN (0,1) OR (%(attname)s IS NULL))',
  52. 'OneToOneField': 'VARCHAR(%(max_length)s)',
  53. 'PhoneNumberField': 'VARCHAR(16)',
  54. 'PositiveIntegerField': 'INTEGER CHECK (%(attname)s >= 0)',
  55. 'PositiveSmallIntegerField': 'SMALLINT CHECK (%(attname)s >= 0)',
  56. 'SlugField': 'VARCHAR(%(max_length)s)',
  57. 'SmallIntegerField': 'SMALLINT',
  58. 'TextField': 'CLOB',
  59. 'TimeField': 'TIME',
  60. 'USStateField': 'VARCHAR(2)',
  61. 'URLField': 'VARCHAR2(%(max_length)s)',
  62. 'XMLField': 'XML',
  63. }
  64. def sql_indexes_for_field( self, model, f, style ):
  65. """Return the CREATE INDEX SQL statements for a single model field"""
  66. output = []
  67. qn = self.connection.ops.quote_name
  68. max_name_length = self.connection.ops.max_name_length
  69. # ignore tablespace information
  70. tablespace_sql = ''
  71. i = 0
  72. if len( model._meta.unique_together_index ) != 0:
  73. for unique_together_index in model._meta.unique_together_index:
  74. i = i + 1
  75. column_list = []
  76. for column in unique_together_index:
  77. for local_field in model._meta.local_fields:
  78. if column == local_field.name:
  79. column_list.extend( [ local_field.column ] )
  80. self.__add_psudokey_column( style, self.connection.cursor(), model._meta.db_table, model._meta.pk.attname, column_list )
  81. column_list.extend( [ truncate_name( "%s%s" % ( self.psudo_column_prefix, "_".join( column_list ) ), max_name_length ) ] )
  82. output.extend( [style.SQL_KEYWORD( 'CREATE UNIQUE INDEX' ) + ' ' + \
  83. style.SQL_TABLE( qn( 'db2_%s_%s' % ( model._meta.db_table, i ) ) ) + ' ' + \
  84. style.SQL_KEYWORD( 'ON' ) + ' ' + \
  85. style.SQL_TABLE( qn( model._meta.db_table ) ) + ' ' + \
  86. '( %s )' % ", ".join( column_list ) + ' ' + \
  87. '%s;' % tablespace_sql] )
  88. model._meta.unique_together_index = []
  89. if f.unique_index:
  90. column_list = []
  91. column_list.extend( [f.column] )
  92. self.__add_psudokey_column( style, self.connection.cursor(), model._meta.db_table, model._meta.pk.attname, column_list )
  93. cisql = 'CREATE UNIQUE INDEX'
  94. output.extend( [style.SQL_KEYWORD( cisql ) + ' ' +
  95. style.SQL_TABLE( qn( '%s_%s' % ( model._meta.db_table, f.column ) ) ) + ' ' +
  96. style.SQL_KEYWORD( 'ON' ) + ' ' +
  97. style.SQL_TABLE( qn( model._meta.db_table ) ) + ' ' +
  98. "(%s, %s )" % ( style.SQL_FIELD( qn( f.column ) ), style.SQL_FIELD( qn( truncate_name( ( self.psudo_column_prefix + f.column ), max_name_length ) ) ) ) +
  99. "%s;" % tablespace_sql] )
  100. elif f.db_index and not f.unique:
  101. cisql = 'CREATE INDEX'
  102. output.extend( [style.SQL_KEYWORD( cisql ) + ' ' +
  103. style.SQL_TABLE( qn( '%s_%s' % ( model._meta.db_table, f.column ) ) ) + ' ' +
  104. style.SQL_KEYWORD( 'ON' ) + ' ' +
  105. style.SQL_TABLE( qn( model._meta.db_table ) ) + ' ' +
  106. "(%s)" % style.SQL_FIELD( qn( f.column ) ) +
  107. "%s;" % tablespace_sql] )
  108. return output
  109. # Method to create and return test database, before creating test database it takes confirmation from user.
  110. # If test database already exists then it takes confirmation from user to recreate that database .
  111. # If create test database not supported in current scenario then it takes confirmation from user to use settings file's database name as test database
  112. # For Jython this method prepare the settings file's database. First it drops the tables from the database,then create tables on the basis of installed models.
  113. def create_test_db( self, verbosity = 0, autoclobber = False ):
  114. kwargs = self.__create_test_kwargs()
  115. if not _IS_JYTHON:
  116. old_database = kwargs['database']
  117. max_db_name_length = self.connection.ops.max_db_name_length()
  118. kwargs['database'] = truncate_name( "%s%s" % ( TEST_DBNAME_PREFIX, old_database ), max_db_name_length )
  119. kwargsKeys = kwargs.keys()
  120. if ( kwargsKeys.__contains__( 'port' ) and
  121. kwargsKeys.__contains__( 'host' ) ):
  122. kwargs['dsn'] = "DATABASE=%s;HOSTNAME=%s;PORT=%s;PROTOCOL=TCPIP;" % (
  123. kwargs.get( 'dbname' ),
  124. kwargs.get( 'host' ),
  125. kwargs.get( 'port' )
  126. )
  127. else:
  128. kwargs['dsn'] = ''
  129. if kwargsKeys.__contains__( 'port' ):
  130. del kwargs['port']
  131. if not autoclobber:
  132. confirm = raw_input( "Wants to create %s as test database. Type yes to create it else type no" % ( kwargs.get( 'database' ) ) )
  133. if autoclobber or confirm == 'yes':
  134. try:
  135. if verbosity > 1:
  136. print "Creating Test Database %s" % ( kwargs.get( 'database' ) )
  137. Database.createdb( **kwargs )
  138. except Exception, inst:
  139. message = repr( inst )
  140. if ( message.find( 'Not supported:' ) != -1 ):
  141. if not autoclobber:
  142. confirm = raw_input( "Not able to create test database, %s. Type yes to use %s as test database, or no to exit" % ( message.split( ":" )[1], old_database ) )
  143. if autoclobber or confirm == 'yes':
  144. kwargs['database'] = old_database
  145. self.__clean_up( self.connection.cursor() )
  146. self.connection._commit()
  147. self.connection.close()
  148. else:
  149. print "Tests cancelled"
  150. sys.exit( 1 )
  151. else:
  152. sys.stderr.write( "Error occurred during creation of test database: %s" % ( message ) )
  153. index = message.find( 'SQLCODE' )
  154. if( message != '' ) & ( index != -1 ):
  155. error_code = message[( index + 8 ): ( index + 13 )]
  156. if( error_code != '-1005' ):
  157. print "Tests cancelled"
  158. sys.exit( 1 )
  159. else:
  160. if not autoclobber:
  161. confirm = raw_input( "\nTest database: %s already exist. Type yes to recreate it, or no to exit" % ( kwargs.get( 'database' ) ) )
  162. if autoclobber or confirm == 'yes':
  163. if verbosity > 1:
  164. print "Recreating Test Database %s" % ( kwargs.get( 'database' ) )
  165. Database.recreatedb( **kwargs )
  166. else:
  167. print "Tests cancelled."
  168. sys.exit( 1 )
  169. else:
  170. confirm = raw_input( "Wants to use %s as test database, Type yes to use it as test database or no to exit" % ( old_database ) )
  171. if confirm == 'yes':
  172. kwargs['database'] = old_database
  173. self.__clean_up( self.connection.cursor() )
  174. self.connection._commit()
  175. self.connection.close()
  176. else:
  177. sys.exit( 1 )
  178. else:
  179. self.__clean_up( self.connection.cursor() )
  180. self.connection._commit()
  181. self.connection.close()
  182. test_database = kwargs.get( 'database' )
  183. if verbosity > 1:
  184. print "Preparing Database..."
  185. if( djangoVersion[0:2] <= ( 1, 1 ) ):
  186. settings.DATABASE_NAME = test_database
  187. settings.__setattr__( 'PCONNECT', False )
  188. call_command( 'syncdb', verbosity = verbosity, interactive = False )
  189. else:
  190. self.connection.settings_dict['NAME'] = test_database
  191. self.connection.settings_dict['PCONNECT'] = False
  192. # Confirm the feature set of the test database
  193. if( djangoVersion[0:2] > ( 1, 2 ) ):
  194. self.connection.features.confirm()
  195. call_command( 'syncdb', database = self.connection.alias, verbosity = verbosity, interactive = False, load_initial_data = False )
  196. # We need to then do a flush to ensure that any data installed by custom SQL has been removed.
  197. call_command('flush', database=self.connection.alias, verbosity = verbosity, interactive=False)
  198. return test_database
  199. # Method to destroy database. For Jython nothing is getting done over here.
  200. def destroy_test_db( self, old_database_name, verbosity = 0 ):
  201. print "Destroying Database..."
  202. if not _IS_JYTHON:
  203. kwargs = self.__create_test_kwargs()
  204. if( old_database_name != kwargs.get( 'database' ) ):
  205. kwargsKeys = kwargs.keys()
  206. if ( kwargsKeys.__contains__( 'port' ) and
  207. kwargsKeys.__contains__( 'host' ) ):
  208. kwargs['dsn'] = "DATABASE=%s;HOSTNAME=%s;PORT=%s;PROTOCOL=TCPIP;" % (
  209. kwargs.get( 'database' ),
  210. kwargs.get( 'host' ),
  211. kwargs.get( 'port' )
  212. )
  213. else:
  214. kwargs['dsn'] = ''
  215. if kwargsKeys.__contains__( 'port' ):
  216. del kwargs['port']
  217. if verbosity > 1:
  218. print "Droping Test Database %s" % ( kwargs.get( 'database' ) )
  219. Database.dropdb( **kwargs )
  220. if( djangoVersion[0:2] <= ( 1, 1 ) ):
  221. settings.DATABASE_NAME = old_database_name
  222. settings.PCONNECT = True
  223. else:
  224. self.connection.settings_dict['NAME'] = old_database_name
  225. self.connection.settings_dict['PCONNECT'] = True
  226. return old_database_name
  227. # As DB2 does not allow to insert NULL value in UNIQUE col, hence modifing model.
  228. def sql_create_model( self, model, style, known_models = set() ):
  229. model._meta.unique_together_index = []
  230. temp_changed_uvalues = []
  231. temp_unique_together = model._meta.unique_together
  232. for i in range( len( model._meta.local_fields ) ):
  233. model._meta.local_fields[i].unique_index = False
  234. if model._meta.local_fields[i]._unique and model._meta.local_fields[i].null:
  235. model._meta.local_fields[i].unique_index = True
  236. model._meta.local_fields[i]._unique = False
  237. temp_changed_uvalues.append( i )
  238. if len( model._meta.unique_together ) != 0:
  239. for unique_together in model._meta.unique_together:
  240. if model._meta.local_fields[i].name in unique_together:
  241. if model._meta.local_fields[i].null:
  242. unique_list = list( model._meta.unique_together )
  243. unique_list.remove( unique_together )
  244. model._meta.unique_together = tuple( unique_list )
  245. model._meta.unique_together_index.append( unique_together )
  246. sql, references = super( DatabaseCreation, self ).sql_create_model( model, style, known_models )
  247. for i in temp_changed_uvalues:
  248. model._meta.local_fields[i]._unique = True
  249. model._meta.unique_together = temp_unique_together
  250. return sql, references
  251. # Private method to clean up database.
  252. def __clean_up( self, cursor ):
  253. tables = self.connection.introspection.django_table_names( only_existing = True )
  254. for table in tables:
  255. sql = "DROP TABLE %s" % self.connection.ops.quote_name( table )
  256. cursor.execute( sql )
  257. cursor.close()
  258. # Private method to alter a table with adding psudokey column
  259. def __add_psudokey_column( self, style, cursor, table_name, pk_name, column_list ):
  260. qn = self.connection.ops.quote_name
  261. max_name_length = self.connection.ops.max_name_length()
  262. sql = style.SQL_KEYWORD( 'ALTER TABLE ' ) + \
  263. style.SQL_TABLE( qn( table_name ) ) + \
  264. style.SQL_KEYWORD( ' ADD COLUMN ' ) + \
  265. style.SQL_FIELD( qn( truncate_name( "%s%s" % ( self.psudo_column_prefix, "_".join( column_list ) ), max_name_length ) ) ) + \
  266. style.SQL_KEYWORD( ' GENERATED ALWAYS AS( CASE WHEN ' ) + \
  267. style.SQL_FIELD( "%s %s" % ( " IS NULL OR ".join( column_list ), 'IS NULL THEN ' ) ) + \
  268. style.SQL_FIELD( qn( pk_name ) ) + \
  269. style.SQL_KEYWORD( ' END ) ;' )
  270. cursor.execute( 'SET INTEGRITY FOR ' + style.SQL_TABLE( qn( table_name ) ) + ' OFF CASCADE DEFERRED;' )
  271. cursor.execute( sql )
  272. cursor.execute( 'SET INTEGRITY FOR ' + style.SQL_TABLE( table_name ) + ' IMMEDIATE CHECKED;' )
  273. cursor.close()
  274. #private method to create dictionary of login credentials for test database
  275. def __create_test_kwargs( self ):
  276. if( djangoVersion[0:2] <= ( 1, 1 ) ):
  277. if( isinstance( settings.TEST_DATABASE_NAME, basestring ) and
  278. ( settings.TEST_DATABASE_NAME != '' ) ):
  279. database = settings.TEST_DATABASE_NAME
  280. else:
  281. database = settings.DATABASE_NAME
  282. database_user = settings.DATABASE_USER
  283. database_pass = settings.DATABASE_PASSWORD
  284. database_host = settings.DATABASE_HOST
  285. database_port = settings.DATABASE_PORT
  286. settings.DATABASE_SUPPORTS_TRANSACTIONS = True
  287. else:
  288. if( isinstance( self.connection.settings_dict['NAME'], basestring ) and
  289. ( self.connection.settings_dict['NAME'] != '' ) ):
  290. database = self.connection.settings_dict['NAME']
  291. else:
  292. from django.core.exceptions import ImproperlyConfigured
  293. raise ImproperlyConfigured( "the database Name doesn't exist" )
  294. database_user = self.connection.settings_dict['USER']
  295. database_pass = self.connection.settings_dict['PASSWORD']
  296. database_host = self.connection.settings_dict['HOST']
  297. database_port = self.connection.settings_dict['PORT']
  298. self.connection.settings_dict['SUPPORTS_TRANSACTIONS'] = True
  299. kwargs = { }
  300. kwargs['database'] = database
  301. if isinstance( database_user, basestring ):
  302. kwargs['user'] = database_user
  303. if isinstance( database_pass, basestring ):
  304. kwargs['password'] = database_pass
  305. if isinstance( database_host, basestring ):
  306. kwargs['host'] = database_host
  307. if isinstance( database_port, basestring ):
  308. kwargs['port'] = database_port
  309. if isinstance( database_host, basestring ):
  310. kwargs['host'] = database_host
  311. return kwargs