Refs #28333 -- Explicitly ordered outer qualify query on window filtering.
While most backends will propagate derived table ordering as long as the outer query doesn't perform additional processing the SQL specs doesn't explicitly state the ordering must be maintained.
This commit is contained in:
parent
a69b0e9cfe
commit
3ba7f2e906
|
@ -607,24 +607,41 @@ class SQLCompiler:
|
||||||
select = {
|
select = {
|
||||||
expr: alias for expr, _, alias in self.get_select(with_col_aliases=True)[0]
|
expr: alias for expr, _, alias in self.get_select(with_col_aliases=True)[0]
|
||||||
}
|
}
|
||||||
|
select_aliases = set(select.values())
|
||||||
qual_aliases = set()
|
qual_aliases = set()
|
||||||
replacements = {}
|
replacements = {}
|
||||||
expressions = list(self.qualify.leaves())
|
|
||||||
while expressions:
|
def collect_replacements(expressions):
|
||||||
expr = expressions.pop()
|
while expressions:
|
||||||
if select_alias := (select.get(expr) or replacements.get(expr)):
|
expr = expressions.pop()
|
||||||
replacements[expr] = select_alias
|
if expr in replacements:
|
||||||
elif isinstance(expr, Lookup):
|
continue
|
||||||
expressions.extend(expr.get_source_expressions())
|
elif select_alias := select.get(expr):
|
||||||
else:
|
replacements[expr] = select_alias
|
||||||
num_qual_alias = len(qual_aliases)
|
elif isinstance(expr, Lookup):
|
||||||
select_alias = f"qual{num_qual_alias}"
|
expressions.extend(expr.get_source_expressions())
|
||||||
qual_aliases.add(select_alias)
|
elif isinstance(expr, Ref):
|
||||||
inner_query.add_annotation(expr, select_alias)
|
if expr.refs not in select_aliases:
|
||||||
replacements[expr] = select_alias
|
expressions.extend(expr.get_source_expressions())
|
||||||
|
else:
|
||||||
|
num_qual_alias = len(qual_aliases)
|
||||||
|
select_alias = f"qual{num_qual_alias}"
|
||||||
|
qual_aliases.add(select_alias)
|
||||||
|
inner_query.add_annotation(expr, select_alias)
|
||||||
|
replacements[expr] = select_alias
|
||||||
|
|
||||||
|
collect_replacements(list(self.qualify.leaves()))
|
||||||
self.qualify = self.qualify.replace_expressions(
|
self.qualify = self.qualify.replace_expressions(
|
||||||
{expr: Ref(alias, expr) for expr, alias in replacements.items()}
|
{expr: Ref(alias, expr) for expr, alias in replacements.items()}
|
||||||
)
|
)
|
||||||
|
order_by = []
|
||||||
|
for order_by_expr, *_ in self.get_order_by():
|
||||||
|
collect_replacements(order_by_expr.get_source_expressions())
|
||||||
|
order_by.append(
|
||||||
|
order_by_expr.replace_expressions(
|
||||||
|
{expr: Ref(alias, expr) for expr, alias in replacements.items()}
|
||||||
|
)
|
||||||
|
)
|
||||||
inner_query_compiler = inner_query.get_compiler(
|
inner_query_compiler = inner_query.get_compiler(
|
||||||
self.using, elide_empty=self.elide_empty
|
self.using, elide_empty=self.elide_empty
|
||||||
)
|
)
|
||||||
|
@ -657,7 +674,18 @@ class SQLCompiler:
|
||||||
")",
|
")",
|
||||||
self.connection.ops.quote_name("qualify_mask"),
|
self.connection.ops.quote_name("qualify_mask"),
|
||||||
]
|
]
|
||||||
return result, list(inner_params) + qualify_params
|
params = list(inner_params) + qualify_params
|
||||||
|
# As the SQL spec is unclear on whether or not derived tables
|
||||||
|
# ordering must propagate it has to be explicitly repeated on the
|
||||||
|
# outer-most query to ensure it's preserved.
|
||||||
|
if order_by:
|
||||||
|
ordering_sqls = []
|
||||||
|
for ordering in order_by:
|
||||||
|
ordering_sql, ordering_params = self.compile(ordering)
|
||||||
|
ordering_sqls.append(ordering_sql)
|
||||||
|
params.extend(ordering_params)
|
||||||
|
result.extend(["ORDER BY", ", ".join(ordering_sqls)])
|
||||||
|
return result, params
|
||||||
|
|
||||||
def as_sql(self, with_limits=True, with_col_aliases=False):
|
def as_sql(self, with_limits=True, with_col_aliases=False):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue