Skip to content

Commit

Permalink
[3.2.x] Fixed #32403 -- Fixed re-raising DatabaseErrors when using on…
Browse files Browse the repository at this point in the history
…ly 'postgres' database.

Thanks Kazantcev Andrey for the report.

Regression in f48f671.
Backport of f131841 from master
  • Loading branch information
felixxm committed Feb 2, 2021
1 parent eab0b8b commit 7d65889
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 2 deletions.
3 changes: 3 additions & 0 deletions django/db/backends/postgresql/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ def _nodb_cursor(self):
yield cursor
finally:
conn.close()
break
else:
raise

@cached_property
def pg_version(self):
Expand Down
4 changes: 3 additions & 1 deletion docs/releases/3.1.7.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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`).
50 changes: 49 additions & 1 deletion tests/backends/postgresql/tests.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -54,6 +55,53 @@ def mocked_connect(self):
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
Expand Down

0 comments on commit 7d65889

Please sign in to comment.