Repository navigation
fix: account for row duplication and padding in join null counts - #25861
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #25861 +/- ##
==========================================
+ Coverage 82.58% 82.64% +0.06%
==========================================
Files 1142 1147 +5
Lines 440884 445806 +4922
Branches 440884 445806 +4922
==========================================
+ Hits 364088 368433 +4345
- Misses 54799 55025 +226
- Partials 21997 22348 +351 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@gabotechs can you take a look at this when you get the chance, it should block the other pr after merged. |
gabotechs
left a comment
There was a problem hiding this comment.
Thanks @kumarUjjawal!
This looks very good, just spotted a couple of edge cases that are probably worth covering.
| } else if keys.contains(&idx) { | ||
| if side.preserved { | ||
| null_matches.max(&null_count).to_inexact() | ||
| } else { |
There was a problem hiding this comment.
For a multi-key NullEqualsNull inner join, null_matches is Absent, so this branch makes a nullable key's output null count Absent even when the corresponding key on the other input has Exact(0) nulls. For example, with (a, b) join keys and right a null-free, no matched row can have NULL in left a; its output null count is provably Exact(0). Could we derive that per-key zero from the opposite key's null count and add a regression test? This keeps a usable statistic for a downstream IS NULL filter.
| &other.column_statistics, | ||
| null_equality, | ||
| ) | ||
| .unwrap_or(inner_rows) |
There was a problem hiding this comment.
The semi-join estimator uses non-NULL NDV overlap for NullEqualsNull and can report zero matched rows even when NULL keys match. For a left join with 100 left rows (90 NULL keys and 10 distinct non-NULL keys) and one right NULL row, this calculation gives inner_rows = 90, unmatched_left = 100, hence 190 output rows; the actual output has 100 rows because the 90 NULL-key rows match. I checked the case with a focused unit test: it passes at the PR base and fails at this head (190 vs 100). Could we include the null-safe NULL matches when estimating matched, or avoid treating the semi estimate as complete in this case, and add a regression test?
Backport Apache DataFusion PR apache#25861 at ebd3acd. Account for outer-join padding and repeated input rows without propagating exact NULL counts that can falsely prune MATCH_RECOGNIZE symbols.
|
@gabotechs can you take another look please |
gabotechs
left a comment
There was a problem hiding this comment.
Looks good 👍, thanks @kumarUjjawal!
Which issue does this PR close?
Related to #25620. Split from #25648 following the review request. This PR can be reviewed and merged independently.
Rationale for this change
Joins repeat or drop input rows, and outer joins add NULL padding. Copying each input column's null count unchanged can therefore produce inconsistent output statistics. For example, a left join can repeat a NULL in a non-key left column many times, while an unmatched left row introduces NULLs in every right column.
What changes are included in this PR?
The semi/anti-join estimator itself is unchanged. The rules follow join semantics and do not contain benchmark-specific cases.
What is the testing strategy for this PR?
Added unit tests cover NULL padding, repeated matches, unknown counts, null-safe keys, projection, input swapping, and provider rescaling. Existing cardinality assertions remain in place.
Passed on this independent branch:
cargo test --profile ci -p datafusion-physical-plan --lib(2,369 tests)cargo clippy --profile ci -p datafusion-physical-plan --all-targets --all-features -- -D warningscargo fmt --allandgit diff --checkBenchmarks were not rerun for the split. This extracts the join implementation already present in #25648; its earlier combined measurements are not isolated measurements of this PR.
Are there any user-facing changes?
Join row and null-count estimates can change, which can affect EXPLAIN output and plan choices. No public API changes.