summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2013-11-09 09:41:03 +0100
committerClaude Paroz <claude@2xlibre.net>2013-11-09 09:42:17 +0100
commite953c78eeb81ee69dccd356145563fd6f9e4c7b6 (patch)
treec108c4b9afddc91a36eeb0516cde7fa4443ae985 /django
parent1830f50493cae042dac11957af986b0fd84f889f (diff)
Fixed #16969 -- Don't connect to named database when possible
Thanks Andreas Pelme for the report and initial patch, and Aymeric Augustin, Shai Berger and Tim Graham for the reviews.
Diffstat (limited to 'django')
-rw-r--r--django/db/backends/creation.py37
-rw-r--r--django/db/backends/postgresql_psycopg2/base.py5
2 files changed, 25 insertions, 17 deletions
diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py
index 866031447d..7c14b6a09c 100644
--- a/django/db/backends/creation.py
+++ b/django/db/backends/creation.py
@@ -6,6 +6,7 @@ import warnings
from django.conf import settings
from django.db.utils import load_backend
from django.utils.encoding import force_bytes
+from django.utils.functional import cached_property
from django.utils.six.moves import input
from .utils import truncate_name
@@ -29,6 +30,24 @@ class BaseDatabaseCreation(object):
def __init__(self, connection):
self.connection = connection
+ @cached_property
+ def _nodb_connection(self):
+ """
+ Alternative connection to be used when there is no need to access
+ the main database, specifically for test db creation/deletion.
+ This also prevents the production database from being exposed to
+ potential child threads while (or after) the test database is destroyed.
+ Refs #10868, #17786, #16969.
+ """
+ settings_dict = self.connection.settings_dict.copy()
+ settings_dict['NAME'] = None
+ backend = load_backend(settings_dict['ENGINE'])
+ nodb_connection = backend.DatabaseWrapper(
+ settings_dict,
+ alias='__no_db__',
+ allow_thread_sharing=False)
+ return nodb_connection
+
@classmethod
def _digest(cls, *args):
"""
@@ -386,7 +405,7 @@ class BaseDatabaseCreation(object):
qn = self.connection.ops.quote_name
# Create the test database and connect to it.
- cursor = self.connection.cursor()
+ cursor = self._nodb_connection.cursor()
try:
cursor.execute(
"CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
@@ -431,18 +450,7 @@ class BaseDatabaseCreation(object):
print("Destroying test database for alias '%s'%s..." % (
self.connection.alias, test_db_repr))
- # Temporarily use a new connection and a copy of the settings dict.
- # This prevents the production database from being exposed to potential
- # child threads while (or after) the test database is destroyed.
- # Refs #10868 and #17786.
- settings_dict = self.connection.settings_dict.copy()
- settings_dict['NAME'] = old_database_name
- backend = load_backend(settings_dict['ENGINE'])
- new_connection = backend.DatabaseWrapper(
- settings_dict,
- alias='__destroy_test_db__',
- allow_thread_sharing=False)
- new_connection.creation._destroy_test_db(test_database_name, verbosity)
+ self._destroy_test_db(test_database_name, verbosity)
def _destroy_test_db(self, test_database_name, verbosity):
"""
@@ -452,12 +460,11 @@ class BaseDatabaseCreation(object):
# ourselves. Connect to the previous database (not the test database)
# to do so, because it's not allowed to delete a database while being
# connected to it.
- cursor = self.connection.cursor()
+ cursor = self._nodb_connection.cursor()
# Wait to avoid "database is being accessed by other users" errors.
time.sleep(1)
cursor.execute("DROP DATABASE %s"
% self.connection.ops.quote_name(test_database_name))
- self.connection.close()
def set_autocommit(self):
"""
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index b22c653bd7..cb23c33a5c 100644
--- a/django/db/backends/postgresql_psycopg2/base.py
+++ b/django/db/backends/postgresql_psycopg2/base.py
@@ -101,13 +101,14 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def get_connection_params(self):
settings_dict = self.settings_dict
- if not settings_dict['NAME']:
+ # None may be used to connect to the default 'postgres' db
+ if settings_dict['NAME'] == '':
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured(
"settings.DATABASES is improperly configured. "
"Please supply the NAME value.")
conn_params = {
- 'database': settings_dict['NAME'],
+ 'database': settings_dict['NAME'] or 'postgres',
}
conn_params.update(settings_dict['OPTIONS'])
if 'autocommit' in conn_params: