feat: null aware RightAnti hash join execution - #23957
Open
saadtajwar wants to merge 4 commits into
Open
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #23957 +/- ##
==========================================
+ Coverage 80.65% 80.70% +0.04%
==========================================
Files 1093 1095 +2
Lines 371629 372600 +971
Branches 371629 372600 +971
==========================================
+ Hits 299725 300690 +965
+ Misses 54012 53938 -74
- Partials 17892 17972 +80 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
saadtajwar
marked this pull request as ready for review
July 29, 2026 01:24
Contributor
Author
|
cc @andygrove - looking forward to your feedback! I'll have the second PR with the planner support up shortly :) |
saadtajwar
commented
Jul 29, 2026
| if !matches!( | ||
| (join_type, partition_mode), | ||
| (JoinType::LeftAnti, _) | ||
| | (JoinType::RightAnti, PartitionMode::CollectLeft) // `PartitionMode::CollectLeft` is safe because `RightAnti` is probe-driven |
Contributor
Author
There was a problem hiding this comment.
Do we need the partition_mode check for CollectLeft here? Or should we just count on it being enforced/never chosen as anything but CollectLeft beforehand?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
Rationale for this change (copied from issue):
DataFusion plans NOT IN (subquery) as a null-aware anti join, but HashJoinExec only supports null_aware = true for LeftAnti with a single join key (validated in datafusion-physical-plan/src/joins/hash_join/exec.rs). Since HashJoinExec always builds on the left input, the build side of a null-aware anti join is the outer table, not the subquery.
This has two costs:
Memory scales with the wrong side. For SELECT ... FROM big_fact WHERE key NOT IN (SELECT k FROM small_dim), the hash table is built over the entire fact table. Memory is O(outer) when it could be O(subquery).
The operator cannot be distributed. The null-aware logic coordinates three pieces of global state across probe partitions through in-process shared memory: probe_side_has_null: AtomicBool, probe_side_non_empty: AtomicBool, and the visited-build-row bitmap, with the last probe partition to finish emitting the unmatched build rows (hash_join/stream.rs). This is correct and cheap in one process, but engines that split probe partitions across processes get independent copies of all three and produce duplicated or incorrect rows. Ballista hit exactly this (apache/datafusion-ballista#2187) and currently has to force the join into a single task (apache/datafusion-ballista#2188), losing all parallelism.
What changes are included in this PR?
This PR is the first of two - when a path IS built as
RightAnti+ null aware +CollectLeftpartition mode, the join executes as expected. The second PR that will be created after this will support the planner changes to emit null-awareRightAnti& theJoinSelectionswap logic!This PR specifically adds the
build_side_has_nullfield and assigns at build-time, and correctly satisfies the invariant of no rows being output if true + build state with null keys not being output + empty build side outputting all probe rowsAre these changes tested?
Yes
Are there any user-facing changes?
No