mirror of
https://github.com/django/django.git
synced 2025-08-31 15:57:45 +00:00
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.
This commit is contained in:
parent
f3112fde98
commit
9f3cce172f
8 changed files with 101 additions and 34 deletions
|
@ -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,
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue