Skip to content

Commit

Permalink
[2.2.x] Fixed #30315 -- Fixed crash of ArrayAgg and StringAgg with or…
Browse files Browse the repository at this point in the history
…dering when used in Subquery.

Backport of a3f9189 from master.
  • Loading branch information
caioariede authored and felixxm committed May 28, 2019
1 parent 5bf2c87 commit 1172f07
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 2 deletions.
6 changes: 6 additions & 0 deletions django/contrib/postgres/aggregates/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ def as_sql(self, compiler, connection):
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

Expand Down
4 changes: 4 additions & 0 deletions docs/releases/2.2.2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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`).
35 changes: 33 additions & 2 deletions tests/postgres_tests/test_aggregates.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -203,6 +204,36 @@ def test_json_agg_empty(self):
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
Expand Down

0 comments on commit 1172f07

Please sign in to comment.