@@ -46,16 +46,16 @@ use datafusion_expr::dml::{
4646use datafusion_expr:: expr_rewriter:: normalize_col_with_schemas_and_ambiguity_check;
4747use datafusion_expr:: logical_plan:: DdlStatement ;
4848use datafusion_expr:: logical_plan:: builder:: project;
49- use datafusion_expr:: utils:: expr_to_columns;
49+ use datafusion_expr:: utils:: { expr_to_columns, merge_schema } ;
5050use datafusion_expr:: {
5151 Analyze , CreateCatalog , CreateCatalogSchema ,
5252 CreateExternalTable as PlanCreateExternalTable , CreateFunction , CreateFunctionBody ,
5353 CreateIndex as PlanCreateIndex , CreateMemoryTable , CreateView , Deallocate ,
5454 DescribeTable , DmlStatement , DropCatalogSchema , DropFunction , DropTable , DropView ,
5555 EmptyRelation , Execute , Explain , ExplainFormat , Expr , ExprSchemable , Filter ,
5656 LogicalPlan , LogicalPlanBuilder , OperateFunctionArg , PlanType , Prepare ,
57- ResetVariable , SetVariable , SortExpr , Statement as PlanStatement , ToStringifiedPlan ,
58- TransactionAccessMode , TransactionConclusion , TransactionEnd ,
57+ ResetVariable , SetVariable , SortExpr , Statement as PlanStatement , Subquery ,
58+ ToStringifiedPlan , TransactionAccessMode , TransactionConclusion , TransactionEnd ,
5959 TransactionIsolationLevel , TransactionStart , Volatility , WriteOp , cast, col,
6060} ;
6161use sqlparser:: ast:: {
@@ -2610,42 +2610,24 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
26102610 . map ( |transformed| transformed. data )
26112611 }
26122612
2613- /// Return true if an expression contains a subquery whose embedded plan
2614- /// has an outer reference qualified by `qualifier`.
2613+ /// Return true if an expression contains a subquery correlated to
2614+ /// `qualifier`, accounting for aliases introduced by intervening subquery
2615+ /// scopes.
26152616 fn has_outer_reference_to_qualifier (
26162617 expr : & Expr ,
26172618 qualifier : & TableReference ,
26182619 ) -> Result < bool > {
26192620 let mut found = false ;
26202621 expr. apply ( |expr| {
2621- let subquery = match expr {
2622- Expr :: Exists ( exists) => Some ( & exists. subquery ) ,
2623- Expr :: InSubquery ( in_subquery) => Some ( & in_subquery. subquery ) ,
2624- Expr :: SetComparison ( set_comparison) => Some ( & set_comparison. subquery ) ,
2625- Expr :: ScalarSubquery ( subquery) => Some ( subquery) ,
2626- _ => None ,
2627- } ;
2628-
2629- if let Some ( subquery) = subquery {
2630- subquery. subquery . apply_with_subqueries ( |plan| {
2631- plan. apply_expressions ( |expr| {
2632- expr. apply ( |expr| {
2633- if let Expr :: OuterReferenceColumn ( _, column) = expr
2634- && column. relation . as_ref ( ) == Some ( qualifier)
2635- {
2636- found = true ;
2637- Ok ( TreeNodeRecursion :: Stop )
2638- } else {
2639- Ok ( TreeNodeRecursion :: Continue )
2640- }
2641- } )
2642- } ) ?;
2643- Ok ( if found {
2644- TreeNodeRecursion :: Stop
2645- } else {
2646- TreeNodeRecursion :: Continue
2647- } )
2648- } ) ?;
2622+ if let Some ( subquery) = Self :: expr_subquery ( expr)
2623+ && Self :: plan_has_outer_reference_to_qualifier (
2624+ & subquery. subquery ,
2625+ qualifier,
2626+ & [ ] ,
2627+ None ,
2628+ ) ?
2629+ {
2630+ found = true ;
26492631 }
26502632
26512633 Ok ( if found {
@@ -2657,6 +2639,164 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
26572639 Ok ( found)
26582640 }
26592641
2642+ fn expr_subquery ( expr : & Expr ) -> Option < & Subquery > {
2643+ match expr {
2644+ Expr :: Exists ( exists) => Some ( & exists. subquery ) ,
2645+ Expr :: InSubquery ( in_subquery) => Some ( & in_subquery. subquery ) ,
2646+ Expr :: SetComparison ( set_comparison) => Some ( & set_comparison. subquery ) ,
2647+ Expr :: ScalarSubquery ( subquery) => Some ( subquery) ,
2648+ _ => None ,
2649+ }
2650+ }
2651+
2652+ #[ cfg_attr( feature = "recursive_protection" , recursive:: recursive) ]
2653+ fn plan_has_outer_reference_to_qualifier (
2654+ plan : & LogicalPlan ,
2655+ qualifier : & TableReference ,
2656+ outer_scopes : & [ DFSchema ] ,
2657+ lateral_scope : Option < & DFSchema > ,
2658+ ) -> Result < bool > {
2659+ // Correlated lateral relations use an explicit Subquery plan node
2660+ // rather than an expression.
2661+ if let LogicalPlan :: Subquery ( subquery) = plan {
2662+ let mut subquery_outer_scopes = outer_scopes. to_vec ( ) ;
2663+ if let Some ( lateral_scope) = lateral_scope {
2664+ subquery_outer_scopes. push ( lateral_scope. clone ( ) ) ;
2665+ }
2666+ return Self :: plan_has_outer_reference_to_qualifier (
2667+ & subquery. subquery ,
2668+ qualifier,
2669+ & subquery_outer_scopes,
2670+ None ,
2671+ ) ;
2672+ }
2673+
2674+ let local_scope = Self :: plan_expression_scope ( plan) ;
2675+
2676+ let mut found = false ;
2677+ plan. apply_expressions ( |expr| {
2678+ expr. apply ( |expr| {
2679+ if let Expr :: OuterReferenceColumn ( _, column) = expr
2680+ && column. relation . as_ref ( ) == Some ( qualifier)
2681+ && !outer_scopes
2682+ . iter ( )
2683+ . rev ( )
2684+ . any ( |scope| scope. maybe_index_of_column ( column) . is_some ( ) )
2685+ {
2686+ found = true ;
2687+ } else if let Some ( subquery) = Self :: expr_subquery ( expr) {
2688+ let mut nested_outer_scopes = outer_scopes. to_vec ( ) ;
2689+ if let Some ( local_scope) = & local_scope {
2690+ nested_outer_scopes. push ( local_scope. clone ( ) ) ;
2691+ }
2692+ if Self :: plan_has_outer_reference_to_qualifier (
2693+ & subquery. subquery ,
2694+ qualifier,
2695+ & nested_outer_scopes,
2696+ None ,
2697+ ) ? {
2698+ found = true ;
2699+ }
2700+ }
2701+
2702+ Ok ( if found {
2703+ TreeNodeRecursion :: Stop
2704+ } else {
2705+ TreeNodeRecursion :: Continue
2706+ } )
2707+ } )
2708+ } ) ?;
2709+ if found {
2710+ return Ok ( true ) ;
2711+ }
2712+
2713+ if let LogicalPlan :: Join ( join) = plan {
2714+ if Self :: plan_has_outer_reference_to_qualifier (
2715+ & join. left ,
2716+ qualifier,
2717+ outer_scopes,
2718+ lateral_scope,
2719+ ) ? {
2720+ return Ok ( true ) ;
2721+ }
2722+
2723+ let mut right_lateral_scope =
2724+ lateral_scope. cloned ( ) . unwrap_or_else ( DFSchema :: empty) ;
2725+ right_lateral_scope. merge ( join. left . schema ( ) ) ;
2726+ if let Some ( subquery) = Self :: lateral_subquery ( & join. right ) {
2727+ let mut lateral_outer_scopes = outer_scopes. to_vec ( ) ;
2728+ lateral_outer_scopes. push ( right_lateral_scope) ;
2729+ return Self :: plan_has_outer_reference_to_qualifier (
2730+ & subquery. subquery ,
2731+ qualifier,
2732+ & lateral_outer_scopes,
2733+ None ,
2734+ ) ;
2735+ }
2736+
2737+ return Self :: plan_has_outer_reference_to_qualifier (
2738+ & join. right ,
2739+ qualifier,
2740+ outer_scopes,
2741+ Some ( & right_lateral_scope) ,
2742+ ) ;
2743+ }
2744+
2745+ for input in plan. inputs ( ) {
2746+ if Self :: plan_has_outer_reference_to_qualifier (
2747+ input,
2748+ qualifier,
2749+ outer_scopes,
2750+ lateral_scope,
2751+ ) ? {
2752+ return Ok ( true ) ;
2753+ }
2754+ }
2755+
2756+ Ok ( false )
2757+ }
2758+
2759+ /// Return the schema used to resolve subqueries in this node's expressions.
2760+ ///
2761+ /// Most relational expressions use their input schema. LIMIT/OFFSET
2762+ /// expressions are deliberately resolved against an empty schema, so they
2763+ /// must not inherit aliases from the limited input.
2764+ fn plan_expression_scope ( plan : & LogicalPlan ) -> Option < DFSchema > {
2765+ match plan {
2766+ LogicalPlan :: Projection ( _)
2767+ | LogicalPlan :: Filter ( _)
2768+ | LogicalPlan :: Repartition ( _)
2769+ | LogicalPlan :: Window ( _)
2770+ | LogicalPlan :: Aggregate ( _)
2771+ | LogicalPlan :: Join ( _)
2772+ | LogicalPlan :: Sort ( _)
2773+ | LogicalPlan :: TableScan ( _)
2774+ | LogicalPlan :: Unnest ( _)
2775+ | LogicalPlan :: Distinct ( _) => {
2776+ let inputs = plan. inputs ( ) ;
2777+ Some ( if inputs. is_empty ( ) {
2778+ plan. schema ( ) . as_ref ( ) . clone ( )
2779+ } else {
2780+ merge_schema ( & inputs)
2781+ } )
2782+ }
2783+ _ => None ,
2784+ }
2785+ }
2786+
2787+ /// Return the correlated subquery wrapped by a lateral relation, if this
2788+ /// plan has the shape produced by `create_relation_subquery`.
2789+ fn lateral_subquery ( mut plan : & LogicalPlan ) -> Option < & Subquery > {
2790+ loop {
2791+ match plan {
2792+ LogicalPlan :: Subquery ( subquery) => return Some ( subquery) ,
2793+ LogicalPlan :: SubqueryAlias ( alias) => plan = & alias. input ,
2794+ LogicalPlan :: Projection ( projection) => plan = & projection. input ,
2795+ _ => return None ,
2796+ }
2797+ }
2798+ }
2799+
26602800 fn merge_target_column_name (
26612801 & self ,
26622802 name : & ObjectName ,
0 commit comments