summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Sanders <shang.xiao.sanders@gmail.com>2023-11-13 00:45:52 +1100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-11-13 12:01:40 +0100
commitb863c5ffde0bafa5eaa9f262103eaeb71877787c (patch)
tree3e0930b96daeddee486a2f011fd4598db3fa7e6b
parent1b56b24f81a2e64b4bd3059abad9b6fd0c801c66 (diff)
Fixed #34967 -- Fixed queryset crash when grouping by constants on SQLite < 3.39.
On SQLite < 3.39, this forces a GROUP BY clause with a HAVING clause when no grouping is specified. Co-authored-by: Simon Charette <charette.s@gmail.com>
-rw-r--r--django/db/backends/base/operations.py7
-rw-r--r--django/db/backends/sqlite3/operations.py5
-rw-r--r--django/db/models/sql/compiler.py2
-rw-r--r--tests/aggregation/tests.py9
4 files changed, 23 insertions, 0 deletions
diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py
index b1d818d90e..d5a40eb46e 100644
--- a/django/db/backends/base/operations.py
+++ b/django/db/backends/base/operations.py
@@ -231,6 +231,13 @@ class BaseDatabaseOperations:
)
return "%s"
+ def force_group_by(self):
+ """
+ Return a GROUP BY clause to use with a HAVING clause when no grouping
+ is specified.
+ """
+ return []
+
def force_no_ordering(self):
"""
Return a list used in the "ORDER BY" clause to force no ordering at
diff --git a/django/db/backends/sqlite3/operations.py b/django/db/backends/sqlite3/operations.py
index 85ad804348..dfc9857b84 100644
--- a/django/db/backends/sqlite3/operations.py
+++ b/django/db/backends/sqlite3/operations.py
@@ -14,6 +14,8 @@ from django.utils import timezone
from django.utils.dateparse import parse_date, parse_datetime, parse_time
from django.utils.functional import cached_property
+from .base import Database
+
class DatabaseOperations(BaseDatabaseOperations):
cast_char_field_without_max_length = "text"
@@ -439,3 +441,6 @@ class DatabaseOperations(BaseDatabaseOperations):
update_fields,
unique_fields,
)
+
+ def force_group_by(self):
+ return ["GROUP BY TRUE"] if Database.sqlite_version_info < (3, 39) else []
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index b28dc925ba..7cec040cee 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -877,6 +877,8 @@ class SQLCompiler:
if self._meta_ordering:
order_by = None
if having:
+ if not grouping:
+ result.extend(self.connection.ops.force_group_by())
result.append("HAVING %s" % having)
params.extend(h_params)
diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py
index 8d8e46e312..a073d01590 100644
--- a/tests/aggregation/tests.py
+++ b/tests/aggregation/tests.py
@@ -2126,6 +2126,15 @@ class AggregateTestCase(TestCase):
qs = Publisher.objects.filter(pk__in=author_qs)
self.assertCountEqual(qs, [self.p1, self.p2, self.p3, self.p4])
+ def test_having_with_no_group_by(self):
+ author_qs = (
+ Author.objects.values(static_value=Value("static-value"))
+ .annotate(sum=Sum("age"))
+ .filter(sum__gte=0)
+ .values_list("sum", flat=True)
+ )
+ self.assertEqual(list(author_qs), [337])
+
class AggregateAnnotationPruningTests(TestCase):
@classmethod