Skip to content

feat: support co-partitioned range right-side equi hash joins#23484

Open
gmhelmold wants to merge 3 commits into
apache:mainfrom
gmhelmold:feat/right-join-range-copartition
Open

feat: support co-partitioned range right-side equi hash joins#23484
gmhelmold wants to merge 3 commits into
apache:mainfrom
gmhelmold:feat/right-join-range-copartition

Conversation

@gmhelmold

Copy link
Copy Markdown

Which issue does this PR close?

Rationale for this change

#23184 let compatible range-partitioned inputs satisfy inner partitioned hash joins without repartitioning. Right-side partitioned equi joins have the same locality guarantee: Right, RightSemi, RightAnti and RightMark all anchor every output row on the probe (right) partition (on_lr_is_preserved is probe-side for all four), so when both inputs are co-partitioned by the join keys, execution stays partition-local and the hash repartition is unnecessary.

This removes unnecessary RepartitionExecs for already-co-located inputs, extending the inner-join behavior from #23184 to the right-side variants. No micro-benchmark included, consistent with #23184; correctness is demonstrated by matched/unmatched execution results below.

What changes are included in this PR?

The only production change is widening the existing inner-only gate in HashJoinExec::input_distribution_requirements from join_type == JoinType::Inner to matches!(join_type, Inner | Right | RightSemi | RightAnti | RightMark) (under PartitionMode::Partitioned). The co_partitioned / range-satisfaction machinery from #23184 is unchanged — the sanity checker and enforce_distribution consume range satisfaction join-type-agnostically.

One behavior note for #23376: range co-partitioned right joins now stay Range/Range, so partitioned dynamic filters are disabled for them (has_partitioned_dynamic_filter_routing returns false), the same safe delta #23184 introduced for inner joins. These variants are probe-not-preserved for pruning, so dynamic filters were not eligible to prune their probe rows regardless.

Are these changes tested?

Yes.

  • Optimizer (enforce_distribution.rs): 4 reuse tests (one per join type) proving compatible range/range inputs keep Range partitioning with no RepartitionExec; 4 incompatibility tests proving that mismatched split points, sort options, partition counts, or join-key expressions still insert a hash repartition; sanity-check pairs mirroring Support co-partitioned range inner equi joins #23184.
  • Execution (range_partitioning.slt): EXPLAIN plan pins plus matched/unmatched result checks for Right (incl. NULL left values), RightSemi, RightAnti, and an incompatible-layout Right join (repartitions, correct results).
  • The new reuse tests fail on main without the gate change (verified by reverting the production diff: exactly the 4 reuse tests fail, everything else passes).
  • RightMark testing note: RightMark is not reachable from SQL in sqllogictest (IN-subquery decorrelation emits LeftMark; physical RightMark only appears via a statistics-based swap), so its co-partitioning behavior is pinned at the optimizer/plan level, and mark null-marker semantics (matched/unmatched/NULL build keys) are pinned via the LeftMark path in the slt.

Are there any user-facing changes?

No API changes. Plans over compatible range-partitioned inputs avoid a hash repartition for right-side equi hash joins.

@github-actions github-actions Bot added core Core DataFusion crate sqllogictest SQL Logic Tests (.slt) physical-plan Changes to the physical-plan crate labels Jul 11, 2026
Comment thread datafusion/core/tests/physical_optimizer/enforce_distribution.rs Outdated
@gmhelmold

Copy link
Copy Markdown
Author

Done in 852a619: the end-to-end matrix now lives in range_partitioning.slt (reuse cases for Right/RightSemi/RightAnti + rehash cases for split-points, partition-count, and non-range join keys — the last two needed one small narrow-fixture addition to the harness). Kept two unit tests in enforce_distribution.rs with comments explaining why they can't move: RightMark plans aren't SQL-producible, and the sort-direction axis needs a descending Range input the SLT fixtures don't declare. Net: unit block 8→2, no scenario lost.

@gene-bordegaray gene-bordegaray left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

few nits but this is great, thank you. Feel free to ping on another round 😄

// Same rows as `range_partitioned` but split into only three range
// partitions on `range_key`. Used to exercise the co-partition check when
// two Range inputs disagree on partition count.
let narrow_output_partitioning = Partitioning::Range(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it 👍

NULL 35 350

##########
# TEST 22: Mark Join Marker Semantics over Range Partitioned Inputs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we move this as the last test in this block?

physical_plan
01)FilterExec: non_range_key@1 = 2 OR mark@3, projection=[range_key@0, value@2]
02)--HashJoinExec: mode=Partitioned, join_type=LeftMark, on=[(range_key@0, range_key@0)]
03)----RepartitionExec: partitioning=Hash([range_key@0], 4), input_partitions=4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be clear this should disappear after #23453 ? Just putting here to show reationale on why the unit is good

30 600
35 700

##########

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add one subset test for sanity check 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Core DataFusion crate physical-plan Changes to the physical-plan crate sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow co-partitioned Partitioning::Range inputs for right-side hash joins

2 participants