Opened 2 years ago
Closed 2 years ago
#34105 closed Bug (fixed)
Invalid query generated on Postgres when annotate and order_by have common sub-expressions
Reported by: | Matt Westcott | Owned by: | Simon Charette |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
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
If a queryset is annotated with an expression, and that same expression appears as a sub-expression within an order_by clause, the generated SQL query will reference the column alias from the annotation within the ORDER BY expression. This is invalid on PostgreSQL (tested on PostgreSQL 14.2).
This is a regression in https://github.com/django/django/commit/04518e310d4552ff7595a34f5a7f93487d78a406.
Given the following model definition:
# books/models.py class Book(models.Model): title = models.CharField(max_length=255)
the bug can be reproduced with:
from django.db.models.functions import Length, Lower from books.models import Book queryset = Book.objects.annotate(lower_title=Lower("title")).order_by(Length(Lower("title"))) print(queryset.query) list(queryset)
Prior to 04518e310d4552ff7595a34f5a7f93487d78a406, the generated query is
SELECT "books_book"."id", "books_book"."title", LOWER("books_book"."title") AS "lower_title" FROM "books_book" ORDER BY LENGTH(LOWER("books_book"."title")) ASC
Following this commit, the generated query is
SELECT "books_book"."id", "books_book"."title", LOWER("books_book"."title") AS "lower_title" FROM "books_book" ORDER BY LENGTH("lower_title") ASC
which fails with the traceback:
Traceback (most recent call last): File "/Users/matthew/Development/tbx/wagtail/devscript/libs/django/django/db/backends/utils.py", line 89, in _execute return self.cursor.execute(sql, params) psycopg2.errors.UndefinedColumn: column "lower_title" does not exist LINE 1: ...S "lower_title" FROM "books_book" ORDER BY LENGTH("lower_tit... ^ The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<console>", line 1, in <module> File "/Users/matthew/Development/tbx/wagtail/devscript/libs/django/django/db/models/query.py", line 376, in __len__ self._fetch_all() File "/Users/matthew/Development/tbx/wagtail/devscript/libs/django/django/db/models/query.py", line 1894, in _fetch_all self._result_cache = list(self._iterable_class(self)) File "/Users/matthew/Development/tbx/wagtail/devscript/libs/django/django/db/models/query.py", line 87, in __iter__ results = compiler.execute_sql( File "/Users/matthew/Development/tbx/wagtail/devscript/libs/django/django/db/models/sql/compiler.py", line 1520, in execute_sql cursor.execute(sql, params) File "/Users/matthew/Development/tbx/wagtail/devscript/libs/django/django/db/backends/utils.py", line 103, in execute return super().execute(sql, params) File "/Users/matthew/Development/tbx/wagtail/devscript/libs/django/django/db/backends/utils.py", line 67, in execute return self._execute_with_wrappers( File "/Users/matthew/Development/tbx/wagtail/devscript/libs/django/django/db/backends/utils.py", line 80, in _execute_with_wrappers return executor(sql, params, many, context) File "/Users/matthew/Development/tbx/wagtail/devscript/libs/django/django/db/backends/utils.py", line 89, in _execute return self.cursor.execute(sql, params) File "/Users/matthew/Development/tbx/wagtail/devscript/libs/django/django/db/utils.py", line 91, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "/Users/matthew/Development/tbx/wagtail/devscript/libs/django/django/db/backends/utils.py", line 89, in _execute return self.cursor.execute(sql, params) django.db.utils.ProgrammingError: column "lower_title" does not exist LINE 1: ...S "lower_title" FROM "books_book" ORDER BY LENGTH("lower_tit... ^
Change History (5)
comment:1 by , 2 years ago
Cc: | added |
---|---|
Severity: | Normal → Release blocker |
Triage Stage: | Unreviewed → Accepted |
comment:2 by , 2 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
Thanks for testing against the main branch and reporting failures, greatly appreciated!
This is a section of the ORDERY BY
documentation I missed when making this change
Note that an output column name has to stand alone, that is, it cannot be used in an expression — for example, this is not correct:
SELECT a + b AS sum, c FROM table1 ORDER BY sum + c; -- wrong
The good news is that is should be pretty straightforward to solve while still supporting the psycopg3 use case that warranted the change.
follow-up: 4 comment:3 by , 2 years ago
Has patch: | set |
---|
Matt, could you confirm this PR fixes your issue?
comment:4 by , 2 years ago
Replying to Simon Charette:
Matt, could you confirm this PR fixes your issue?
Yep - all working nicely, both on the above test case and the original place I ran into it (https://github.com/wagtail/wagtail/issues/9364). Thanks!
Thanks for the report! Regression in 04518e310d4552ff7595a34f5a7f93487d78a406.