creation.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. import hashlib
  2. import sys
  3. import time
  4. from django.conf import settings
  5. from django.db.utils import load_backend
  6. from django.utils.encoding import force_bytes
  7. from django.utils.functional import cached_property
  8. from django.utils.six.moves import input
  9. from .utils import truncate_name
  10. # The prefix to put on the default database name when creating
  11. # the test database.
  12. TEST_DATABASE_PREFIX = 'test_'
  13. NO_DB_ALIAS = '__no_db__'
  14. class BaseDatabaseCreation(object):
  15. """
  16. This class encapsulates all backend-specific differences that pertain to
  17. database *creation*, such as the column types to use for particular Django
  18. Fields, the SQL used to create and destroy tables, and the creation and
  19. destruction of test databases.
  20. """
  21. data_types = {}
  22. data_types_suffix = {}
  23. data_type_check_constraints = {}
  24. def __init__(self, connection):
  25. self.connection = connection
  26. @cached_property
  27. def _nodb_connection(self):
  28. """
  29. Alternative connection to be used when there is no need to access
  30. the main database, specifically for test db creation/deletion.
  31. This also prevents the production database from being exposed to
  32. potential child threads while (or after) the test database is destroyed.
  33. Refs #10868, #17786, #16969.
  34. """
  35. settings_dict = self.connection.settings_dict.copy()
  36. settings_dict['NAME'] = None
  37. backend = load_backend(settings_dict['ENGINE'])
  38. nodb_connection = backend.DatabaseWrapper(
  39. settings_dict,
  40. alias=NO_DB_ALIAS,
  41. allow_thread_sharing=False)
  42. return nodb_connection
  43. @classmethod
  44. def _digest(cls, *args):
  45. """
  46. Generates a 32-bit digest of a set of arguments that can be used to
  47. shorten identifying names.
  48. """
  49. h = hashlib.md5()
  50. for arg in args:
  51. h.update(force_bytes(arg))
  52. return h.hexdigest()[:8]
  53. def sql_create_model(self, model, style, known_models=set()):
  54. """
  55. Returns the SQL required to create a single model, as a tuple of:
  56. (list_of_sql, pending_references_dict)
  57. """
  58. opts = model._meta
  59. if not opts.managed or opts.proxy or opts.swapped:
  60. return [], {}
  61. final_output = []
  62. table_output = []
  63. pending_references = {}
  64. qn = self.connection.ops.quote_name
  65. for f in opts.local_fields:
  66. col_type = f.db_type(connection=self.connection)
  67. col_type_suffix = f.db_type_suffix(connection=self.connection)
  68. tablespace = f.db_tablespace or opts.db_tablespace
  69. if col_type is None:
  70. # Skip ManyToManyFields, because they're not represented as
  71. # database columns in this table.
  72. continue
  73. # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
  74. field_output = [style.SQL_FIELD(qn(f.column)),
  75. style.SQL_COLTYPE(col_type)]
  76. # Oracle treats the empty string ('') as null, so coerce the null
  77. # option whenever '' is a possible value.
  78. null = f.null
  79. if (f.empty_strings_allowed and not f.primary_key and
  80. self.connection.features.interprets_empty_strings_as_nulls):
  81. null = True
  82. if not null:
  83. field_output.append(style.SQL_KEYWORD('NOT NULL'))
  84. if f.primary_key:
  85. field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
  86. elif f.unique:
  87. field_output.append(style.SQL_KEYWORD('UNIQUE'))
  88. if tablespace and f.unique:
  89. # We must specify the index tablespace inline, because we
  90. # won't be generating a CREATE INDEX statement for this field.
  91. tablespace_sql = self.connection.ops.tablespace_sql(
  92. tablespace, inline=True)
  93. if tablespace_sql:
  94. field_output.append(tablespace_sql)
  95. if f.rel and f.db_constraint:
  96. ref_output, pending = self.sql_for_inline_foreign_key_references(
  97. model, f, known_models, style)
  98. if pending:
  99. pending_references.setdefault(f.rel.to, []).append(
  100. (model, f))
  101. else:
  102. field_output.extend(ref_output)
  103. if col_type_suffix:
  104. field_output.append(style.SQL_KEYWORD(col_type_suffix))
  105. table_output.append(' '.join(field_output))
  106. for field_constraints in opts.unique_together:
  107. table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' %
  108. ", ".join(
  109. [style.SQL_FIELD(qn(opts.get_field(f).column))
  110. for f in field_constraints]))
  111. full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' +
  112. style.SQL_TABLE(qn(opts.db_table)) + ' (']
  113. for i, line in enumerate(table_output): # Combine and add commas.
  114. full_statement.append(
  115. ' %s%s' % (line, ',' if i < len(table_output) - 1 else ''))
  116. full_statement.append(')')
  117. if opts.db_tablespace:
  118. tablespace_sql = self.connection.ops.tablespace_sql(
  119. opts.db_tablespace)
  120. if tablespace_sql:
  121. full_statement.append(tablespace_sql)
  122. full_statement.append(';')
  123. final_output.append('\n'.join(full_statement))
  124. if opts.has_auto_field:
  125. # Add any extra SQL needed to support auto-incrementing primary
  126. # keys.
  127. auto_column = opts.auto_field.db_column or opts.auto_field.name
  128. autoinc_sql = self.connection.ops.autoinc_sql(opts.db_table,
  129. auto_column)
  130. if autoinc_sql:
  131. for stmt in autoinc_sql:
  132. final_output.append(stmt)
  133. return final_output, pending_references
  134. def sql_for_inline_foreign_key_references(self, model, field, known_models, style):
  135. """
  136. Return the SQL snippet defining the foreign key reference for a field.
  137. """
  138. qn = self.connection.ops.quote_name
  139. rel_to = field.rel.to
  140. if rel_to in known_models or rel_to == model:
  141. output = [style.SQL_KEYWORD('REFERENCES') + ' ' +
  142. style.SQL_TABLE(qn(rel_to._meta.db_table)) + ' (' +
  143. style.SQL_FIELD(qn(rel_to._meta.get_field(
  144. field.rel.field_name).column)) + ')' +
  145. self.connection.ops.deferrable_sql()
  146. ]
  147. pending = False
  148. else:
  149. # We haven't yet created the table to which this field
  150. # is related, so save it for later.
  151. output = []
  152. pending = True
  153. return output, pending
  154. def sql_for_pending_references(self, model, style, pending_references):
  155. """
  156. Returns any ALTER TABLE statements to add constraints after the fact.
  157. """
  158. opts = model._meta
  159. if not opts.managed or opts.swapped:
  160. return []
  161. qn = self.connection.ops.quote_name
  162. final_output = []
  163. if model in pending_references:
  164. for rel_class, f in pending_references[model]:
  165. rel_opts = rel_class._meta
  166. r_table = rel_opts.db_table
  167. r_col = f.column
  168. table = opts.db_table
  169. col = opts.get_field(f.rel.field_name).column
  170. # For MySQL, r_name must be unique in the first 64 characters.
  171. # So we are careful with character usage here.
  172. r_name = '%s_refs_%s_%s' % (
  173. r_col, col, self._digest(r_table, table))
  174. final_output.append(style.SQL_KEYWORD('ALTER TABLE') +
  175. ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
  176. (qn(r_table), qn(truncate_name(
  177. r_name, self.connection.ops.max_name_length())),
  178. qn(r_col), qn(table), qn(col),
  179. self.connection.ops.deferrable_sql()))
  180. del pending_references[model]
  181. return final_output
  182. def sql_indexes_for_model(self, model, style):
  183. """
  184. Returns the CREATE INDEX SQL statements for a single model.
  185. """
  186. if not model._meta.managed or model._meta.proxy or model._meta.swapped:
  187. return []
  188. output = []
  189. for f in model._meta.local_fields:
  190. output.extend(self.sql_indexes_for_field(model, f, style))
  191. for fs in model._meta.index_together:
  192. fields = [model._meta.get_field_by_name(f)[0] for f in fs]
  193. output.extend(self.sql_indexes_for_fields(model, fields, style))
  194. return output
  195. def sql_indexes_for_field(self, model, f, style):
  196. """
  197. Return the CREATE INDEX SQL statements for a single model field.
  198. """
  199. if f.db_index and not f.unique:
  200. return self.sql_indexes_for_fields(model, [f], style)
  201. else:
  202. return []
  203. def sql_indexes_for_fields(self, model, fields, style):
  204. if len(fields) == 1 and fields[0].db_tablespace:
  205. tablespace_sql = self.connection.ops.tablespace_sql(fields[0].db_tablespace)
  206. elif model._meta.db_tablespace:
  207. tablespace_sql = self.connection.ops.tablespace_sql(model._meta.db_tablespace)
  208. else:
  209. tablespace_sql = ""
  210. if tablespace_sql:
  211. tablespace_sql = " " + tablespace_sql
  212. field_names = []
  213. qn = self.connection.ops.quote_name
  214. for f in fields:
  215. field_names.append(style.SQL_FIELD(qn(f.column)))
  216. index_name = "%s_%s" % (model._meta.db_table, self._digest([f.name for f in fields]))
  217. return [
  218. style.SQL_KEYWORD("CREATE INDEX") + " " +
  219. style.SQL_TABLE(qn(truncate_name(index_name, self.connection.ops.max_name_length()))) + " " +
  220. style.SQL_KEYWORD("ON") + " " +
  221. style.SQL_TABLE(qn(model._meta.db_table)) + " " +
  222. "(%s)" % style.SQL_FIELD(", ".join(field_names)) +
  223. "%s;" % tablespace_sql,
  224. ]
  225. def sql_destroy_model(self, model, references_to_delete, style):
  226. """
  227. Return the DROP TABLE and restraint dropping statements for a single
  228. model.
  229. """
  230. if not model._meta.managed or model._meta.proxy or model._meta.swapped:
  231. return []
  232. # Drop the table now
  233. qn = self.connection.ops.quote_name
  234. output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
  235. style.SQL_TABLE(qn(model._meta.db_table)))]
  236. if model in references_to_delete:
  237. output.extend(self.sql_remove_table_constraints(
  238. model, references_to_delete, style))
  239. if model._meta.has_auto_field:
  240. ds = self.connection.ops.drop_sequence_sql(model._meta.db_table)
  241. if ds:
  242. output.append(ds)
  243. return output
  244. def sql_remove_table_constraints(self, model, references_to_delete, style):
  245. if not model._meta.managed or model._meta.proxy or model._meta.swapped:
  246. return []
  247. output = []
  248. qn = self.connection.ops.quote_name
  249. for rel_class, f in references_to_delete[model]:
  250. table = rel_class._meta.db_table
  251. col = f.column
  252. r_table = model._meta.db_table
  253. r_col = model._meta.get_field(f.rel.field_name).column
  254. r_name = '%s_refs_%s_%s' % (
  255. col, r_col, self._digest(table, r_table))
  256. output.append('%s %s %s %s;' % (
  257. style.SQL_KEYWORD('ALTER TABLE'),
  258. style.SQL_TABLE(qn(table)),
  259. style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()),
  260. style.SQL_FIELD(qn(truncate_name(
  261. r_name, self.connection.ops.max_name_length())))
  262. ))
  263. del references_to_delete[model]
  264. return output
  265. def sql_destroy_indexes_for_model(self, model, style):
  266. """
  267. Returns the DROP INDEX SQL statements for a single model.
  268. """
  269. if not model._meta.managed or model._meta.proxy or model._meta.swapped:
  270. return []
  271. output = []
  272. for f in model._meta.local_fields:
  273. output.extend(self.sql_destroy_indexes_for_field(model, f, style))
  274. for fs in model._meta.index_together:
  275. fields = [model._meta.get_field_by_name(f)[0] for f in fs]
  276. output.extend(self.sql_destroy_indexes_for_fields(model, fields, style))
  277. return output
  278. def sql_destroy_indexes_for_field(self, model, f, style):
  279. """
  280. Return the DROP INDEX SQL statements for a single model field.
  281. """
  282. if f.db_index and not f.unique:
  283. return self.sql_destroy_indexes_for_fields(model, [f], style)
  284. else:
  285. return []
  286. def sql_destroy_indexes_for_fields(self, model, fields, style):
  287. if len(fields) == 1 and fields[0].db_tablespace:
  288. tablespace_sql = self.connection.ops.tablespace_sql(fields[0].db_tablespace)
  289. elif model._meta.db_tablespace:
  290. tablespace_sql = self.connection.ops.tablespace_sql(model._meta.db_tablespace)
  291. else:
  292. tablespace_sql = ""
  293. if tablespace_sql:
  294. tablespace_sql = " " + tablespace_sql
  295. field_names = []
  296. qn = self.connection.ops.quote_name
  297. for f in fields:
  298. field_names.append(style.SQL_FIELD(qn(f.column)))
  299. index_name = "%s_%s" % (model._meta.db_table, self._digest([f.name for f in fields]))
  300. return [
  301. style.SQL_KEYWORD("DROP INDEX") + " " +
  302. style.SQL_TABLE(qn(truncate_name(index_name, self.connection.ops.max_name_length()))) + " " +
  303. ";",
  304. ]
  305. def create_test_db(self, verbosity=1, autoclobber=False, keepdb=False):
  306. """
  307. Creates a test database, prompting the user for confirmation if the
  308. database already exists. Returns the name of the test database created.
  309. """
  310. # Don't import django.core.management if it isn't needed.
  311. from django.core.management import call_command
  312. test_database_name = self._get_test_db_name()
  313. if verbosity >= 1:
  314. test_db_repr = ''
  315. action = 'Creating'
  316. if verbosity >= 2:
  317. test_db_repr = " ('%s')" % test_database_name
  318. if keepdb:
  319. action = "Using existing"
  320. print("%s test database for alias '%s'%s..." % (
  321. action, self.connection.alias, test_db_repr))
  322. # We could skip this call if keepdb is True, but we instead
  323. # give it the keepdb param. This is to handle the case
  324. # where the test DB doesn't exist, in which case we need to
  325. # create it, then just not destroy it. If we instead skip
  326. # this, we will get an exception.
  327. self._create_test_db(verbosity, autoclobber, keepdb)
  328. self.connection.close()
  329. settings.DATABASES[self.connection.alias]["NAME"] = test_database_name
  330. self.connection.settings_dict["NAME"] = test_database_name
  331. # Report migrate messages at one level lower than that requested.
  332. # This ensures we don't get flooded with messages during testing
  333. # (unless you really ask to be flooded)
  334. call_command('migrate',
  335. verbosity=max(verbosity - 1, 0),
  336. interactive=False,
  337. database=self.connection.alias,
  338. load_initial_data=False,
  339. test_database=True)
  340. # We need to then do a flush to ensure that any data installed by
  341. # custom SQL has been removed. The only test data should come from
  342. # test fixtures, or autogenerated from post_migrate triggers.
  343. # This has the side effect of loading initial data (which was
  344. # intentionally skipped in the syncdb).
  345. call_command('flush',
  346. verbosity=max(verbosity - 1, 0),
  347. interactive=False,
  348. database=self.connection.alias)
  349. call_command('createcachetable', database=self.connection.alias)
  350. # Ensure a connection for the side effect of initializing the test database.
  351. self.connection.ensure_connection()
  352. return test_database_name
  353. def _get_test_db_name(self):
  354. """
  355. Internal implementation - returns the name of the test DB that will be
  356. created. Only useful when called from create_test_db() and
  357. _create_test_db() and when no external munging is done with the 'NAME'
  358. or 'TEST_NAME' settings.
  359. """
  360. if self.connection.settings_dict['TEST']['NAME']:
  361. return self.connection.settings_dict['TEST']['NAME']
  362. return TEST_DATABASE_PREFIX + self.connection.settings_dict['NAME']
  363. def _create_test_db(self, verbosity, autoclobber, keepdb=False):
  364. """
  365. Internal implementation - creates the test db tables.
  366. """
  367. suffix = self.sql_table_creation_suffix()
  368. test_database_name = self._get_test_db_name()
  369. qn = self.connection.ops.quote_name
  370. # Create the test database and connect to it.
  371. with self._nodb_connection.cursor() as cursor:
  372. try:
  373. cursor.execute(
  374. "CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
  375. except Exception as e:
  376. # if we want to keep the db, then no need to do any of the below,
  377. # just return and skip it all.
  378. if keepdb:
  379. return test_database_name
  380. sys.stderr.write(
  381. "Got an error creating the test database: %s\n" % e)
  382. if not autoclobber:
  383. confirm = input(
  384. "Type 'yes' if you would like to try deleting the test "
  385. "database '%s', or 'no' to cancel: " % test_database_name)
  386. if autoclobber or confirm == 'yes':
  387. try:
  388. if verbosity >= 1:
  389. print("Destroying old test database '%s'..."
  390. % self.connection.alias)
  391. cursor.execute(
  392. "DROP DATABASE %s" % qn(test_database_name))
  393. cursor.execute(
  394. "CREATE DATABASE %s %s" % (qn(test_database_name),
  395. suffix))
  396. except Exception as e:
  397. sys.stderr.write(
  398. "Got an error recreating the test database: %s\n" % e)
  399. sys.exit(2)
  400. else:
  401. print("Tests cancelled.")
  402. sys.exit(1)
  403. return test_database_name
  404. def destroy_test_db(self, old_database_name, verbosity=1):
  405. """
  406. Destroy a test database, prompting the user for confirmation if the
  407. database already exists.
  408. """
  409. self.connection.close()
  410. test_database_name = self.connection.settings_dict['NAME']
  411. if verbosity >= 1:
  412. test_db_repr = ''
  413. if verbosity >= 2:
  414. test_db_repr = " ('%s')" % test_database_name
  415. print("Destroying test database for alias '%s'%s..." % (
  416. self.connection.alias, test_db_repr))
  417. self._destroy_test_db(test_database_name, verbosity)
  418. def _destroy_test_db(self, test_database_name, verbosity):
  419. """
  420. Internal implementation - remove the test db tables.
  421. """
  422. # Remove the test database to clean up after
  423. # ourselves. Connect to the previous database (not the test database)
  424. # to do so, because it's not allowed to delete a database while being
  425. # connected to it.
  426. with self._nodb_connection.cursor() as cursor:
  427. # Wait to avoid "database is being accessed by other users" errors.
  428. time.sleep(1)
  429. cursor.execute("DROP DATABASE %s"
  430. % self.connection.ops.quote_name(test_database_name))
  431. def sql_table_creation_suffix(self):
  432. """
  433. SQL to append to the end of the test table creation statements.
  434. """
  435. return ''
  436. def test_db_signature(self):
  437. """
  438. Returns a tuple with elements of self.connection.settings_dict (a
  439. DATABASES setting value) that uniquely identify a database
  440. accordingly to the RDBMS particularities.
  441. """
  442. settings_dict = self.connection.settings_dict
  443. return (
  444. settings_dict['HOST'],
  445. settings_dict['PORT'],
  446. settings_dict['ENGINE'],
  447. settings_dict['NAME']
  448. )