diff options
| author | Simon Charette <charette.s@gmail.com> | 2020-10-04 18:25:29 -0400 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2020-10-29 22:22:58 +0100 |
| commit | bbe6fbb8768e8fb1aecb96d51c049d7ceaf802d3 (patch) | |
| tree | 7c0e7ac6defc405cd5320066f75ade21bf9943ac /django | |
| parent | 4ac2d4fa42e1659f328c35b6b8d4761b3419c11a (diff) | |
Refs #32061 -- Unified DatabaseClient.runshell() in db backends.
Diffstat (limited to 'django')
| -rw-r--r-- | django/db/backends/base/client.py | 16 | ||||
| -rw-r--r-- | django/db/backends/mysql/client.py | 10 | ||||
| -rw-r--r-- | django/db/backends/mysql/creation.py | 12 | ||||
| -rw-r--r-- | django/db/backends/oracle/base.py | 15 | ||||
| -rw-r--r-- | django/db/backends/oracle/client.py | 21 | ||||
| -rw-r--r-- | django/db/backends/oracle/utils.py | 7 | ||||
| -rw-r--r-- | django/db/backends/postgresql/client.py | 45 | ||||
| -rw-r--r-- | django/db/backends/sqlite3/client.py | 19 |
8 files changed, 80 insertions, 65 deletions
diff --git a/django/db/backends/base/client.py b/django/db/backends/base/client.py index 32901764aa..339f1e863c 100644 --- a/django/db/backends/base/client.py +++ b/django/db/backends/base/client.py @@ -1,3 +1,7 @@ +import os +import subprocess + + class BaseDatabaseClient: """Encapsulate backend-specific methods for opening a client shell.""" # This should be a string representing the name of the executable @@ -8,5 +12,15 @@ class BaseDatabaseClient: # connection is an instance of BaseDatabaseWrapper. self.connection = connection + @classmethod + def settings_to_cmd_args_env(cls, settings_dict, parameters): + raise NotImplementedError( + 'subclasses of BaseDatabaseClient must provide a ' + 'settings_to_cmd_args_env() method or override a runshell().' + ) + def runshell(self, parameters): - raise NotImplementedError('subclasses of BaseDatabaseClient must provide a runshell() method') + args, env = self.settings_to_cmd_args_env(self.connection.settings_dict, parameters) + if env: + env = {**os.environ, **env} + subprocess.run(args, env=env, check=True) diff --git a/django/db/backends/mysql/client.py b/django/db/backends/mysql/client.py index a94d388ccd..79032c1207 100644 --- a/django/db/backends/mysql/client.py +++ b/django/db/backends/mysql/client.py @@ -1,5 +1,3 @@ -import subprocess - from django.db.backends.base.client import BaseDatabaseClient @@ -7,7 +5,7 @@ class DatabaseClient(BaseDatabaseClient): executable_name = 'mysql' @classmethod - def settings_to_cmd_args(cls, settings_dict, parameters): + def settings_to_cmd_args_env(cls, settings_dict, parameters): args = [cls.executable_name] db = settings_dict['OPTIONS'].get('db', settings_dict['NAME']) user = settings_dict['OPTIONS'].get('user', settings_dict['USER']) @@ -48,8 +46,4 @@ class DatabaseClient(BaseDatabaseClient): if db: args += [db] args.extend(parameters) - return args - - def runshell(self, parameters): - args = DatabaseClient.settings_to_cmd_args(self.connection.settings_dict, parameters) - subprocess.run(args, check=True) + return args, None diff --git a/django/db/backends/mysql/creation.py b/django/db/backends/mysql/creation.py index 99372fd7ee..1f0261b667 100644 --- a/django/db/backends/mysql/creation.py +++ b/django/db/backends/mysql/creation.py @@ -1,3 +1,4 @@ +import os import subprocess import sys @@ -55,12 +56,13 @@ class DatabaseCreation(BaseDatabaseCreation): self._clone_db(source_database_name, target_database_name) def _clone_db(self, source_database_name, target_database_name): - dump_args = DatabaseClient.settings_to_cmd_args(self.connection.settings_dict, [])[1:] - dump_cmd = ['mysqldump', *dump_args[:-1], '--routines', '--events', source_database_name] - load_cmd = DatabaseClient.settings_to_cmd_args(self.connection.settings_dict, []) + cmd_args, cmd_env = DatabaseClient.settings_to_cmd_args_env(self.connection.settings_dict, []) + dump_cmd = ['mysqldump', *cmd_args[1:-1], '--routines', '--events', source_database_name] + dump_env = load_env = {**os.environ, **cmd_env} if cmd_env else None + load_cmd = cmd_args load_cmd[-1] = target_database_name - with subprocess.Popen(dump_cmd, stdout=subprocess.PIPE) as dump_proc: - with subprocess.Popen(load_cmd, stdin=dump_proc.stdout, stdout=subprocess.DEVNULL): + with subprocess.Popen(dump_cmd, stdout=subprocess.PIPE, env=dump_env) as dump_proc: + with subprocess.Popen(load_cmd, stdin=dump_proc.stdout, stdout=subprocess.DEVNULL, env=load_env): # Allow dump_proc to receive a SIGPIPE if the load process exits. dump_proc.stdout.close() diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 10fff26fec..781f123a82 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -56,7 +56,7 @@ from .features import DatabaseFeatures # NOQA isort:skip from .introspection import DatabaseIntrospection # NOQA isort:skip from .operations import DatabaseOperations # NOQA isort:skip from .schema import DatabaseSchemaEditor # NOQA isort:skip -from .utils import Oracle_datetime # NOQA isort:skip +from .utils import dsn, Oracle_datetime # NOQA isort:skip from .validation import DatabaseValidation # NOQA isort:skip @@ -218,17 +218,6 @@ class DatabaseWrapper(BaseDatabaseWrapper): use_returning_into = self.settings_dict["OPTIONS"].get('use_returning_into', True) self.features.can_return_columns_from_insert = use_returning_into - def _dsn(self): - settings_dict = self.settings_dict - if not settings_dict['HOST'].strip(): - settings_dict['HOST'] = 'localhost' - if settings_dict['PORT']: - return Database.makedsn(settings_dict['HOST'], int(settings_dict['PORT']), settings_dict['NAME']) - return settings_dict['NAME'] - - def _connect_string(self): - return '%s/"%s"@%s' % (self.settings_dict['USER'], self.settings_dict['PASSWORD'], self._dsn()) - def get_connection_params(self): conn_params = self.settings_dict['OPTIONS'].copy() if 'use_returning_into' in conn_params: @@ -240,7 +229,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): return Database.connect( user=self.settings_dict['USER'], password=self.settings_dict['PASSWORD'], - dsn=self._dsn(), + dsn=dsn(self.settings_dict), **conn_params, ) diff --git a/django/db/backends/oracle/client.py b/django/db/backends/oracle/client.py index 434481ba81..9920f4ca67 100644 --- a/django/db/backends/oracle/client.py +++ b/django/db/backends/oracle/client.py @@ -1,5 +1,4 @@ import shutil -import subprocess from django.db.backends.base.client import BaseDatabaseClient @@ -8,11 +7,21 @@ class DatabaseClient(BaseDatabaseClient): executable_name = 'sqlplus' wrapper_name = 'rlwrap' - def runshell(self, parameters): - conn_string = self.connection._connect_string() - args = [self.executable_name, "-L", conn_string] - wrapper_path = shutil.which(self.wrapper_name) + @staticmethod + def connect_string(settings_dict): + from django.db.backends.oracle.utils import dsn + + return '%s/"%s"@%s' % ( + settings_dict['USER'], + settings_dict['PASSWORD'], + dsn(settings_dict), + ) + + @classmethod + def settings_to_cmd_args_env(cls, settings_dict, parameters): + args = [cls.executable_name, '-L', cls.connect_string(settings_dict)] + wrapper_path = shutil.which(cls.wrapper_name) if wrapper_path: args = [wrapper_path, *args] args.extend(parameters) - subprocess.run(args, check=True) + return args, None diff --git a/django/db/backends/oracle/utils.py b/django/db/backends/oracle/utils.py index 62285f5ee4..5665079aa2 100644 --- a/django/db/backends/oracle/utils.py +++ b/django/db/backends/oracle/utils.py @@ -82,3 +82,10 @@ class BulkInsertMapper: 'TextField': CLOB, 'TimeField': TIMESTAMP, } + + +def dsn(settings_dict): + if settings_dict['PORT']: + host = settings_dict['HOST'].strip() or 'localhost' + return Database.makedsn(host, int(settings_dict['PORT']), settings_dict['NAME']) + return settings_dict['NAME'] diff --git a/django/db/backends/postgresql/client.py b/django/db/backends/postgresql/client.py index 9d390b3807..7965401163 100644 --- a/django/db/backends/postgresql/client.py +++ b/django/db/backends/postgresql/client.py @@ -1,6 +1,4 @@ -import os import signal -import subprocess from django.db.backends.base.client import BaseDatabaseClient @@ -9,18 +7,19 @@ class DatabaseClient(BaseDatabaseClient): executable_name = 'psql' @classmethod - def runshell_db(cls, conn_params, parameters): + def settings_to_cmd_args_env(cls, settings_dict, parameters): args = [cls.executable_name] + options = settings_dict.get('OPTIONS', {}) - host = conn_params.get('host', '') - port = conn_params.get('port', '') - dbname = conn_params.get('database', '') - user = conn_params.get('user', '') - passwd = conn_params.get('password', '') - sslmode = conn_params.get('sslmode', '') - sslrootcert = conn_params.get('sslrootcert', '') - sslcert = conn_params.get('sslcert', '') - sslkey = conn_params.get('sslkey', '') + host = settings_dict.get('HOST') + port = settings_dict.get('PORT') + dbname = settings_dict.get('NAME') or 'postgres' + user = settings_dict.get('USER') + passwd = settings_dict.get('PASSWORD') + sslmode = options.get('sslmode') + sslrootcert = options.get('sslrootcert') + sslcert = options.get('sslcert') + sslkey = options.get('sslkey') if user: args += ['-U', user] @@ -31,25 +30,25 @@ class DatabaseClient(BaseDatabaseClient): args += [dbname] args.extend(parameters) - sigint_handler = signal.getsignal(signal.SIGINT) - subprocess_env = os.environ.copy() + env = {} if passwd: - subprocess_env['PGPASSWORD'] = str(passwd) + env['PGPASSWORD'] = str(passwd) if sslmode: - subprocess_env['PGSSLMODE'] = str(sslmode) + env['PGSSLMODE'] = str(sslmode) if sslrootcert: - subprocess_env['PGSSLROOTCERT'] = str(sslrootcert) + env['PGSSLROOTCERT'] = str(sslrootcert) if sslcert: - subprocess_env['PGSSLCERT'] = str(sslcert) + env['PGSSLCERT'] = str(sslcert) if sslkey: - subprocess_env['PGSSLKEY'] = str(sslkey) + env['PGSSLKEY'] = str(sslkey) + return args, env + + def runshell(self, parameters): + sigint_handler = signal.getsignal(signal.SIGINT) try: # Allow SIGINT to pass to psql to abort queries. signal.signal(signal.SIGINT, signal.SIG_IGN) - subprocess.run(args, check=True, env=subprocess_env) + super().runshell(parameters) finally: # Restore the original SIGINT handler. signal.signal(signal.SIGINT, sigint_handler) - - def runshell(self, parameters): - self.runshell_db(self.connection.get_connection_params(), parameters) diff --git a/django/db/backends/sqlite3/client.py b/django/db/backends/sqlite3/client.py index a71005fd5b..59a2fe7f50 100644 --- a/django/db/backends/sqlite3/client.py +++ b/django/db/backends/sqlite3/client.py @@ -1,15 +1,16 @@ -import subprocess - from django.db.backends.base.client import BaseDatabaseClient class DatabaseClient(BaseDatabaseClient): executable_name = 'sqlite3' - def runshell(self, parameters): - # TODO: Remove str() when dropping support for PY37. - # args parameter accepts path-like objects on Windows since Python 3.8. - args = [self.executable_name, - str(self.connection.settings_dict['NAME'])] - args.extend(parameters) - subprocess.run(args, check=True) + @classmethod + def settings_to_cmd_args_env(cls, settings_dict, parameters): + args = [ + cls.executable_name, + # TODO: Remove str() when dropping support for PY37. args + # parameter accepts path-like objects on Windows since Python 3.8. + str(settings_dict['NAME']), + *parameters, + ] + return args, None |
