#35339: Ordering and filtering a Postgres ArrayAgg with parameters inverts SQL param order -------------------------------------+------------------------------------- Reporter: Chris M | Owner: nobody Type: | Status: new Uncategorized | Component: Database | Version: 5.0 layer (models, ORM) | Severity: Normal | Keywords: Triage Stage: | Has patch: 0 Unreviewed | Needs documentation: 0 | Needs tests: 0 Patch needs improvement: 0 | Easy pickings: 0 UI/UX: 0 | -------------------------------------+------------------------------------- When trying to build an ArrayAgg annotation that has a `filter` with parameters and a `ordering` with parameters, the SQL that is built will invert the parameters, putting the filter parameters before the ordering parameters. I was not able to find an existing ticket for this issue.
I have verified this against the current dev branch as well as version 3.2. I was able to reproduce this in a test in the test_aggregates.py file. {{{#!python def test_array_agg_filter_and_ordering_params(self): values = AggregateTestModel.objects.aggregate( arrayagg=ArrayAgg( "char_field", filter=Q(json_field__has_key="lang"), ordering=LPad(Cast("integer_field", CharField()), 2, Value("0")), ) ) self.assertEqual(values, {"arrayagg": ["Foo2", "Foo4"]}) }}} The resulting error is something like {{{ Traceback (most recent call last): File "/Users/camuthig/projects/oss/django/django/db/backends/utils.py", line 105, in _execute return self.cursor.execute(sql, params) psycopg2.errors.UndefinedFunction: function lpad(character varying, integer, integer) does not exist LINE 1: ...s_tests_aggregatetestmodel"."char_field" ORDER BY LPAD(("pos... ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. }}} The issue is that the result of the `OrderableAggMixin.as_sql` function is SQL: {{{ ARRAY_AGG("postgres_tests_aggregatetestmodel"."char_field" ORDER BY LPAD(("postgres_tests_aggregatetestmodel"."integer_field")::varchar, %s, %s)) FILTER (WHERE "postgres_tests_aggregatetestmodel"."json_field" ? %s) }}} Parameters {{{ [ "lang", 2, "0"] }}} So we are trying to use "lang" and 2 as the values for the ordering function, and "0" as the parameter for the filtering function. This is made a bit more confusing if the expression you are aggregating also has a parameter, because that should be before the ordering parameters. It should be 1. Expression parameters 2. Ordering parameters 3. Filtering parameters This happens because both the expression and filtering parameters come from the standard Aggregate parent class, and are then put in front of the ordering parameters in the Postgres-specific orderable mixin. I have been able to resolve this issue locally by altering the `OrderableAggMixin.as_sql` function to retrieve the parameters from the parent and then split them manually. {{{#!python class OrderableAggMixin: # ... other functions def as_sql(self, compiler, connection): if self.order_by is not None: order_by_sql, order_by_params = compiler.compile(self.order_by) else: order_by_sql, order_by_params = "", () sql, expression_params = super().as_sql(compiler, connection, ordering=order_by_sql) filter_params = () if self.filter: try: _, filter_params = self.filter.as_sql(compiler, connection) expression_params = expression_params[:-len(filter_params)] except FullResultSet: pass return sql, (*expression_params, *order_by_params, *filter_params) }}} This solution technically works, but it feels a bit clunky, so I am open to suggestions on how to improve it. I can also create a pull request with the change if you would like to see it, though my changes are all captured here already. -- Ticket URL: <https://code.djangoproject.com/ticket/35339> Django <https://code.djangoproject.com/> The Web framework for perfectionists with deadlines. -- You received this message because you are subscribed to the Google Groups "Django updates" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-updates+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-updates/0107018e855e5e26-a8034f57-cc14-4365-92a7-186c14ea8ddb-000000%40eu-central-1.amazonses.com.