Skip to content

feat: push dynamic filters through nested loop joins - #25030

Merged
kosiew merged 3 commits into
apache:mainfrom
yashrb24:fix/nlj-dynamic-filter-propagation
Sep 17, 2026
Merged

kosiew merged 3 commits into
apache:mainfrom
yashrb24:fix/nlj-dynamic-filter-propagation

Conversation

@yashrb24

@yashrb24 yashrb24 commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Part of #16973.

Rationale for this change

Dynamic filters from a hash join stop at a nested loop join. Scans below it may read rows that cannot match.

What changes are included in this PR?

Allow filters to pass through a nested loop join when all their columns belong to one input and filtering preserves the results. Filters using both inputs remain unchanged (ideally will raise a separate PR for this)

What is the testing strategy for this PR?

Tests cover join types, reordered columns, duplicate names, and filter updates.

Are there any user-facing changes?

Some queries can filter rows earlier. No new settings or API changes.

@github-actions github-actions Bot added core Core DataFusion crate physical-plan Changes to the physical-plan crate labels Sep 7, 2026
@codecov-commenter

codecov-commenter commented Sep 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.94%. Comparing base (606ae0f) to head (b410269).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...fusion/physical-plan/src/joins/nested_loop_join.rs 92.30% 4 Missing and 8 partials ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main   #25030    +/-   ##
========================================
  Coverage   81.94%   81.94%            
========================================
  Files        1135     1135            
  Lines      428278   428433   +155     
  Branches   428278   428433   +155     
========================================
+ Hits       350952   351085   +133     
- Misses      56369    56383    +14     
- Partials    20957    20965     +8     

☔ 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.

@kosiew kosiew 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.

@yashrb24,

Thanks for working on this. The dynamic filter routing through NestedLoopJoinExec looks well scoped, and the tests do a good job covering the join-type routing and projection remapping. I just have one non-blocking suggestion for some additional integration coverage.

}

#[tokio::test]
async fn test_nlj_dynamic_filter_pushdown_from_hashjoin() {

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.

Could we add an end-to-end outer or mark NLJ case with unmatched rows? The unit test already covers the routing decisions for all join types, but the integration test only executes an inner NLJ. An unmatched-row case would give us some extra protection around the key safety invariant here: dynamic filters should never be routed to a non-preserved input.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

makes sense, have added this. let me know if that looks fine

@kosiew kosiew 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.

@yashrb24, thanks for the follow-up changes. I went through the updates and the earlier review comments look addressed.

The new end-to-end LEFT NLJ test covers both matching and unmatched rows, and confirms that enabling dynamic-filter routing preserves the results while keeping the filter off the non-preserved right input. This satisfies the request for outer or mark join coverage.

I also checked the NLJ child remapping changes. Using explicit output-to-child column mappings avoids the ambiguity from name-based remapping, and the duplicate-name and reordered-projection tests provide good coverage for those cases.

Looks good to me. Thanks for tightening this up!

@kosiew

kosiew commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

🚀
@yashrb24
Thank you for your contribution.

@kosiew
kosiew added this pull request to the merge queue Sep 17, 2026
Merged via the queue into apache:main with commit 902ee8a Sep 17, 2026
41 checks passed
Omega359 pushed a commit to Omega359/arrow-datafusion that referenced this pull request Oct 11, 2026
## Which issue does this PR close?

Part of apache#16973.

## Rationale for this change

Dynamic filters from a hash join stop at a nested loop join. Scans below
it may read rows that cannot match.

## What changes are included in this PR?

Allow filters to pass through a nested loop join when all their columns
belong to one input and filtering preserves the results. Filters using
both inputs remain unchanged (ideally will raise a separate PR for this)

## What is the testing strategy for this PR?

Tests cover join types, reordered columns, duplicate names, and filter
updates.

## Are there any user-facing changes?

Some queries can filter rows earlier. No new settings or API changes.
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 v56.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants