Skip to content

fix(physical-plan): preserve Exact(0) in FilterExec for null_count, distinct_count and total_byte_size upon empty input - #24000

Merged
alamb merged 4 commits into
apache:mainfrom
asolimando:asolimando/filter-exact-empty-counts
Aug 4, 2026
Merged

alamb merged 4 commits into
apache:mainfrom
asolimando:asolimando/filter-exact-empty-counts

Conversation

@asolimando

Copy link
Copy Markdown
Member

Which issue does this PR close?

Rationale for this change

Follow-up to #23936 (suggested here).

#23936 made FilterExec report num_rows: Exact(0) for a provably empty result. Two places in the same operator still turn the same proof into an estimate:

  • cap_at_rows demotes null_count and distinct_count unconditionally
  • total_byte_size is computed after the "infeasibility branch", so a contradictory predicate (a = 1 AND a = 2) gives Exact(0) for num_rows and per-column byte_size but an inexact total_byte_size

What changes are included in this PR?

  • cap_at_rows now maps an Exact(0) row bound to Exact(0)
  • statistics_helper reports total_byte_size: Exact(0) for a contradictory predicate instead of routing it through the selectivity estimate

Are these changes tested?

Yes, both by extending test_filter_statistics_preserves_exactly_empty_input with two assertions.

Both assertions were confirmed to fail without the respective fix. No sqllogictest baselines change.

Are there any user-facing changes?

No breaking changes and no signature changes. FilterExec reports exact rather than inexact zeros for these statistics, visible in EXPLAIN output that shows statistics.


Disclaimer: I used AI to assist in the code generation, I have manually reviewed the output and it matches my intention and understanding.

…d distinct_count at num_rows

`cap_at_rows` demoted `null_count` and `distinct_count` to inexact
unconditionally, so an exactly empty input came out of `FilterExec` with
`Exact(0)` for `num_rows`, `total_byte_size` and `byte_size` but `Inexact(0)`
for the two counts.
…dictory predicate

A contradictory predicate already yielded `Exact(0)` for `num_rows` and
per-column `byte_size`, but `total_byte_size` went through the selectivity
estimate and came out inexact for any non-empty input.
@github-actions github-actions Bot added the physical-plan Changes to the physical-plan crate label Jul 30, 2026
@codecov-commenter

codecov-commenter commented Jul 30, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.39640% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.85%. Comparing base (455a3ad) to head (1f05749).
⚠️ Report is 15 commits behind head on main.

Files with missing lines Patch % Lines
datafusion/physical-plan/src/filter.rs 96.39% 0 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #24000      +/-   ##
==========================================
+ Coverage   80.84%   80.85%   +0.01%     
==========================================
  Files        1096     1101       +5     
  Lines      373832   375032    +1200     
  Branches   373832   375032    +1200     
==========================================
+ Hits       302240   303248    +1008     
- Misses      53563    53679     +116     
- Partials    18029    18105      +76     

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

@asolimando

Copy link
Copy Markdown
Member Author

cc: @kosiew and @tschwarzinger, in case you have bandwidth, as you have the full context from #23936 and #23670

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

Looks like a good change to me. Thanks again!

Comment thread datafusion/physical-plan/src/filter.rs Resolved

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

@asolimando
Thanks for the update! I found a correctness issue around preserving exact empty statistics.

Comment thread datafusion/physical-plan/src/filter.rs Resolved
…ty FilterExec output

`total_byte_size` was exact only for a contradictory predicate, and the
per-column `byte_size` was never exact, so an input with `num_rows: Exact(0)`
produced an exact zero row count next to an inexact or `Absent` byte size.

`scale_byte_size_at_rows` returns an exact zero when the filtered row count is
an exact zero, which also covers the `is_infeasible` case, so that branch is no
longer needed. It tests the row count, unlike the `scale_byte_size` removed in
 apache#23936, which tested the byte size itself.
@asolimando
asolimando requested a review from kosiew August 1, 2026 07:53

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

@asolimando

Thanks for the iteration.

Looks 👍 to me

@alamb
alamb added this pull request to the merge queue Aug 4, 2026
@alamb

alamb commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Thanks @asolimando and @kosiew

Merged via the queue into apache:main with commit 9eb31bf Aug 4, 2026
40 checks passed
@asolimando
asolimando deleted the asolimando/filter-exact-empty-counts branch August 5, 2026 09:17
kosiew pushed a commit to kosiew/datafusion that referenced this pull request Aug 12, 2026
…istinct_count and total_byte_size upon empty input (apache#24000)

## Which issue does this PR close?

- Part of apache#8227

## Rationale for this change

Follow-up to apache#23936 (suggested
[here](apache#23936 (comment))).

apache#23936 made `FilterExec` report `num_rows: Exact(0)` for a provably
empty result. Two places in the same operator still turn the same proof
into an estimate:

- `cap_at_rows` demotes `null_count` and `distinct_count`
unconditionally
- `total_byte_size` is computed after the "infeasibility branch", so a
contradictory predicate (`a = 1 AND a = 2`) gives `Exact(0)` for
`num_rows` and per-column `byte_size` but an inexact `total_byte_size`

## What changes are included in this PR?

- `cap_at_rows` now maps an `Exact(0)` row bound to `Exact(0)`
- `statistics_helper` reports `total_byte_size: Exact(0)` for a
contradictory predicate instead of routing it through the selectivity
estimate

## Are these changes tested?

Yes, both by extending
`test_filter_statistics_preserves_exactly_empty_input` with two
assertions.

Both assertions were confirmed to fail without the respective fix. No
sqllogictest baselines change.

## Are there any user-facing changes?

No breaking changes and no signature changes. `FilterExec` reports exact
rather than inexact zeros for these statistics, visible in `EXPLAIN`
output that shows statistics.

----

Disclaimer: I used AI to assist in the code generation, I have manually
reviewed the output and it matches my intention and understanding.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

physical-plan Changes to the physical-plan crate v55.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants