summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Joseph <ajosephau@users.noreply.github.com>2024-10-23 22:13:18 +1100
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2024-10-30 15:54:48 +0100
commit3a8f52fbc6fe2ff646a2da4165497d82117e4a92 (patch)
treeb2f496aadef5091699c8305ba52edc71a88e5a78
parent97a6a678c406b0049bd17bcd34f1d71d96141994 (diff)
Fixed #35856 -- Added QuerySet.explain() support for MEMORY/SERIALIZE option on PostgreSQL 17+.
-rw-r--r--django/db/backends/postgresql/features.py4
-rw-r--r--django/db/backends/postgresql/operations.py5
-rw-r--r--docs/ref/models/querysets.txt5
-rw-r--r--docs/releases/5.2.txt3
-rw-r--r--tests/queries/test_explain.py13
5 files changed, 29 insertions, 1 deletions
diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py
index 6170b5501a..16653a0519 100644
--- a/django/db/backends/postgresql/features.py
+++ b/django/db/backends/postgresql/features.py
@@ -160,6 +160,10 @@ class DatabaseFeatures(BaseDatabaseFeatures):
def is_postgresql_16(self):
return self.connection.pg_version >= 160000
+ @cached_property
+ def is_postgresql_17(self):
+ return self.connection.pg_version >= 170000
+
supports_unlimited_charfield = True
supports_nulls_distinct_unique_constraints = property(
operator.attrgetter("is_postgresql_15")
diff --git a/django/db/backends/postgresql/operations.py b/django/db/backends/postgresql/operations.py
index 4b179ca83f..d89f81bf7e 100644
--- a/django/db/backends/postgresql/operations.py
+++ b/django/db/backends/postgresql/operations.py
@@ -32,7 +32,9 @@ class DatabaseOperations(BaseDatabaseOperations):
"BUFFERS",
"COSTS",
"GENERIC_PLAN",
+ "MEMORY",
"SETTINGS",
+ "SERIALIZE",
"SUMMARY",
"TIMING",
"VERBOSE",
@@ -365,6 +367,9 @@ class DatabaseOperations(BaseDatabaseOperations):
def explain_query_prefix(self, format=None, **options):
extra = {}
+ if serialize := options.pop("serialize", None):
+ if serialize.upper() in {"TEXT", "BINARY"}:
+ extra["SERIALIZE"] = serialize.upper()
# Normalize options.
if options:
options = {
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index c6af3dd7f0..ec27936cdb 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -3110,6 +3110,11 @@ there are triggers or if a function is called, even for a ``SELECT`` query.
Support for the ``generic_plan`` option on PostgreSQL 16+ was added.
+.. versionchanged:: 5.2
+
+ Support for the ``memory`` and ``serialize`` options on PostgreSQL 17+ was
+ added.
+
.. _field-lookups:
``Field`` lookups
diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt
index e103de847b..96007887bc 100644
--- a/docs/releases/5.2.txt
+++ b/docs/releases/5.2.txt
@@ -277,6 +277,9 @@ Models
longer required to be set on SQLite, which supports unlimited ``VARCHAR``
columns.
+* :meth:`.QuerySet.explain` now supports the ``memory`` and ``serialize``
+ options on PostgreSQL 17+.
+
Requests and Responses
~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/queries/test_explain.py b/tests/queries/test_explain.py
index 67440cb502..95ca913cfc 100644
--- a/tests/queries/test_explain.py
+++ b/tests/queries/test_explain.py
@@ -90,13 +90,24 @@ class ExplainTests(TestCase):
]
if connection.features.is_postgresql_16:
test_options.append({"generic_plan": True})
+ if connection.features.is_postgresql_17:
+ test_options.append({"memory": True})
+ test_options.append({"serialize": "TEXT", "analyze": True})
+ test_options.append({"serialize": "text", "analyze": True})
+ test_options.append({"serialize": "BINARY", "analyze": True})
+ test_options.append({"serialize": "binary", "analyze": True})
for options in test_options:
with self.subTest(**options), transaction.atomic():
with CaptureQueriesContext(connection) as captured_queries:
qs.explain(format="text", **options)
self.assertEqual(len(captured_queries), 1)
for name, value in options.items():
- option = "{} {}".format(name.upper(), "true" if value else "false")
+ if isinstance(value, str):
+ option = "{} {}".format(name.upper(), value.upper())
+ else:
+ option = "{} {}".format(
+ name.upper(), "true" if value else "false"
+ )
self.assertIn(option, captured_queries[0]["sql"])
@skipUnlessDBFeature("supports_select_union")