Skip to content

feat: derive per-column pruning guarantees from tuple IN lists - #25469

Open
yashrb24 wants to merge 4 commits into
apache:mainfrom
yashrb24:feat/tuple-in-pruning-guarantees
Open

yashrb24 wants to merge 4 commits into
apache:mainfrom
yashrb24:feat/tuple-in-pruning-guarantees

Conversation

@yashrb24

@yashrb24 yashrb24 commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Closes #25463

Rationale for this change

Multi-column joins can produce filters such as:

(a, b) IN ((1, 10), (2, 20))

DataFusion can evaluate this filter on individual rows, but it does not extract the allowed values for each column. This prevents bloom filters from using those values to skip row groups.

What changes are included in this PR?

  • Extract necessary per-column values: a IN (1, 2) and b IN (10, 20).
  • Keep the original tuple filter, so combinations such as (1, 20) are still rejected.

This will only enable bloom filter pruning for row groups.

What is the testing strategy for this PR?

Added tests for value extraction, NULLs, dictionary values, and reordered named fields.

Are there any user-facing changes?

Queries with supported tuple IN filters, including dynamic filters from multi-column joins, may read fewer row groups when Bloom filters are available. Query results remain unchanged.

@github-actions github-actions Bot added physical-expr Changes to the physical-expr crates sqllogictest SQL Logic Tests (.slt) functions Changes to functions implementation datasource Changes to the datasource crate labels Sep 18, 2026
@codecov-commenter

codecov-commenter commented Sep 18, 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 82.42%. Comparing base (1e09a2a) to head (879fc87).

Files with missing lines Patch % Lines
datafusion/physical-expr/src/utils/guarantee.rs 91.83% 3 Missing and 9 partials ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main   #25469    +/-   ##
========================================
  Coverage   82.42%   82.42%            
========================================
  Files        1138     1138            
  Lines      435429   435583   +154     
  Branches   435429   435583   +154     
========================================
+ Hits       358889   359023   +134     
- Misses      54839    54847     +8     
- Partials    21701    21713    +12     

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

@yashrb24
yashrb24 marked this pull request as ready for review September 18, 2026 11:22
Comment on lines +156 to +166
fn struct_field_mapping(
&self,
literal_args: &[Option<ScalarValue>],
) -> Option<StructFieldMapping> {
Some(StructFieldMapping {
field_accessor: Arc::new(ScalarUDF::from(GetFieldFunc::new())),
fields: (0..literal_args.len())
.map(|i| (vec![ScalarValue::Utf8(Some(format!("c{i}")))], i))
.collect(),
})
}

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.

StructFunc from datafusion-functions crate already provides support for this, but instead of adding another dependency I thought it might be a better idea to have it defined separately here instead

}

#[tokio::test]
async fn test_tuple_in_bloom_pruning_preserves_correlation() -> Result<()> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The unit test nicely covers both pruning and the crossed-pair correlation case, while the SLT exercises the real Parquet scan path. Would it be valuable to combine these in one end-to-end test with multiple row groups? For example, the test could include:

  1. A row group containing an exact tuple match.
  2. A row group that statistics cannot eliminate but Bloom filters can, verifying that it is counted as pruned in row_groups_pruned_bloom_filter.
  3. A crossed-pair row group that satisfies the derived per-column guarantees and therefore survives Bloom pruning, but produces no rows after evaluation of the original tuple predicate.

This would verify that the derived per-column conditions are used only for pruning, while the original tuple predicate is still applied for exact row filtering.

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.

thanks for the review! this makes sense to me, will extend this test

@github-actions github-actions Bot removed the datasource Changes to the datasource crate label Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

functions Changes to functions implementation physical-expr Changes to the physical-expr crates sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enable Bloom-filter pruning for joins on multiple columns

3 participants