1818//! [`FileScanConfig`] to configure scanning of possibly partitioned 
1919//! file sources. 
2020
21+ use  crate :: file:: ProjectionPushdownResult ; 
2122use  crate :: file_groups:: FileGroup ; 
2223#[ allow( unused_imports) ]  
2324use  crate :: schema_adapter:: SchemaAdapterFactory ; 
@@ -44,16 +45,14 @@ use datafusion_execution::{
4445    object_store:: ObjectStoreUrl ,  SendableRecordBatchStream ,  TaskContext , 
4546} ; 
4647use  datafusion_expr:: Operator ; 
47- use  datafusion_physical_expr:: expressions:: { BinaryExpr ,   Column } ; 
48+ use  datafusion_physical_expr:: expressions:: BinaryExpr ; 
4849use  datafusion_physical_expr:: projection:: ProjectionExprs ; 
4950use  datafusion_physical_expr:: utils:: reassign_expr_columns; 
5051use  datafusion_physical_expr:: { split_conjunction,  EquivalenceProperties ,  Partitioning } ; 
5152use  datafusion_physical_expr_adapter:: PhysicalExprAdapterFactory ; 
5253use  datafusion_physical_expr_common:: physical_expr:: PhysicalExpr ; 
5354use  datafusion_physical_expr_common:: sort_expr:: LexOrdering ; 
54- use  datafusion_physical_plan:: projection:: { 
55-     all_alias_free_columns,  new_projections_for_columns,  ProjectionExpr , 
56- } ; 
55+ use  datafusion_physical_plan:: projection:: ProjectionExpr ; 
5756use  datafusion_physical_plan:: { 
5857    display:: { display_orderings,  ProjectSchemaDisplay } , 
5958    filter_pushdown:: FilterPushdownPropagation , 
@@ -679,42 +678,42 @@ impl DataSource for FileScanConfig {
679678    fn  try_swapping_with_projection ( 
680679        & self , 
681680        projection :  & [ ProjectionExpr ] , 
682-     )  -> Result < Option < Arc < dyn  DataSource > > >  { 
683-         // This process can be moved into CsvExec, but it would be an overlap of their responsibility. 
684- 
685-         // Must be all column references, with no table partition columns (which can not be projected) 
686-         let  partitioned_columns_in_proj = projection. iter ( ) . any ( |proj_expr| { 
687-             proj_expr
688-                 . expr 
689-                 . as_any ( ) 
690-                 . downcast_ref :: < Column > ( ) 
691-                 . map ( |expr| expr. index ( )  >= self . file_schema ( ) . fields ( ) . len ( ) ) 
692-                 . unwrap_or ( false ) 
693-         } ) ; 
681+     )  -> Result < crate :: source:: ProjectionPushdownResult >  { 
682+         let  new_projection_exprs = ProjectionExprs :: from ( projection) ; 
683+ 
684+         // get current projection indices if they exist 
685+         let  current_projection = self 
686+             . projection_exprs 
687+             . as_ref ( ) 
688+             . map ( |p| p. ordered_column_indices ( ) ) ; 
689+ 
690+         // pass the new projections to the file source, along wiht the current projection 
691+         // the file source will merge them if possible 
692+         let  res = self . file_source ( ) . try_pushdown_projections ( 
693+             & new_projection_exprs, 
694+             self . file_schema ( ) , 
695+             current_projection. as_deref ( ) , 
696+         ) ?; 
694697
695-         // If there is any non-column or alias-carrier expression, Projection should not be removed. 
696-         let  no_aliases =  all_alias_free_columns ( projection ) ; 
697- 
698-         Ok ( ( no_aliases && !partitioned_columns_in_proj ) . then ( ||  { 
699-             let  file_scan =  self . clone ( ) ; 
700-             let  source =  Arc :: clone ( & file_scan . file_source ) ; 
701-             let  new_projections =  new_projections_for_columns ( 
702-                 projection , 
703-                  & file_scan 
704-                      . projection_exprs 
705-                     . as_ref ( ) 
706-                      . map ( |p| p . ordered_column_indices ( ) ) 
707-                      . unwrap_or_else ( ||  ( 0 .. self . file_schema ( ) . fields ( ) . len ( ) ) . collect ( ) ) , 
708-             ) ; 
698+         match  res  { 
699+              ProjectionPushdownResult :: None  =>  Ok ( None ) , 
700+              ProjectionPushdownResult :: Partial   { 
701+                 new_file_source , 
702+                 remaining_projections , 
703+                 new_projection_indices , 
704+             }  =>  { 
705+                 let   mut  builder =  FileScanConfigBuilder :: from ( self . clone ( ) ) ; 
706+ 
707+                 if   let   Some ( new_source )  = new_file_source  { 
708+                     builder = builder . with_source ( new_source ) ; 
709+                 } 
710+ 
711+                 builder = builder . with_projection_indices ( new_projection_indices ) ; 
709712
710-             Arc :: new ( 
711-                 FileScanConfigBuilder :: from ( file_scan) 
712-                     // Assign projected statistics to source 
713-                     . with_projection_indices ( Some ( new_projections) ) 
714-                     . with_source ( source) 
715-                     . build ( ) , 
716-             )  as  _ 
717-         } ) ) 
713+                 let  new_config = Arc :: new ( builder. build ( ) )  as  Arc < dyn  DataSource > ; 
714+                 Ok ( Some ( ( new_config,  remaining_projections) ) ) 
715+             } 
716+         } 
718717    } 
719718
720719    fn  try_pushdown_filters ( 
@@ -2300,7 +2299,7 @@ mod tests {
23002299
23012300        // Simulate projection being updated. Since the filter has already been pushed down, 
23022301        // the new projection won't include the filtered column. 
2303-         let  data_source = config
2302+         let  ( data_source,  _remaining_projections )  = config
23042303            . try_swapping_with_projection ( & [ ProjectionExpr :: new ( 
23052304                col ( "c3" ,  & file_schema) . unwrap ( ) , 
23062305                "c3" . to_string ( ) , 
0 commit comments