summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2022-10-29 03:21:25 -0400
committerGitHub <noreply@github.com>2022-10-29 09:21:25 +0200
commitc2cc80756b8949cdd87b88bbfdfee698ced441e0 (patch)
tree64793d602461a8951b3d1a997d85834406137dd9
parent09397f5cfade93c2e79a391ded7ab6b01e51730e (diff)
Fixed #34125 -- Fixed sliced QuerySet.union() crash on a single non-empty queryset.
The bug existed since sliced query union was added but was elevated to query union slices by moving the .exists() optimization to the compiler in 3d734c09ff0138441dfe0a59010435871d17950f. Thanks Stefan Hammer for the report.
-rw-r--r--django/db/models/sql/compiler.py6
-rw-r--r--tests/queries/test_qs_combinators.py26
2 files changed, 31 insertions, 1 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 8d6b667828..318e6b8707 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -531,7 +531,6 @@ class SQLCompiler:
compilers = [
query.get_compiler(self.using, self.connection, self.elide_empty)
for query in self.query.combined_queries
- if not query.is_empty()
]
if not features.supports_slicing_ordering_in_compound:
for compiler in compilers:
@@ -546,6 +545,11 @@ class SQLCompiler:
elif self.query.is_sliced and combinator == "union":
limit = (self.query.low_mark, self.query.high_mark)
for compiler in compilers:
+ # A sliced union cannot have its parts elided as some of them
+ # might be sliced as well and in the event where only a single
+ # part produces a non-empty resultset it might be impossible to
+ # generate valid SQL.
+ compiler.elide_empty = False
if not compiler.query.is_sliced:
compiler.query.set_limits(*limit)
parts = ()
diff --git a/tests/queries/test_qs_combinators.py b/tests/queries/test_qs_combinators.py
index 97b3f97b68..865e172816 100644
--- a/tests/queries/test_qs_combinators.py
+++ b/tests/queries/test_qs_combinators.py
@@ -61,6 +61,32 @@ class QuerySetSetOperationTests(TestCase):
self.assertSequenceEqual(qs3.none(), [])
self.assertNumbersEqual(qs3, [0, 1, 8, 9], ordered=False)
+ def test_union_none_slice(self):
+ qs1 = Number.objects.filter(num__lte=0)
+ qs2 = Number.objects.none()
+ qs3 = qs1.union(qs2)
+ self.assertNumbersEqual(qs3[:1], [0])
+
+ def test_union_empty_filter_slice(self):
+ qs1 = Number.objects.filter(num__lte=0)
+ qs2 = Number.objects.filter(pk__in=[])
+ qs3 = qs1.union(qs2)
+ self.assertNumbersEqual(qs3[:1], [0])
+
+ @skipUnlessDBFeature("supports_slicing_ordering_in_compound")
+ def test_union_slice_compound_empty(self):
+ qs1 = Number.objects.filter(num__lte=0)[:1]
+ qs2 = Number.objects.none()
+ qs3 = qs1.union(qs2)
+ self.assertNumbersEqual(qs3[:1], [0])
+
+ @skipUnlessDBFeature("supports_slicing_ordering_in_compound")
+ def test_union_combined_slice_compound_empty(self):
+ qs1 = Number.objects.filter(num__lte=2)[:3]
+ qs2 = Number.objects.none()
+ qs3 = qs1.union(qs2)
+ self.assertNumbersEqual(qs3.order_by("num")[2:3], [2])
+
def test_union_order_with_null_first_last(self):
Number.objects.filter(other_num=5).update(other_num=None)
qs1 = Number.objects.filter(num__lte=1)