github-actions[bot] commented on code in PR #67019:
URL: https://github.com/apache/doris/pull/67019#discussion_r3827778975


##########
be/src/storage/predicate/comparison_predicate.h:
##########
@@ -130,26 +130,55 @@ class ComparisonPredicateBase final : public 
ColumnPredicate {
         _evaluate_bit<true>(column, sel, size, flags);
     }
 
-    bool evaluate_and(const segment_v2::ZoneMap& zone_map) const override {
+    ZoneMapFilterResult evaluate_zonemap_filter_impl(
+            const segment_v2::ZoneMap& zone_map) const override {
+        // Every row is NULL, and a comparison against NULL never passes the 
filter.
         if (!zone_map.has_not_null) {
-            return false;
+            return ZoneMapFilterResult::kNoMatch;
         }
+        const auto& min_value = zone_map.min_value.template get<Type>();
+        const auto& max_value = zone_map.max_value.template get<Type>();
+        // One NULL row is enough to stop the whole zone from matching.
+        const bool can_match_all = !zone_map.has_null;
 
+        // EQ and NE read the same two facts and reach opposite conclusions.
         if constexpr (PT == PredicateType::EQ) {
-            return _operator(
-                    Compare::less_equal(zone_map.min_value.template 
get<Type>(), _value) &&
-                            Compare::greater_equal(zone_map.max_value.template 
get<Type>(), _value),
-                    true);
+            // The value sits outside [min, max], so no row can be equal to it.
+            if (Compare::less(_value, min_value) || Compare::less(max_value, 
_value)) {
+                return ZoneMapFilterResult::kNoMatch;
+            }
+            // The zone holds nothing but the value.
+            if (can_match_all && Compare::equal(min_value, _value) &&
+                Compare::equal(max_value, _value)) {
+                return ZoneMapFilterResult::kAllMatch;
+            }
+            return ZoneMapFilterResult::kMayMatch;
         } else if constexpr (PT == PredicateType::NE) {
-            return _operator(
-                    Compare::equal(zone_map.min_value.template get<Type>(), 
_value) &&
-                            Compare::equal(zone_map.max_value.template 
get<Type>(), _value),
-                    true);
-        } else if constexpr (PT == PredicateType::LT || PT == 
PredicateType::LE) {
-            return _operator(zone_map.min_value.template get<Type>(), _value);
+            // The zone holds nothing but the value, so no row can differ from 
it.
+            if (Compare::equal(min_value, _value) && Compare::equal(max_value, 
_value)) {
+                return ZoneMapFilterResult::kNoMatch;
+            }
+            // The value sits outside [min, max], so no row can be equal to it.
+            if (can_match_all &&
+                (Compare::less(_value, min_value) || Compare::less(max_value, 
_value))) {
+                return ZoneMapFilterResult::kAllMatch;

Review Comment:
   This new `kAllMatch` case is unsafe for truncated string zone maps. Doris 
VARCHAR/STRING can store invalid UTF-8 (the existing `is_valid_utf8` regression 
inserts `unhex('FF')`), while `modify_index_before_flush` increments byte 511 
of every 512-byte maximum. For `L = repeat('a', 511) || 0xff`, that increment 
wraps the serialized max to the same prefix ending in `0x00`, so a one-row 
segment has `min=L` and `max<L`. `WHERE v != L` then takes `max < _value`, 
reports all-match, and drops the residual, returning the row instead of zero; 
the new NOT IN branch has the same problem. The same inexact bound also 
invalidates the new EQ/IN/`starts_with` all-match proofs on multi-value zones. 
Please carry the successor across trailing `0xff` bytes or track bound 
exactness and forbid all-match proofs from inexact string bounds, with 
regressions for these operators.



##########
be/src/exprs/vcompound_pred.h:
##########
@@ -142,7 +142,7 @@ class VCompoundPred : public VectorizedFnCall {
                 return child->can_evaluate_zonemap_filter();
             });
         case TExprOpcode::COMPOUND_OR:
-            return !_children.empty() && std::ranges::all_of(_children, 
[](const VExprSPtr& child) {
+            return std::ranges::any_of(_children, [](const VExprSPtr& child) {

Review Comment:
   Please keep partially supported ORs from being considered safe metadata 
filters here. With an unsupported/error-sensitive branch, a supported sibling 
can return `kAllMatch`, after which `Segment::new_iterator` erases the whole 
residual. For example, `assert_true(k > 0, 'boom') OR v < 100` on a segment 
with `v=[1,2]` normally raises from the first child for `k=0`, but this path 
drops the expression and returns the row. It also makes the Parquet readers 
fetch page indexes for partial ORs that can never return `kNoMatch`, since the 
unsupported child holds the result at `kMayMatch`. Please require every OR 
branch to support the proof, or separately represent `can disprove` versus 
`safe to drop` and retain error-sensitive residuals.



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