[2.2.x] Fixed #30315 -- Fixed crash of ArrayAgg and StringAgg with ordering when used in Subquery.

Backport of a3f91891d2 from master.
This commit is contained in:
Caio Ariede 2019-05-25 10:19:32 -03:00 committed by Mariusz Felisiak
parent 5bf2c87ece
commit 1172f078eb
3 changed files with 43 additions and 2 deletions

View File

@ -33,6 +33,12 @@ class OrderableAggMixin:
return sql, sql_params + ordering_params
return super().as_sql(compiler, connection, ordering='')
def set_source_expressions(self, exprs):
# Extract the ordering expressions because ORDER BY clause is handled
# in a custom way.
self.ordering = exprs[self._get_ordering_expressions_index():]
return super().set_source_expressions(exprs[:self._get_ordering_expressions_index()])
def get_source_expressions(self):
return self.source_expressions + self.ordering

View File

@ -21,3 +21,7 @@ Bugfixes
* Fixed a regression in Django 2.2 where auto-reloader doesn't detect changes
in ``manage.py`` file when using ``StatReloader`` (:ticket:`30479`).
* Fixed crash of :class:`~django.contrib.postgres.aggregates.ArrayAgg` and
:class:`~django.contrib.postgres.aggregates.StringAgg` with ``ordering``
argument when used in a ``Subquery`` (:ticket:`30315`).

View File

@ -1,7 +1,8 @@
import json
from django.db.models.expressions import F, Value
from django.db.models.functions import Concat, Substr
from django.db.models import CharField
from django.db.models.expressions import F, OuterRef, Subquery, Value
from django.db.models.functions import Cast, Concat, Substr
from django.test.testcases import skipUnlessDBFeature
from django.test.utils import Approximate
@ -203,6 +204,36 @@ class TestGeneralAggregate(PostgreSQLTestCase):
values = AggregateTestModel.objects.none().aggregate(jsonagg=JSONBAgg('integer_field'))
self.assertEqual(values, json.loads('{"jsonagg": []}'))
def test_string_agg_array_agg_ordering_in_subquery(self):
stats = []
for i, agg in enumerate(AggregateTestModel.objects.order_by('char_field')):
stats.append(StatTestModel(related_field=agg, int1=i, int2=i + 1))
stats.append(StatTestModel(related_field=agg, int1=i + 1, int2=i))
StatTestModel.objects.bulk_create(stats)
for aggregate, expected_result in (
(
ArrayAgg('stattestmodel__int1', ordering='-stattestmodel__int2'),
[('Foo1', [0, 1]), ('Foo2', [1, 2]), ('Foo3', [2, 3]), ('Foo4', [3, 4])],
),
(
StringAgg(
Cast('stattestmodel__int1', CharField()),
delimiter=';',
ordering='-stattestmodel__int2',
),
[('Foo1', '0;1'), ('Foo2', '1;2'), ('Foo3', '2;3'), ('Foo4', '3;4')],
),
):
with self.subTest(aggregate=aggregate.__class__.__name__):
subquery = AggregateTestModel.objects.filter(
pk=OuterRef('pk'),
).annotate(agg=aggregate).values('agg')
values = AggregateTestModel.objects.annotate(
agg=Subquery(subquery),
).order_by('char_field').values_list('char_field', 'agg')
self.assertEqual(list(values), expected_result)
class TestAggregateDistinct(PostgreSQLTestCase):
@classmethod