Skip to content

Commit 0d223f9

Browse files
fix: preserve subquery structure when unparsing SubqueryAlias over Aggregate
When the SQL unparser encountered a SubqueryAlias node whose direct child was an Aggregate (or other clause-building plan like Window, Sort, Limit, Union), it would flatten the subquery into a simple table alias, losing the aggregate entirely. For example, a plan representing: SELECT j1.col FROM j1 JOIN (SELECT max(id) AS m FROM j2) AS b ON j1.id = b.m would unparse to: SELECT j1.col FROM j1 INNER JOIN j2 AS b ON j1.id = b.m dropping the MAX aggregate and the subquery. Root cause: the SubqueryAlias handler in select_to_sql_recursively would call subquery_alias_inner_query_and_columns (which only unwraps Projection children) and unparse_table_scan_pushdown (which only handles TableScan/SubqueryAlias/Projection). When both returned nothing useful for an Aggregate child, the code recursed directly into the Aggregate, merging its GROUP BY into the outer SELECT instead of wrapping it in a derived subquery. The fix adds an early check: if the SubqueryAlias's direct child is a plan type that builds its own SELECT clauses (Aggregate, Window, Sort, Limit, Union), emit it as a derived subquery via self.derive() with the alias always attached, rather than falling through to the recursive path that would flatten it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2f8f667 commit 0d223f9

2 files changed

Lines changed: 5 additions & 2 deletions

File tree

datafusion/sql/src/unparser/plan.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,7 @@ impl Unparser<'_> {
10851085
/// must be emitted as a derived subquery `(SELECT ...) AS alias`.
10861086
///
10871087
/// Plans like Aggregate or Window build their own SELECT clauses (GROUP BY,
1088-
/// window functions).
1088+
/// window functions).
10891089
fn requires_derived_subquery(plan: &LogicalPlan) -> bool {
10901090
matches!(
10911091
plan,

datafusion/sql/tests/cases/plan_to_sql.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2916,7 +2916,10 @@ fn test_unparse_manual_join_with_subquery_aggregate() -> Result<()> {
29162916
// Build the right side: SELECT max(j2_id) AS max_id FROM j2
29172917
let right_scan = table_scan(Some("j2"), &j2_schema, None)?.build()?;
29182918
let right_agg = LogicalPlanBuilder::from(right_scan)
2919-
.aggregate(vec![] as Vec<Expr>, vec![max(col("j2.j2_id")).alias("max_id")])?
2919+
.aggregate(
2920+
vec![] as Vec<Expr>,
2921+
vec![max(col("j2.j2_id")).alias("max_id")],
2922+
)?
29202923
.build()?;
29212924
let right_subquery = subquery_alias(right_agg, "b")?;
29222925

0 commit comments

Comments
 (0)