summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2020-10-04 18:25:29 -0400
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-10-29 22:22:58 +0100
commitbbe6fbb8768e8fb1aecb96d51c049d7ceaf802d3 (patch)
tree7c0e7ac6defc405cd5320066f75ade21bf9943ac /django
parent4ac2d4fa42e1659f328c35b6b8d4761b3419c11a (diff)
Refs #32061 -- Unified DatabaseClient.runshell() in db backends.
Diffstat (limited to 'django')
-rw-r--r--django/db/backends/base/client.py16
-rw-r--r--django/db/backends/mysql/client.py10
-rw-r--r--django/db/backends/mysql/creation.py12
-rw-r--r--django/db/backends/oracle/base.py15
-rw-r--r--django/db/backends/oracle/client.py21
-rw-r--r--django/db/backends/oracle/utils.py7
-rw-r--r--django/db/backends/postgresql/client.py45
-rw-r--r--django/db/backends/sqlite3/client.py19
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