summaryrefslogtreecommitdiff
path: root/tests/aggregation
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2021-05-21 22:32:16 -0400
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-07-02 07:25:42 +0200
commit9f3cce172f6913c5ac74272fa5fc07f847b4e112 (patch)
tree027dec2a7a1bef8c02c2e58ffded8718401b28d1 /tests/aggregation
parentf3112fde981052801e0c2121027424496c03efdf (diff)
downloaddjango-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.py84
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,
+ )