Skip to content

fix: don't infer join predicates for null-aware joins in push_down_filter - #23901

Merged
viirya merged 1 commit into
apache:mainfrom
viirya:fix-null-aware-pushdown
Jul 26, 2026
Merged

fix: don't infer join predicates for null-aware joins in push_down_filter#23901
viirya merged 1 commit into
apache:mainfrom
viirya:fix-null-aware-pushdown

Conversation

@viirya

@viirya viirya commented Jul 25, 2026

Copy link
Copy Markdown
Member

Which issue does this PR close?

Rationale for this change

push_down_filter infers equi-key predicates across a join's ON keys and pushes them to the opposite side. For a null-aware join (the LeftAnti join produced by NOT IN with a nullable subquery), an outer predicate on the left key like outer.id > 5 is rewritten to sub.id > 5 and pushed onto the subquery input. Since the inferred predicate must be null-rejecting to be pushed, this drops the subquery's NULL rows and breaks the three-valued NOT IN semantics — a NULL in the subquery key must reach the join so the result is empty.

Same class of bug as #23848, in a different rule.

What changes are included in this PR?

Are these changes tested?

Yes — new unit test (verified it fails without the guard) and SLT cases. The full optimizer lib suite passes.

Are there any user-facing changes?

No, aside from the correctness fix.

…lter

`push_down_filter` infers equi-key predicates across a join's ON keys and
pushes them to the opposite side. For a null-aware join (the `LeftAnti`
join produced by `NOT IN` with a nullable subquery), an outer predicate on
the left key such as `outer.id > 5` is rewritten to `sub.id > 5` and pushed
onto the right/subquery input. Because the inferred predicate must be
null-rejecting to be pushed, this drops the subquery's NULL rows, which
breaks the three-valued `NOT IN` semantics: a NULL in the subquery key must
reach the join so the result is empty (UNKNOWN for every row).

For example:

    CREATE TABLE outer_t(id INT) AS VALUES (3), (7);
    CREATE TABLE sub_t(id INT) AS VALUES (NULL);
    SELECT id FROM outer_t WHERE id > 5 AND id NOT IN (SELECT id FROM sub_t);

correctly returns no rows, but previously returned `7` because `sub.id > 5`
was inferred onto the subquery, dropping its NULL row.

Skip predicate inference entirely when `join.null_aware` is set. This
mirrors the null-aware guard added to `FilterNullJoinKeys` in apache#23848.

Add a `push_down_filter` regression unit test asserting no predicate is
inferred onto the subquery side of a null-aware `LeftAnti` join, and SLT
coverage for the failing query (including a `prefer_hash_join = false`,
multi-partition variant).

Co-authored-by: Claude Code
@github-actions github-actions Bot added optimizer Optimizer rules sqllogictest SQL Logic Tests (.slt) labels Jul 25, 2026
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.28571% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.65%. Comparing base (8393fd3) to head (897f177).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
datafusion/optimizer/src/push_down_filter.rs 74.28% 1 Missing and 8 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #23901      +/-   ##
==========================================
- Coverage   80.65%   80.65%   -0.01%     
==========================================
  Files        1092     1092              
  Lines      371106   371175      +69     
  Branches   371106   371175      +69     
==========================================
+ Hits       299323   299366      +43     
- Misses      53937    53944       +7     
- Partials    17846    17865      +19     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@viirya
viirya added this pull request to the merge queue Jul 26, 2026
@viirya

viirya commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

Thanks @AdamGS for review

Merged via the queue into apache:main with commit 0fcf628 Jul 26, 2026
40 checks passed
@viirya
viirya deleted the fix-null-aware-pushdown branch July 26, 2026 00:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

optimizer Optimizer rules sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

push_down_filter infers a null-rejecting predicate onto the subquery side of a null-aware NOT IN join, producing wrong results

3 participants