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

This commit is contained in:
Caio Ariede 2019-05-25 10:19:32 -03:00 committed by Mariusz Felisiak
parent b2790f74d4
commit a3f91891d2
3 changed files with 43 additions and 2 deletions

View File

@ -33,6 +33,12 @@ class OrderableAggMixin:
return sql, sql_params + ordering_params return sql, sql_params + ordering_params
return super().as_sql(compiler, connection, ordering='') 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): def get_source_expressions(self):
return self.source_expressions + self.ordering 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 * Fixed a regression in Django 2.2 where auto-reloader doesn't detect changes
in ``manage.py`` file when using ``StatReloader`` (:ticket:`30479`). 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 import json
from django.db.models.expressions import F, Value from django.db.models import CharField
from django.db.models.functions import Concat, Substr from django.db.models.expressions import F, OuterRef, Subquery, Value
from django.db.models.functions import Cast, Concat, Substr
from django.test.utils import Approximate from django.test.utils import Approximate
from . import PostgreSQLTestCase from . import PostgreSQLTestCase
@ -202,6 +203,36 @@ class TestGeneralAggregate(PostgreSQLTestCase):
values = AggregateTestModel.objects.none().aggregate(jsonagg=JSONBAgg('integer_field')) values = AggregateTestModel.objects.none().aggregate(jsonagg=JSONBAgg('integer_field'))
self.assertEqual(values, json.loads('{"jsonagg": []}')) 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): class TestAggregateDistinct(PostgreSQLTestCase):
@classmethod @classmethod