summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnders Kaseorg <andersk@mit.edu>2023-04-06 12:44:37 -0700
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-04-07 09:21:54 +0200
commit0bc2bbf041df40a6ecb6262b2e5f3bb659dd8da8 (patch)
treeb1dfb41f38d3eec0f9f91d4fb704fc0b19562c3d
parent511dc3db539122577aaba71f5a24d65d5adab092 (diff)
[4.2.x] Fixed #34466 -- Reallowed setting cursor_factory in DATABASES["options"] on PostgreSQL.
Regression in 09ffc5c1212d4ced58b708cbbf3dfbfb77b782ca. Backport of 73cbb372baa45d1fdafd571e2f430a980831f722 from main
-rw-r--r--django/db/backends/postgresql/base.py17
-rw-r--r--docs/releases/4.2.1.txt4
-rw-r--r--tests/backends/postgresql/tests.py18
3 files changed, 30 insertions, 9 deletions
diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py
index 3db3d6abd2..5ed856448f 100644
--- a/django/db/backends/postgresql/base.py
+++ b/django/db/backends/postgresql/base.py
@@ -223,7 +223,13 @@ class DatabaseWrapper(BaseDatabaseWrapper):
conn_params.pop("assume_role", None)
conn_params.pop("isolation_level", None)
- conn_params.pop("server_side_binding", None)
+ server_side_binding = conn_params.pop("server_side_binding", None)
+ conn_params.setdefault(
+ "cursor_factory",
+ ServerBindingCursor
+ if is_psycopg3 and server_side_binding is True
+ else Cursor,
+ )
if settings_dict["USER"]:
conn_params["user"] = settings_dict["USER"]
if settings_dict["PASSWORD"]:
@@ -269,20 +275,13 @@ class DatabaseWrapper(BaseDatabaseWrapper):
connection = self.Database.connect(**conn_params)
if set_isolation_level:
connection.isolation_level = self.isolation_level
- if is_psycopg3:
- connection.cursor_factory = (
- ServerBindingCursor
- if options.get("server_side_binding") is True
- else Cursor
- )
- else:
+ if not is_psycopg3:
# Register dummy loads() to avoid a round trip from psycopg2's
# decode to json.dumps() to json.loads(), when using a custom
# decoder in JSONField.
psycopg2.extras.register_default_jsonb(
conn_or_curs=connection, loads=lambda x: x
)
- connection.cursor_factory = Cursor
return connection
def ensure_timezone(self):
diff --git a/docs/releases/4.2.1.txt b/docs/releases/4.2.1.txt
index f8a4edf787..dd172a1a89 100644
--- a/docs/releases/4.2.1.txt
+++ b/docs/releases/4.2.1.txt
@@ -18,3 +18,7 @@ Bugfixes
* Fixed a regression in Django 4.2 that caused aggregation over query that
uses explicit grouping to group against the wrong columns (:ticket:`34464`).
+
+* Reallowed, following a regression in Django 4.2, setting the
+ ``"cursor_factory"`` option in :setting:`OPTIONS` on PostgreSQL
+ (:ticket:`34466`).
diff --git a/tests/backends/postgresql/tests.py b/tests/backends/postgresql/tests.py
index ab66e7ab0e..5589daa0c2 100644
--- a/tests/backends/postgresql/tests.py
+++ b/tests/backends/postgresql/tests.py
@@ -296,6 +296,24 @@ class Tests(TestCase):
finally:
new_connection.close()
+ def test_connect_custom_cursor_factory(self):
+ """
+ A custom cursor factory can be configured with DATABASES["options"]
+ ["cursor_factory"].
+ """
+ from django.db.backends.postgresql.base import Cursor
+
+ class MyCursor(Cursor):
+ pass
+
+ new_connection = connection.copy()
+ new_connection.settings_dict["OPTIONS"]["cursor_factory"] = MyCursor
+ try:
+ new_connection.connect()
+ self.assertEqual(new_connection.connection.cursor_factory, MyCursor)
+ finally:
+ new_connection.close()
+
def test_connect_no_is_usable_checks(self):
new_connection = connection.copy()
try: