@@ -27,7 +27,7 @@ use datafusion::physical_plan::execution_plan::{Boundedness, EmissionType};
2727use datafusion:: physical_plan:: stream:: RecordBatchStreamAdapter ;
2828use datafusion:: physical_plan:: { DisplayAs , ExecutionPlan , Partitioning , PlanProperties } ;
2929use futures:: { StreamExt , TryStreamExt } ;
30- use paimon:: spec:: Predicate ;
30+ use paimon:: spec:: { DataField , Predicate } ;
3131use paimon:: table:: { ScanTrace , Table } ;
3232use paimon:: DataSplit ;
3333
@@ -41,8 +41,8 @@ use crate::error::to_datafusion_error;
4141#[ derive( Debug ) ]
4242pub struct PaimonTableScan {
4343 table : Table ,
44- /// Projected column names (if None, reads all columns) .
45- projected_columns : Option < Vec < String > > ,
44+ /// Full Paimon read type for nested or connector-defined projections .
45+ read_type : Vec < DataField > ,
4646 /// Filter translated from DataFusion expressions and reused during execute()
4747 /// so reader-side pruning reaches the actual read path.
4848 pushed_predicate : Option < Predicate > ,
@@ -58,19 +58,22 @@ pub struct PaimonTableScan {
5858 filter_exact : bool ,
5959 /// Metadata-pruning trace captured during eager scan planning.
6060 scan_trace : Option < ScanTrace > ,
61+ /// Human-readable Variant extraction summary for explain output.
62+ pushed_variants : Option < String > ,
6163}
6264
6365impl PaimonTableScan {
6466 #[ allow( clippy:: too_many_arguments) ]
6567 pub ( crate ) fn new (
6668 schema : ArrowSchemaRef ,
6769 table : Table ,
68- projected_columns : Option < Vec < String > > ,
70+ read_type : Vec < DataField > ,
6971 pushed_predicate : Option < Predicate > ,
7072 planned_partitions : Vec < Arc < [ DataSplit ] > > ,
7173 limit : Option < usize > ,
7274 filter_exact : bool ,
7375 scan_trace : Option < ScanTrace > ,
76+ pushed_variants : Option < String > ,
7477 ) -> Self {
7578 let plan_properties = Arc :: new ( PlanProperties :: new (
7679 EquivalenceProperties :: new ( schema. clone ( ) ) ,
@@ -80,13 +83,14 @@ impl PaimonTableScan {
8083 ) ) ;
8184 Self {
8285 table,
83- projected_columns ,
86+ read_type ,
8487 pushed_predicate,
8588 planned_partitions,
8689 plan_properties,
8790 limit,
8891 filter_exact,
8992 scan_trace,
93+ pushed_variants,
9094 }
9195 }
9296
@@ -143,16 +147,13 @@ impl ExecutionPlan for PaimonTableScan {
143147
144148 let table = self . table . clone ( ) ;
145149 let schema = self . schema ( ) ;
146- let projected_columns = self . projected_columns . clone ( ) ;
150+ let read_type = self . read_type . clone ( ) ;
147151 let pushed_predicate = self . pushed_predicate . clone ( ) ;
148152
149153 let fut = async move {
150154 let mut read_builder = table. new_read_builder ( ) ;
151155
152- if let Some ( ref columns) = projected_columns {
153- let col_refs: Vec < & str > = columns. iter ( ) . map ( |s| s. as_str ( ) ) . collect ( ) ;
154- read_builder. with_projection ( & col_refs) ;
155- }
156+ read_builder. with_read_type ( read_type) ;
156157 if let Some ( filter) = pushed_predicate {
157158 read_builder. with_filter ( filter) ;
158159 }
@@ -236,9 +237,12 @@ impl DisplayAs for PaimonTableScan {
236237 self . planned_partitions. len( )
237238 ) ?;
238239
239- if let Some ( ref columns) = self . projected_columns {
240- write ! ( f, ", projection=[{}]" , columns. join( ", " ) ) ?;
241- }
240+ let columns = self
241+ . read_type
242+ . iter ( )
243+ . map ( |field| field. name ( ) )
244+ . collect :: < Vec < _ > > ( ) ;
245+ write ! ( f, ", projection=[{}]" , columns. join( ", " ) ) ?;
242246 if let Some ( ref predicate) = self . pushed_predicate {
243247 write ! ( f, ", predicate={predicate}" ) ?;
244248 }
@@ -248,6 +252,9 @@ impl DisplayAs for PaimonTableScan {
248252 if let Some ( ref trace) = self . scan_trace {
249253 write ! ( f, ", trace={trace}" ) ?;
250254 }
255+ if let Some ( ref pushed_variants) = self . pushed_variants {
256+ write ! ( f, ", PushedVariants=[{pushed_variants}]" ) ?;
257+ }
251258 Ok ( ( ) )
252259 }
253260}
@@ -282,18 +289,27 @@ mod tests {
282289 ) ] ) )
283290 }
284291
292+ fn test_read_type ( ) -> Vec < DataField > {
293+ vec ! [ DataField :: new(
294+ 0 ,
295+ "id" . to_string( ) ,
296+ DataType :: Int ( IntType :: new( ) ) ,
297+ ) ]
298+ }
299+
285300 #[ test]
286301 fn test_partition_count_empty_plan ( ) {
287302 let schema = test_schema ( ) ;
288303 let scan = PaimonTableScan :: new (
289304 schema,
290305 dummy_table ( ) ,
291- None ,
306+ test_read_type ( ) ,
292307 None ,
293308 vec ! [ Arc :: from( Vec :: new( ) ) ] ,
294309 None ,
295310 false ,
296311 None ,
312+ None ,
297313 ) ;
298314 assert_eq ! ( scan. properties( ) . output_partitioning( ) . partition_count( ) , 1 ) ;
299315 }
@@ -309,12 +325,13 @@ mod tests {
309325 let scan = PaimonTableScan :: new (
310326 schema,
311327 dummy_table ( ) ,
312- None ,
328+ test_read_type ( ) ,
313329 None ,
314330 planned_partitions,
315331 None ,
316332 false ,
317333 None ,
334+ None ,
318335 ) ;
319336 assert_eq ! ( scan. properties( ) . output_partitioning( ) . partition_count( ) , 3 ) ;
320337 }
@@ -387,12 +404,17 @@ mod tests {
387404 let scan = PaimonTableScan :: new (
388405 schema,
389406 table,
390- Some ( vec ! [ "id" . to_string( ) ] ) ,
407+ vec ! [ DataField :: new(
408+ 0 ,
409+ "id" . to_string( ) ,
410+ DataType :: Int ( IntType :: new( ) ) ,
411+ ) ] ,
391412 Some ( pushed_predicate) ,
392413 vec ! [ Arc :: from( vec![ split] ) ] ,
393414 None ,
394415 false ,
395416 None ,
417+ None ,
396418 ) ;
397419
398420 let ctx = SessionContext :: new ( ) ;
0 commit comments