viirya opened a new pull request, #26214:
URL: https://github.com/apache/datafusion/pull/26214
## Which issue does this PR close?
- Closes #26088.
## Rationale for this change
A multi-column `(a, b) NOT IN (SELECT x, y ...)` plans as a null-aware hash
join that decides UNKNOWN per build row. Without correlation keys, every row
with a NULL in any tuple element is paired with every row on the other side, so
the NULL path costs O(rows x NULL-valued rows).
A tuple element that cannot be NULL in scope on either side never makes the
tuple comparison UNKNOWN: against a definite value it is either equal, which
leaves the other elements to decide, or a definite mismatch, which makes the
comparison FALSE. It only selects which rows a NULL in another element is
compared with, which is exactly what a correlation scope key does. For example,
with `b` and `y` NOT NULL, a NULL `a` only needs to be compared with the
subquery rows whose `y` equals its `b`.
## What changes are included in this PR?
- **Decorrelation** (`build_join`): for a null-aware tuple, the elements are
partitioned by `may_be_null_in_scope`, keeping their order within each group.
The elements that can be NULL come first and are the `NOT IN` value keys
(`null_aware_value_keys` counts only them); the others follow as scope keys,
before any correlation keys. The "value keys first" layout is unchanged, so the
hash join needs no change: it already narrows the NULL candidate pairs through
its scope hash map.
- All elements NOT NULL: the join is not null-aware, as before.
- Exactly one nullable element: `V = 1`, a scalar `NOT IN` on that element
correlated by the others.
- `LeftMark` (projection / `OR`) is reordered the same way and stays
three-valued exact.
- A NOT NULL element that is not a hashable equi-join key (e.g.
RunEndEncoded) now stays in the join filter like any correlation instead of
being rejected. A nullable one is still rejected.
- Docs: `Join::null_aware_value_keys`, `HashJoinExec::null_aware_value_keys`
and the 56.0.0 upgrade guide describe the new key layout.
- The Q11 / Q12 comments of the `null_aware_join` benchmark now describe the
scope-key path: their `z` column (`value % 1000`) is NOT NULL.
## What is the testing strategy for this PR?
- `null_aware_anti_join.slt`: EXPLAIN plans for the reordering, with a
correlation, with two nullable elements and one NOT NULL element, a constant
element, NOT NULL on the outer side only (not reordered), and a mark join.
- `null_aware_multi_column.slt` (batch size matrix): results with NOT NULL
elements for `LeftAnti`, `LeftMark` and `OR`, equivalence with the scalar `NOT
IN` shape, correlated subqueries, an empty subquery, a constant element and a
non-hashable NOT NULL element.
- `null_aware_join_fuzz.rs`: NOT NULL columns on both sides and seven mixed
shapes, compared with the `NOT EXISTS ... IS NOT FALSE` reference. Placing the
NOT NULL elements first fails this fuzz on its first seed.
- `roundtrip_logical_plan.rs`: the existing multi-column case had only NOT
NULL `VALUES` columns for `b`/`y`, so it now expects one value key; a variant
with a nullable `b` keeps covering two value keys.
Benchmark `null_aware_join` against `main` (mean of 3 alternating runs):
| NAJ_ROWS | Q10 (no NULL) | Q11 (1% NULL in subquery) | Q12 (1% NULL in
outer) |
|---|---|---|---|
| 10000 | 0.34 ms -> 0.34 ms | 3.55 ms -> 0.35 ms | 0.72 ms -> 0.36 ms |
| 20000 | 0.42 ms -> 0.47 ms | 7.85 ms -> 0.49 ms | 0.77 ms -> 0.51 ms |
Q01 to Q09 are unchanged. Q10 (20000 rows, one outlier run dropped) is
slightly slower: with a scope key, the build side eagerly builds a scope hash
map over all build rows, which is only probed for NULL-valued probe rows. This
is the existing behavior of correlated scalar `NOT IN`; building that map
lazily could be a follow-up.
## Are there any user-facing changes?
Faster multi-column `NOT IN` when some tuple elements are NOT NULL. Plans
show fewer `NOT IN` value keys (`null_aware(value_keys=N)`) for such tuples. No
API change.
This pull request and its description were written by Isaac.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]