#35235: ArrayAgg() doesn't return default when filter contains __in=[].
----------------------------------+--------------------------------------
Reporter: Per Carlsen | Owner: Sharon Woo
Type: Bug | Status: assigned
Component: contrib.postgres | Version: 5.0
Severity: Normal | Resolution:
Keywords: ArrayAgg | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------+--------------------------------------
Comment (by David Sanders):
I may have misled you there… on GH I mentioned not needing test data but
you do actually need at least one row present in the table to get the
wrong result. (The reason why my simplified test worked is because
there's data in `setupTestData()`)
To explain:
The source of the issue is when the aggregate filter is testing for a
"contradiction" (ie something that's guaranteed to be false), eg:
{{{
ArrayAgg(…, filter=Q(whatever__in=[]))
}}}
here it's guaranteed that the filter will always be false. Django has a
chance to do some optimisations when this occurs.
Normally when you do
{{{
Foo.objects.filter(whatever__in=[])
}}}
Django will raise an `EmptyResultSet` and the catching code will skip
calling the db altogether so as not to run an unnecessary query & save on
time.
However, when it's within an annotation, Django still needs to run the
query. The optimisation that occurs in this case is that Django will
simplify the expression instead.
If you observe the query (by doing `print(queryset.query)`) you'll see
that the annotation has been optimised to:
{{{
SELECT …, COALESCE(NULL, '{}') …
}}}
where Django has deliberately replaced the input to `COALESCE` with
`NULL`.
This reveals the source of the problem: the expression `COALESCE(NULL,
'{}')` is of type string. It _should_ be something along the lines of
`COALESCE(NULL, '{}'::integer[])` to force the expression to be of type
integer array.
Hope that helps? Sorry for the long-winded explanation but it took me
yonks to realise what was happening under the hood with Django so thought
you could use the primer 👍😊
--
Ticket URL: <
https://code.djangoproject.com/ticket/35235#comment:9>