LuciferYang opened a new pull request, #67774:
URL: https://github.com/apache/doris/pull/67774

   ### What problem does this PR solve?
   
   Issue Number: close #67772
   
   Related PR: #67771
   
   Problem Summary:
   
   Zone map pruning only fires when a predicate compares a column against a 
constant. A predicate over two columns of the same table, `WHERE a != b` or 
`WHERE a < b`, never participates, so every segment and every Parquet row group 
is read even when the two columns' min/max ranges make the predicate 
unsatisfiable.
   
   Everything on the path except the leaf already supports this. 
`ZoneMapEvalContext` is keyed per slot and the segment-level and Parquet 
row-group-level callers already populate an entry for every slot a compound 
expression references, which is why cross-column AND / OR compounds prune 
today. What was missing is the comparison operator's capability gate: it 
accepts one slot plus one literal and nothing else, so a two-slot comparison is 
dropped before evaluation.
   
   This PR adds the slot-vs-slot shape next to the existing slot-vs-literal one 
and gives the six always-false tests a single implementation shared by both. 
With the left operand's non-null values in `[lmin, lmax]` and the right 
operand's in `[rmin, rmax]`, the comparison cannot be TRUE for any row when:
   
   | operator | condition |
   | --- | --- |
   | `=` | `lmin > rmax \|\| rmin > lmax` |
   | `!=` | `lmin == lmax && rmin == rmax && lmin == rmin` |
   | `<` | `lmin >= rmax` |
   | `<=` | `lmin > rmax` |
   | `>` | `rmin >= lmax` |
   | `>=` | `rmin > lmax` |
   
   Substituting the degenerate range `[literal, literal]` for the right operand 
reduces each row to the slot-vs-literal test already in the tree, so the 
single-column path keeps its exact current behaviour. The existing tests 
passing unchanged is the mechanical check on that.
   
   `kNoMatch` means "no row in this zone can make the conjunct TRUE", not 
"every row is FALSE", so NULL does not weaken these rules: a WHERE conjunct 
discards FALSE and NULL alike, and the bounds summarize exactly the non-null 
values. A column with no non-null value at all makes the comparison NULL on 
every row and returns `kNoMatch` unconditionally.
   
   One trap for reviewers: `can_evaluate` must not be widened. It is shared by 
dictionary filtering and by `can_evaluate_equality`, and both dereference 
`extract_slot_and_literal` behind a `DORIS_CHECK`, so accepting a slot-vs-slot 
expression there would abort. This PR adds a separate `can_evaluate_slot_slot` 
and ORs it into `can_evaluate_zonemap_filter` only; a unit test pins that down.
   
   Two deliberate limitations: a cast around either operand is rejected, 
because the bounds are raw stored values and the optimizer inserts a cast 
whenever the two column types differ; and on Parquet a floating column pair 
does not prune at all, because Parquet bounds omit NaN without reporting how 
many were skipped, which would flip the `!=` rule.
   
   This also fixes a bug it uncovered. The flag that carries "these bounds came 
from Parquet, so a hidden NaN is possible" was only ever set in the format_v2 
reader; the v1 reader assigned the slot's data type and left the flag at its 
default, so the NaN guard never fired on that path. Two DOUBLE columns whose 
Parquet bounds both read `[1.0, 1.0]` were then pruned by the `!=` rule even 
when one of them hid a NaN, dropping a row that should have come back. The rule 
now lives in one setter on `SlotZoneMap` that assigns the type and the flag 
together, and all three Parquet call sites go through it.
   
   ### Release note
   
   None
   
   ### Check List (For Author)
   
   - Test <!-- At least one of them must be included. -->
       - [X] Regression test
       - [X] Unit Test
       - [ ] Manual test (add detailed scripts or steps below)
       - [ ] No need to test or manual test. Explain why:
           - [ ] This is a refactor/code format and no logic has been changed.
           - [ ] Previous test can cover this change.
           - [ ] No code files have been changed.
           - [ ] Other reason <!-- Add your reason?  -->
   
   Unit tests cover the six operators against an interval matrix, both fully 
separated and partially overlapping, plus the guard matrix: slot missing from 
the context, zone map absent, incompatible types, `pass_all`, an all-null 
column on either side, the same slot on both sides, and an unknown floating NaN 
count. 83 tests pass across `ExprZonemapFilterTest`, 
`SegmentIteratorExprZonemapTest` and `ColumnZoneMapTest`.
   
   The regression suite adds three loads of one segment each holding a constant 
in both columns, which is the shape that forces a per-segment decision: `a != 
b` drops exactly two of the three segments and `a = b` exactly one. Every query 
there runs with `enable_expr_zonemap_filter` on and off and the row counts are 
compared, since the counter only shows that pruning fired.
   
   The interval rules were also checked by mutation: seven mutants (each of 
`<`, `<=`, `>`, `>=` reading the wrong end of a range, dropping the right-side 
`has_not_null` guard, dropping the `rmin == rmax` term from `!=`, and making 
the slot-vs-slot path bail out unconditionally) each break at least one test.
   
   - Behavior changed:
       - [ ] No.
       - [X] Yes.
   
   Two changes, both intended. Cross-column comparisons now prune at the 
segment level and at the row-group level on both Parquet readers, so those 
queries read less. And on the v1 Parquet reader, single-column float and double 
predicates using `>`, `>=`, `!=` against a non-NaN literal, or `=` against NaN, 
no longer prune: that is the NaN correctness fix above, and it was already the 
behaviour on format_v2.
   
   The benefit was measured rather than estimated. On two columns whose 
per-segment ranges separate naturally (a batch-increasing column against a 
fixed-range one, 20 segments) `WHERE lo < hi` skips 19 of 20 segments. On 
TPC-H-shaped correlated dates, where `d2` is `d1` plus a small offset, it skips 
none, for an always-false and an always-true predicate alike. The window is 
narrow and depends entirely on whether the two ranges separate; TPC-H Q12 is a 
regression baseline here, not a benefit demonstration.
   
   - Does this need documentation?
       - [X] No.
   


-- 
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]

Reply via email to