This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git
The following commit(s) were added to refs/heads/main by this push:
new 426b351513 Add config-matrix tests in enforce_distribution.rs for
range-satisfaction settings (#23627)
426b351513 is described below
commit 426b351513be6317734aa2e425ac72ddde790fb4
Author: blinding-pixels <[email protected]>
AuthorDate: Wed Aug 5 03:47:46 2026 -0400
Add config-matrix tests in enforce_distribution.rs for range-satisfaction
settings (#23627)
## Which issue does this PR close?
- Closes #23572
- Part of #22395
## Rationale for this change
Range partitioning reuse is controlled by shared optimizer settings, but
the same configuration combinations were repeated across
operator-specific SQL logic tests. This PR moves that shared coverage
into one table-driven Rust test while retaining the operator-specific
cases that test separate planning behavior.
## What changes are included in this PR?
- Extends `RequirementsTestExec` with a configurable input distribution.
- Adds a 12-row configuration matrix covering 36 combinations of key
compatibility, subset threshold, preserve-file setting, and target
partition count.
- Requires reuse cases to contain no repartition and Hash cases to
contain exactly one Hash repartition.
- Removes three redundant aggregate SLT cases and renumbers the
remaining tests.
## Are these changes tested?
Yes. The 36-cell matrix and `range_partitioning.slt` pass.
## Are there any user-facing changes?
No. These changes only affect tests and test utilities.
---------
Co-authored-by: blinding-pixels
<[email protected]>
---
.../physical_optimizer/enforce_distribution.rs | 95 ++++++++++-
.../core/tests/physical_optimizer/test_utils.rs | 23 ++-
.../sqllogictest/test_files/range_partitioning.slt | 174 ++++++---------------
3 files changed, 157 insertions(+), 135 deletions(-)
diff --git a/datafusion/core/tests/physical_optimizer/enforce_distribution.rs
b/datafusion/core/tests/physical_optimizer/enforce_distribution.rs
index 189650fe4a..ac7e7a75a2 100644
--- a/datafusion/core/tests/physical_optimizer/enforce_distribution.rs
+++ b/datafusion/core/tests/physical_optimizer/enforce_distribution.rs
@@ -20,10 +20,10 @@ use std::ops::Deref;
use std::sync::Arc;
use crate::physical_optimizer::test_utils::{
- bounded_window_exec_with_can_repartition, check_integrity,
coalesce_partitions_exec,
- parquet_exec_with_sort, parquet_exec_with_stats, repartition_exec, schema,
sort_exec,
- sort_exec_with_preserve_partitioning, sort_merge_join_exec,
- sort_preserving_merge_exec, union_exec,
+ RequirementsTestExec, bounded_window_exec_with_can_repartition,
check_integrity,
+ coalesce_partitions_exec, parquet_exec_with_sort, parquet_exec_with_stats,
+ repartition_exec, schema, sort_exec, sort_exec_with_preserve_partitioning,
+ sort_merge_join_exec, sort_preserving_merge_exec, union_exec,
};
use arrow::array::{RecordBatch, UInt8Array, UInt64Array};
@@ -747,6 +747,93 @@ impl TestConfig {
}
}
+#[derive(Debug, Clone, Copy)]
+enum ExpectedPlan {
+ Reuse,
+ Hash,
+}
+
+#[test]
+fn range_satisfaction_config_matrix() -> Result<()> {
+ const INPUT_PARTITIONS: usize = 4;
+ const MET: usize = INPUT_PARTITIONS;
+ const NOT_MET: usize = INPUT_PARTITIONS + 1;
+ const DISABLED: usize = 0;
+ const EQUAL: usize = INPUT_PARTITIONS;
+ const GREATER: usize = INPUT_PARTITIONS + 1;
+ use ExpectedPlan::{Hash, Reuse};
+
+ let config_cases = [
+ // subset preserve target exact subset incompatible
+ (NOT_MET, DISABLED, EQUAL, [Reuse, Hash, Hash]),
+ (NOT_MET, DISABLED, GREATER, [Hash, Hash, Hash]),
+ (NOT_MET, NOT_MET, EQUAL, [Reuse, Hash, Hash]),
+ (NOT_MET, NOT_MET, GREATER, [Hash, Hash, Hash]),
+ (NOT_MET, MET, EQUAL, [Reuse, Hash, Hash]),
+ (NOT_MET, MET, GREATER, [Reuse, Reuse, Hash]),
+ (MET, DISABLED, EQUAL, [Reuse, Reuse, Hash]),
+ (MET, DISABLED, GREATER, [Reuse, Reuse, Hash]),
+ (MET, NOT_MET, EQUAL, [Reuse, Reuse, Hash]),
+ (MET, NOT_MET, GREATER, [Reuse, Reuse, Hash]),
+ (MET, MET, EQUAL, [Reuse, Reuse, Hash]),
+ (MET, MET, GREATER, [Reuse, Reuse, Hash]),
+ ];
+ for (subset_threshold, preserve_file_partitions, target_partitions,
expected) in
+ config_cases
+ {
+ let key_cases = [
+ ("exact", vec![col("a", &schema())?], expected[0]),
+ (
+ "subset",
+ vec![col("a", &schema())?, col("b", &schema())?],
+ expected[1],
+ ),
+ ("incompatible", vec![col("b", &schema())?], expected[2]),
+ ];
+ for (key_match, partition_keys, expected_plan) in key_cases {
+ let input =
parquet_exec_with_output_partitioning(range_partitioning(
+ "a",
+ [10, 20, 30],
+ SortOptions::default(),
+ )?);
+ let requirement = RequirementsTestExec::new(input)
+ .with_required_input_distribution(Distribution::KeyPartitioned(
+ partition_keys,
+ ))
+ .into_arc();
+
+ let mut config =
+
TestConfig::default().with_query_execution_partitions(target_partitions);
+ config.config.optimizer.subset_repartition_threshold =
subset_threshold;
+ config.config.optimizer.preserve_file_partitions =
preserve_file_partitions;
+
+ let plan = config.to_plan(requirement, &DISTRIB_DISTRIB_SORT);
+ let plan = displayable(plan.as_ref()).indent(true).to_string();
+ let repartitions = plan
+ .lines()
+ .filter(|line| line.contains("RepartitionExec:"))
+ .collect::<Vec<_>>();
+
+ let matches_expected = match expected_plan {
+ Reuse => repartitions.is_empty(),
+ Hash => matches!(
+ repartitions.as_slice(),
+ [repartition] if repartition.contains("partitioning=Hash")
+ ),
+ };
+ assert!(
+ matches_expected,
+ "unexpected optimized plan for key_match={key_match}, \
+ subset_threshold={subset_threshold}, \
+ preserve_file_partitions={preserve_file_partitions}, \
+ target_partitions={target_partitions}:\n{plan}"
+ );
+ }
+ }
+
+ Ok(())
+}
+
#[test]
fn range_aggregate_reuses_range_partitioning() -> Result<()> {
let input = parquet_exec_with_output_partitioning(range_partitioning(
diff --git a/datafusion/core/tests/physical_optimizer/test_utils.rs
b/datafusion/core/tests/physical_optimizer/test_utils.rs
index 74230b24e2..3235ea25fd 100644
--- a/datafusion/core/tests/physical_optimizer/test_utils.rs
+++ b/datafusion/core/tests/physical_optimizer/test_utils.rs
@@ -40,10 +40,10 @@ use datafusion_execution::object_store::ObjectStoreUrl;
use datafusion_execution::{SendableRecordBatchStream, TaskContext};
use datafusion_expr::{WindowFrame, WindowFunctionDefinition};
use datafusion_functions_aggregate::count::count_udaf;
-use datafusion_physical_expr::EquivalenceProperties;
use datafusion_physical_expr::aggregate::{AggregateExprBuilder,
AggregateFunctionExpr};
use datafusion_physical_expr::expressions::{self, col};
use datafusion_physical_expr::projection::ProjectionExprs;
+use datafusion_physical_expr::{Distribution, EquivalenceProperties};
use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
use datafusion_physical_expr_common::sort_expr::{
LexOrdering, OrderingRequirements, PhysicalSortExpr,
@@ -68,8 +68,9 @@ use datafusion_physical_plan::tree_node::PlanContext;
use datafusion_physical_plan::union::UnionExec;
use datafusion_physical_plan::windows::{BoundedWindowAggExec,
create_window_expr};
use datafusion_physical_plan::{
- DisplayAs, DisplayFormatType, ExecutionPlan, InputOrderMode, Partitioning,
- PlanProperties, SortOrderPushdownResult, StatisticsArgs, displayable,
+ DisplayAs, DisplayFormatType, ExecutionPlan, InputDistributionRequirements,
+ InputOrderMode, Partitioning, PlanProperties, SortOrderPushdownResult,
+ StatisticsArgs, displayable,
};
/// Create a non sorted parquet exec
@@ -435,6 +436,7 @@ pub fn projection_exec(
#[derive(Debug)]
pub struct RequirementsTestExec {
required_input_ordering: Option<LexOrdering>,
+ required_input_distribution: Distribution,
maintains_input_order: bool,
input: Arc<dyn ExecutionPlan>,
}
@@ -443,6 +445,7 @@ impl RequirementsTestExec {
pub fn new(input: Arc<dyn ExecutionPlan>) -> Self {
Self {
required_input_ordering: None,
+ required_input_distribution: Distribution::UnspecifiedDistribution,
maintains_input_order: true,
input,
}
@@ -457,6 +460,15 @@ impl RequirementsTestExec {
self
}
+ /// sets the required input distribution
+ pub fn with_required_input_distribution(
+ mut self,
+ required_input_distribution: Distribution,
+ ) -> Self {
+ self.required_input_distribution = required_input_distribution;
+ self
+ }
+
/// set the maintains_input_order flag
pub fn with_maintains_input_order(mut self, maintains_input_order: bool)
-> Self {
self.maintains_input_order = maintains_input_order;
@@ -500,6 +512,10 @@ impl ExecutionPlan for RequirementsTestExec {
]
}
+ fn input_distribution_requirements(&self) -> InputDistributionRequirements
{
+
InputDistributionRequirements::new(vec![self.required_input_distribution.clone()])
+ }
+
fn maintains_input_order(&self) -> Vec<bool> {
vec![self.maintains_input_order]
}
@@ -515,6 +531,7 @@ impl ExecutionPlan for RequirementsTestExec {
assert_eq!(children.len(), 1);
Ok(RequirementsTestExec::new(Arc::clone(&children[0]))
.with_required_input_ordering(self.required_input_ordering.clone())
+
.with_required_input_distribution(self.required_input_distribution.clone())
.with_maintains_input_order(self.maintains_input_order)
.into_arc())
}
diff --git a/datafusion/sqllogictest/test_files/range_partitioning.slt
b/datafusion/sqllogictest/test_files/range_partitioning.slt
index 1e0a1582ea..9701c41377 100644
--- a/datafusion/sqllogictest/test_files/range_partitioning.slt
+++ b/datafusion/sqllogictest/test_files/range_partitioning.slt
@@ -119,89 +119,7 @@ SELECT range_key, non_range_key, SUM(value) FROM
range_partitioned GROUP BY rang
##########
-# TEST 4: Exact Range Aggregate Below Subset Threshold
-# Even when subset satisfaction is disabled, exact Range([range_key])
-# satisfies GROUP BY range_key when repartitioning would not increase
-# partition count.
-##########
-
-statement ok
-set datafusion.execution.target_partitions = 4;
-
-statement ok
-set datafusion.optimizer.subset_repartition_threshold = 5;
-
-statement ok
-set datafusion.optimizer.preserve_file_partitions = 0;
-
-query TT
-EXPLAIN SELECT range_key, SUM(value) FROM range_partitioned GROUP BY range_key;
-----
-physical_plan
-01)AggregateExec: mode=SinglePartitioned, gby=[range_key@0 as range_key],
aggr=[sum(range_partitioned.value)]
-02)--DataSourceExec: file_groups=<slt:ignore>, projection=[range_key, value],
output_partitioning=Range([range_key@0 ASC], [(10), (20), (30)], 4),
file_type=csv, has_header=false
-
-
-##########
-# TEST 5: Range Subset Aggregate Rehashes Below Subset Threshold
-# Range([range_key]) is only a subset of GROUP BY (range_key, non_range_key),
-# so it should not satisfy the aggregate key when subset satisfaction is
-# disabled.
-##########
-
-statement ok
-set datafusion.execution.target_partitions = 4;
-
-statement ok
-set datafusion.optimizer.subset_repartition_threshold = 5;
-
-statement ok
-set datafusion.optimizer.preserve_file_partitions = 0;
-
-query TT
-EXPLAIN SELECT range_key, non_range_key, SUM(value) FROM range_partitioned
GROUP BY range_key, non_range_key;
-----
-physical_plan
-01)AggregateExec: mode=FinalPartitioned, gby=[range_key@0 as range_key,
non_range_key@1 as non_range_key], aggr=[sum(range_partitioned.value)]
-02)--RepartitionExec: partitioning=Hash([range_key@0, non_range_key@1], 4),
input_partitions=4
-03)----AggregateExec: mode=Partial, gby=[range_key@0 as range_key,
non_range_key@1 as non_range_key], aggr=[sum(range_partitioned.value)]
-04)------DataSourceExec: file_groups=<slt:ignore>, projection=[range_key,
non_range_key, value], output_partitioning=Range([range_key@0 ASC], [(10),
(20), (30)], 4), file_type=csv, has_header=false
-
-
-##########
-# TEST 6: Aggregate Rehashes Below Subset Threshold
-# With subset threshold 5 and only 4 input partitions, planning repartitions
-# to increase parallelism instead of reusing Range partitioning.
-##########
-
-statement ok
-set datafusion.execution.target_partitions = 5;
-
-statement ok
-set datafusion.optimizer.subset_repartition_threshold = 5;
-
-statement ok
-set datafusion.optimizer.preserve_file_partitions = 0;
-
-query TT
-EXPLAIN SELECT range_key, SUM(value) FROM range_partitioned GROUP BY range_key;
-----
-physical_plan
-01)AggregateExec: mode=FinalPartitioned, gby=[range_key@0 as range_key],
aggr=[sum(range_partitioned.value)]
-02)--RepartitionExec: partitioning=Hash([range_key@0], 5), input_partitions=5
-03)----AggregateExec: mode=Partial, gby=[range_key@0 as range_key],
aggr=[sum(range_partitioned.value)]
-04)------RepartitionExec: partitioning=RoundRobinBatch(5), input_partitions=4
-05)--------DataSourceExec: file_groups=<slt:ignore>, projection=[range_key,
value], output_partitioning=Range([range_key@0 ASC], [(10), (20), (30)], 4),
file_type=csv, has_header=false
-
-statement ok
-set datafusion.execution.target_partitions = 4;
-
-statement ok
-reset datafusion.optimizer.subset_repartition_threshold;
-
-
-##########
-# TEST 7: Aggregate Preserves Range When Preserve File Threshold Met
+# TEST 4: Aggregate Preserves Range When Preserve File Threshold Met
# With preserve-file threshold 1 and 4 input partitions, Range is preserved
# even though target_partitions is 5.
##########
@@ -230,7 +148,7 @@ reset datafusion.optimizer.preserve_file_partitions;
##########
-# TEST 8: Aggregate Rehashes When Preserve File Threshold Not Met
+# TEST 5: Aggregate Rehashes When Preserve File Threshold Not Met
# With preserve-file threshold 5 and only 4 input partitions, planning can
# repartition to increase parallelism.
##########
@@ -271,7 +189,7 @@ set datafusion.optimizer.preserve_file_partitions = 0;
##########
-# TEST 9: Join on Range Partition Column
+# TEST 6: Join on Range Partition Column
# A partitioned inner hash join requires co-partitioned KeyPartitioned inputs.
# Compatible Range layouts satisfy both the per-child key requirements and the
# cross-child layout requirement, so no Hash repartitioning is inserted.
@@ -303,7 +221,7 @@ ORDER BY l.range_key;
35 350 350
##########
-# TEST 10: Incompatible Range Join Repartitions
+# TEST 7: Incompatible Range Join Repartitions
# Both inputs are independently range partitioned on range_key, but their split
# points differ. The per-child key requirements can be satisfied by Range, but
# the co-partitioned layout requirement cannot, so Hash repartitioning repairs
@@ -338,7 +256,7 @@ ORDER BY l.range_key;
35 350 350
##########
-# TEST 11: Non-Range Join Repartitions
+# TEST 8: Non-Range Join Repartitions
# Range([range_key]) does not satisfy KeyPartitioned([non_range_key]), so
# planning inserts Hash repartitioning on the actual join key.
##########
@@ -395,7 +313,7 @@ ORDER BY l.non_range_key, l.value, r.value;
2 350 350
##########
-# TEST 12: Left-Side Range Hash Joins
+# TEST 9: Left-Side Range Hash Joins
# Compatible Range layouts satisfy left-side partitioned hash join
# requirements without Hash repartitioning.
##########
@@ -477,7 +395,7 @@ ORDER BY l.range_key;
35 350
##########
-# TEST 13: Left-Side Range Hash Joins With Incomplete Range Keys
+# TEST 10: Left-Side Range Hash Joins With Incomplete Range Keys
# Range partitioning covers only range_key, so joins requiring additional
# or different keys are repaired with Hash repartitioning.
##########
@@ -512,7 +430,7 @@ physical_plan
05)----DataSourceExec: file_groups=<slt:ignore>, projection=[non_range_key,
value], output_partitioning=UnknownPartitioning(4), file_type=csv,
has_header=false
##########
-# TEST 14: Left-Side Range Hash Joins With Incompatible Range Layouts
+# TEST 11: Left-Side Range Hash Joins With Incompatible Range Layouts
# Different split points or partition counts do not satisfy the
# co-partitioned layout requirement.
##########
@@ -560,7 +478,7 @@ physical_plan
05)----DataSourceExec: file_groups=<slt:ignore>, projection=[range_key,
value], output_partitioning=Range([range_key@0 ASC], [(10), (20)], 3),
file_type=csv, has_header=false
##########
-# TEST 15: LeftMark Subqueries Over Range Hash Joins
+# TEST 12: LeftMark Subqueries Over Range Hash Joins
# SQL IN subqueries decorrelate to LeftMark joins. These queries pin matched,
# unmatched, and NULL marker behavior over compatible Range inputs.
##########
@@ -608,7 +526,7 @@ ORDER BY l.range_key;
35 350
##########
-# TEST 16: Compatible Range Join Repartitions to Increase Parallelism
+# TEST 13: Compatible Range Join Repartitions to Increase Parallelism
# Co-partitioning satisfaction does not prevent a repartition that increases
# parallelism. With target_partitions larger than the Range partition count,
# both sides are hash repartitioned.
@@ -645,7 +563,7 @@ ORDER BY l.range_key;
35 350 350
##########
-# TEST 17: Preserve File Partitions Preserves Range Join Inputs
+# TEST 14: Preserve File Partitions Preserves Range Join Inputs
# preserve_file_partitions preserves compatible Range inputs for partitioned
# joins even when target_partitions is higher than the input partition count.
##########
@@ -685,7 +603,7 @@ statement ok
set datafusion.optimizer.preserve_file_partitions = 0;
##########
-# TEST 18: Nested Range Joins
+# TEST 15: Nested Range Joins
# Compatible Range partitioning is preserved through the lower join, allowing
# the upper join to consume it without Hash repartitioning either input.
##########
@@ -720,7 +638,7 @@ ORDER BY l.range_key;
35 350 350 350
##########
-# TEST 19: Range Aggregates Feed Range Join
+# TEST 16: Range Aggregates Feed Range Join
# Aggregates on range_key preserve reusable partitioning for the downstream
# partitioned join.
##########
@@ -775,7 +693,7 @@ ORDER BY l.range_key;
35 350 350
##########
-# TEST 20: Range Join Feeds Aggregate
+# TEST 17: Range Join Feeds Aggregate
# The join preserves compatible Range partitioning on range_key, allowing the
# aggregate above it to avoid Hash repartitioning.
##########
@@ -809,7 +727,7 @@ ORDER BY l.range_key;
35 700
##########
-# TEST 21: Right Join on Range Partition Column
+# TEST 18: Right Join on Range Partition Column
# Compatible Range inputs satisfy the join's partitioning requirements, so no
# Hash repartitioning is inserted. The left filter keeps its Range partitioning
# and the unmatched right rows above 150 are preserved.
@@ -842,7 +760,7 @@ NULL 30 300
NULL 35 350
##########
-# TEST 22: Right Semi Join on Range Partition Column
+# TEST 19: Right Semi Join on Range Partition Column
# Compatible Range inputs avoid Hash repartitioning for RightSemi joins.
# Only right rows with a match on the filtered left side are returned.
##########
@@ -870,7 +788,7 @@ ORDER BY r.range_key;
15 150
##########
-# TEST 23: Right Anti Join on Range Partition Column
+# TEST 20: Right Anti Join on Range Partition Column
# Compatible Range inputs avoid Hash repartitioning for RightAnti joins.
# Only right rows without a match on the filtered left side are returned.
##########
@@ -898,7 +816,7 @@ ORDER BY r.range_key;
35 350
##########
-# TEST 24: Incompatible Range Right Join Repartitions
+# TEST 21: Incompatible Range Right Join Repartitions
# The split points of the two inputs differ, so the co-partitioned layout
# requirement cannot be satisfied and Hash repartitioning repairs both sides
# of the right join. Results stay correct on the repartitioned path.
@@ -933,7 +851,7 @@ NULL 30 300
NULL 35 350
##########
-# TEST 25: Composite-Key Right Join Repartitions
+# TEST 22: Composite-Key Right Join Repartitions
# Range([range_key]) does not satisfy a partitioned join on
# (range_key, non_range_key), so both sides repartition on the full key.
##########
@@ -972,7 +890,7 @@ statement ok
reset datafusion.optimizer.subset_repartition_threshold;
##########
-# TEST 26: Right Join with Mismatched Range Partition Counts Repartitions
+# TEST 23: Right Join with Mismatched Range Partition Counts Repartitions
# Both inputs are range partitioned on range_key, but declare a different
number
# of partitions (four vs three). The per-child key requirements can be
satisfied
# by Range, but the co-partitioned layout requirement cannot, so Hash
@@ -1007,7 +925,7 @@ ORDER BY r.range_key;
350 35 350
##########
-# TEST 27: Right Join on Non-Range Key Repartitions
+# TEST 24: Right Join on Non-Range Key Repartitions
# Both inputs expose Range([range_key]), but the join key is non_range_key.
# Range([range_key]) does not satisfy KeyPartitioned([non_range_key]), so
# planning inserts Hash repartitioning on the actual join key for the right
join.
@@ -1042,7 +960,7 @@ ORDER BY r.range_key;
50 35 350
##########
-# TEST 28: Mark Join Marker Semantics
+# TEST 25: Mark Join Marker Semantics
# Mark joins preserve matched, unmatched, and NULL-key marker behavior over
# range-partitioned inputs.
##########
@@ -1094,7 +1012,7 @@ ORDER BY r.range_key;
35 350
##########
-# TEST 29: Sort Merge Join Avoids Repartition for Compatible Range Inputs
+# TEST 26: Sort Merge Join Avoids Repartition for Compatible Range Inputs
# Compatible Range inputs satisfy SortMergeJoinExec's co-partitioned
# KeyPartitioned requirements.
##########
@@ -1131,7 +1049,7 @@ ORDER BY l.range_key;
35 350 350
##########
-# TEST 30: Sort Merge Join Repartitions Incompatible Range Inputs
+# TEST 27: Sort Merge Join Repartitions Incompatible Range Inputs
# Different Range split points do not satisfy SortMergeJoinExec's
# co-partitioned KeyPartitioned requirements.
##########
@@ -1170,7 +1088,7 @@ statement ok
reset datafusion.optimizer.prefer_hash_join;
##########
-# TEST 31: Symmetric Hash Join Avoids Repartition for Compatible Range Inputs
+# TEST 28: Symmetric Hash Join Avoids Repartition for Compatible Range Inputs
# Compatible Range streams satisfy SymmetricHashJoinExec's co-partitioned
# KeyPartitioned requirements.
##########
@@ -1204,7 +1122,7 @@ FULL JOIN unbounded_range_like r ON l.range_key =
r.range_key;
5 50 50
##########
-# TEST 32: Symmetric Hash Join Repartitions Incompatible Range Inputs
+# TEST 29: Symmetric Hash Join Repartitions Incompatible Range Inputs
# Different Range split points do not satisfy SymmetricHashJoinExec's
# co-partitioned KeyPartitioned requirements.
##########
@@ -1237,7 +1155,7 @@ FULL JOIN unbounded_range_like_shifted r ON l.range_key =
r.range_key;
5 50 50
##########
-# TEST 33: Full Outer Join on Range Partition Column
+# TEST 30: Full Outer Join on Range Partition Column
# Full partitioned hash joins also opt in to Range satisfying KeyPartitioned
# requirements, so compatible Range layouts avoid Hash repartitioning here too.
##########
@@ -1268,9 +1186,9 @@ ORDER BY l.range_key;
35 350 350
##########
-# TEST 34: Full Outer Join Incompatible Range Repartitions
-# Same as TEST 10, but for Full: differing split points between the two
-# Range-partitioned inputs still require Hash repartitioning to co-partition.
+# TEST 31: Full Outer Join Incompatible Range Repartitions
+# For Full joins, differing split points between the two Range-partitioned
+# inputs still require Hash repartitioning to co-partition.
##########
query TT
@@ -1301,7 +1219,7 @@ ORDER BY l.range_key;
35 350 350
##########
-# TEST 35: Full Outer Join Produces Matched and Unmatched Rows
+# TEST 32: Full Outer Join Produces Matched and Unmatched Rows
# `range_partitioned` and `range_partitioned_sparse` share the same Range
# split points/partition count but only partially overlapping range_key
# values, so this exercises matched rows, left-only unmatched rows (NULLs on
@@ -1346,7 +1264,7 @@ statement ok
reset datafusion.optimizer.preserve_file_partitions;
##########
-# TEST 36: Union of Range Partitioned Inputs
+# TEST 33: Union of Range Partitioned Inputs
# Each input exposes the same Range partitioning on range_key, so the optimizer
# converts UnionExec to InterleaveExec to avoid redundant repartitioning.
##########
@@ -1395,7 +1313,7 @@ set datafusion.optimizer.preserve_file_partitions = 0;
##########
-# TEST 37: Window on Range Partition Column
+# TEST 34: Window on Range Partition Column
# Range([range_key]) colocates equal range_key values, so
# PARTITION BY range_key is satisfied without a hash repartition.
##########
@@ -1423,7 +1341,7 @@ SELECT range_key, SUM(value) OVER (PARTITION BY range_key
ORDER BY value) FROM r
##########
-# TEST 38: Unbounded-Frame Window on Range Partition Column
+# TEST 35: Unbounded-Frame Window on Range Partition Column
# The unbounded frame makes DataFusion use WindowAggExec instead of
# BoundedWindowAggExec, which likewise reuses Range partitioning without a
# hash repartition.
@@ -1452,7 +1370,7 @@ SELECT range_key, SUM(value) OVER (PARTITION BY range_key
ORDER BY value ROWS BE
##########
-# TEST 39: Window on Non-Range Column Rehashes
+# TEST 36: Window on Non-Range Column Rehashes
# Range([range_key]) does not colocate non_range_key values, so
# PARTITION BY non_range_key still requires a hash repartition.
##########
@@ -1481,7 +1399,7 @@ SELECT non_range_key, value, SUM(value) OVER (PARTITION
BY non_range_key ORDER B
##########
-# TEST 40: Unbounded-Frame Window on Non-Range Column Rehashes
+# TEST 37: Unbounded-Frame Window on Non-Range Column Rehashes
# The unbounded frame makes DataFusion use WindowAggExec; Range([range_key])
# does not colocate non_range_key values, so PARTITION BY non_range_key
# still requires a hash repartition.
@@ -1511,7 +1429,7 @@ SELECT non_range_key, value, SUM(value) OVER (PARTITION
BY non_range_key ORDER B
##########
-# TEST 41: Window Subset Satisfaction on Range Partition Column
+# TEST 38: Window Subset Satisfaction on Range Partition Column
# With the subset threshold met, Range([range_key]) satisfies
# PARTITION BY (range_key, non_range_key): equal composite keys share the
# same range_key, so they are already colocated.
@@ -1543,7 +1461,7 @@ SELECT range_key, SUM(value) OVER (PARTITION BY
range_key, non_range_key ORDER B
##########
-# TEST 42: Window Subset Rehashes Below Subset Threshold
+# TEST 39: Window Subset Rehashes Below Subset Threshold
# Range([range_key]) is only a subset of PARTITION BY
# (range_key, non_range_key), so it should not satisfy the window key when
# subset satisfaction is disabled.
@@ -1582,7 +1500,7 @@ reset datafusion.optimizer.preserve_file_partitions;
##########
-# TEST 43: Window Without Partition Keys Uses a Single Partition
+# TEST 40: Window Without Partition Keys Uses a Single Partition
# A window with no PARTITION BY requires a single partition; range
# partitioning is not applicable.
##########
@@ -1612,7 +1530,7 @@ SELECT range_key, SUM(value) OVER (ORDER BY value) FROM
range_partitioned ORDER
##########
-# TEST 44: PartitionedTopK on Range Partition Column
+# TEST 41: PartitionedTopK on Range Partition Column
# Exact Range([range_key]) satisfies the TopK partition key and avoids
repartitioning.
##########
@@ -1655,7 +1573,7 @@ ORDER BY range_key;
##########
-# TEST 45: PartitionedTopK on Non-Range Column
+# TEST 42: PartitionedTopK on Non-Range Column
# Partitioning on a non-range key cannot reuse Range([range_key]) and
# requires hash repartitioning.
##########
@@ -1691,7 +1609,7 @@ ORDER BY non_range_key;
##########
-# TEST 46: PartitionedTopK Reuses Range Subset Partitioning
+# TEST 43: PartitionedTopK Reuses Range Subset Partitioning
# With subset threshold met and preserve-file disabled, Range([range_key])
# satisfies partitioning by (range_key, non_range_key).
##########
@@ -1732,7 +1650,7 @@ ORDER BY range_key, non_range_key;
##########
-# TEST 47: Range Subset PartitionedTopK Rehashes Below Subset Threshold
+# TEST 44: Range Subset PartitionedTopK Rehashes Below Subset Threshold
# Range([range_key]) is only a subset of PARTITION BY (range_key,
non_range_key),
# so it should not satisfy the TopK partition key when subset satisfaction is
# disabled.
@@ -1770,7 +1688,7 @@ statement ok
reset datafusion.optimizer.enable_window_topn;
##########
-# TEST 48: Subset of Inputs Compatible Does Not Trigger InterleaveExec
+# TEST 45: Subset of Inputs Compatible Does Not Trigger InterleaveExec
# In a three-way union, two inputs share the same Range split points [10,20,30]
# while the third has a partially-overlapping but different set [15,20,30].
# can_interleave requires ALL inputs to match, so UnionExec is kept.
@@ -1826,7 +1744,7 @@ ORDER BY range_key, value;
35 350
##########
-# TEST 49: Incompatible Range Split Points Falls Back to UnionExec
+# TEST 46: Incompatible Range Split Points Falls Back to UnionExec
# Two range-partitioned inputs with different split points cannot be
interleaved,
# so the optimizer keeps UnionExec instead of converting to InterleaveExec.
##########
@@ -1868,7 +1786,7 @@ ORDER BY range_key, value;
35 350
##########
-# TEST 50: InterleaveExec Propagates Range Partitioning to Aggregate
+# TEST 47: InterleaveExec Propagates Range Partitioning to Aggregate
# InterleaveExec outputs the same Range partitioning as its compatible inputs,
# allowing a downstream aggregate on range_key to run SinglePartitioned without
# a Hash repartition.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]