@@ -19,7 +19,7 @@ use datafusion_common::{internal_err, not_impl_err, plan_err, DataFusionError, R
19
19
use datafusion_expr:: {
20
20
expr:: Alias , Distinct , Expr , JoinConstraint , JoinType , LogicalPlan , Projection ,
21
21
} ;
22
- use sqlparser:: ast:: { self , SetExpr } ;
22
+ use sqlparser:: ast:: { self , Ident , SetExpr } ;
23
23
24
24
use crate :: unparser:: utils:: unproject_agg_exprs;
25
25
@@ -457,15 +457,11 @@ impl Unparser<'_> {
457
457
}
458
458
LogicalPlan :: SubqueryAlias ( plan_alias) => {
459
459
// Handle bottom-up to allocate relation
460
- self . select_to_sql_recursively (
461
- plan_alias. input . as_ref ( ) ,
462
- query,
463
- select,
464
- relation,
465
- ) ?;
460
+ let ( plan, columns) = subquery_alias_inner_query_and_columns ( plan_alias) ;
466
461
462
+ self . select_to_sql_recursively ( plan, query, select, relation) ?;
467
463
relation. alias ( Some (
468
- self . new_table_alias ( plan_alias. alias . table ( ) . to_string ( ) ) ,
464
+ self . new_table_alias ( plan_alias. alias . table ( ) . to_string ( ) , columns ) ,
469
465
) ) ;
470
466
471
467
Ok ( ( ) )
@@ -599,10 +595,10 @@ impl Unparser<'_> {
599
595
self . binary_op_to_sql ( lhs, rhs, ast:: BinaryOperator :: And )
600
596
}
601
597
602
- fn new_table_alias ( & self , alias : String ) -> ast:: TableAlias {
598
+ fn new_table_alias ( & self , alias : String , columns : Vec < Ident > ) -> ast:: TableAlias {
603
599
ast:: TableAlias {
604
600
name : self . new_ident_quoted_if_needs ( alias) ,
605
- columns : Vec :: new ( ) ,
601
+ columns,
606
602
}
607
603
}
608
604
@@ -611,6 +607,67 @@ impl Unparser<'_> {
611
607
}
612
608
}
613
609
610
+ // This logic is to work out the columns and inner query for SubqueryAlias plan for both types of
611
+ // subquery
612
+ // - `(SELECT column_a as a from table) AS A`
613
+ // - `(SELECT column_a from table) AS A (a)`
614
+ //
615
+ // A roundtrip example for table alias with columns
616
+ //
617
+ // query: SELECT id FROM (SELECT j1_id from j1) AS c (id)
618
+ //
619
+ // LogicPlan:
620
+ // Projection: c.id
621
+ // SubqueryAlias: c
622
+ // Projection: j1.j1_id AS id
623
+ // Projection: j1.j1_id
624
+ // TableScan: j1
625
+ //
626
+ // Before introducing this logic, the unparsed query would be `SELECT c.id FROM (SELECT j1.j1_id AS
627
+ // id FROM (SELECT j1.j1_id FROM j1)) AS c`.
628
+ // The query is invalid as `j1.j1_id` is not a valid identifier in the derived table
629
+ // `(SELECT j1.j1_id FROM j1)`
630
+ //
631
+ // With this logic, the unparsed query will be:
632
+ // `SELECT c.id FROM (SELECT j1.j1_id FROM j1) AS c (id)`
633
+ //
634
+ // Caveat: this won't handle the case like `select * from (select 1, 2) AS a (b, c)`
635
+ // as the parser gives a wrong plan which has mismatch `Int(1)` types: Literal and
636
+ // Column in the Projections. Once the parser side is fixed, this logic should work
637
+ fn subquery_alias_inner_query_and_columns (
638
+ subquery_alias : & datafusion_expr:: SubqueryAlias ,
639
+ ) -> ( & LogicalPlan , Vec < Ident > ) {
640
+ let plan: & LogicalPlan = subquery_alias. input . as_ref ( ) ;
641
+
642
+ let LogicalPlan :: Projection ( outer_projections) = plan else {
643
+ return ( plan, vec ! [ ] ) ;
644
+ } ;
645
+
646
+ // check if it's projection inside projection
647
+ let LogicalPlan :: Projection ( inner_projection) = outer_projections. input . as_ref ( )
648
+ else {
649
+ return ( plan, vec ! [ ] ) ;
650
+ } ;
651
+
652
+ let mut columns: Vec < Ident > = vec ! [ ] ;
653
+ // check if the inner projection and outer projection have a matching pattern like
654
+ // Projection: j1.j1_id AS id
655
+ // Projection: j1.j1_id
656
+ for ( i, inner_expr) in inner_projection. expr . iter ( ) . enumerate ( ) {
657
+ let Expr :: Alias ( ref outer_alias) = & outer_projections. expr [ i] else {
658
+ return ( plan, vec ! [ ] ) ;
659
+ } ;
660
+
661
+ if outer_alias. expr . as_ref ( ) != inner_expr {
662
+ return ( plan, vec ! [ ] ) ;
663
+ } ;
664
+
665
+ columns. push ( outer_alias. name . as_str ( ) . into ( ) ) ;
666
+ }
667
+
668
+ ( outer_projections. input . as_ref ( ) , columns)
669
+ }
670
+
614
671
impl From < BuilderError > for DataFusionError {
615
672
fn from ( e : BuilderError ) -> Self {
616
673
DataFusionError :: External ( Box :: new ( e) )
0 commit comments