Fixed #35856 -- Added QuerySet.explain() support for MEMORY/SERIALIZE option on PostgreSQL 17+.

This commit is contained in:
Anthony Joseph 2024-10-23 22:13:18 +11:00 committed by Sarah Boyce
parent 97a6a678c4
commit 3a8f52fbc6
5 changed files with 29 additions and 1 deletions

View File

@ -160,6 +160,10 @@ class DatabaseFeatures(BaseDatabaseFeatures):
def is_postgresql_16(self): def is_postgresql_16(self):
return self.connection.pg_version >= 160000 return self.connection.pg_version >= 160000
@cached_property
def is_postgresql_17(self):
return self.connection.pg_version >= 170000
supports_unlimited_charfield = True supports_unlimited_charfield = True
supports_nulls_distinct_unique_constraints = property( supports_nulls_distinct_unique_constraints = property(
operator.attrgetter("is_postgresql_15") operator.attrgetter("is_postgresql_15")

View File

@ -32,7 +32,9 @@ class DatabaseOperations(BaseDatabaseOperations):
"BUFFERS", "BUFFERS",
"COSTS", "COSTS",
"GENERIC_PLAN", "GENERIC_PLAN",
"MEMORY",
"SETTINGS", "SETTINGS",
"SERIALIZE",
"SUMMARY", "SUMMARY",
"TIMING", "TIMING",
"VERBOSE", "VERBOSE",
@ -365,6 +367,9 @@ class DatabaseOperations(BaseDatabaseOperations):
def explain_query_prefix(self, format=None, **options): def explain_query_prefix(self, format=None, **options):
extra = {} extra = {}
if serialize := options.pop("serialize", None):
if serialize.upper() in {"TEXT", "BINARY"}:
extra["SERIALIZE"] = serialize.upper()
# Normalize options. # Normalize options.
if options: if options:
options = { options = {

View File

@ -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. 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:
``Field`` lookups ``Field`` lookups

View File

@ -277,6 +277,9 @@ Models
longer required to be set on SQLite, which supports unlimited ``VARCHAR`` longer required to be set on SQLite, which supports unlimited ``VARCHAR``
columns. columns.
* :meth:`.QuerySet.explain` now supports the ``memory`` and ``serialize``
options on PostgreSQL 17+.
Requests and Responses Requests and Responses
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~

View File

@ -90,13 +90,24 @@ class ExplainTests(TestCase):
] ]
if connection.features.is_postgresql_16: if connection.features.is_postgresql_16:
test_options.append({"generic_plan": True}) 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: for options in test_options:
with self.subTest(**options), transaction.atomic(): with self.subTest(**options), transaction.atomic():
with CaptureQueriesContext(connection) as captured_queries: with CaptureQueriesContext(connection) as captured_queries:
qs.explain(format="text", **options) qs.explain(format="text", **options)
self.assertEqual(len(captured_queries), 1) self.assertEqual(len(captured_queries), 1)
for name, value in options.items(): 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"]) self.assertIn(option, captured_queries[0]["sql"])
@skipUnlessDBFeature("supports_select_union") @skipUnlessDBFeature("supports_select_union")