From b863c5ffde0bafa5eaa9f262103eaeb71877787c Mon Sep 17 00:00:00 2001 From: David Sanders Date: Mon, 13 Nov 2023 00:45:52 +1100 Subject: [PATCH] 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 --- django/db/backends/base/operations.py | 7 +++++++ django/db/backends/sqlite3/operations.py | 5 +++++ django/db/models/sql/compiler.py | 2 ++ tests/aggregation/tests.py | 9 +++++++++ 4 files changed, 23 insertions(+) diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py index b1d818d90e2..d5a40eb46ee 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 85ad8043487..dfc9857b84c 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 b28dc925ba3..7cec040ceee 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 8d8e46e312b..a073d01590f 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