creation.py 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. import os
  2. import subprocess
  3. import sys
  4. from django.db.backends.base.creation import BaseDatabaseCreation
  5. from .client import DatabaseClient
  6. class DatabaseCreation(BaseDatabaseCreation):
  7. def sql_table_creation_suffix(self):
  8. suffix = []
  9. test_settings = self.connection.settings_dict['TEST']
  10. if test_settings['CHARSET']:
  11. suffix.append('CHARACTER SET %s' % test_settings['CHARSET'])
  12. if test_settings['COLLATION']:
  13. suffix.append('COLLATE %s' % test_settings['COLLATION'])
  14. return ' '.join(suffix)
  15. def _execute_create_test_db(self, cursor, parameters, keepdb=False):
  16. try:
  17. super()._execute_create_test_db(cursor, parameters, keepdb)
  18. except Exception as e:
  19. if len(e.args) < 1 or e.args[0] != 1007:
  20. # All errors except "database exists" (1007) cancel tests.
  21. self.log('Got an error creating the test database: %s' % e)
  22. sys.exit(2)
  23. else:
  24. raise
  25. def _clone_test_db(self, suffix, verbosity, keepdb=False):
  26. source_database_name = self.connection.settings_dict['NAME']
  27. target_database_name = self.get_test_db_clone_settings(suffix)['NAME']
  28. test_db_params = {
  29. 'dbname': self.connection.ops.quote_name(target_database_name),
  30. 'suffix': self.sql_table_creation_suffix(),
  31. }
  32. with self._nodb_cursor() as cursor:
  33. try:
  34. self._execute_create_test_db(cursor, test_db_params, keepdb)
  35. except Exception:
  36. if keepdb:
  37. # If the database should be kept, skip everything else.
  38. return
  39. try:
  40. if verbosity >= 1:
  41. self.log('Destroying old test database for alias %s...' % (
  42. self._get_database_display_str(verbosity, target_database_name),
  43. ))
  44. cursor.execute('DROP DATABASE %(dbname)s' % test_db_params)
  45. self._execute_create_test_db(cursor, test_db_params, keepdb)
  46. except Exception as e:
  47. self.log('Got an error recreating the test database: %s' % e)
  48. sys.exit(2)
  49. self._clone_db(source_database_name, target_database_name)
  50. def _clone_db(self, source_database_name, target_database_name):
  51. cmd_args, cmd_env = DatabaseClient.settings_to_cmd_args_env(self.connection.settings_dict, [])
  52. dump_cmd = ['mysqldump', *cmd_args[1:-1], '--routines', '--events', source_database_name]
  53. dump_env = load_env = {**os.environ, **cmd_env} if cmd_env else None
  54. load_cmd = cmd_args
  55. load_cmd[-1] = target_database_name
  56. with subprocess.Popen(dump_cmd, stdout=subprocess.PIPE, env=dump_env) as dump_proc:
  57. with subprocess.Popen(load_cmd, stdin=dump_proc.stdout, stdout=subprocess.DEVNULL, env=load_env):
  58. # Allow dump_proc to receive a SIGPIPE if the load process exits.
  59. dump_proc.stdout.close()