@@ -464,6 +464,87 @@ mod tests {
464464 assert_eq ! ( metric, 2 , "Expected all rows to be pruned" ) ;
465465 }
466466
467+ #[ tokio:: test]
468+ async fn test_pushdown_with_missing_column_nested_conditions ( ) {
469+ // Create test data with c1 and c3 columns
470+ let c1: ArrayRef = Arc :: new ( Int32Array :: from ( vec ! [ 1 , 2 , 3 , 4 , 5 ] ) ) ;
471+ let c3: ArrayRef = Arc :: new ( Int32Array :: from ( vec ! [ 10 , 20 , 30 , 40 , 50 ] ) ) ;
472+
473+ let file_schema = Arc :: new ( Schema :: new ( vec ! [
474+ Field :: new( "c1" , DataType :: Int32 , true ) ,
475+ Field :: new( "c3" , DataType :: Int32 , true ) ,
476+ ] ) ) ;
477+
478+ let table_schema = Arc :: new ( Schema :: new ( vec ! [
479+ Field :: new( "c1" , DataType :: Int32 , true ) ,
480+ Field :: new( "c2" , DataType :: Int32 , true ) ,
481+ Field :: new( "c3" , DataType :: Int32 , true ) ,
482+ ] ) ) ;
483+
484+ let batch = RecordBatch :: try_new ( file_schema. clone ( ) , vec ! [ c1, c3] ) . unwrap ( ) ;
485+
486+ // Test with complex nested AND/OR:
487+ // (c1 = 1 OR c2 = 5) AND (c3 = 10 OR c2 IS NULL)
488+ // Should return 1 row where c1=1 AND c3=10 (since c2 IS NULL is always true)
489+ let filter = col ( "c1" )
490+ . eq ( lit ( 1_i32 ) )
491+ . or ( col ( "c2" ) . eq ( lit ( 5_i32 ) ) )
492+ . and ( col ( "c3" ) . eq ( lit ( 10_i32 ) ) . or ( col ( "c2" ) . is_null ( ) ) ) ;
493+
494+ let rt = RoundTrip :: new ( )
495+ . with_schema ( table_schema. clone ( ) )
496+ . with_predicate ( filter. clone ( ) )
497+ . with_pushdown_predicate ( )
498+ . round_trip ( vec ! [ batch. clone( ) ] )
499+ . await ;
500+
501+ let batches = rt. batches . unwrap ( ) ;
502+ #[ rustfmt:: skip]
503+ let expected = [
504+ "+----+----+----+" ,
505+ "| c1 | c2 | c3 |" ,
506+ "+----+----+----+" ,
507+ "| 1 | | 10 |" ,
508+ "+----+----+----+" ,
509+ ] ;
510+ assert_batches_sorted_eq ! ( expected, & batches) ;
511+ let metrics = rt. parquet_exec . metrics ( ) . unwrap ( ) ;
512+ let metric = get_value ( & metrics, "pushdown_rows_pruned" ) ;
513+ assert_eq ! ( metric, 4 , "Expected 4 rows to be pruned" ) ;
514+
515+ // Test a more complex nested condition:
516+ // (c1 < 3 AND c2 IS NOT NULL) OR (c3 > 20 AND c2 IS NULL)
517+ // First part should return 0 rows (c2 IS NOT NULL is always false)
518+ // Second part should return rows where c3 > 20 (3 rows: where c3 is 30, 40, 50)
519+ let filter = col ( "c1" )
520+ . lt ( lit ( 3_i32 ) )
521+ . and ( col ( "c2" ) . is_not_null ( ) )
522+ . or ( col ( "c3" ) . gt ( lit ( 20_i32 ) ) . and ( col ( "c2" ) . is_null ( ) ) ) ;
523+
524+ let rt = RoundTrip :: new ( )
525+ . with_schema ( table_schema)
526+ . with_predicate ( filter. clone ( ) )
527+ . with_pushdown_predicate ( )
528+ . round_trip ( vec ! [ batch] )
529+ . await ;
530+
531+ let batches = rt. batches . unwrap ( ) ;
532+ #[ rustfmt:: skip]
533+ let expected = [
534+ "+----+----+----+" ,
535+ "| c1 | c2 | c3 |" ,
536+ "+----+----+----+" ,
537+ "| 3 | | 30 |" ,
538+ "| 4 | | 40 |" ,
539+ "| 5 | | 50 |" ,
540+ "+----+----+----+" ,
541+ ] ;
542+ assert_batches_sorted_eq ! ( expected, & batches) ;
543+ let metrics = rt. parquet_exec . metrics ( ) . unwrap ( ) ;
544+ let metric = get_value ( & metrics, "pushdown_rows_pruned" ) ;
545+ assert_eq ! ( metric, 2 , "Expected 2 rows to be pruned" ) ;
546+ }
547+
467548 #[ tokio:: test]
468549 async fn evolved_schema ( ) {
469550 let c1: ArrayRef =
0 commit comments