creation.py 22 KB

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