Opened 18 hours ago
Last modified 18 hours ago
#31136 assigned Bug
Multiple annotations with Exists() should not group by aliases.
| Reported by: | Sigurd Ljødal | Owned by: | felixxm |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 3.0 |
| Severity: | Release blocker | Keywords: | |
| Cc: | Simon Charette | Triage Stage: | Accepted |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
There's a regression in Django 3.0 where an annotation that is not selected is included in the group by section of the query on PostgreSQL. I looks a bit like https://code.djangoproject.com/ticket/31109, but it is still present in Django 3.0.2 (I haven't tested earlier versions yet). I haven't had time to look into where the incorrect group by cause is generated yet.
I have created a test case to reproduce the issue here: https://github.com/django/django/compare/master...ljodal:regression/annotate-groupby-postgresql
It only crashes when running against PostgreSQL. Here's the test case query:
latest_book_pubdate_qs = Book.objects.filter( publisher=OuterRef('pk') ).values_list('id') publisher_qs = Publisher.objects.values('pk').annotate( has_published_books=Exists(latest_book_pubdate_qs), ).filter( has_published_books=True ).annotate( book_count=Count('book') ).values_list('id')
The generated query is this:
SELECT "aggregation_publisher"."id" FROM "aggregation_publisher" LEFT OUTER JOIN "aggregation_book" ON ("aggregation_publisher"."id" = "aggregation_book"."publisher_id") WHERE EXISTS(SELECT U0."id" FROM "aggregation_book" U0 WHERE U0."publisher_id" = "aggregation_publisher"."id") GROUP BY "aggregation_publisher"."id", "has_published_books"
Which crashes because "has_published_books" in the "GROUP BY" clause is not a column on the table:
Traceback (most recent call last):
File "/Users/sigurdljodal/Code/django/tests/aggregation/tests.py", line 1166, in test_aggregation_annotation
print(list(publisher_qs))
File "/Users/sigurdljodal/Code/django/django/db/models/query.py", line 270, in __len__
self._fetch_all()
File "/Users/sigurdljodal/Code/django/django/db/models/query.py", line 1284, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "/Users/sigurdljodal/Code/django/django/db/models/query.py", line 144, in __iter__
return compiler.results_iter(tuple_expected=True, chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File "/Users/sigurdljodal/Code/django/django/db/models/sql/compiler.py", line 1085, in results_iter
results = self.execute_sql(MULTI, chunked_fetch=chunked_fetch, chunk_size=chunk_size)
File "/Users/sigurdljodal/Code/django/django/db/models/sql/compiler.py", line 1133, in execute_sql
cursor.execute(sql, params)
File "/Users/sigurdljodal/Code/django/django/db/backends/utils.py", line 66, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/Users/sigurdljodal/Code/django/django/db/backends/utils.py", line 75, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/Users/sigurdljodal/Code/django/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "/Users/sigurdljodal/Code/django/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/Users/sigurdljodal/Code/django/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: column "has_published_books" does not exist
LINE 1: ...her"."id") GROUP BY "aggregation_publisher"."id", "has_publi...
Change History (3)
comment:1 Changed 18 hours ago by
| Cc: | Simon Charette added |
|---|---|
| Severity: | Normal → Release blocker |
| Summary: | Group by with annotation regression on PostgreSQL → Multiple annotations with Exists() should not group by aliases. |
| Triage Stage: | Unreviewed → Accepted |
comment:2 Changed 18 hours ago by
| Owner: | changed from nobody to felixxm |
|---|---|
| Status: | new → assigned |
Thanks for this report. As a temporary workaround you can use
Exists()directly inQuerySet.filter()(without an annotation), i.e.Publisher.objects.values('pk').filter( Exists(latest_book_pubdate_qs) ).annotate( book_count=Count('book') ).values_list('id')Regression in fb3f034f1c63160c0ff13c609acd01c18be12f80.
Reproduced at 495d7a1ddf3c8c83d49f099ee3599afbc28306fe.