datafusion_sql/unparser/plan.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use super::{
19 Unparser,
20 ast::{
21 BuilderError, DerivedRelationBuilder, QueryBuilder, RelationBuilder,
22 SelectBuilder, TableRelationBuilder, TableWithJoinsBuilder,
23 },
24 rewrite::{
25 TableAliasRewriter, inject_column_aliases_into_subquery, normalize_union_schema,
26 rewrite_plan_for_sort_on_non_projected_fields,
27 subquery_alias_inner_query_and_columns,
28 },
29 utils::{
30 find_agg_node_within_select, find_unnest_node_within_select,
31 find_window_nodes_within_select, try_transform_to_simple_table_scan_with_filters,
32 unproject_sort_expr, unproject_unnest_expr,
33 unproject_unnest_expr_as_flatten_value, unproject_window_exprs,
34 },
35};
36use crate::unparser::extension_unparser::{
37 UnparseToStatementResult, UnparseWithinStatementResult,
38};
39use crate::unparser::utils::{find_unnest_node_until_relation, unproject_agg_exprs};
40use crate::unparser::{
41 ast::FlattenRelationBuilder, ast::UnnestRelationBuilder, rewrite::rewrite_qualify,
42};
43use crate::utils::UNNEST_PLACEHOLDER;
44use datafusion_common::{
45 Column, DFSchema, DataFusionError, Result, ScalarValue, TableReference,
46 assert_or_internal_err, internal_datafusion_err, internal_err, not_impl_err,
47 tree_node::{Transformed, TransformedResult, TreeNode, TreeNodeRecursion},
48 utils::combine_limit,
49};
50use datafusion_expr::expr::{OUTER_REFERENCE_COLUMN_PREFIX, UNNEST_COLUMN_PREFIX};
51use datafusion_expr::{
52 Aggregate, BinaryExpr, Distinct, Expr, FetchType, JoinConstraint, JoinType,
53 LogicalPlan, LogicalPlanBuilder, Operator, Projection, SkipType, Sort, SortExpr,
54 TableScan, Unnest, UserDefinedLogicalNode, Window, expr::Alias,
55};
56use sqlparser::ast::{self, Ident, OrderByKind, SetExpr, TableAliasColumnDef};
57use std::{sync::Arc, vec};
58
59/// Convert a DataFusion [`LogicalPlan`] to [`ast::Statement`]
60///
61/// This function is the opposite of [`SqlToRel::sql_statement_to_plan`] and can
62/// be used to, among other things, to convert `LogicalPlan`s to SQL strings.
63///
64/// # Errors
65///
66/// This function returns an error if the plan cannot be converted to SQL.
67///
68/// # See Also
69///
70/// * [`expr_to_sql`] for converting [`Expr`], a single expression to SQL
71///
72/// # Example
73/// ```
74/// use arrow::datatypes::{DataType, Field, Schema};
75/// use datafusion_expr::{col, logical_plan::table_scan};
76/// use datafusion_sql::unparser::plan_to_sql;
77/// let schema = Schema::new(vec![
78/// Field::new("id", DataType::Utf8, false),
79/// Field::new("value", DataType::Utf8, false),
80/// ]);
81/// // Scan 'table' and select columns 'id' and 'value'
82/// let plan = table_scan(Some("table"), &schema, None)
83/// .unwrap()
84/// .project(vec![col("id"), col("value")])
85/// .unwrap()
86/// .build()
87/// .unwrap();
88/// // convert to AST
89/// let sql = plan_to_sql(&plan).unwrap();
90/// // use the Display impl to convert to SQL text
91/// assert_eq!(
92/// sql.to_string(),
93/// "SELECT \"table\".id, \"table\".\"value\" FROM \"table\""
94/// )
95/// ```
96///
97/// [`SqlToRel::sql_statement_to_plan`]: crate::planner::SqlToRel::sql_statement_to_plan
98/// [`expr_to_sql`]: crate::unparser::expr_to_sql
99pub fn plan_to_sql(plan: &LogicalPlan) -> Result<ast::Statement> {
100 let unparser = Unparser::default();
101 unparser.plan_to_sql(plan)
102}
103
104/// Aggregate-expression scope for one rendered SELECT block.
105///
106/// When an aggregate's input is itself emitted as a derived subquery (a
107/// projection sits between the aggregate and its relation), the input columns
108/// are only reachable by that derived table's output names. Base-table
109/// qualifiers like `t.col` name a relation that is out of scope above the
110/// boundary, so emitting them produces SQL a strict engine rejects.
111///
112/// Every clause that renders an aggregate expression (SELECT / GROUP BY /
113/// HAVING / QUALIFY / ORDER BY) has to apply the same rule. Detect the
114/// boundary once here and reuse it, so the clauses can't drift apart (which is
115/// how earlier fixes left some clauses correct and others not).
116struct UnparserAggScope<'a> {
117 agg: &'a Aggregate,
118 /// `agg.input` renders as a derived projection, so out-of-scope qualifiers
119 /// must be stripped from expressions in this scope.
120 input_is_derived_projection: bool,
121}
122
123impl<'a> UnparserAggScope<'a> {
124 fn new(agg: &'a Aggregate) -> Self {
125 Self {
126 agg,
127 input_is_derived_projection: Unparser::contains_projection_before_relation(
128 agg.input.as_ref(),
129 ),
130 }
131 }
132
133 /// Prepare a projected column or predicate that still references the
134 /// aggregate by its output columns: unproject it back onto the aggregate
135 /// (and `windows`) expressions, then normalize it for this scope.
136 fn prepare(&self, expr: Expr, windows: Option<&[&Window]>) -> Result<Expr> {
137 self.normalize(unproject_agg_exprs(expr, self.agg, windows)?)
138 }
139
140 /// Normalize an expression that is already in aggregate form (group / aggr
141 /// exprs, or an unprojected sort expr): strip the qualifiers that fall out
142 /// of scope once the input is a derived projection. No-op otherwise.
143 fn normalize(&self, expr: Expr) -> Result<Expr> {
144 if self.input_is_derived_projection {
145 Unparser::strip_column_qualifiers_for_schema(
146 expr,
147 self.agg.input.schema().as_ref(),
148 )
149 } else {
150 Ok(expr)
151 }
152 }
153
154 /// Unproject a sort expression onto this aggregate, then normalize it so
155 /// ORDER BY uses the same scope as the other clauses.
156 fn prepare_sort_expr(
157 &self,
158 sort_expr: SortExpr,
159 input: &LogicalPlan,
160 ) -> Result<SortExpr> {
161 let mut sort_expr = unproject_sort_expr(sort_expr, Some(self.agg), input)?;
162 sort_expr.expr = self.normalize(sort_expr.expr)?;
163 Ok(sort_expr)
164 }
165}
166
167impl Unparser<'_> {
168 pub fn plan_to_sql(&self, plan: &LogicalPlan) -> Result<ast::Statement> {
169 let mut plan = normalize_union_schema(plan)?;
170 if !self.dialect.supports_qualify() {
171 plan = rewrite_qualify(plan)?;
172 }
173
174 match plan {
175 LogicalPlan::Projection(_)
176 | LogicalPlan::Filter(_)
177 | LogicalPlan::Window(_)
178 | LogicalPlan::Aggregate(_)
179 | LogicalPlan::Sort(_)
180 | LogicalPlan::Join(_)
181 | LogicalPlan::Repartition(_)
182 | LogicalPlan::Union(_)
183 | LogicalPlan::TableScan(_)
184 | LogicalPlan::EmptyRelation(_)
185 | LogicalPlan::Subquery(_)
186 | LogicalPlan::SubqueryAlias(_)
187 | LogicalPlan::Limit(_)
188 | LogicalPlan::Statement(_)
189 | LogicalPlan::Values(_)
190 | LogicalPlan::Distinct(_) => self.select_to_sql_statement(&plan),
191 LogicalPlan::Dml(_) => self.dml_to_sql(&plan),
192 LogicalPlan::Extension(extension) => {
193 self.extension_to_statement(extension.node.as_ref())
194 }
195 LogicalPlan::Explain(_)
196 | LogicalPlan::Analyze(_)
197 | LogicalPlan::Ddl(_)
198 | LogicalPlan::Copy(_)
199 | LogicalPlan::DescribeTable(_)
200 | LogicalPlan::RecursiveQuery(_)
201 | LogicalPlan::Unnest(_) => not_impl_err!("Unsupported plan: {plan:?}"),
202 }
203 }
204
205 /// Try to unparse a [UserDefinedLogicalNode] to a SQL statement.
206 /// If multiple unparsers are registered for the same [UserDefinedLogicalNode],
207 /// the first unparsing result will be returned.
208 fn extension_to_statement(
209 &self,
210 node: &dyn UserDefinedLogicalNode,
211 ) -> Result<ast::Statement> {
212 let mut statement = None;
213 for unparser in &self.extension_unparsers {
214 match unparser.unparse_to_statement(node, self)? {
215 UnparseToStatementResult::Modified(stmt) => {
216 statement = Some(stmt);
217 break;
218 }
219 UnparseToStatementResult::Unmodified => {}
220 }
221 }
222 if let Some(statement) = statement {
223 Ok(statement)
224 } else {
225 not_impl_err!("Unsupported extension node: {node:?}")
226 }
227 }
228
229 /// Try to unparse a [UserDefinedLogicalNode] to a SQL statement.
230 /// If multiple unparsers are registered for the same [UserDefinedLogicalNode],
231 /// the first unparser supporting the node will be used.
232 fn extension_to_sql(
233 &self,
234 node: &dyn UserDefinedLogicalNode,
235 query: &mut Option<&mut QueryBuilder>,
236 select: &mut Option<&mut SelectBuilder>,
237 relation: &mut Option<&mut RelationBuilder>,
238 ) -> Result<()> {
239 for unparser in &self.extension_unparsers {
240 match unparser.unparse(node, self, query, select, relation)? {
241 UnparseWithinStatementResult::Modified => return Ok(()),
242 UnparseWithinStatementResult::Unmodified => {}
243 }
244 }
245 not_impl_err!("Unsupported extension node: {node:?}")
246 }
247
248 fn select_to_sql_statement(&self, plan: &LogicalPlan) -> Result<ast::Statement> {
249 let mut query_builder = Some(QueryBuilder::default());
250
251 let body = self.select_to_sql_expr(plan, &mut query_builder)?;
252
253 let query = query_builder.unwrap().body(Box::new(body)).build()?;
254
255 Ok(ast::Statement::Query(Box::new(query)))
256 }
257
258 fn select_to_sql_expr(
259 &self,
260 plan: &LogicalPlan,
261 query: &mut Option<QueryBuilder>,
262 ) -> Result<SetExpr> {
263 let mut select_builder = SelectBuilder::default();
264 select_builder.push_from(TableWithJoinsBuilder::default());
265 let mut relation_builder = RelationBuilder::default();
266 self.select_to_sql_recursively(
267 plan,
268 query,
269 &mut select_builder,
270 &mut relation_builder,
271 )?;
272
273 // If we were able to construct a full body (i.e. UNION ALL), return it
274 if let Some(body) = query.as_mut().and_then(|q| q.take_body()) {
275 return Ok(*body);
276 }
277
278 // If no projection is set, add a wildcard projection to the select
279 // which will be translated to `SELECT *` in the SQL statement
280 if !select_builder.already_projected() {
281 select_builder.projection(vec![ast::SelectItem::Wildcard(
282 ast::WildcardAdditionalOptions::default(),
283 )]);
284 }
285
286 let mut twj = select_builder.pop_from().unwrap();
287 twj.relation(relation_builder);
288 select_builder.push_from(twj);
289
290 Ok(SetExpr::Select(Box::new(select_builder.build()?)))
291 }
292
293 /// Reconstructs a SELECT SQL statement from a logical plan by
294 /// unprojecting column expressions found in a [Projection] node. This
295 /// requires scanning the plan tree for relevant Aggregate and Window
296 /// nodes and matching column expressions to the appropriate agg or
297 /// window expressions.
298 ///
299 /// `fully_absorbed` reports whether the Projection arm was able to
300 /// absorb every `Sort`/`Limit` node between this Projection and the
301 /// Aggregate/Window into the current SELECT. When `false`, the
302 /// Aggregate/Window will end up in a derived subquery, so we fall
303 /// back to passthrough column references that resolve against that
304 /// subquery's output instead of unprojecting onto the original
305 /// aggregate expressions.
306 ///
307 /// Returns `true` if an Aggregate node was found and claimed for this
308 /// SELECT.
309 fn reconstruct_select_statement(
310 &self,
311 plan: &LogicalPlan,
312 p: &Projection,
313 select: &mut SelectBuilder,
314 fully_absorbed: bool,
315 ) -> Result<bool> {
316 let mut exprs = p.expr.clone();
317
318 // If an Unnest node is found within the select, find and unproject the unnest column
319 let flatten_alias = select.current_flatten_alias();
320 if let Some(unnest) = find_unnest_node_within_select(plan) {
321 if let Some(ref alias) = flatten_alias {
322 exprs = exprs
323 .into_iter()
324 .map(|e| unproject_unnest_expr_as_flatten_value(e, unnest, alias))
325 .collect::<Result<Vec<_>>>()?;
326 } else {
327 exprs = exprs
328 .into_iter()
329 .map(|e| unproject_unnest_expr(e, unnest))
330 .collect::<Result<Vec<_>>>()?;
331 }
332 };
333
334 // Rewrite column references that point to FLATTEN table aliases:
335 // in Snowflake, FLATTEN output is accessed via .VALUE, not the
336 // original column name.
337 if !select.flatten_table_aliases_empty() {
338 exprs = exprs
339 .into_iter()
340 .map(|e| {
341 e.transform(|expr| {
342 if let Expr::Column(ref col) = expr
343 && let Some(ref relation) = col.relation
344 && select.is_flatten_table_alias(relation.table())
345 {
346 return Ok(Transformed::yes(Expr::Column(Column::new(
347 Some(relation.clone()),
348 "VALUE",
349 ))));
350 }
351 Ok(Transformed::no(expr))
352 })
353 .map(|t| t.data)
354 })
355 .collect::<Result<Vec<_>>>()?;
356 }
357
358 // When some Sort/Limit nodes between this Projection and the
359 // Aggregate/Window couldn't be absorbed into the current SELECT,
360 // the Aggregate/Window will live inside a derived subquery. In
361 // that case we use the passthrough projection path — column refs
362 // resolve against the derived subquery's output columns instead
363 // of being unprojected onto the original aggregate/window
364 // expressions.
365 let agg = if fully_absorbed {
366 find_agg_node_within_select(plan, true)
367 } else {
368 None
369 };
370 let window = if fully_absorbed {
371 find_window_nodes_within_select(plan, None, true)
372 } else {
373 None
374 };
375 match (agg, window) {
376 (Some(agg), window) => {
377 let window_option = window.as_deref();
378 let unparser_agg_scope = UnparserAggScope::new(agg);
379 let items = exprs
380 .into_iter()
381 .map(|proj_expr| {
382 let unproj =
383 unparser_agg_scope.prepare(proj_expr, window_option)?;
384 self.select_item_to_sql(&unproj)
385 })
386 .collect::<Result<Vec<_>>>()?;
387
388 select.projection(items);
389 select.group_by(ast::GroupByExpr::Expressions(
390 agg.group_expr
391 .iter()
392 .cloned()
393 .map(|expr| {
394 self.expr_to_sql(&unparser_agg_scope.normalize(expr)?)
395 })
396 .collect::<Result<Vec<_>>>()?,
397 vec![],
398 ));
399 Ok(true)
400 }
401 (None, Some(window)) => {
402 let items = exprs
403 .into_iter()
404 .map(|proj_expr| {
405 let unproj = unproject_window_exprs(proj_expr, &window)?;
406 self.select_item_to_sql(&unproj)
407 })
408 .collect::<Result<Vec<_>>>()?;
409
410 select.projection(items);
411 Ok(false)
412 }
413 _ => {
414 let items = exprs
415 .iter()
416 .map(|e| {
417 // After unproject_unnest_expr_as_flatten_value, an
418 // internal UNNEST display-name alias may still wrap
419 // the rewritten _unnest.VALUE column. Replace it
420 // with the bare FLATTEN VALUE select item.
421 if let Some(ref alias) = flatten_alias
422 && Self::has_internal_unnest_alias(e)
423 {
424 return Ok(self.build_flatten_value_select_item(alias, None));
425 }
426 self.select_item_to_sql(e)
427 })
428 .collect::<Result<Vec<_>>>()?;
429 select.projection(items);
430 Ok(false)
431 }
432 }
433 }
434
435 fn contains_projection_before_relation(plan: &LogicalPlan) -> bool {
436 match plan {
437 LogicalPlan::Projection(_) => true,
438 LogicalPlan::TableScan(_)
439 | LogicalPlan::Subquery(_)
440 | LogicalPlan::SubqueryAlias(_)
441 | LogicalPlan::Join(_)
442 | LogicalPlan::EmptyRelation(_)
443 | LogicalPlan::Values(_) => false,
444 _ => {
445 let inputs = plan.inputs();
446 matches!(
447 inputs.as_slice(),
448 [input] if Self::contains_projection_before_relation(input)
449 )
450 }
451 }
452 }
453
454 fn contains_aggregate_before_relation(plan: &LogicalPlan) -> bool {
455 match plan {
456 LogicalPlan::Aggregate(_) => true,
457 LogicalPlan::TableScan(_)
458 | LogicalPlan::Subquery(_)
459 | LogicalPlan::SubqueryAlias(_)
460 | LogicalPlan::Join(_)
461 | LogicalPlan::EmptyRelation(_)
462 | LogicalPlan::Values(_) => false,
463 _ => {
464 let inputs = plan.inputs();
465 matches!(
466 inputs.as_slice(),
467 [input] if Self::contains_aggregate_before_relation(input)
468 )
469 }
470 }
471 }
472
473 /// Unproject a sort expression; normalize it when the sort is above an
474 /// aggregate, otherwise just unproject (no scope to normalize against).
475 fn unproject_sort_expr_in_scope(
476 sort_expr: SortExpr,
477 agg: Option<&Aggregate>,
478 input: &LogicalPlan,
479 ) -> Result<SortExpr> {
480 match agg {
481 Some(agg) => UnparserAggScope::new(agg).prepare_sort_expr(sort_expr, input),
482 None => unproject_sort_expr(sort_expr, None, input),
483 }
484 }
485
486 fn derive(
487 &self,
488 plan: &LogicalPlan,
489 relation: &mut RelationBuilder,
490 alias: Option<ast::TableAlias>,
491 lateral: bool,
492 ) -> Result<()> {
493 let mut derived_builder = DerivedRelationBuilder::default();
494 derived_builder.lateral(lateral).alias(alias).subquery({
495 let inner_statement = self.plan_to_sql(plan)?;
496 if let ast::Statement::Query(inner_query) = inner_statement {
497 inner_query
498 } else {
499 return internal_err!(
500 "Subquery must be a Query, but found {inner_statement:?}"
501 );
502 }
503 });
504 relation.derived(derived_builder);
505
506 Ok(())
507 }
508
509 fn derive_with_dialect_alias(
510 &self,
511 alias: &str,
512 plan: &LogicalPlan,
513 relation: &mut RelationBuilder,
514 lateral: bool,
515 columns: Vec<Ident>,
516 ) -> Result<()> {
517 if self.dialect.requires_derived_table_alias() || !columns.is_empty() {
518 self.derive(
519 plan,
520 relation,
521 Some(self.new_table_alias(alias.to_string(), columns)),
522 lateral,
523 )
524 } else {
525 self.derive(plan, relation, None, lateral)
526 }
527 }
528
529 /// Projection unparsing when [`super::dialect::Dialect::unnest_as_lateral_flatten`] is enabled:
530 /// Snowflake-style `LATERAL FLATTEN` for unnest (not other dialect spellings).
531 ///
532 /// [`Self::peel_to_unnest_with_modifiers`] walks through any intermediate
533 /// Limit/Sort nodes (the optimizer can insert these between the Projection
534 /// and the Unnest), applies their modifiers to the query, and returns the
535 /// Unnest plus the [`LogicalPlan`] ref to recurse into. This bypasses the
536 /// normal Limit/Sort handlers which would wrap the subtree in a derived
537 /// subquery.
538 ///
539 /// SELECT rendering is delegated to [`Self::reconstruct_select_statement`],
540 /// which rewrites placeholder columns to `alias."VALUE"` via
541 /// [`unproject_unnest_expr_as_flatten_value`].
542 ///
543 /// Returns `Ok(true)` when this path fully handled the projection.
544 fn try_projection_unnest_as_lateral_flatten(
545 &self,
546 plan: &LogicalPlan,
547 p: &Projection,
548 query: &mut Option<QueryBuilder>,
549 select: &mut SelectBuilder,
550 relation: &mut RelationBuilder,
551 unnest_input_type: Option<&UnnestInputType>,
552 ) -> Result<bool> {
553 // unnest_as_lateral_flatten: Snowflake LATERAL FLATTEN
554 //
555 // Generate the alias up front so that peel_to_unnest_with_modifiers
556 // can rewrite ORDER BY placeholder columns to alias.VALUE.
557 if self.dialect.unnest_as_lateral_flatten() && unnest_input_type.is_some() {
558 let flatten_alias_name = if !select.already_projected() {
559 select.next_flatten_alias()
560 } else {
561 select
562 .current_flatten_alias()
563 .unwrap_or_else(|| select.next_flatten_alias())
564 };
565
566 if let Some((unnest, unnest_plan)) = self.peel_to_unnest_with_modifiers(
567 p.input.as_ref(),
568 query,
569 Some(&flatten_alias_name),
570 )? && let Some(mut flatten) =
571 self.try_unnest_to_lateral_flatten_sql(unnest)?
572 {
573 let inner_projection = Self::peel_to_inner_projection(
574 unnest.input.as_ref(),
575 )
576 .ok_or_else(|| {
577 internal_datafusion_err!(
578 "Unnest input is not a Projection: {:?}",
579 unnest.input
580 )
581 })?;
582
583 flatten.alias(Some(ast::TableAlias {
584 name: Ident::with_quote('"', &flatten_alias_name),
585 columns: vec![],
586 explicit: true,
587 at: None,
588 }));
589
590 if !select.already_projected() {
591 self.reconstruct_select_statement(plan, p, select, true)?;
592 }
593
594 if matches!(
595 inner_projection.input.as_ref(),
596 LogicalPlan::EmptyRelation(_)
597 ) {
598 relation.flatten(flatten);
599 self.select_to_sql_recursively(unnest_plan, query, select, relation)?;
600 return Ok(true);
601 }
602
603 self.select_to_sql_recursively(unnest_plan, query, select, relation)?;
604
605 let flatten_factor = flatten.build().map_err(|e| {
606 internal_datafusion_err!("Failed to build FLATTEN: {e}")
607 })?;
608 let cross_join = ast::Join {
609 relation: flatten_factor,
610 global: false,
611 join_operator: ast::JoinOperator::CrossJoin(
612 ast::JoinConstraint::None,
613 ),
614 };
615 if let Some(mut from) = select.pop_from() {
616 from.push_join(cross_join);
617 select.push_from(from);
618 } else {
619 let mut twj = TableWithJoinsBuilder::default();
620 twj.push_join(cross_join);
621 select.push_from(twj);
622 }
623
624 return Ok(true);
625 }
626 }
627
628 Ok(false)
629 }
630
631 fn project_window_output(
632 &self,
633 window_expr: &[Expr],
634 select: &mut SelectBuilder,
635 agg: Option<&Aggregate>,
636 ) -> Result<()> {
637 let mut items = if select.already_projected() {
638 select.pop_projections()
639 } else {
640 vec![ast::SelectItem::Wildcard(
641 ast::WildcardAdditionalOptions::default(),
642 )]
643 };
644
645 items.extend(
646 window_expr
647 .iter()
648 .map(|expr| {
649 // No normalization: this agg branch is only reachable from a
650 // hand-built plan. SQL wraps windows in a projection, which
651 // reconstruct_select_statement handles (and normalizes).
652 let expr = if let Some(agg) = agg {
653 unproject_agg_exprs(expr.clone(), agg, None)?
654 } else {
655 expr.clone()
656 };
657 self.select_item_to_sql(&expr)
658 })
659 .collect::<Result<Vec<_>>>()?,
660 );
661 select.projection(items);
662
663 Ok(())
664 }
665
666 fn window_input_requires_derived_subquery(plan: &LogicalPlan) -> bool {
667 // These operators either produce a SELECT list or apply SQL clauses
668 // that are evaluated after window functions in a single SELECT block.
669 // Keep them below the Window node by emitting a derived table.
670 matches!(
671 plan,
672 LogicalPlan::Projection(_)
673 | LogicalPlan::Distinct(_)
674 | LogicalPlan::Limit(_)
675 | LogicalPlan::Sort(_)
676 | LogicalPlan::Union(_)
677 )
678 }
679
680 fn window_to_sql_with_derived_input(
681 &self,
682 window: &Window,
683 select: &mut SelectBuilder,
684 relation: &mut RelationBuilder,
685 ) -> Result<()> {
686 let input_alias = "derived_window_input";
687 self.derive(
688 window.input.as_ref(),
689 relation,
690 Some(self.new_table_alias(input_alias.to_string(), vec![])),
691 false,
692 )?;
693
694 let input_schema = window.input.schema();
695 let mut alias_rewriter = TableAliasRewriter {
696 table_schema: input_schema.as_ref(),
697 alias_name: TableReference::bare(input_alias),
698 rewrite_unqualified: true,
699 };
700 let window_expr = window
701 .window_expr
702 .iter()
703 .map(|expr| expr.clone().rewrite(&mut alias_rewriter).data())
704 .collect::<Result<Vec<_>>>()?;
705
706 self.project_window_output(&window_expr, select, None)
707 }
708
709 fn extract_join_input_table_scan_filters(
710 plan: &Arc<LogicalPlan>,
711 table_scan_filters: &mut Vec<Expr>,
712 ) -> Result<Arc<LogicalPlan>> {
713 match try_transform_to_simple_table_scan_with_filters(plan)? {
714 Some((plan, filters)) => {
715 table_scan_filters.extend(filters);
716 Ok(Arc::new(plan))
717 }
718 None => Ok(Arc::clone(plan)),
719 }
720 }
721
722 #[cfg_attr(feature = "recursive_protection", recursive::recursive)]
723 fn select_to_sql_recursively(
724 &self,
725 plan: &LogicalPlan,
726 query: &mut Option<QueryBuilder>,
727 select: &mut SelectBuilder,
728 relation: &mut RelationBuilder,
729 ) -> Result<()> {
730 match plan {
731 LogicalPlan::TableScan(scan) => {
732 if let Some(unparsed_table_scan) = self.unparse_table_scan_pushdown(
733 plan,
734 None,
735 select.already_projected(),
736 )? {
737 return self.select_to_sql_recursively(
738 &unparsed_table_scan,
739 query,
740 select,
741 relation,
742 );
743 }
744 let mut builder = TableRelationBuilder::default();
745 let mut table_parts = vec![];
746 if let Some(catalog_name) = scan.table_name.catalog() {
747 table_parts
748 .push(self.new_ident_quoted_if_needs(catalog_name.to_string()));
749 }
750 if let Some(schema_name) = scan.table_name.schema() {
751 table_parts
752 .push(self.new_ident_quoted_if_needs(schema_name.to_string()));
753 }
754 table_parts.push(
755 self.new_ident_quoted_if_needs(scan.table_name.table().to_string()),
756 );
757 builder.name(ast::ObjectName::from(table_parts));
758 relation.table(builder);
759
760 Ok(())
761 }
762 LogicalPlan::Projection(p) => {
763 if let Some(new_plan) = rewrite_plan_for_sort_on_non_projected_fields(p) {
764 return self
765 .select_to_sql_recursively(&new_plan, query, select, relation);
766 }
767
768 // Projection can be top-level plan for unnest relation.
769 // The projection generated by the `RecursiveUnnestRewriter`
770 // will have at least one expression referencing an unnest
771 // placeholder column.
772 let unnest_input_type: Option<UnnestInputType> =
773 p.expr.iter().find_map(Self::find_unnest_placeholder);
774
775 // --- UNNEST table factor path (BigQuery, etc.) ---
776 // Only fires for a single bare-placeholder projection.
777 // Uses peel_to_unnest_with_modifiers (rather than matching
778 // p.input directly) to handle Limit/Sort between Projection
779 // and Unnest.
780 if self.dialect.unnest_as_table_factor()
781 && p.expr.len() == 1
782 && Self::is_bare_unnest_placeholder(&p.expr[0])
783 && let Some((unnest, unnest_plan)) =
784 self.peel_to_unnest_with_modifiers(p.input.as_ref(), query, None)?
785 && let Some(unnest_relation) =
786 self.try_unnest_to_table_factor_sql(unnest)?
787 {
788 relation.unnest(unnest_relation);
789 return self.select_to_sql_recursively(
790 unnest_plan,
791 query,
792 select,
793 relation,
794 );
795 }
796
797 if self.try_projection_unnest_as_lateral_flatten(
798 plan,
799 p,
800 query,
801 select,
802 relation,
803 unnest_input_type.as_ref(),
804 )? {
805 return Ok(());
806 }
807
808 // If it's a unnest projection, we should provide the table column alias
809 // to provide a column name for the unnest relation.
810 let columns = if unnest_input_type.is_some() {
811 p.expr
812 .iter()
813 .map(|e| {
814 self.new_ident_quoted_if_needs(e.schema_name().to_string())
815 })
816 .collect()
817 } else {
818 vec![]
819 };
820 // Projection can be top-level plan for derived table
821 if select.already_projected() {
822 return self.derive_with_dialect_alias(
823 "derived_projection",
824 plan,
825 relation,
826 unnest_input_type
827 .filter(|t| matches!(t, UnnestInputType::OuterReference))
828 .is_some(),
829 columns,
830 );
831 }
832 // For Snowflake FLATTEN: when the outer Projection has
833 // UNNEST(...) display-name columns (from SELECT * / SELECT
834 // UNNEST(...)), generate a flatten alias now so that
835 // reconstruct_select_statement and the downstream Unnest
836 // handler both use the same alias.
837 if self.dialect.unnest_as_lateral_flatten()
838 && p.expr.iter().any(Self::has_internal_unnest_alias)
839 {
840 select.next_flatten_alias();
841 }
842 // Pre-register FLATTEN table aliases from SubqueryAlias
843 // nodes in the plan tree so that
844 // reconstruct_select_statement can rewrite column
845 // references (e.g. a.col → a.VALUE) before the
846 // SubqueryAlias handler runs.
847 if self.dialect.unnest_as_lateral_flatten() {
848 Self::collect_flatten_aliases(p.input.as_ref(), select);
849 }
850 // Walk down through consecutive Sort/Limit nodes, greedily
851 // absorbing what can be folded into the SELECT we're
852 // building around the Aggregate. A single SQL SELECT can
853 // carry at most one `ORDER BY` (applied before `LIMIT`),
854 // so the safe shape between us and the Aggregate is
855 // `Limit* Sort?` (outer→inner). We stop at the first node
856 // that would violate this; that node becomes the
857 // subquery boundary, and recursion (seeing
858 // `already_projected = true`) wraps it in a derived
859 // relation. If we walk all the way to a non-Sort/non-Limit
860 // terminator, the entire chain folds into one SELECT.
861 //
862 // Stacked Sorts with nothing between them collapse to the
863 // outermost — the same simplification `EnforceSorting`
864 // applies on the physical side — but only when no Limit
865 // has been absorbed since the previous Sort, since the
866 // inner Sort would otherwise be determining which rows
867 // the Limit keeps.
868 //
869 // The fold is collected here without touching `query`
870 // (apart from non-literal direct Limits, which don't
871 // depend on projection form). Once we know whether every
872 // Sort/Limit was absorbed we can pick the right
873 // projection form and emit `ORDER BY` with or without
874 // unprojection.
875 let mut cur = p.input.as_ref();
876 let mut absorbed_sort: Option<&Sort> = None;
877 let mut combined_skip: usize = 0;
878 let mut combined_fetch: Option<usize> = None;
879 let mut have_combined_limit = false;
880 let mut have_direct_limit = false;
881 let mut have_order_by = false;
882 loop {
883 match cur {
884 LogicalPlan::Limit(limit) => {
885 if have_order_by {
886 // Limit-below-Sort: `ORDER BY … LIMIT N`
887 // would apply the sort first, but the
888 // logical plan applies the Limit first.
889 break;
890 }
891 let skip_lit = limit.get_skip_type()?;
892 let fetch_lit = limit.get_fetch_type()?;
893 match (skip_lit, fetch_lit) {
894 (SkipType::Literal(s), FetchType::Literal(f)) => {
895 if have_direct_limit {
896 break;
897 }
898 if have_combined_limit {
899 // outer = already-accumulated;
900 // inner = this Limit. Same merge
901 // rule as the optimizer.
902 let (cs, cf) = combine_limit(
903 combined_skip,
904 combined_fetch,
905 s,
906 f,
907 );
908 combined_skip = cs;
909 combined_fetch = cf;
910 } else {
911 combined_skip = s;
912 combined_fetch = f;
913 have_combined_limit = true;
914 }
915 }
916 _ => {
917 if have_combined_limit || have_direct_limit {
918 // Cannot safely merge a
919 // non-literal Limit with a prior
920 // one; let recursion handle it.
921 break;
922 }
923 let Some(query_ref) = query.as_mut() else {
924 return internal_err!(
925 "Limit operator only valid in a statement context."
926 );
927 };
928 if let Some(fetch) = &limit.fetch {
929 query_ref.limit(Some(self.expr_to_sql(fetch)?));
930 }
931 if let Some(skip) = &limit.skip {
932 query_ref.offset(Some(ast::Offset {
933 rows: ast::OffsetRows::None,
934 value: self.expr_to_sql(skip)?,
935 }));
936 }
937 have_direct_limit = true;
938 }
939 }
940 cur = limit.input.as_ref();
941 }
942 LogicalPlan::Sort(sort) if sort.fetch.is_some() => {
943 // `Sort { fetch }` is logically
944 // `Limit(fetch) -> Sort`. Try to absorb the
945 // virtual Limit first; only if that succeeds
946 // do we absorb the Sort. Otherwise we'd
947 // silently drop the fetch.
948 let fetch = sort.fetch.expect("guarded above");
949 if have_order_by {
950 // The virtual Limit would sit below an
951 // already-absorbed outer Sort.
952 break;
953 }
954 if have_direct_limit {
955 // Cannot combine a literal fetch with a
956 // non-literal direct Limit; let the
957 // derived subquery preserve both.
958 break;
959 }
960 if have_combined_limit {
961 let (cs, cf) = combine_limit(
962 combined_skip,
963 combined_fetch,
964 0,
965 Some(fetch),
966 );
967 combined_skip = cs;
968 combined_fetch = cf;
969 } else {
970 combined_skip = 0;
971 combined_fetch = Some(fetch);
972 have_combined_limit = true;
973 }
974 // Now the Sort itself. We know
975 // `!have_order_by` from the check above.
976 absorbed_sort = Some(sort);
977 have_order_by = true;
978 cur = sort.input.as_ref();
979 }
980 LogicalPlan::Sort(sort) => {
981 // Sort without `fetch`.
982 if have_order_by {
983 // Outer Sort already absorbed; the inner
984 // Sort is reordered by it and is
985 // conventionally dropped, matching
986 // `EnforceSorting` on the physical side.
987 cur = sort.input.as_ref();
988 continue;
989 }
990 absorbed_sort = Some(sort);
991 have_order_by = true;
992 cur = sort.input.as_ref();
993 }
994 _ => break,
995 }
996 }
997
998 // `fully_absorbed` is the bottom-up algorithm's "walked
999 // all the way to the terminator without stopping": the
1000 // Aggregate/Window will live in the same SELECT as this
1001 // Projection, so we can unproject sort exprs and let
1002 // `reconstruct_select_statement` claim it.
1003 let fully_absorbed =
1004 !matches!(cur, LogicalPlan::Limit(_) | LogicalPlan::Sort(_));
1005 let found_agg =
1006 self.reconstruct_select_statement(plan, p, select, fully_absorbed)?;
1007
1008 // Whether to bother emitting the absorbed clauses: only
1009 // if there's an Aggregate either claimed in this SELECT
1010 // or about to live in a derived subquery below us. If
1011 // there's nothing aggregate-like to fold over, fall
1012 // through and let the normal recursion handle the
1013 // Projection's input.
1014 let agg_below =
1015 !fully_absorbed && find_agg_node_within_select(plan, true).is_some();
1016 if !(found_agg || agg_below) {
1017 return self.select_to_sql_recursively(
1018 p.input.as_ref(),
1019 query,
1020 select,
1021 relation,
1022 );
1023 }
1024
1025 if let Some(sort) = absorbed_sort {
1026 let Some(query_ref) = query.as_mut() else {
1027 return internal_err!(
1028 "Sort operator only valid in a statement context."
1029 );
1030 };
1031 let sort_exprs: Vec<SortExpr> = if fully_absorbed {
1032 let agg =
1033 find_agg_node_within_select(plan, select.already_projected());
1034 sort.expr
1035 .iter()
1036 .map(|sort_expr| {
1037 Self::unproject_sort_expr_in_scope(
1038 sort_expr.clone(),
1039 agg,
1040 sort.input.as_ref(),
1041 )
1042 })
1043 .collect::<Result<Vec<_>>>()?
1044 } else {
1045 sort.expr.clone()
1046 };
1047 query_ref.order_by(self.sorts_to_sql(&sort_exprs)?);
1048 }
1049 if have_combined_limit {
1050 let Some(query_ref) = query.as_mut() else {
1051 return internal_err!(
1052 "Limit operator only valid in a statement context."
1053 );
1054 };
1055 if let Some(fetch) = combined_fetch {
1056 query_ref.limit(Some(ast::Expr::value(ast::Value::Number(
1057 fetch.to_string(),
1058 false,
1059 ))));
1060 }
1061 if combined_skip > 0 {
1062 query_ref.offset(Some(ast::Offset {
1063 rows: ast::OffsetRows::None,
1064 value: ast::Expr::value(ast::Value::Number(
1065 combined_skip.to_string(),
1066 false,
1067 )),
1068 }));
1069 }
1070 }
1071
1072 self.select_to_sql_recursively(cur, query, select, relation)
1073 }
1074 LogicalPlan::Filter(filter) => {
1075 let window = find_window_nodes_within_select(
1076 plan,
1077 None,
1078 select.already_projected(),
1079 );
1080 let agg = find_agg_node_within_select(plan, select.already_projected());
1081
1082 if let (Some(window), true) =
1083 (window.as_deref(), self.dialect.supports_qualify())
1084 {
1085 let mut unprojected =
1086 unproject_window_exprs(filter.predicate.clone(), window)?;
1087 if let Some(agg) = agg {
1088 unprojected =
1089 UnparserAggScope::new(agg).prepare(unprojected, None)?;
1090 }
1091 let filter_expr = self.expr_to_sql(&unprojected)?;
1092 select.qualify(Some(filter_expr));
1093 } else if let Some(agg) = agg {
1094 let unprojected = UnparserAggScope::new(agg)
1095 .prepare(filter.predicate.clone(), None)?;
1096 let filter_expr = self.expr_to_sql(&unprojected)?;
1097 select.having(Some(filter_expr));
1098 } else {
1099 let filter_expr = self.expr_to_sql(&filter.predicate)?;
1100 select.selection(Some(filter_expr));
1101 }
1102
1103 self.select_to_sql_recursively(
1104 filter.input.as_ref(),
1105 query,
1106 select,
1107 relation,
1108 )
1109 }
1110 LogicalPlan::Limit(limit) => {
1111 // Limit can be top-level plan for derived table
1112 if select.already_projected() {
1113 return self.derive_with_dialect_alias(
1114 "derived_limit",
1115 plan,
1116 relation,
1117 false,
1118 vec![],
1119 );
1120 }
1121 if let Some(fetch) = &limit.fetch {
1122 let Some(query) = query.as_mut() else {
1123 return internal_err!(
1124 "Limit operator only valid in a statement context."
1125 );
1126 };
1127 query.limit(Some(self.expr_to_sql(fetch)?));
1128 }
1129
1130 if let Some(skip) = &limit.skip {
1131 let Some(query) = query.as_mut() else {
1132 return internal_err!(
1133 "Offset operator only valid in a statement context."
1134 );
1135 };
1136
1137 query.offset(Some(ast::Offset {
1138 rows: ast::OffsetRows::None,
1139 value: self.expr_to_sql(skip)?,
1140 }));
1141 }
1142
1143 self.select_to_sql_recursively(
1144 limit.input.as_ref(),
1145 query,
1146 select,
1147 relation,
1148 )
1149 }
1150 LogicalPlan::Sort(sort) => {
1151 // Sort can be top-level plan for derived table
1152 if select.already_projected() {
1153 return self.derive_with_dialect_alias(
1154 "derived_sort",
1155 plan,
1156 relation,
1157 false,
1158 vec![],
1159 );
1160 }
1161
1162 let Some(query_ref) = query else {
1163 return internal_err!(
1164 "Sort operator only valid in a statement context."
1165 );
1166 };
1167
1168 if let Some(fetch) = sort.fetch {
1169 query_ref.limit(Some(ast::Expr::value(ast::Value::Number(
1170 fetch.to_string(),
1171 false,
1172 ))));
1173 };
1174
1175 let agg = find_agg_node_within_select(plan, select.already_projected());
1176 // unproject sort expressions
1177 let sort_exprs: Vec<SortExpr> = sort
1178 .expr
1179 .iter()
1180 .map(|sort_expr| {
1181 Self::unproject_sort_expr_in_scope(
1182 sort_expr.clone(),
1183 agg,
1184 sort.input.as_ref(),
1185 )
1186 })
1187 .collect::<Result<Vec<_>>>()?;
1188
1189 query_ref.order_by(self.sorts_to_sql(&sort_exprs)?);
1190
1191 self.select_to_sql_recursively(
1192 sort.input.as_ref(),
1193 query,
1194 select,
1195 relation,
1196 )
1197 }
1198 LogicalPlan::Aggregate(agg) => {
1199 // Aggregation can be already handled in the projection case
1200 if !select.already_projected() {
1201 let unparser_agg_scope = UnparserAggScope::new(agg);
1202 // The query returns aggregate and group expressions. If that weren't the case,
1203 // the aggregate would have been placed inside a projection, making the check above^ false
1204 let exprs: Vec<_> = agg
1205 .aggr_expr
1206 .iter()
1207 .chain(agg.group_expr.iter())
1208 .cloned()
1209 .map(|expr| {
1210 self.select_item_to_sql(&unparser_agg_scope.normalize(expr)?)
1211 })
1212 .collect::<Result<Vec<_>>>()?;
1213 select.projection(exprs);
1214
1215 select.group_by(ast::GroupByExpr::Expressions(
1216 agg.group_expr
1217 .iter()
1218 .cloned()
1219 .map(|expr| {
1220 self.expr_to_sql(&unparser_agg_scope.normalize(expr)?)
1221 })
1222 .collect::<Result<Vec<_>>>()?,
1223 vec![],
1224 ));
1225 } else if Self::contains_aggregate_before_relation(agg.input.as_ref()) {
1226 return self.derive_with_dialect_alias(
1227 "derived_aggregate",
1228 agg.input.as_ref(),
1229 relation,
1230 false,
1231 vec![],
1232 );
1233 }
1234
1235 self.select_to_sql_recursively(
1236 agg.input.as_ref(),
1237 query,
1238 select,
1239 relation,
1240 )
1241 }
1242 LogicalPlan::Distinct(distinct) => {
1243 // Distinct can be top-level plan for derived table
1244 if select.already_projected() {
1245 return self.derive_with_dialect_alias(
1246 "derived_distinct",
1247 plan,
1248 relation,
1249 false,
1250 vec![],
1251 );
1252 }
1253
1254 // If this distinct is the parent of a Union and we're in a query context,
1255 // then we need to unparse as a `UNION` rather than a `UNION ALL`.
1256 if let Distinct::All(input) = distinct
1257 && matches!(input.as_ref(), LogicalPlan::Union(_))
1258 && let Some(query_mut) = query.as_mut()
1259 {
1260 query_mut.distinct_union();
1261 return self.select_to_sql_recursively(
1262 input.as_ref(),
1263 query,
1264 select,
1265 relation,
1266 );
1267 }
1268
1269 let (select_distinct, input) = match distinct {
1270 Distinct::All(input) => (ast::Distinct::Distinct, input.as_ref()),
1271 Distinct::On(on) => {
1272 let exprs = on
1273 .on_expr
1274 .iter()
1275 .map(|e| self.expr_to_sql(e))
1276 .collect::<Result<Vec<_>>>()?;
1277 let items = on
1278 .select_expr
1279 .iter()
1280 .map(|e| self.select_item_to_sql(e))
1281 .collect::<Result<Vec<_>>>()?;
1282 if let Some(sort_expr) = &on.sort_expr {
1283 if let Some(query_ref) = query {
1284 query_ref.order_by(self.sorts_to_sql(sort_expr)?);
1285 } else {
1286 return internal_err!(
1287 "Sort operator only valid in a statement context."
1288 );
1289 }
1290 }
1291 select.projection(items);
1292 (ast::Distinct::On(exprs), on.input.as_ref())
1293 }
1294 };
1295 select.distinct(Some(select_distinct));
1296 self.select_to_sql_recursively(input, query, select, relation)
1297 }
1298 LogicalPlan::Join(join) => {
1299 let mut table_scan_filters = vec![];
1300 let (left_plan, right_plan) = match join.join_type {
1301 JoinType::RightSemi | JoinType::RightAnti => {
1302 (&join.right, &join.left)
1303 }
1304 _ => (&join.left, &join.right),
1305 };
1306 // If there's an outer projection plan, it will already set up the projection.
1307 // In that case, we don't need to worry about setting up the projection here.
1308 // The outer projection plan will handle projecting the correct columns.
1309 let already_projected = select.already_projected();
1310
1311 let left_plan = Self::extract_join_input_table_scan_filters(
1312 left_plan,
1313 &mut table_scan_filters,
1314 )?;
1315 let left_plan = if already_projected {
1316 Self::unwrap_qualified_passthrough_join_projection(left_plan)
1317 } else {
1318 left_plan
1319 };
1320
1321 self.select_to_sql_recursively(
1322 left_plan.as_ref(),
1323 query,
1324 select,
1325 relation,
1326 )?;
1327
1328 let left_projection: Option<Vec<ast::SelectItem>> = if !already_projected
1329 {
1330 Some(select.pop_projections())
1331 } else {
1332 None
1333 };
1334
1335 let right_plan = Self::extract_join_input_table_scan_filters(
1336 right_plan,
1337 &mut table_scan_filters,
1338 )?;
1339
1340 let mut right_relation = RelationBuilder::default();
1341 if already_projected
1342 && let Some(nested_relation) = self
1343 .qualified_passthrough_join_projection_to_nested_relation(
1344 right_plan.as_ref(),
1345 query,
1346 )?
1347 {
1348 right_relation = nested_relation;
1349 } else {
1350 self.select_to_sql_recursively(
1351 right_plan.as_ref(),
1352 query,
1353 select,
1354 &mut right_relation,
1355 )?;
1356 }
1357
1358 let (join_filters, where_filters) = Self::split_join_on_and_where_filters(
1359 join.join_type,
1360 &join.filter,
1361 table_scan_filters,
1362 );
1363 for filter in where_filters {
1364 let filter_expr = self.expr_to_sql(&filter)?;
1365 select.selection(Some(filter_expr));
1366 }
1367
1368 let join_constraint = self.join_constraint_to_sql(
1369 join.join_constraint,
1370 &join.on,
1371 join_filters.as_ref(),
1372 )?;
1373
1374 let right_projection: Option<Vec<ast::SelectItem>> = if !already_projected
1375 {
1376 Some(select.pop_projections())
1377 } else {
1378 None
1379 };
1380
1381 match join.join_type {
1382 JoinType::LeftSemi
1383 | JoinType::LeftAnti
1384 | JoinType::LeftMark
1385 | JoinType::RightSemi
1386 | JoinType::RightAnti
1387 | JoinType::RightMark => {
1388 let mut query_builder = QueryBuilder::default();
1389 let mut from = TableWithJoinsBuilder::default();
1390 let mut exists_select: SelectBuilder = SelectBuilder::default();
1391 from.relation(right_relation);
1392 exists_select.push_from(from);
1393 if let Some(filter) = &join.filter {
1394 exists_select.selection(Some(self.expr_to_sql(filter)?));
1395 }
1396 for (left, right) in &join.on {
1397 exists_select.selection(Some(
1398 self.expr_to_sql(&left.clone().eq(right.clone()))?,
1399 ));
1400 }
1401 exists_select.projection(vec![ast::SelectItem::UnnamedExpr(
1402 ast::Expr::value(ast::Value::Number("1".to_string(), false)),
1403 )]);
1404 query_builder.body(Box::new(SetExpr::Select(Box::new(
1405 exists_select.build()?,
1406 ))));
1407
1408 let negated = match join.join_type {
1409 JoinType::LeftSemi
1410 | JoinType::RightSemi
1411 | JoinType::LeftMark
1412 | JoinType::RightMark => false,
1413 JoinType::LeftAnti | JoinType::RightAnti => true,
1414 _ => unreachable!(),
1415 };
1416 let exists_expr = ast::Expr::Exists {
1417 subquery: Box::new(query_builder.build()?),
1418 negated,
1419 };
1420
1421 match join.join_type {
1422 JoinType::LeftMark | JoinType::RightMark => {
1423 let source_schema =
1424 if join.join_type == JoinType::LeftMark {
1425 right_plan.schema()
1426 } else {
1427 left_plan.schema()
1428 };
1429 let (table_ref, _) = source_schema.qualified_field(0);
1430 let column = self.col_to_sql(&Column::new(
1431 table_ref.cloned(),
1432 "mark",
1433 ))?;
1434 select.replace_mark(&column, &exists_expr);
1435 }
1436 _ => {
1437 select.selection(Some(exists_expr));
1438 }
1439 }
1440 if let Some(projection) = left_projection {
1441 select.projection(projection);
1442 }
1443 }
1444 JoinType::Inner
1445 | JoinType::Left
1446 | JoinType::Right
1447 | JoinType::Full => {
1448 let Ok(Some(relation)) = right_relation.build() else {
1449 return internal_err!("Failed to build right relation");
1450 };
1451 let ast_join = ast::Join {
1452 relation,
1453 global: false,
1454 join_operator: self
1455 .join_operator_to_sql(join.join_type, join_constraint)?,
1456 };
1457 let mut from = select.pop_from().unwrap();
1458 from.push_join(ast_join);
1459 select.push_from(from);
1460 if !already_projected {
1461 let Some(left_projection) = left_projection else {
1462 return internal_err!("Left projection is missing");
1463 };
1464
1465 let Some(right_projection) = right_projection else {
1466 return internal_err!("Right projection is missing");
1467 };
1468
1469 let projection = left_projection
1470 .into_iter()
1471 .chain(right_projection)
1472 .collect();
1473 select.projection(projection);
1474 }
1475 }
1476 };
1477
1478 Ok(())
1479 }
1480 LogicalPlan::SubqueryAlias(plan_alias) => {
1481 let (plan, mut columns) =
1482 subquery_alias_inner_query_and_columns(plan_alias);
1483 let unparsed_table_scan = self.unparse_table_scan_pushdown(
1484 plan,
1485 Some(plan_alias.alias.clone()),
1486 select.already_projected(),
1487 )?;
1488
1489 // If the (possibly rewritten) inner plan builds its own
1490 // SELECT clauses (e.g. Aggregate adds GROUP BY, Window adds
1491 // OVER, etc.) and unparse_table_scan_pushdown couldn't reduce it,
1492 // we must emit a derived subquery: (SELECT ...) AS alias.
1493 // Without this, the recursive handler would merge those clauses
1494 // into the outer SELECT, losing the subquery structure entirely.
1495 if unparsed_table_scan.is_none() && Self::requires_derived_subquery(plan)
1496 {
1497 // When the dialect does not support column aliases in
1498 // table aliases (e.g. SQLite), inject the aliases into
1499 // the inner projection before wrapping as a derived
1500 // subquery.
1501 if !columns.is_empty()
1502 && !self.dialect.supports_column_alias_in_table_alias()
1503 {
1504 let Ok(rewritten_plan) =
1505 inject_column_aliases_into_subquery(plan.clone(), columns)
1506 else {
1507 return internal_err!(
1508 "Failed to transform SubqueryAlias plan"
1509 );
1510 };
1511 return self.derive(
1512 &rewritten_plan,
1513 relation,
1514 Some(self.new_table_alias(
1515 plan_alias.alias.table().to_string(),
1516 vec![],
1517 )),
1518 false,
1519 );
1520 }
1521 return self.derive(
1522 plan,
1523 relation,
1524 Some(self.new_table_alias(
1525 plan_alias.alias.table().to_string(),
1526 columns,
1527 )),
1528 false,
1529 );
1530 }
1531
1532 // if the child plan is a TableScan with pushdown operations, we don't need to
1533 // create an additional subquery for it
1534 if !select.already_projected() && unparsed_table_scan.is_none() {
1535 select.projection(vec![ast::SelectItem::Wildcard(
1536 ast::WildcardAdditionalOptions::default(),
1537 )]);
1538 }
1539 let plan = unparsed_table_scan.unwrap_or_else(|| plan.clone());
1540 if !columns.is_empty()
1541 && !self.dialect.supports_column_alias_in_table_alias()
1542 {
1543 // Instead of specifying column aliases as part of the outer table, inject them directly into the inner projection
1544 let rewritten_plan =
1545 match inject_column_aliases_into_subquery(plan, columns) {
1546 Ok(p) => p,
1547 Err(e) => {
1548 return internal_err!(
1549 "Failed to transform SubqueryAlias plan: {e}"
1550 );
1551 }
1552 };
1553
1554 columns = vec![];
1555
1556 self.select_to_sql_recursively(
1557 &rewritten_plan,
1558 query,
1559 select,
1560 relation,
1561 )?;
1562 } else {
1563 self.select_to_sql_recursively(&plan, query, select, relation)?;
1564 }
1565
1566 relation.alias(Some(
1567 self.new_table_alias(plan_alias.alias.table().to_string(), columns),
1568 ));
1569
1570 // If this SubqueryAlias wraps a FLATTEN (Snowflake unnest),
1571 // register the alias so the outer Projection can rewrite
1572 // column references to use VALUE.
1573 if self.dialect.unnest_as_lateral_flatten()
1574 && find_unnest_node_until_relation(plan_alias.input.as_ref())
1575 .is_some()
1576 {
1577 select.add_flatten_table_alias(plan_alias.alias.table().to_string());
1578 }
1579
1580 Ok(())
1581 }
1582 LogicalPlan::Union(union) => {
1583 // Covers cases where the UNION is a subquery and the projection is at the top level
1584 if select.already_projected() {
1585 return self.derive_with_dialect_alias(
1586 "derived_union",
1587 plan,
1588 relation,
1589 false,
1590 vec![],
1591 );
1592 }
1593
1594 let input_exprs: Vec<SetExpr> = union
1595 .inputs
1596 .iter()
1597 .map(|input| self.select_to_sql_expr(input, query))
1598 .collect::<Result<Vec<_>>>()?;
1599
1600 assert_or_internal_err!(
1601 input_exprs.len() >= 2,
1602 "UNION operator requires at least 2 inputs"
1603 );
1604
1605 let set_quantifier =
1606 if query.as_ref().is_some_and(|q| q.is_distinct_union()) {
1607 // Setting the SetQuantifier to None will unparse as a `UNION`
1608 // rather than a `UNION ALL`.
1609 ast::SetQuantifier::None
1610 } else {
1611 ast::SetQuantifier::All
1612 };
1613
1614 // Build the union expression tree bottom-up by reversing the order
1615 // note that we are also swapping left and right inputs because of the rev
1616 let union_expr = input_exprs
1617 .into_iter()
1618 .rev()
1619 .reduce(|a, b| SetExpr::SetOperation {
1620 op: ast::SetOperator::Union,
1621 set_quantifier,
1622 left: Box::new(b),
1623 right: Box::new(a),
1624 })
1625 .unwrap();
1626
1627 let Some(query) = query.as_mut() else {
1628 return internal_err!(
1629 "UNION ALL operator only valid in a statement context"
1630 );
1631 };
1632 query.body(Box::new(union_expr));
1633
1634 Ok(())
1635 }
1636 LogicalPlan::Window(window) => {
1637 // Window nodes are usually handled simultaneously with Projection
1638 // nodes, where projected columns are unprojected back into their
1639 // corresponding window expressions. Manually built plans can have
1640 // Window nodes without an enclosing Projection, so in that case
1641 // the Window node itself must contribute its output expressions.
1642 let project_window_output = !select.already_projected();
1643 if project_window_output
1644 && Self::window_input_requires_derived_subquery(window.input.as_ref())
1645 {
1646 return self
1647 .window_to_sql_with_derived_input(window, select, relation);
1648 }
1649
1650 let agg = if project_window_output {
1651 find_agg_node_within_select(plan, false)
1652 } else {
1653 None
1654 };
1655
1656 self.select_to_sql_recursively(
1657 window.input.as_ref(),
1658 query,
1659 select,
1660 relation,
1661 )?;
1662
1663 if project_window_output {
1664 self.project_window_output(&window.window_expr, select, agg)?;
1665 }
1666
1667 Ok(())
1668 }
1669 LogicalPlan::EmptyRelation(_) => {
1670 // An EmptyRelation could be behind an UNNEST node. If the dialect supports UNNEST as a table factor,
1671 // a TableRelationBuilder will be created for the UNNEST node first.
1672 if !relation.has_relation() {
1673 relation.empty();
1674 }
1675 Ok(())
1676 }
1677 LogicalPlan::Extension(extension) => {
1678 if let Some(query) = query.as_mut() {
1679 self.extension_to_sql(
1680 extension.node.as_ref(),
1681 &mut Some(query),
1682 &mut Some(select),
1683 &mut Some(relation),
1684 )
1685 } else {
1686 self.extension_to_sql(
1687 extension.node.as_ref(),
1688 &mut None,
1689 &mut Some(select),
1690 &mut Some(relation),
1691 )
1692 }
1693 }
1694 LogicalPlan::Unnest(unnest) => {
1695 if !unnest.struct_type_columns.is_empty() {
1696 if self.dialect.unnest_as_lateral_flatten() {
1697 return not_impl_err!(
1698 "Snowflake FLATTEN cannot unparse struct unnest: \
1699 DataFusion expands struct fields into columns (horizontal), \
1700 but Snowflake FLATTEN expands them into rows (vertical). \
1701 Columns: {:?}",
1702 unnest.struct_type_columns
1703 );
1704 }
1705 return internal_err!(
1706 "Struct type columns are not currently supported in UNNEST: {:?}",
1707 unnest.struct_type_columns
1708 );
1709 }
1710
1711 // For Snowflake FLATTEN: if the relation hasn't been set yet
1712 // (UNNEST was in SELECT clause, not FROM clause), set the FLATTEN
1713 // relation here so the FROM clause is emitted.
1714 if self.dialect.unnest_as_lateral_flatten()
1715 && !relation.has_relation()
1716 && let Some(mut flatten_relation) =
1717 self.try_unnest_to_lateral_flatten_sql(unnest)?
1718 {
1719 // Use the alias already generated by the Projection
1720 // handler so SELECT items and the FLATTEN relation
1721 // reference the same name.
1722 if let Some(alias) = select.current_flatten_alias() {
1723 flatten_relation.alias(Some(ast::TableAlias {
1724 name: Ident::with_quote('"', &alias),
1725 columns: vec![],
1726 explicit: true,
1727 at: None,
1728 }));
1729 }
1730 relation.flatten(flatten_relation);
1731 }
1732
1733 // In the case of UNNEST, the Unnest node is followed by a duplicate Projection node that we should skip.
1734 // Otherwise, there will be a duplicate SELECT clause.
1735 // | Projection: table.col1, UNNEST(table.col2)
1736 // | Unnest: UNNEST(table.col2)
1737 // | Projection: table.col1, table.col2 AS UNNEST(table.col2)
1738 // | Filter: table.col3 = Int64(3)
1739 // | TableScan: table projection=None
1740 if let Some(p) = Self::peel_to_inner_projection(unnest.input.as_ref()) {
1741 // Skip the inner Projection (synthetic rewriter node)
1742 // and continue with its input.
1743 self.select_to_sql_recursively(&p.input, query, select, relation)
1744 } else {
1745 internal_err!("Unnest input is not a Projection: {unnest:?}")
1746 }
1747 }
1748 LogicalPlan::Subquery(subquery)
1749 if find_unnest_node_until_relation(subquery.subquery.as_ref())
1750 .is_some() =>
1751 {
1752 if self.dialect.unnest_as_table_factor()
1753 || self.dialect.unnest_as_lateral_flatten()
1754 {
1755 self.select_to_sql_recursively(
1756 subquery.subquery.as_ref(),
1757 query,
1758 select,
1759 relation,
1760 )
1761 } else {
1762 self.derive_with_dialect_alias(
1763 "derived_unnest",
1764 subquery.subquery.as_ref(),
1765 relation,
1766 true,
1767 vec![],
1768 )
1769 }
1770 }
1771 _ => {
1772 not_impl_err!("Unsupported operator: {plan:?}")
1773 }
1774 }
1775 }
1776
1777 /// Walk through transparent nodes (SubqueryAlias) to find the inner
1778 /// Projection that feeds an Unnest node.
1779 ///
1780 /// The inner Projection is created atomically by the
1781 /// `RecursiveUnnestRewriter` and contains the array expression that the
1782 /// Unnest operates on. A `SubqueryAlias` (e.g. from a virtual/passthrough
1783 /// table) may wrap the Projection.
1784 fn peel_to_inner_projection(plan: &LogicalPlan) -> Option<&Projection> {
1785 match plan {
1786 LogicalPlan::Projection(p) => Some(p),
1787 LogicalPlan::SubqueryAlias(alias) => {
1788 Self::peel_to_inner_projection(alias.input.as_ref())
1789 }
1790 _ => None,
1791 }
1792 }
1793
1794 /// Walk through transparent nodes (Limit, Sort) between the outer
1795 /// Projection and the Unnest, applying their SQL modifiers (LIMIT,
1796 /// OFFSET, ORDER BY) to the query builder. Returns the `Unnest` node
1797 /// and a reference to the enclosing `LogicalPlan` for recursion, or
1798 /// `Ok(None)` if no Unnest is found.
1799 ///
1800 /// By processing Limit/Sort inline and then recursing into the Unnest
1801 /// plan directly, we bypass the normal Limit/Sort handlers which would
1802 /// create unwanted derived subqueries (since `already_projected` is
1803 /// set at the point this is called).
1804 fn peel_to_unnest_with_modifiers<'a>(
1805 &self,
1806 plan: &'a LogicalPlan,
1807 query: &mut Option<QueryBuilder>,
1808 flatten_alias: Option<&str>,
1809 ) -> Result<Option<(&'a Unnest, &'a LogicalPlan)>> {
1810 match plan {
1811 LogicalPlan::Unnest(unnest) => Ok(Some((unnest, plan))),
1812 LogicalPlan::Limit(limit) => {
1813 if let Some(fetch) = &limit.fetch
1814 && let Some(q) = query.as_mut()
1815 {
1816 q.limit(Some(self.expr_to_sql(fetch)?));
1817 }
1818 if let Some(skip) = &limit.skip
1819 && let Some(q) = query.as_mut()
1820 {
1821 q.offset(Some(ast::Offset {
1822 rows: ast::OffsetRows::None,
1823 value: self.expr_to_sql(skip)?,
1824 }));
1825 }
1826 self.peel_to_unnest_with_modifiers(
1827 limit.input.as_ref(),
1828 query,
1829 flatten_alias,
1830 )
1831 }
1832 LogicalPlan::Sort(sort) => {
1833 let Some(query_ref) = query.as_mut() else {
1834 return internal_err!(
1835 "Sort between Projection and Unnest requires a statement context."
1836 );
1837 };
1838 if let Some(fetch) = sort.fetch {
1839 query_ref.limit(Some(ast::Expr::value(ast::Value::Number(
1840 fetch.to_string(),
1841 false,
1842 ))));
1843 }
1844 // When a flatten_alias is provided, rewrite
1845 // __unnest_placeholder(...) columns in sort expressions to
1846 // alias.VALUE so ORDER BY references the FLATTEN output.
1847 let unnest_node = match sort.input.as_ref() {
1848 LogicalPlan::Unnest(u) => Some(u),
1849 _ => find_unnest_node_within_select(sort.input.as_ref()),
1850 };
1851 let sort_exprs = if let Some(alias) = flatten_alias
1852 && let Some(unnest) = unnest_node
1853 {
1854 sort.expr
1855 .iter()
1856 .map(|s| {
1857 let rewritten = unproject_unnest_expr_as_flatten_value(
1858 s.expr.clone(),
1859 unnest,
1860 alias,
1861 )?;
1862 Ok(SortExpr {
1863 expr: rewritten,
1864 ..s.clone()
1865 })
1866 })
1867 .collect::<Result<Vec<_>>>()?
1868 } else {
1869 sort.expr.clone()
1870 };
1871 query_ref.order_by(self.sorts_to_sql(&sort_exprs)?);
1872 self.peel_to_unnest_with_modifiers(
1873 sort.input.as_ref(),
1874 query,
1875 flatten_alias,
1876 )
1877 }
1878 _ => Ok(None),
1879 }
1880 }
1881
1882 /// Search an expression tree for an unnest placeholder column reference.
1883 ///
1884 /// Returns the [`UnnestInputType`] if any sub-expression is a column
1885 /// whose name starts with `__unnest_placeholder`. The placeholder may
1886 /// be at the top level (bare), inside a function call, or one of several
1887 /// expressions — this function finds it regardless.
1888 fn find_unnest_placeholder(expr: &Expr) -> Option<UnnestInputType> {
1889 let mut result = None;
1890 let _ = expr.apply(|e| {
1891 if let Some(t) = Self::classify_placeholder_column(e) {
1892 result = Some(t);
1893 return Ok(TreeNodeRecursion::Stop);
1894 }
1895 Ok(TreeNodeRecursion::Continue)
1896 });
1897 result
1898 }
1899
1900 /// Returns true if `expr` is a placeholder column, optionally wrapped
1901 /// in a single alias (the rewriter's internal `UNNEST(...)` name).
1902 /// Does NOT match when a user alias wraps the internal alias
1903 /// (e.g. `Alias("c1", Alias("UNNEST(...)", Column(placeholder)))`),
1904 /// so the table-factor path correctly falls through to
1905 /// `reconstruct_select_statement` which preserves user aliases.
1906 fn is_bare_unnest_placeholder(expr: &Expr) -> bool {
1907 // Peel at most one alias layer (the rewriter's internal name).
1908 let inner = match expr {
1909 Expr::Alias(Alias { expr, .. }) => expr.as_ref(),
1910 other => other,
1911 };
1912 Self::classify_placeholder_column(inner).is_some()
1913 }
1914
1915 /// If `expr` is a `Column` whose name starts with `__unnest_placeholder`,
1916 /// classify it as [`UnnestInputType::OuterReference`] or
1917 /// [`UnnestInputType::Scalar`].
1918 fn classify_placeholder_column(expr: &Expr) -> Option<UnnestInputType> {
1919 if let Expr::Column(Column { name, .. }) = expr
1920 && let Some(prefix) = name.strip_prefix(UNNEST_PLACEHOLDER)
1921 {
1922 if prefix.starts_with(&format!("({OUTER_REFERENCE_COLUMN_PREFIX}(")) {
1923 return Some(UnnestInputType::OuterReference);
1924 }
1925 return Some(UnnestInputType::Scalar);
1926 }
1927 None
1928 }
1929
1930 /// Check whether an expression carries an internal `UNNEST(...)` display
1931 /// name as its column name or outermost alias. After
1932 /// [`unproject_unnest_expr_as_flatten_value`] rewrites the placeholder
1933 /// column to `_unnest.VALUE`, the internal alias may still linger
1934 /// (e.g. `Alias("UNNEST(make_array(...))", Column("_unnest.VALUE"))`).
1935 /// Callers use this to replace the expression with a clean
1936 /// `_unnest."VALUE"` select item.
1937 fn has_internal_unnest_alias(expr: &Expr) -> bool {
1938 match expr {
1939 Expr::Column(col) => {
1940 col.name.starts_with(&format!("{UNNEST_COLUMN_PREFIX}("))
1941 }
1942 Expr::Alias(Alias { name, .. }) => {
1943 name.starts_with(&format!("{UNNEST_COLUMN_PREFIX}("))
1944 }
1945 _ => false,
1946 }
1947 }
1948
1949 /// Walk the plan tree and register any SubqueryAlias that wraps an
1950 /// unnest as a FLATTEN table alias on the SelectBuilder. This allows
1951 /// `reconstruct_select_statement` to rewrite column references (e.g.
1952 /// `a.col` → `a.VALUE`) before the SubqueryAlias handler runs.
1953 /// Returns true if a plan tree contains an Unnest node, searching
1954 /// through Projection, Subquery, and SubqueryAlias wrappers.
1955 fn contains_unnest(plan: &LogicalPlan) -> bool {
1956 match plan {
1957 LogicalPlan::Unnest(_) => true,
1958 LogicalPlan::Projection(p) => Self::contains_unnest(&p.input),
1959 LogicalPlan::Subquery(s) => Self::contains_unnest(&s.subquery),
1960 LogicalPlan::SubqueryAlias(a) => Self::contains_unnest(&a.input),
1961 _ => false,
1962 }
1963 }
1964
1965 fn collect_flatten_aliases(plan: &LogicalPlan, select: &mut SelectBuilder) {
1966 match plan {
1967 LogicalPlan::SubqueryAlias(alias)
1968 if Self::contains_unnest(alias.input.as_ref()) =>
1969 {
1970 select.add_flatten_table_alias(alias.alias.table().to_string());
1971 }
1972 LogicalPlan::Join(join) => {
1973 Self::collect_flatten_aliases(&join.left, select);
1974 Self::collect_flatten_aliases(&join.right, select);
1975 }
1976 _ => {}
1977 }
1978 }
1979
1980 fn try_unnest_to_table_factor_sql(
1981 &self,
1982 unnest: &Unnest,
1983 ) -> Result<Option<UnnestRelationBuilder>> {
1984 let mut unnest_relation = UnnestRelationBuilder::default();
1985 let LogicalPlan::Projection(projection) = unnest.input.as_ref() else {
1986 return Ok(None);
1987 };
1988
1989 if !matches!(projection.input.as_ref(), LogicalPlan::EmptyRelation(_)) {
1990 // It may be possible that UNNEST is used as a source for the query.
1991 // However, at this point, we don't yet know if it is just a single expression
1992 // from another source or if it's from UNNEST.
1993 //
1994 // Unnest(Projection(EmptyRelation)) denotes a case with `UNNEST([...])`,
1995 // which is normally safe to unnest as a table factor.
1996 // However, in the future, more comprehensive checks can be added here.
1997 return Ok(None);
1998 };
1999
2000 let exprs = projection
2001 .expr
2002 .iter()
2003 .map(|e| self.expr_to_sql(e))
2004 .collect::<Result<Vec<_>>>()?;
2005 unnest_relation.array_exprs(exprs);
2006
2007 Ok(Some(unnest_relation))
2008 }
2009
2010 /// Build a `SELECT alias."VALUE"` item for Snowflake FLATTEN output.
2011 fn build_flatten_value_select_item(
2012 &self,
2013 flatten_alias: &str,
2014 user_alias: Option<&str>,
2015 ) -> ast::SelectItem {
2016 let compound = ast::Expr::CompoundIdentifier(vec![
2017 self.new_ident_quoted_if_needs(flatten_alias.to_string()),
2018 Ident::with_quote('"', "VALUE"),
2019 ]);
2020 match user_alias {
2021 Some(alias) => ast::SelectItem::ExprWithAlias {
2022 expr: compound,
2023 alias: self.new_ident_quoted_if_needs(alias.to_string()),
2024 },
2025 None => ast::SelectItem::UnnamedExpr(compound),
2026 }
2027 }
2028
2029 /// Convert an `Unnest` logical plan node to a `LATERAL FLATTEN(INPUT => expr, ...)`
2030 /// table factor for Snowflake-style SQL output.
2031 fn try_unnest_to_lateral_flatten_sql(
2032 &self,
2033 unnest: &Unnest,
2034 ) -> Result<Option<FlattenRelationBuilder>> {
2035 let Some(projection) = Self::peel_to_inner_projection(unnest.input.as_ref())
2036 else {
2037 return Ok(None);
2038 };
2039
2040 // For now, handle the simple case of a single expression to flatten.
2041 // Multi-expression would require multiple LATERAL FLATTEN calls chained together.
2042 let Some(first_expr) = projection.expr.first() else {
2043 return Ok(None);
2044 };
2045
2046 let input_expr = self.expr_to_sql(first_expr)?;
2047
2048 let mut flatten = FlattenRelationBuilder::default();
2049 flatten.input_expr(input_expr);
2050 flatten.outer(unnest.options.preserve_nulls());
2051
2052 Ok(Some(flatten))
2053 }
2054
2055 fn is_scan_with_pushdown(scan: &TableScan) -> bool {
2056 scan.projection.is_some() || !scan.filters.is_empty() || scan.fetch.is_some()
2057 }
2058
2059 /// Returns true if a plan, when used as the direct child of a SubqueryAlias,
2060 /// must be emitted as a derived subquery `(SELECT ...) AS alias`.
2061 ///
2062 /// Plans like Aggregate or Window build their own SELECT clauses (GROUP BY,
2063 /// window functions).
2064 fn requires_derived_subquery(plan: &LogicalPlan) -> bool {
2065 matches!(
2066 plan,
2067 LogicalPlan::Aggregate(_)
2068 | LogicalPlan::Window(_)
2069 | LogicalPlan::Sort(_)
2070 | LogicalPlan::Limit(_)
2071 | LogicalPlan::Union(_)
2072 )
2073 }
2074
2075 fn is_qualified_passthrough_projection(projection: &Projection) -> bool {
2076 projection
2077 .expr
2078 .iter()
2079 .all(|expr| matches!(expr, Expr::Column(column) if column.relation.is_some()))
2080 }
2081
2082 fn unwrap_qualified_passthrough_join_projection(
2083 plan: Arc<LogicalPlan>,
2084 ) -> Arc<LogicalPlan> {
2085 if let LogicalPlan::Projection(projection) = plan.as_ref()
2086 && matches!(projection.input.as_ref(), LogicalPlan::Join(_))
2087 && Self::is_qualified_passthrough_projection(projection)
2088 {
2089 Arc::clone(&projection.input)
2090 } else {
2091 plan
2092 }
2093 }
2094
2095 fn qualified_passthrough_join_projection_to_nested_relation(
2096 &self,
2097 plan: &LogicalPlan,
2098 query: &mut Option<QueryBuilder>,
2099 ) -> Result<Option<RelationBuilder>> {
2100 let LogicalPlan::Projection(projection) = plan else {
2101 return Ok(None);
2102 };
2103 if !matches!(projection.input.as_ref(), LogicalPlan::Join(_))
2104 || !Self::is_qualified_passthrough_projection(projection)
2105 {
2106 return Ok(None);
2107 }
2108
2109 let original_query = query.clone();
2110 let mut nested_select = SelectBuilder::default();
2111 nested_select.push_from(TableWithJoinsBuilder::default());
2112 let mut nested_relation = RelationBuilder::default();
2113 self.select_to_sql_recursively(
2114 projection.input.as_ref(),
2115 query,
2116 &mut nested_select,
2117 &mut nested_relation,
2118 )?;
2119 if nested_select.has_selection() {
2120 *query = original_query;
2121 return Ok(None);
2122 }
2123
2124 let Some(mut nested_from) = nested_select.pop_from() else {
2125 return internal_err!("Failed to build nested join relation");
2126 };
2127 nested_from.relation(nested_relation);
2128 let Some(table_with_joins) = nested_from.build()? else {
2129 return internal_err!("Failed to build nested join relation");
2130 };
2131
2132 let mut relation = RelationBuilder::default();
2133 relation.nested_join(table_with_joins, None);
2134 Ok(Some(relation))
2135 }
2136
2137 /// Strip the table qualifier from every column in an expression that must
2138 /// resolve against an unnamed derived table's output columns rather than a
2139 /// deeper table alias that is out of scope at this nesting level.
2140 fn strip_column_qualifiers(expr: Expr) -> Result<Expr> {
2141 expr.transform(|e| match e {
2142 Expr::Column(mut column) => {
2143 column.relation = None;
2144 Ok(Transformed::yes(Expr::Column(column)))
2145 }
2146 other => Ok(Transformed::no(other)),
2147 })
2148 .data()
2149 }
2150
2151 fn strip_column_qualifiers_for_schema(expr: Expr, schema: &DFSchema) -> Result<Expr> {
2152 expr.transform(|e| match e {
2153 Expr::Column(mut column)
2154 if column.relation.is_some()
2155 && schema.index_of_column(&column).is_ok() =>
2156 {
2157 column.relation = None;
2158 Ok(Transformed::yes(Expr::Column(column)))
2159 }
2160 other => Ok(Transformed::no(other)),
2161 })
2162 .data()
2163 }
2164
2165 /// Try to unparse a table scan with pushdown operations into a new subquery plan.
2166 /// If the table scan is without any pushdown operations, return None.
2167 fn unparse_table_scan_pushdown(
2168 &self,
2169 plan: &LogicalPlan,
2170 alias: Option<TableReference>,
2171 already_projected: bool,
2172 ) -> Result<Option<LogicalPlan>> {
2173 match plan {
2174 LogicalPlan::TableScan(table_scan) => {
2175 if !Self::is_scan_with_pushdown(table_scan) {
2176 return Ok(None);
2177 }
2178 let table_schema = table_scan.source.schema();
2179 let filter_schema = DFSchema::try_from_qualified_schema(
2180 table_scan.table_name.clone(),
2181 table_schema.as_ref(),
2182 )?;
2183 let mut filter_alias_rewriter =
2184 alias.as_ref().map(|alias_name| TableAliasRewriter {
2185 table_schema: &filter_schema,
2186 alias_name: alias_name.clone(),
2187 rewrite_unqualified: true,
2188 });
2189
2190 let mut builder = LogicalPlanBuilder::scan(
2191 table_scan.table_name.clone(),
2192 Arc::clone(&table_scan.source),
2193 None,
2194 )?;
2195 // We will rebase the column references to the new alias if it exists.
2196 // If the projection or filters are empty, we will append alias to the table scan.
2197 //
2198 // Example:
2199 // select t1.c1 from t1 where t1.c1 > 1 -> select a.c1 from t1 as a where a.c1 > 1
2200 if let Some(ref alias) = alias
2201 && (table_scan.projection.is_some() || !table_scan.filters.is_empty())
2202 {
2203 builder = builder.alias(alias.clone())?;
2204 }
2205
2206 // Avoid creating a duplicate Projection node, which would result in an additional subquery if a projection already exists.
2207 // For example, if the `optimize_projection` rule is applied, there will be a Projection node, and duplicate projection
2208 // information included in the TableScan node.
2209 if !already_projected && let Some(project_vec) = &table_scan.projection {
2210 if project_vec.is_empty() {
2211 builder = builder.project(self.empty_projection_fallback())?;
2212 } else {
2213 let project_columns = project_vec
2214 .iter()
2215 .cloned()
2216 .map(|i| {
2217 let schema = table_scan.source.schema();
2218 let field = schema.field(i);
2219 if alias.is_some() {
2220 Column::new(alias.clone(), field.name().clone())
2221 } else {
2222 Column::new(
2223 Some(table_scan.table_name.clone()),
2224 field.name().clone(),
2225 )
2226 }
2227 })
2228 .collect::<Vec<_>>();
2229 builder = builder.project(project_columns)?;
2230 };
2231 }
2232
2233 let filter_expr: Result<Option<Expr>> = table_scan
2234 .filters
2235 .iter()
2236 .cloned()
2237 .map(|expr| {
2238 if let Some(ref mut rewriter) = filter_alias_rewriter {
2239 expr.rewrite(rewriter).data()
2240 } else {
2241 Ok(expr)
2242 }
2243 })
2244 .reduce(|acc, expr_result| {
2245 acc.and_then(|acc_expr| {
2246 expr_result.map(|expr| acc_expr.and(expr))
2247 })
2248 })
2249 .transpose();
2250
2251 if let Some(filter) = filter_expr? {
2252 builder = builder.filter(filter)?;
2253 }
2254
2255 if let Some(fetch) = table_scan.fetch {
2256 builder = builder.limit(0, Some(fetch))?;
2257 }
2258
2259 // If the table scan has an alias but no projection or filters, it means no column references are rebased.
2260 // So we will append the alias to this subquery.
2261 // Example:
2262 // select * from t1 limit 10 -> (select * from t1 limit 10) as a
2263 if let Some(alias) = alias
2264 && table_scan.projection.is_none()
2265 && table_scan.filters.is_empty()
2266 {
2267 builder = builder.alias(alias)?;
2268 }
2269
2270 Ok(Some(builder.build()?))
2271 }
2272 LogicalPlan::SubqueryAlias(subquery_alias) => {
2273 let ret = self.unparse_table_scan_pushdown(
2274 &subquery_alias.input,
2275 Some(subquery_alias.alias.clone()),
2276 already_projected,
2277 )?;
2278 if let Some(alias) = alias
2279 && let Some(plan) = ret
2280 {
2281 let plan = LogicalPlanBuilder::new(plan).alias(alias)?.build()?;
2282 return Ok(Some(plan));
2283 }
2284 Ok(ret)
2285 }
2286 // SubqueryAlias could be rewritten to a plan with a projection as the top node by [rewrite::subquery_alias_inner_query_and_columns].
2287 // The inner table scan could be a scan with pushdown operations.
2288 LogicalPlan::Projection(projection) => {
2289 if let Some(plan) = self.unparse_table_scan_pushdown(
2290 &projection.input,
2291 alias.clone(),
2292 already_projected,
2293 )? {
2294 // The pushed-down scan alias is only in scope for the
2295 // projection directly above the aliased table scan. `plan`
2296 // is the result of pushing the alias further down: if it is
2297 // itself a `Projection`, the input was another projection
2298 // (e.g. common subexpression elimination stacked one), so
2299 // this projection sits over a derived table rather than
2300 // directly over the aliased scan, and the alias is out of
2301 // scope here. Its qualified pass-through columns must then
2302 // reference the derived table's output unqualified instead
2303 // of being rebased to the alias. Build it directly so the
2304 // unqualified columns are not re-normalized back to the
2305 // alias. (Otherwise `plan` is the scan-derived plan and we
2306 // fall through to rebase to the alias, correct one level
2307 // above the scan.)
2308 if alias.is_some() && matches!(plan, LogicalPlan::Projection(_)) {
2309 let exprs = projection
2310 .expr
2311 .iter()
2312 .cloned()
2313 .map(Self::strip_column_qualifiers)
2314 .collect::<Result<Vec<_>>>()?;
2315 return Ok(Some(LogicalPlan::Projection(Projection::try_new(
2316 exprs,
2317 Arc::new(plan),
2318 )?)));
2319 }
2320
2321 let exprs = if alias.is_some() {
2322 let mut alias_rewriter =
2323 alias.as_ref().map(|alias_name| TableAliasRewriter {
2324 table_schema: plan.schema().as_ref(),
2325 alias_name: alias_name.clone(),
2326 rewrite_unqualified: false,
2327 });
2328 projection
2329 .expr
2330 .iter()
2331 .cloned()
2332 .map(|expr| {
2333 if let Some(ref mut rewriter) = alias_rewriter {
2334 expr.rewrite(rewriter).data()
2335 } else {
2336 Ok(expr)
2337 }
2338 })
2339 .collect::<Result<Vec<_>>>()?
2340 } else {
2341 projection.expr.clone()
2342 };
2343 Ok(Some(
2344 LogicalPlanBuilder::from(plan).project(exprs)?.build()?,
2345 ))
2346 } else {
2347 Ok(None)
2348 }
2349 }
2350 _ => Ok(None),
2351 }
2352 }
2353
2354 fn select_item_to_sql(&self, expr: &Expr) -> Result<ast::SelectItem> {
2355 match expr {
2356 Expr::Alias(Alias { expr, name, .. }) => {
2357 let inner = self.expr_to_sql(expr)?;
2358
2359 // Determine the alias name to use
2360 let col_name = if let Some(rewritten_name) =
2361 self.dialect.col_alias_overrides(name)?
2362 {
2363 rewritten_name.to_string()
2364 } else {
2365 name.to_string()
2366 };
2367
2368 Ok(ast::SelectItem::ExprWithAlias {
2369 expr: inner,
2370 alias: self.new_ident_quoted_if_needs(col_name),
2371 })
2372 }
2373 _ => {
2374 let inner = self.expr_to_sql(expr)?;
2375
2376 Ok(ast::SelectItem::UnnamedExpr(inner))
2377 }
2378 }
2379 }
2380
2381 fn sorts_to_sql(&self, sort_exprs: &[SortExpr]) -> Result<OrderByKind> {
2382 Ok(OrderByKind::Expressions(
2383 sort_exprs
2384 .iter()
2385 .map(|sort_expr| self.sort_to_sql(sort_expr))
2386 .collect::<Result<Vec<_>>>()?,
2387 ))
2388 }
2389
2390 fn join_operator_to_sql(
2391 &self,
2392 join_type: JoinType,
2393 constraint: ast::JoinConstraint,
2394 ) -> Result<ast::JoinOperator> {
2395 Ok(match join_type {
2396 JoinType::Inner => match &constraint {
2397 ast::JoinConstraint::On(_)
2398 | ast::JoinConstraint::Using(_)
2399 | ast::JoinConstraint::Natural => ast::JoinOperator::Inner(constraint),
2400 ast::JoinConstraint::None => {
2401 // Inner joins with no conditions or filters are not valid SQL in most systems,
2402 // return a CROSS JOIN instead
2403 ast::JoinOperator::CrossJoin(constraint)
2404 }
2405 },
2406 JoinType::Left => ast::JoinOperator::LeftOuter(constraint),
2407 JoinType::Right => ast::JoinOperator::RightOuter(constraint),
2408 JoinType::Full => ast::JoinOperator::FullOuter(constraint),
2409 JoinType::LeftAnti => ast::JoinOperator::LeftAnti(constraint),
2410 JoinType::LeftSemi => ast::JoinOperator::LeftSemi(constraint),
2411 JoinType::RightAnti => ast::JoinOperator::RightAnti(constraint),
2412 JoinType::RightSemi => ast::JoinOperator::RightSemi(constraint),
2413 JoinType::LeftMark | JoinType::RightMark => {
2414 unimplemented!("Unparsing of Mark join type")
2415 }
2416 })
2417 }
2418
2419 /// Convert the components of a USING clause to the USING AST. Returns
2420 /// 'None' if the conditions are not compatible with a USING expression,
2421 /// e.g. non-column expressions or non-matching names.
2422 fn join_using_to_sql(
2423 &self,
2424 join_conditions: &[(Expr, Expr)],
2425 ) -> Option<ast::JoinConstraint> {
2426 let mut object_names = Vec::with_capacity(join_conditions.len());
2427 for (left, right) in join_conditions {
2428 match (left, right) {
2429 (
2430 Expr::Column(Column {
2431 relation: _,
2432 name: left_name,
2433 spans: _,
2434 }),
2435 Expr::Column(Column {
2436 relation: _,
2437 name: right_name,
2438 spans: _,
2439 }),
2440 ) if left_name == right_name => {
2441 // For example, if the join condition `t1.id = t2.id`
2442 // this is represented as two columns like `[t1.id, t2.id]`
2443 // This code forms `id` (without relation name)
2444 let ident = self.new_ident_quoted_if_needs(left_name.to_string());
2445 object_names.push(ast::ObjectName::from(vec![ident]));
2446 }
2447 // USING is only valid with matching column names; arbitrary expressions
2448 // are not allowed
2449 _ => return None,
2450 }
2451 }
2452 Some(ast::JoinConstraint::Using(object_names))
2453 }
2454
2455 /// Convert a join constraint and associated conditions and filter to a SQL AST node
2456 fn join_constraint_to_sql(
2457 &self,
2458 constraint: JoinConstraint,
2459 conditions: &[(Expr, Expr)],
2460 filter: Option<&Expr>,
2461 ) -> Result<ast::JoinConstraint> {
2462 match (constraint, conditions, filter) {
2463 // No constraints
2464 (JoinConstraint::On | JoinConstraint::Using, [], None) => {
2465 Ok(ast::JoinConstraint::None)
2466 }
2467
2468 (JoinConstraint::Using, conditions, None) => {
2469 match self.join_using_to_sql(conditions) {
2470 Some(using) => Ok(using),
2471 // As above, this should not be reachable from parsed SQL,
2472 // but a user could create this; we "downgrade" to ON.
2473 None => self.join_conditions_to_sql_on(conditions, None),
2474 }
2475 }
2476
2477 // Two cases here:
2478 // 1. Straightforward ON case, with possible equi-join conditions
2479 // and additional filters
2480 // 2. USING with additional filters; we "downgrade" to ON, because
2481 // you can't use USING with arbitrary filters. (This should not
2482 // be accessible from parsed SQL, but may have been a
2483 // custom-built JOIN by a user.)
2484 (JoinConstraint::On | JoinConstraint::Using, conditions, filter) => {
2485 self.join_conditions_to_sql_on(conditions, filter)
2486 }
2487 }
2488 }
2489
2490 // Convert a list of equi0join conditions and an optional filter to a SQL ON
2491 // AST node, with the equi-join conditions and the filter merged into a
2492 // single conditional expression
2493 fn join_conditions_to_sql_on(
2494 &self,
2495 join_conditions: &[(Expr, Expr)],
2496 filter: Option<&Expr>,
2497 ) -> Result<ast::JoinConstraint> {
2498 let mut condition = None;
2499 // AND the join conditions together to create the overall condition
2500 for (left, right) in join_conditions {
2501 // Parse left and right
2502 let l = self.expr_to_sql(left)?;
2503 let r = self.expr_to_sql(right)?;
2504 let e = self.binary_op_to_sql(l, r, ast::BinaryOperator::Eq);
2505 condition = match condition {
2506 Some(expr) => Some(self.and_op_to_sql(expr, e)),
2507 None => Some(e),
2508 };
2509 }
2510
2511 // Then AND the non-equijoin filter condition as well
2512 condition = match (condition, filter) {
2513 (Some(expr), Some(filter)) => {
2514 Some(self.and_op_to_sql(expr, self.expr_to_sql(filter)?))
2515 }
2516 (Some(expr), None) => Some(expr),
2517 (None, Some(filter)) => Some(self.expr_to_sql(filter)?),
2518 (None, None) => None,
2519 };
2520
2521 let constraint = match condition {
2522 Some(filter) => ast::JoinConstraint::On(filter),
2523 None => ast::JoinConstraint::None,
2524 };
2525
2526 Ok(constraint)
2527 }
2528
2529 fn and_op_to_sql(&self, lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
2530 self.binary_op_to_sql(lhs, rhs, ast::BinaryOperator::And)
2531 }
2532
2533 fn new_table_alias(&self, alias: String, columns: Vec<Ident>) -> ast::TableAlias {
2534 let columns = columns
2535 .into_iter()
2536 .map(|ident| TableAliasColumnDef {
2537 name: ident,
2538 data_type: None,
2539 })
2540 .collect();
2541 ast::TableAlias {
2542 name: self.new_ident_quoted_if_needs(alias),
2543 columns,
2544 explicit: true,
2545 at: None,
2546 }
2547 }
2548
2549 fn dml_to_sql(&self, plan: &LogicalPlan) -> Result<ast::Statement> {
2550 not_impl_err!("Unsupported plan: {plan:?}")
2551 }
2552
2553 /// Generates appropriate projection expression for empty projection lists.
2554 /// Returns an empty vec for dialects supporting empty select lists,
2555 /// or a dummy literal `1` for other dialects.
2556 fn empty_projection_fallback(&self) -> Vec<Expr> {
2557 if self.dialect.supports_empty_select_list() {
2558 Vec::new()
2559 } else {
2560 vec![Expr::Literal(ScalarValue::Int64(Some(1)), None)]
2561 }
2562 }
2563
2564 /// Decides where extracted table-scan filters belong in the unparsed SQL:
2565 /// in the `JOIN ON` clause or in `WHERE`.
2566 ///
2567 /// For inner joins the two are semantically equivalent, so filters go to
2568 /// `WHERE` (some dialects reject subqueries inside `JOIN ON`).
2569 /// For outer joins the filters are AND-folded into `ON` to preserve correctness.
2570 ///
2571 /// Returns `(on_filter, where_filters)`.
2572 fn split_join_on_and_where_filters(
2573 join_type: JoinType,
2574 join_filter: &Option<Expr>,
2575 table_scan_filters: Vec<Expr>,
2576 ) -> (Option<Expr>, Vec<Expr>) {
2577 if table_scan_filters.is_empty() {
2578 return (join_filter.clone(), vec![]);
2579 }
2580
2581 if join_type == JoinType::Inner {
2582 // ON and WHERE are equivalent for inner joins; prefer WHERE
2583 // because some dialects reject subqueries inside JOIN ON.
2584 return (join_filter.clone(), table_scan_filters);
2585 }
2586
2587 // Outer joins: fold table-scan filters into ON to preserve semantics.
2588 let combined = table_scan_filters.into_iter().reduce(|acc, filter| {
2589 Expr::BinaryExpr(BinaryExpr {
2590 left: Box::new(acc),
2591 op: Operator::And,
2592 right: Box::new(filter),
2593 })
2594 });
2595
2596 let on_filter = match (join_filter, combined) {
2597 (Some(jf), Some(c)) => Some(Expr::BinaryExpr(BinaryExpr {
2598 left: Box::new(jf.clone()),
2599 op: Operator::And,
2600 right: Box::new(c),
2601 })),
2602 (Some(jf), None) => Some(jf.clone()),
2603 (None, Some(c)) => Some(c),
2604 (None, None) => None,
2605 };
2606
2607 (on_filter, vec![])
2608 }
2609}
2610
2611impl From<BuilderError> for DataFusionError {
2612 fn from(e: BuilderError) -> Self {
2613 DataFusionError::External(Box::new(e))
2614 }
2615}
2616
2617/// The type of the input to the UNNEST table factor.
2618#[derive(Debug)]
2619enum UnnestInputType {
2620 /// The input is a column reference. It will be presented like `outer_ref(column_name)`.
2621 OuterReference,
2622 /// The input is a scalar value. It will be presented like a scalar array or struct.
2623 Scalar,
2624}