diff options
| author | Simon Charette <charette.s@gmail.com> | 2023-02-17 20:38:08 -0500 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2023-02-20 06:22:18 +0100 |
| commit | aab25a69dd09e6717ff86175ff62c29b847a7791 (patch) | |
| tree | ffecc4f2f9d2415f0206b7f8183522f8fd74bf56 /django/db/models/sql/compiler.py | |
| parent | 312d0f88b4ba47278f53f37ee2735309eda3e913 (diff) | |
[4.2.x] Fixed #34346 -- Ordered selected expressions by position.
Used the same approach as for #34176 by using selected expressions
position to prevent ambiguous aliases in collisions.
Thanks henribru for the report.
Regression in 04518e310d4552ff7595a34f5a7f93487d78a406.
Backport of 278881e37619278789942513916acafaa88d26f3 from main
Diffstat (limited to 'django/db/models/sql/compiler.py')
| -rw-r--r-- | django/db/models/sql/compiler.py | 43 |
1 files changed, 35 insertions, 8 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index c07076d54a..348cfa381a 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -27,6 +27,15 @@ from django.utils.hashable import make_hashable from django.utils.regex_helper import _lazy_re_compile +class PositionRef(Ref): + def __init__(self, ordinal, refs, source): + self.ordinal = ordinal + super().__init__(refs, source) + + def as_sql(self, compiler, connection): + return str(self.ordinal), () + + class SQLCompiler: # Multiline ordering SQL clause may appear from RawSQL. ordering_parts = _lazy_re_compile( @@ -321,6 +330,14 @@ class SQLCompiler: else: default_order, _ = ORDER_DIR["DESC"] + selected_exprs = {} + if select := self.select: + for ordinal, (expr, _, alias) in enumerate(select, start=1): + pos_expr = PositionRef(ordinal, alias, expr) + if alias: + selected_exprs[alias] = pos_expr + selected_exprs[expr] = pos_expr + for field in ordering: if hasattr(field, "resolve_expression"): if isinstance(field, Value): @@ -331,13 +348,23 @@ class SQLCompiler: if not self.query.standard_ordering: field = field.copy() field.reverse_ordering() - if isinstance(field.expression, F) and ( - annotation := self.query.annotation_select.get( - field.expression.name - ) + select_ref = selected_exprs.get(field.expression) + if select_ref or ( + isinstance(field.expression, F) + and (select_ref := selected_exprs.get(field.expression.name)) ): - field.expression = Ref(field.expression.name, annotation) - yield field, isinstance(field.expression, Ref) + # Emulation of NULLS (FIRST|LAST) cannot be combined with + # the usage of ordering by position. + if ( + field.nulls_first is None and field.nulls_last is None + ) or self.connection.features.supports_order_by_nulls_modifier: + field.expression = select_ref + # Alias collisions are not possible when dealing with + # combined queries so fallback to it if emulation of NULLS + # handling is required. + elif self.query.combinator: + field.expression = Ref(select_ref.refs, select_ref.source) + yield field, select_ref is not None continue if field == "?": # random yield OrderBy(Random()), False @@ -346,11 +373,11 @@ class SQLCompiler: col, order = get_order_dir(field, default_order) descending = order == "DESC" - if col in self.query.annotation_select: + if select_ref := selected_exprs.get(col): # Reference to expression in SELECT clause yield ( OrderBy( - Ref(col, self.query.annotation_select[col]), + select_ref, descending=descending, ), True, |
