diff options
author | Simon Charette <charette.s@gmail.com> | 2021-05-21 22:32:16 -0400 |
---|---|---|
committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-07-02 07:25:42 +0200 |
commit | 9f3cce172f6913c5ac74272fa5fc07f847b4e112 (patch) | |
tree | 027dec2a7a1bef8c02c2e58ffded8718401b28d1 /tests/aggregation | |
parent | f3112fde981052801e0c2121027424496c03efdf (diff) | |
download | django-9f3cce172f6913c5ac74272fa5fc07f847b4e112.tar.gz |
Refs #26430 -- Re-introduced empty aggregation optimization.
The introduction of the Expression.empty_aggregate_value interface
allows the compilation stage to enable the EmptyResultSet optimization
if all the aggregates expressions implement it.
This also removes unnecessary RegrCount/Count.convert_value() methods.
Disabling the empty result set aggregation optimization when it wasn't
appropriate prevented None returned for a Count aggregation value.
Thanks Nick Pope for the review.
Diffstat (limited to 'tests/aggregation')
-rw-r--r-- | tests/aggregation/tests.py | 84 |
1 files changed, 57 insertions, 27 deletions
diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py index db24f36a79..f7b2331d34 100644 --- a/tests/aggregation/tests.py +++ b/tests/aggregation/tests.py @@ -8,7 +8,7 @@ from django.db.models import ( Avg, Case, Count, DecimalField, DurationField, Exists, F, FloatField, IntegerField, Max, Min, OuterRef, Subquery, Sum, Value, When, ) -from django.db.models.expressions import RawSQL +from django.db.models.expressions import Func, RawSQL from django.db.models.functions import Coalesce, Greatest from django.test import TestCase from django.test.testcases import skipUnlessDBFeature @@ -1342,33 +1342,63 @@ class AggregateTestCase(TestCase): ('Peter Norvig', 2), ], lambda a: (a.name, a.contact_count), ordered=False) + def test_empty_result_optimization(self): + with self.assertNumQueries(0): + self.assertEqual( + Publisher.objects.none().aggregate( + sum_awards=Sum('num_awards'), + books_count=Count('book'), + ), { + 'sum_awards': None, + 'books_count': 0, + } + ) + # Expression without empty_aggregate_value forces queries to be + # executed even if they would return an empty result set. + raw_books_count = Func('book', function='COUNT') + raw_books_count.contains_aggregate = True + with self.assertNumQueries(1): + self.assertEqual( + Publisher.objects.none().aggregate( + sum_awards=Sum('num_awards'), + books_count=raw_books_count, + ), { + 'sum_awards': None, + 'books_count': 0, + } + ) + def test_coalesced_empty_result_set(self): - self.assertEqual( - Publisher.objects.none().aggregate( - sum_awards=Coalesce(Sum('num_awards'), 0), - )['sum_awards'], - 0, - ) + with self.assertNumQueries(0): + self.assertEqual( + Publisher.objects.none().aggregate( + sum_awards=Coalesce(Sum('num_awards'), 0), + )['sum_awards'], + 0, + ) # Multiple expressions. - self.assertEqual( - Publisher.objects.none().aggregate( - sum_awards=Coalesce(Sum('num_awards'), None, 0), - )['sum_awards'], - 0, - ) + with self.assertNumQueries(0): + self.assertEqual( + Publisher.objects.none().aggregate( + sum_awards=Coalesce(Sum('num_awards'), None, 0), + )['sum_awards'], + 0, + ) # Nested coalesce. - self.assertEqual( - Publisher.objects.none().aggregate( - sum_awards=Coalesce(Coalesce(Sum('num_awards'), None), 0), - )['sum_awards'], - 0, - ) + with self.assertNumQueries(0): + self.assertEqual( + Publisher.objects.none().aggregate( + sum_awards=Coalesce(Coalesce(Sum('num_awards'), None), 0), + )['sum_awards'], + 0, + ) # Expression coalesce. - self.assertIsInstance( - Store.objects.none().aggregate( - latest_opening=Coalesce( - Max('original_opening'), RawSQL('CURRENT_TIMESTAMP', []), - ), - )['latest_opening'], - datetime.datetime, - ) + with self.assertNumQueries(1): + self.assertIsInstance( + Store.objects.none().aggregate( + latest_opening=Coalesce( + Max('original_opening'), RawSQL('CURRENT_TIMESTAMP', []), + ), + )['latest_opening'], + datetime.datetime, + ) |