Skip to content

Commit dee0dc7

Browse files
authored
Fix: limit is missing after removing SPM (#14569)
* Fix: limit is missing after removing SPM * fix test * use limitexec
1 parent 17396a5 commit dee0dc7

File tree

3 files changed

+42
-4
lines changed

3 files changed

+42
-4
lines changed

datafusion/core/tests/physical_optimizer/enforce_sorting.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use crate::physical_optimizer::test_utils::{
2424
create_test_schema3, create_test_schema4, filter_exec, global_limit_exec,
2525
hash_join_exec, limit_exec, local_limit_exec, memory_exec, parquet_exec,
2626
repartition_exec, sort_exec, sort_expr, sort_expr_options, sort_merge_join_exec,
27-
sort_preserving_merge_exec, spr_repartition_exec, stream_exec_ordered, union_exec,
28-
RequirementsTestExec,
27+
sort_preserving_merge_exec, sort_preserving_merge_exec_with_fetch,
28+
spr_repartition_exec, stream_exec_ordered, union_exec, RequirementsTestExec,
2929
};
3030

3131
use datafusion_physical_plan::displayable;
@@ -1943,6 +1943,30 @@ async fn test_remove_unnecessary_spm1() -> Result<()> {
19431943
Ok(())
19441944
}
19451945

1946+
#[tokio::test]
1947+
async fn test_remove_unnecessary_spm2() -> Result<()> {
1948+
let schema = create_test_schema()?;
1949+
let source = memory_exec(&schema);
1950+
let input = sort_preserving_merge_exec_with_fetch(
1951+
vec![sort_expr("non_nullable_col", &schema)],
1952+
source,
1953+
100,
1954+
);
1955+
1956+
let expected_input = [
1957+
"SortPreservingMergeExec: [non_nullable_col@1 ASC], fetch=100",
1958+
" DataSourceExec: partitions=1, partition_sizes=[0]",
1959+
];
1960+
let expected_optimized = [
1961+
"LocalLimitExec: fetch=100",
1962+
" SortExec: expr=[non_nullable_col@1 ASC], preserve_partitioning=[false]",
1963+
" DataSourceExec: partitions=1, partition_sizes=[0]",
1964+
];
1965+
assert_optimized!(expected_input, expected_optimized, input, true);
1966+
1967+
Ok(())
1968+
}
1969+
19461970
#[tokio::test]
19471971
async fn test_change_wrong_sorting() -> Result<()> {
19481972
let schema = create_test_schema()?;

datafusion/core/tests/physical_optimizer/test_utils.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,15 @@ pub fn sort_preserving_merge_exec(
288288
Arc::new(SortPreservingMergeExec::new(sort_exprs, input))
289289
}
290290

291+
pub fn sort_preserving_merge_exec_with_fetch(
292+
sort_exprs: impl IntoIterator<Item = PhysicalSortExpr>,
293+
input: Arc<dyn ExecutionPlan>,
294+
fetch: usize,
295+
) -> Arc<dyn ExecutionPlan> {
296+
let sort_exprs = sort_exprs.into_iter().collect();
297+
Arc::new(SortPreservingMergeExec::new(sort_exprs, input).with_fetch(Some(fetch)))
298+
}
299+
291300
pub fn union_exec(input: Vec<Arc<dyn ExecutionPlan>>) -> Arc<dyn ExecutionPlan> {
292301
Arc::new(UnionExec::new(input))
293302
}

datafusion/physical-optimizer/src/enforce_sorting/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,8 +375,13 @@ pub fn ensure_sorting(
375375
&& child_node.plan.output_partitioning().partition_count() <= 1
376376
{
377377
// This `SortPreservingMergeExec` is unnecessary, input already has a
378-
// single partition.
379-
let child_node = requirements.children.swap_remove(0);
378+
// single partition and no fetch is required.
379+
let mut child_node = requirements.children.swap_remove(0);
380+
if let Some(fetch) = plan.fetch() {
381+
// Add the limit exec if the spm has a fetch
382+
child_node.plan =
383+
Arc::new(LocalLimitExec::new(Arc::clone(&child_node.plan), fetch));
384+
}
380385
return Ok(Transformed::yes(child_node));
381386
}
382387

0 commit comments

Comments
 (0)