From 38b2f401b3491e71da19e41542c84b4fb6f75e47 Mon Sep 17 00:00:00 2001 From: Anton Karpenko Date: Thu, 20 Aug 2026 12:05:46 +0300 Subject: [PATCH] fix: return EmptyExec for LIMIT 0 in physical planning Short-circuit physical planning when a limit requests zero rows by returning an EmptyExec with the input schema. --- datafusion/core/src/physical_planner.rs | 27 +++++++++++++++---------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/datafusion/core/src/physical_planner.rs b/datafusion/core/src/physical_planner.rs index 29eeef9e305e4..24e8ac6ea71b3 100644 --- a/datafusion/core/src/physical_planner.rs +++ b/datafusion/core/src/physical_planner.rs @@ -1238,20 +1238,25 @@ impl DefaultPhysicalPlanner { ); }; - // GlobalLimitExec requires a single partition for input - let input = if input.output_partitioning().partition_count() == 1 { - input + if let Some(0) = fetch { + // Return an empty exec node if the number of requested rows is 0 + Arc::new(EmptyExec::new(input.schema())) } else { - // Apply a LocalLimitExec to each partition. The optimizer will also insert - // a CoalescePartitionsExec between the GlobalLimitExec and LocalLimitExec - if let Some(fetch) = fetch { - Arc::new(LocalLimitExec::new(input, fetch + skip)) - } else { + // GlobalLimitExec requires a single partition for input + let input = if input.output_partitioning().partition_count() == 1 { input - } - }; + } else { + // Apply a LocalLimitExec to each partition. The optimizer will also insert + // a CoalescePartitionsExec between the GlobalLimitExec and LocalLimitExec + if let Some(fetch) = fetch { + Arc::new(LocalLimitExec::new(input, fetch + skip)) + } else { + input + } + }; - Arc::new(GlobalLimitExec::new(input, skip, fetch)) + Arc::new(GlobalLimitExec::new(input, skip, fetch)) + } } LogicalPlan::Unnest(Unnest { list_type_columns,