Coverage Report

Created: 2026-07-29 22:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/vtopn_pred.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "exprs/vtopn_pred.h"
19
20
#include <cstring>
21
22
#include "exprs/expr_zonemap_filter.h"
23
#include "util/simd/parquet_kernels.h"
24
25
namespace doris {
26
27
namespace {
28
29
using simd::RawComparisonOp;
30
31
7.73k
size_t topn_raw_value_size(PrimitiveType type) {
32
7.73k
    switch (type) {
33
0
#define RETURN_TOPN_RAW_SIZE(TYPE) \
34
3.01k
    case TYPE:                     \
35
3.01k
        return sizeof(typename PrimitiveTypeTraits<TYPE>::CppType)
36
6
        RETURN_TOPN_RAW_SIZE(TYPE_BOOLEAN);
37
2
        RETURN_TOPN_RAW_SIZE(TYPE_TINYINT);
38
2
        RETURN_TOPN_RAW_SIZE(TYPE_SMALLINT);
39
1.40k
        RETURN_TOPN_RAW_SIZE(TYPE_INT);
40
918
        RETURN_TOPN_RAW_SIZE(TYPE_BIGINT);
41
2
        RETURN_TOPN_RAW_SIZE(TYPE_LARGEINT);
42
0
        RETURN_TOPN_RAW_SIZE(TYPE_FLOAT);
43
0
        RETURN_TOPN_RAW_SIZE(TYPE_DOUBLE);
44
2
        RETURN_TOPN_RAW_SIZE(TYPE_DATE);
45
2
        RETURN_TOPN_RAW_SIZE(TYPE_DATETIME);
46
62
        RETURN_TOPN_RAW_SIZE(TYPE_DATEV2);
47
436
        RETURN_TOPN_RAW_SIZE(TYPE_DATETIMEV2);
48
2
        RETURN_TOPN_RAW_SIZE(TYPE_TIMESTAMPTZ);
49
        // Master no longer defines a C++ carrier for legacy TYPE_TIME; Parquet time values use
50
        // TYPE_TIMEV2, so advertising the deprecated tag would make direct filtering unusable.
51
2
        RETURN_TOPN_RAW_SIZE(TYPE_TIMEV2);
52
2
        RETURN_TOPN_RAW_SIZE(TYPE_DECIMAL32);
53
162
        RETURN_TOPN_RAW_SIZE(TYPE_DECIMAL64);
54
2
        RETURN_TOPN_RAW_SIZE(TYPE_DECIMALV2);
55
2
        RETURN_TOPN_RAW_SIZE(TYPE_DECIMAL128I);
56
2
        RETURN_TOPN_RAW_SIZE(TYPE_DECIMAL256);
57
2
        RETURN_TOPN_RAW_SIZE(TYPE_IPV4);
58
2
        RETURN_TOPN_RAW_SIZE(TYPE_IPV6);
59
0
#undef RETURN_TOPN_RAW_SIZE
60
4.71k
    default:
61
4.71k
        return 0;
62
7.73k
    }
63
7.73k
}
64
65
4.33k
bool topn_binary_type(PrimitiveType type) {
66
4.33k
    return is_string_type(type) || type == TYPE_VARBINARY;
67
4.33k
}
68
69
template <typename T, PrimitiveType PT>
70
void execute_topn_raw_comparison(const uint8_t* values, size_t num_values, const Field& bound,
71
25
                                 RawComparisonOp op, uint8_t* matches) {
72
25
    simd::raw_compare(values, num_values, bound.get<PT>(), op, matches);
73
25
}
vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_127execute_topn_raw_comparisonIiLNS_13PrimitiveTypeE5EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
71
25
                                 RawComparisonOp op, uint8_t* matches) {
72
25
    simd::raw_compare(values, num_values, bound.get<PT>(), op, matches);
73
25
}
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_127execute_topn_raw_comparisonIlLNS_13PrimitiveTypeE6EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_127execute_topn_raw_comparisonIfLNS_13PrimitiveTypeE8EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_127execute_topn_raw_comparisonIdLNS_13PrimitiveTypeE9EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
74
75
template <typename T>
76
0
bool topn_raw_scalar_matches(const T& value, const T& bound, RawComparisonOp op) {
77
0
    return op == RawComparisonOp::LE ? value <= bound : value >= bound;
78
0
}
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesIhEEbRKT_S4_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesIaEEbRKT_S4_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesIsEEbRKT_S4_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesInEEbRKT_S4_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesINS_16VecDateTimeValueEEEbRKT_S5_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesINS_11DateV2ValueINS_15DateV2ValueTypeEEEEEbRKT_S7_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesINS_11DateV2ValueINS_19DateTimeV2ValueTypeEEEEEbRKT_S7_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesINS_16TimestampTzValueEEEbRKT_S5_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesIdEEbRKT_S4_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesINS_7DecimalIiEEEEbRKT_S6_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesINS_7DecimalIlEEEEbRKT_S6_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesINS_14DecimalV2ValueEEEbRKT_S5_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesINS_12Decimal128V3EEEbRKT_S5_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesINS_7DecimalIN4wide7integerILm256EiEEEEEEbRKT_S9_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesIjEEbRKT_S4_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesIoEEbRKT_S4_NS_4simd15RawComparisonOpE
79
80
template <PrimitiveType PT>
81
void execute_topn_raw_scalar(const uint8_t* values, size_t num_values, const Field& bound,
82
0
                             RawComparisonOp op, uint8_t* matches) {
83
0
    using T = typename PrimitiveTypeTraits<PT>::CppType;
84
0
    const T& typed_bound = bound.get<PT>();
85
0
    for (size_t row = 0; row < num_values; ++row) {
86
0
        T value;
87
0
        std::memcpy(&value, values + row * sizeof(T), sizeof(T));
88
0
        matches[row] &= topn_raw_scalar_matches(value, typed_bound, op) ? 1 : 0;
89
0
    }
90
0
}
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE2EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE3EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE4EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE7EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE11EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE12EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE25EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE26EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE42EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE27EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE28EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE29EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE20EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE30EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE35EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE36EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE37EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
91
92
6
bool topn_string_matches(int comparison, RawComparisonOp op) {
93
6
    return op == RawComparisonOp::LE ? comparison <= 0 : comparison >= 0;
94
6
}
95
96
} // namespace
97
98
806
bool VTopNPred::can_execute_on_raw_fixed_values(const DataTypePtr& data_type, int column_id) const {
99
806
    if (_predicate == nullptr || data_type == nullptr || _children.size() != 1) {
100
0
        return false;
101
0
    }
102
806
    const auto slot = std::dynamic_pointer_cast<VSlotRef>(_children[0]);
103
806
    if (slot == nullptr || slot->column_id() != column_id || slot->data_type() == nullptr) {
104
0
        return false;
105
0
    }
106
806
    const auto raw_type = remove_nullable(data_type);
107
806
    return remove_nullable(slot->data_type())->equals(*raw_type) &&
108
806
           topn_raw_value_size(raw_type->get_primitive_type()) != 0;
109
806
}
110
111
Status VTopNPred::execute_on_raw_fixed_values(const uint8_t* values, size_t num_values,
112
                                              size_t value_width, const DataTypePtr& data_type,
113
198
                                              int column_id, uint8_t* matches) const {
114
198
    if (!can_execute_on_raw_fixed_values(data_type, column_id)) {
115
0
        return Status::NotSupported("TopN predicate cannot evaluate raw fixed-width values");
116
0
    }
117
198
    DORIS_CHECK(values != nullptr || num_values == 0);
118
198
    DORIS_CHECK(matches != nullptr || num_values == 0);
119
198
    const auto primitive_type = remove_nullable(data_type)->get_primitive_type();
120
198
    const size_t expected_width = topn_raw_value_size(primitive_type);
121
198
    if (value_width != expected_width) {
122
0
        return Status::Corruption("Raw TopN width {} does not match expected {}", value_width,
123
0
                                  expected_width);
124
0
    }
125
126
    // The bound is mutable and may arrive after reader initialization. Snapshot it once per batch
127
    // so every row observes one monotonic TopN frontier without invalidating cached capability.
128
198
    const Field bound = _predicate->get_value();
129
198
    if (bound.is_null()) {
130
173
        return Status::OK();
131
173
    }
132
25
    if (!expr_zonemap::field_types_compatible(bound.get_type(), primitive_type)) {
133
0
        return Status::InternalError("TopN bound type {} does not match raw value type {}",
134
0
                                     type_to_string(bound.get_type()),
135
0
                                     type_to_string(primitive_type));
136
0
    }
137
25
    const auto op = _predicate->is_asc() ? RawComparisonOp::LE : RawComparisonOp::GE;
138
25
    switch (primitive_type) {
139
25
    case TYPE_INT:
140
25
        execute_topn_raw_comparison<int32_t, TYPE_INT>(values, num_values, bound, op, matches);
141
25
        break;
142
0
    case TYPE_BIGINT:
143
0
        execute_topn_raw_comparison<int64_t, TYPE_BIGINT>(values, num_values, bound, op, matches);
144
0
        break;
145
0
    case TYPE_FLOAT:
146
0
        execute_topn_raw_comparison<float, TYPE_FLOAT>(values, num_values, bound, op, matches);
147
0
        break;
148
0
    case TYPE_DOUBLE:
149
0
        execute_topn_raw_comparison<double, TYPE_DOUBLE>(values, num_values, bound, op, matches);
150
0
        break;
151
0
#define EXECUTE_TOPN_RAW_SCALAR(TYPE)                                          \
152
0
    case TYPE:                                                                 \
153
0
        execute_topn_raw_scalar<TYPE>(values, num_values, bound, op, matches); \
154
0
        break
155
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_BOOLEAN);
156
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_TINYINT);
157
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_SMALLINT);
158
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_LARGEINT);
159
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DATE);
160
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DATETIME);
161
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DATEV2);
162
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DATETIMEV2);
163
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_TIMESTAMPTZ);
164
        // Keep dispatch aligned with topn_raw_value_size(): legacy TYPE_TIME has no master carrier.
165
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_TIMEV2);
166
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DECIMAL32);
167
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DECIMAL64);
168
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DECIMALV2);
169
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DECIMAL128I);
170
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DECIMAL256);
171
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_IPV4);
172
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_IPV6);
173
0
#undef EXECUTE_TOPN_RAW_SCALAR
174
0
    default:
175
0
        return Status::NotSupported("TopN raw fixed-width type {} is unsupported",
176
0
                                    type_to_string(primitive_type));
177
25
    }
178
25
    return Status::OK();
179
25
}
180
181
bool VTopNPred::can_execute_on_raw_binary_values(const DataTypePtr& data_type,
182
8
                                                 int column_id) const {
183
8
    if (_predicate == nullptr || data_type == nullptr || _children.size() != 1) {
184
0
        return false;
185
0
    }
186
8
    const auto raw_type = remove_nullable(data_type);
187
8
    const auto raw_primitive_type = raw_type->get_primitive_type();
188
8
    if (!topn_binary_type(raw_primitive_type)) {
189
1
        return false;
190
1
    }
191
7
    const auto slot = std::dynamic_pointer_cast<VSlotRef>(_children[0]);
192
7
    if (slot == nullptr || slot->column_id() != column_id || slot->data_type() == nullptr) {
193
0
        return false;
194
0
    }
195
7
    const auto slot_primitive_type = remove_nullable(slot->data_type())->get_primitive_type();
196
7
    return (is_string_type(raw_primitive_type) && is_string_type(slot_primitive_type)) ||
197
7
           (raw_primitive_type == TYPE_VARBINARY && slot_primitive_type == TYPE_VARBINARY);
198
7
}
199
200
Status VTopNPred::execute_on_raw_binary_values(const StringRef* values, size_t num_values,
201
                                               const DataTypePtr& data_type, int column_id,
202
2
                                               uint8_t* matches) const {
203
2
    if (!can_execute_on_raw_binary_values(data_type, column_id)) {
204
0
        return Status::NotSupported("TopN predicate cannot evaluate raw binary values");
205
0
    }
206
2
    DORIS_CHECK(values != nullptr || num_values == 0);
207
2
    DORIS_CHECK(matches != nullptr || num_values == 0);
208
2
    const Field bound = _predicate->get_value();
209
2
    if (bound.is_null()) {
210
0
        return Status::OK();
211
0
    }
212
2
    const auto primitive_type = remove_nullable(data_type)->get_primitive_type();
213
2
    StringRef bound_ref;
214
2
    if (is_string_type(primitive_type) && is_string_type(bound.get_type())) {
215
1
        const auto& value = bound.get<TYPE_STRING>();
216
1
        bound_ref = StringRef(value.data(), value.size());
217
1
    } else if (primitive_type == TYPE_VARBINARY && bound.get_type() == TYPE_VARBINARY) {
218
1
        bound_ref = bound.get<TYPE_VARBINARY>().to_string_ref();
219
1
    } else {
220
0
        return Status::InternalError("TopN bound type {} does not match raw binary type {}",
221
0
                                     type_to_string(bound.get_type()),
222
0
                                     type_to_string(primitive_type));
223
0
    }
224
2
    const auto op = _predicate->is_asc() ? RawComparisonOp::LE : RawComparisonOp::GE;
225
8
    for (size_t row = 0; row < num_values; ++row) {
226
6
        matches[row] &= topn_string_matches(values[row].compare(bound_ref), op) ? 1 : 0;
227
6
    }
228
2
    return Status::OK();
229
2
}
230
231
12.7k
bool VTopNPred::can_evaluate_dictionary_filter() const {
232
12.7k
    if (_predicate == nullptr || _predicate->nulls_first() || _children.size() != 1) {
233
6.00k
        return false;
234
6.00k
    }
235
6.71k
    const auto slot = std::dynamic_pointer_cast<VSlotRef>(_children[0]);
236
6.72k
    if (slot == nullptr || slot->data_type() == nullptr) {
237
40
        return false;
238
40
    }
239
6.67k
    const auto primitive_type = remove_nullable(slot->data_type())->get_primitive_type();
240
6.67k
    return topn_raw_value_size(primitive_type) != 0 || topn_binary_type(primitive_type);
241
6.71k
}
242
243
1.53k
ZoneMapFilterResult VTopNPred::evaluate_dictionary_filter(const DictionaryEvalContext& ctx) const {
244
1.53k
    if (!can_evaluate_dictionary_filter()) {
245
0
        return ZoneMapFilterResult::kUnsupported;
246
0
    }
247
1.53k
    const auto slot = std::dynamic_pointer_cast<VSlotRef>(_children[0]);
248
1.53k
    DORIS_CHECK(slot != nullptr);
249
1.53k
    const auto* dictionary = ctx.slot(slot->column_id());
250
1.53k
    if (dictionary == nullptr ||
251
1.53k
        !expr_zonemap::data_types_compatible(dictionary->data_type, slot->data_type())) {
252
0
        return ZoneMapFilterResult::kUnsupported;
253
0
    }
254
1.53k
    const Field bound = _predicate->get_value();
255
1.53k
    if (bound.is_null()) {
256
1.49k
        return ZoneMapFilterResult::kMayMatch;
257
1.49k
    }
258
46
    const auto primitive_type = remove_nullable(dictionary->data_type)->get_primitive_type();
259
46
    if (!expr_zonemap::field_types_compatible(bound.get_type(), primitive_type)) {
260
0
        return ZoneMapFilterResult::kUnsupported;
261
0
    }
262
76
    for (const Field& value : dictionary->values) {
263
76
        if (value.is_null()) {
264
0
            continue;
265
0
        }
266
76
        if (!expr_zonemap::field_types_compatible(value.get_type(), primitive_type)) {
267
0
            return ZoneMapFilterResult::kUnsupported;
268
0
        }
269
76
        if ((_predicate->is_asc() && value <= bound) || (!_predicate->is_asc() && value >= bound)) {
270
22
            return ZoneMapFilterResult::kMayMatch;
271
22
        }
272
76
    }
273
24
    return ZoneMapFilterResult::kNoMatch;
274
46
}
275
276
} // namespace doris