diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 8d6b6678286..318e6b87071 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 97b3f97b68f..865e1728162 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)