@@ -35,8 +35,8 @@ use datafusion_common::tree_node::{
3535} ;
3636use datafusion_common:: utils:: get_at_indices;
3737use datafusion_common:: {
38- internal_err, plan_datafusion_err, plan_err, Column , DFSchema , DFSchemaRef ,
39- DataFusionError , HashMap , Result , TableReference ,
38+ internal_err, plan_datafusion_err, plan_err, Column , DFSchema , DFSchemaRef , HashMap ,
39+ Result , TableReference ,
4040} ;
4141
4242use indexmap:: IndexSet ;
@@ -379,14 +379,12 @@ fn get_exprs_except_skipped(
379379 }
380380}
381381
382- /// Resolves an `Expr::Wildcard` to a collection of `Expr::Column`'s.
383- pub fn expand_wildcard (
384- schema : & DFSchema ,
385- plan : & LogicalPlan ,
386- wildcard_options : Option < & WildcardOptions > ,
387- ) -> Result < Vec < Expr > > {
382+ /// For each column specified in the USING JOIN condition, the JOIN plan outputs it twice
383+ /// (once for each join side), but an unqualified wildcard should include it only once.
384+ /// This function returns the columns that should be excluded.
385+ fn exclude_using_columns ( plan : & LogicalPlan ) -> Result < HashSet < Column > > {
388386 let using_columns = plan. using_columns ( ) ?;
389- let mut columns_to_skip = using_columns
387+ let excluded = using_columns
390388 . into_iter ( )
391389 // For each USING JOIN condition, only expand to one of each join column in projection
392390 . flat_map ( |cols| {
@@ -395,18 +393,26 @@ pub fn expand_wildcard(
395393 // qualified column
396394 cols. sort ( ) ;
397395 let mut out_column_names: HashSet < String > = HashSet :: new ( ) ;
398- cols. into_iter ( )
399- . filter_map ( |c| {
400- if out_column_names. contains ( & c. name ) {
401- Some ( c)
402- } else {
403- out_column_names. insert ( c. name ) ;
404- None
405- }
406- } )
407- . collect :: < Vec < _ > > ( )
396+ cols. into_iter ( ) . filter_map ( move |c| {
397+ if out_column_names. contains ( & c. name ) {
398+ Some ( c)
399+ } else {
400+ out_column_names. insert ( c. name ) ;
401+ None
402+ }
403+ } )
408404 } )
409405 . collect :: < HashSet < _ > > ( ) ;
406+ Ok ( excluded)
407+ }
408+
409+ /// Resolves an `Expr::Wildcard` to a collection of `Expr::Column`'s.
410+ pub fn expand_wildcard (
411+ schema : & DFSchema ,
412+ plan : & LogicalPlan ,
413+ wildcard_options : Option < & WildcardOptions > ,
414+ ) -> Result < Vec < Expr > > {
415+ let mut columns_to_skip = exclude_using_columns ( plan) ?;
410416 let excluded_columns = if let Some ( WildcardOptions {
411417 exclude : opt_exclude,
412418 except : opt_except,
@@ -705,27 +711,20 @@ pub fn exprlist_to_fields<'a>(
705711 . map ( |e| match e {
706712 Expr :: Wildcard { qualifier, options } => match qualifier {
707713 None => {
708- let excluded: Vec < String > = get_excluded_columns (
714+ let mut excluded = exclude_using_columns ( plan) ?;
715+ excluded. extend ( get_excluded_columns (
709716 options. exclude . as_ref ( ) ,
710717 options. except . as_ref ( ) ,
711718 wildcard_schema,
712719 None ,
713- ) ?
714- . into_iter ( )
715- . map ( |c| c. flat_name ( ) )
716- . collect ( ) ;
717- Ok :: < _ , DataFusionError > (
718- wildcard_schema
719- . field_names ( )
720- . iter ( )
721- . enumerate ( )
722- . filter ( |( _, s) | !excluded. contains ( s) )
723- . map ( |( i, _) | wildcard_schema. qualified_field ( i) )
724- . map ( |( qualifier, f) | {
725- ( qualifier. cloned ( ) , Arc :: new ( f. to_owned ( ) ) )
726- } )
727- . collect :: < Vec < _ > > ( ) ,
728- )
720+ ) ?) ;
721+ Ok ( wildcard_schema
722+ . iter ( )
723+ . filter ( |( q, f) | {
724+ !excluded. contains ( & Column :: new ( q. cloned ( ) , f. name ( ) ) )
725+ } )
726+ . map ( |( q, f) | ( q. cloned ( ) , Arc :: clone ( f) ) )
727+ . collect :: < Vec < _ > > ( ) )
729728 }
730729 Some ( qualifier) => {
731730 let excluded: Vec < String > = get_excluded_columns (
0 commit comments