mirror of
https://github.com/django/django.git
synced 2025-10-25 22:56:12 +00:00
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.
This commit is contained in:
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user