[3.1.x] Fixed #32403 -- Fixed re-raising DatabaseErrors when using only 'postgres' database.

Thanks Kazantcev Andrey for the report.

Regression in f48f671223.
Backport of f131841c60 from master
This commit is contained in:
Mariusz Felisiak 2021-02-02 21:34:36 +01:00
parent 2d560af8fb
commit 9efe832ee1
3 changed files with 55 additions and 2 deletions

View File

@ -320,6 +320,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
yield cursor
finally:
conn.close()
break
else:
raise
@cached_property
def pg_version(self):

View File

@ -9,4 +9,6 @@ Django 3.1.7 fixes several bugs in 3.1.6.
Bugfixes
========
* ...
* Fixed a regression in Django 3.1 that caused ``RuntimeError`` instead of
connection errors when using only the ``'postgres'`` database
(:ticket:`32403`).

View File

@ -1,9 +1,10 @@
import copy
import unittest
from io import StringIO
from unittest import mock
from django.core.exceptions import ImproperlyConfigured
from django.db import DatabaseError, connection, connections
from django.db import DEFAULT_DB_ALIAS, DatabaseError, connection, connections
from django.db.backends.base.base import BaseDatabaseWrapper
from django.test import TestCase, override_settings
@ -54,6 +55,53 @@ class Tests(TestCase):
self.assertIsNone(cursor.db.connection)
self.assertIsNotNone(cursor.db.settings_dict['NAME'])
self.assertEqual(cursor.db.settings_dict['NAME'], connections['other'].settings_dict['NAME'])
# Cursor is yielded only for the first PostgreSQL database.
with self.assertWarnsMessage(RuntimeWarning, msg):
with mock.patch(
'django.db.backends.base.base.BaseDatabaseWrapper.connect',
side_effect=mocked_connect,
autospec=True,
):
with connection._nodb_cursor() as cursor:
self.assertIs(cursor.closed, False)
self.assertIsNotNone(cursor.db.connection)
def test_nodb_cursor_raises_postgres_authentication_failure(self):
"""
_nodb_cursor() re-raises authentication failure to the 'postgres' db
when other connection to the PostgreSQL database isn't available.
"""
def mocked_connect(self):
raise DatabaseError()
def mocked_all(self):
test_connection = copy.copy(connections[DEFAULT_DB_ALIAS])
test_connection.settings_dict = copy.deepcopy(connection.settings_dict)
test_connection.settings_dict['NAME'] = 'postgres'
return [test_connection]
msg = (
"Normally Django will use a connection to the 'postgres' database "
"to avoid running initialization queries against the production "
"database when it's not needed (for example, when running tests). "
"Django was unable to create a connection to the 'postgres' "
"database and will use the first PostgreSQL database instead."
)
with self.assertWarnsMessage(RuntimeWarning, msg):
mocker_connections_all = mock.patch(
'django.utils.connection.BaseConnectionHandler.all',
side_effect=mocked_all,
autospec=True,
)
mocker_connect = mock.patch(
'django.db.backends.base.base.BaseDatabaseWrapper.connect',
side_effect=mocked_connect,
autospec=True,
)
with mocker_connections_all, mocker_connect:
with self.assertRaises(DatabaseError):
with connection._nodb_cursor():
pass
def test_database_name_too_long(self):
from django.db.backends.postgresql.base import DatabaseWrapper