@@ -1140,6 +1140,16 @@ impl OptimizerRule for PushDownFilter {
11401140 }
11411141 LogicalPlan :: Join ( join) => push_down_join ( join, Some ( & filter. predicate ) ) ,
11421142 LogicalPlan :: TableScan ( scan) => {
1143+ // If the scan has a fetch (limit), pushing filters into it
1144+ // would change semantics: the limit should apply before the
1145+ // filter, not after.
1146+ if scan. fetch . is_some ( ) {
1147+ let plan = LogicalPlan :: Filter ( Filter :: try_new (
1148+ filter. predicate ,
1149+ Arc :: new ( LogicalPlan :: TableScan ( scan) ) ,
1150+ ) ?) ;
1151+ return Ok ( Transformed :: no ( plan) ) ;
1152+ }
11431153 let filter_predicates = split_conjunction ( & filter. predicate ) ;
11441154
11451155 let ( volatile_filters, non_volatile_filters) : ( Vec < & Expr > , Vec < & Expr > ) =
@@ -4326,6 +4336,29 @@ mod tests {
43264336 )
43274337 }
43284338
4339+ #[ test]
4340+ fn filter_not_pushed_down_through_table_scan_with_fetch ( ) -> Result < ( ) > {
4341+ let scan = test_table_scan ( ) ?;
4342+ let scan_with_fetch = match scan {
4343+ LogicalPlan :: TableScan ( scan) => LogicalPlan :: TableScan ( TableScan {
4344+ fetch : Some ( 10 ) ,
4345+ ..scan
4346+ } ) ,
4347+ _ => unreachable ! ( ) ,
4348+ } ;
4349+ let plan = LogicalPlanBuilder :: from ( scan_with_fetch)
4350+ . filter ( col ( "a" ) . gt ( lit ( 10i64 ) ) ) ?
4351+ . build ( ) ?;
4352+ // Filter must NOT be pushed into the table scan when it has a fetch (limit)
4353+ assert_optimized_plan_equal ! (
4354+ plan,
4355+ @r"
4356+ Filter: test.a > Int64(10)
4357+ TableScan: test, fetch=10
4358+ "
4359+ )
4360+ }
4361+
43294362 #[ test]
43304363 fn filter_push_down_through_sort_without_fetch ( ) -> Result < ( ) > {
43314364 let table_scan = test_table_scan ( ) ?;
0 commit comments