Coverage Report

Created: 2026-11-04 01:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/doris/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
488
size_t topn_raw_value_size(PrimitiveType type) {
32
488
    switch (type) {
33
0
#define RETURN_TOPN_RAW_SIZE(TYPE) \
34
473
    case TYPE:                     \
35
473
        return sizeof(typename PrimitiveTypeTraits<TYPE>::CppType)
36
2
        RETURN_TOPN_RAW_SIZE(TYPE_BOOLEAN);
37
2
        RETURN_TOPN_RAW_SIZE(TYPE_TINYINT);
38
2
        RETURN_TOPN_RAW_SIZE(TYPE_SMALLINT);
39
418
        RETURN_TOPN_RAW_SIZE(TYPE_INT);
40
2
        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
2
        RETURN_TOPN_RAW_SIZE(TYPE_DATEV2);
47
2
        RETURN_TOPN_RAW_SIZE(TYPE_DATETIMEV2);
48
5
        RETURN_TOPN_RAW_SIZE(TYPE_TIMESTAMP_NS);
49
2
        RETURN_TOPN_RAW_SIZE(TYPE_TIMESTAMPTZ);
50
0
        RETURN_TOPN_RAW_SIZE(TYPE_TIME);
51
2
        RETURN_TOPN_RAW_SIZE(TYPE_TIMEV2);
52
2
        RETURN_TOPN_RAW_SIZE(TYPE_DECIMAL32);
53
2
        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
14
        RETURN_TOPN_RAW_SIZE(TYPE_UUID);
60
0
#undef RETURN_TOPN_RAW_SIZE
61
15
    default:
62
15
        return 0;
63
488
    }
64
488
}
65
66
62
bool topn_binary_type(PrimitiveType type) {
67
62
    return is_string_type(type) || type == TYPE_VARBINARY;
68
62
}
69
70
template <typename T, PrimitiveType PT>
71
void execute_topn_raw_comparison(const uint8_t* values, size_t num_values, const Field& bound,
72
45
                                 RawComparisonOp op, uint8_t* matches) {
73
45
    simd::raw_compare(values, num_values, bound.get<PT>(), op, matches);
74
45
}
vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_127execute_topn_raw_comparisonIiLNS_13PrimitiveTypeE5EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
72
45
                                 RawComparisonOp op, uint8_t* matches) {
73
45
    simd::raw_compare(values, num_values, bound.get<PT>(), op, matches);
74
45
}
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
75
76
template <typename T>
77
23
bool topn_raw_scalar_matches(const T& value, const T& bound, RawComparisonOp op) {
78
23
    return op == RawComparisonOp::LE ? value <= bound : value >= bound;
79
23
}
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
vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesINS_16TimeStampNsValueEEEbRKT_S5_NS_4simd15RawComparisonOpE
Line
Count
Source
77
3
bool topn_raw_scalar_matches(const T& value, const T& bound, RawComparisonOp op) {
78
3
    return op == RawComparisonOp::LE ? value <= bound : value >= bound;
79
3
}
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
vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123topn_raw_scalar_matchesIoEEbRKT_S4_NS_4simd15RawComparisonOpE
Line
Count
Source
77
20
bool topn_raw_scalar_matches(const T& value, const T& bound, RawComparisonOp op) {
78
20
    return op == RawComparisonOp::LE ? value <= bound : value >= bound;
79
20
}
80
81
template <PrimitiveType PT>
82
void execute_topn_raw_scalar(const uint8_t* values, size_t num_values, const Field& bound,
83
5
                             RawComparisonOp op, uint8_t* matches) {
84
5
    using T = typename PrimitiveTypeTraits<PT>::CppType;
85
5
    const T& typed_bound = bound.get<PT>();
86
28
    for (size_t row = 0; row < num_values; ++row) {
87
23
        T value;
88
23
        std::memcpy(&value, values + row * sizeof(T), sizeof(T));
89
23
        matches[row] &= topn_raw_scalar_matches(value, typed_bound, op) ? 1 : 0;
90
23
    }
91
5
}
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
vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE43EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
83
1
                             RawComparisonOp op, uint8_t* matches) {
84
1
    using T = typename PrimitiveTypeTraits<PT>::CppType;
85
1
    const T& typed_bound = bound.get<PT>();
86
4
    for (size_t row = 0; row < num_values; ++row) {
87
3
        T value;
88
3
        std::memcpy(&value, values + row * sizeof(T), sizeof(T));
89
3
        matches[row] &= topn_raw_scalar_matches(value, typed_bound, op) ? 1 : 0;
90
3
    }
91
1
}
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_13PrimitiveTypeE21EEEvPKhmRKNS_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
vtopn_pred.cpp:_ZN5doris12_GLOBAL__N_123execute_topn_raw_scalarILNS_13PrimitiveTypeE44EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
83
4
                             RawComparisonOp op, uint8_t* matches) {
84
4
    using T = typename PrimitiveTypeTraits<PT>::CppType;
85
4
    const T& typed_bound = bound.get<PT>();
86
24
    for (size_t row = 0; row < num_values; ++row) {
87
20
        T value;
88
20
        std::memcpy(&value, values + row * sizeof(T), sizeof(T));
89
20
        matches[row] &= topn_raw_scalar_matches(value, typed_bound, op) ? 1 : 0;
90
20
    }
91
4
}
92
93
6
bool topn_string_matches(int comparison, RawComparisonOp op) {
94
6
    return op == RawComparisonOp::LE ? comparison <= 0 : comparison >= 0;
95
6
}
96
97
} // namespace
98
99
1.27k
bool VTopNPred::can_evaluate_zonemap_filter() const {
100
    // Capability must remain available before the first bound; readers cache it during open.
101
1.27k
    return _predicate != nullptr && _children.size() == 1 && _children[0]->is_slot_ref();
102
1.27k
}
103
104
308
ZoneMapFilterResult VTopNPred::evaluate_zonemap_filter(const ZoneMapEvalContext& ctx) const {
105
308
    if (!can_evaluate_zonemap_filter()) {
106
0
        return unsupported_zonemap_filter(ctx);
107
0
    }
108
308
    const Field bound = _predicate->get_value();
109
308
    if (bound.is_null()) {
110
178
        return ZoneMapFilterResult::kMayMatch;
111
178
    }
112
130
    const auto* slot = assert_cast<const VSlotRef*>(_children[0].get());
113
130
    const auto zone = ctx.zone_map(slot->column_id());
114
130
    const auto type = ctx.data_type(slot->column_id());
115
130
    if (zone == nullptr || !expr_zonemap::data_types_compatible(type, slot->data_type())) {
116
6
        return unsupported_zonemap_filter(ctx);
117
6
    }
118
124
    if (_predicate->nulls_first() && zone->has_null) {
119
4
        return ZoneMapFilterResult::kMayMatch;
120
4
    }
121
120
    if (!zone->has_not_null) {
122
2
        return ZoneMapFilterResult::kNoMatch;
123
2
    }
124
118
    if (ctx.floating_nan_count_unknown(slot->column_id()) || bound.is_nan() ||
125
118
        !expr_zonemap::range_stats_usable_for_zonemap(*zone, type) ||
126
118
        !expr_zonemap::field_types_compatible(bound.get_type(),
127
118
                                              remove_nullable(type)->get_primitive_type())) {
128
0
        return unsupported_zonemap_filter(ctx);
129
0
    }
130
    // Keep equality: with ORDER BY a, b a row tied on a can still improve b.
131
118
    const bool excluded = _predicate->is_asc() ? bound < zone->min_value : zone->max_value < bound;
132
118
    return excluded ? ZoneMapFilterResult::kNoMatch : ZoneMapFilterResult::kMayMatch;
133
118
}
134
135
246
bool VTopNPred::can_execute_on_raw_fixed_values(const DataTypePtr& data_type, int column_id) const {
136
246
    if (_predicate == nullptr || data_type == nullptr || _children.size() != 1) {
137
0
        return false;
138
0
    }
139
246
    const auto slot = std::dynamic_pointer_cast<VSlotRef>(_children[0]);
140
246
    if (slot == nullptr || slot->column_id() != column_id || slot->data_type() == nullptr) {
141
0
        return false;
142
0
    }
143
246
    const auto raw_type = remove_nullable(data_type);
144
246
    return remove_nullable(slot->data_type())->equals(*raw_type) &&
145
246
           topn_raw_value_size(raw_type->get_primitive_type()) != 0;
146
246
}
147
148
Status VTopNPred::execute_on_raw_fixed_values(const uint8_t* values, size_t num_values,
149
                                              size_t value_width, const DataTypePtr& data_type,
150
51
                                              int column_id, uint8_t* matches) const {
151
51
    if (!can_execute_on_raw_fixed_values(data_type, column_id)) {
152
0
        return Status::NotSupported("TopN predicate cannot evaluate raw fixed-width values");
153
0
    }
154
51
    DORIS_CHECK(values != nullptr || num_values == 0);
155
51
    DORIS_CHECK(matches != nullptr || num_values == 0);
156
51
    const auto primitive_type = remove_nullable(data_type)->get_primitive_type();
157
51
    const size_t expected_width = topn_raw_value_size(primitive_type);
158
51
    if (value_width != expected_width) {
159
0
        return Status::Corruption("Raw TopN width {} does not match expected {}", value_width,
160
0
                                  expected_width);
161
0
    }
162
163
    // The bound is mutable and may arrive after reader initialization. Snapshot it once per batch
164
    // so every row observes one monotonic TopN frontier without invalidating cached capability.
165
51
    const Field bound = _predicate->get_value();
166
51
    if (bound.is_null()) {
167
1
        return Status::OK();
168
1
    }
169
50
    if (!expr_zonemap::field_types_compatible(bound.get_type(), primitive_type)) {
170
0
        return Status::InternalError("TopN bound type {} does not match raw value type {}",
171
0
                                     type_to_string(bound.get_type()),
172
0
                                     type_to_string(primitive_type));
173
0
    }
174
50
    const auto op = _predicate->is_asc() ? RawComparisonOp::LE : RawComparisonOp::GE;
175
50
    switch (primitive_type) {
176
45
    case TYPE_INT:
177
45
        execute_topn_raw_comparison<int32_t, TYPE_INT>(values, num_values, bound, op, matches);
178
45
        break;
179
0
    case TYPE_BIGINT:
180
0
        execute_topn_raw_comparison<int64_t, TYPE_BIGINT>(values, num_values, bound, op, matches);
181
0
        break;
182
0
    case TYPE_FLOAT:
183
0
        execute_topn_raw_comparison<float, TYPE_FLOAT>(values, num_values, bound, op, matches);
184
0
        break;
185
0
    case TYPE_DOUBLE:
186
0
        execute_topn_raw_comparison<double, TYPE_DOUBLE>(values, num_values, bound, op, matches);
187
0
        break;
188
0
#define EXECUTE_TOPN_RAW_SCALAR(TYPE)                                          \
189
5
    case TYPE:                                                                 \
190
5
        execute_topn_raw_scalar<TYPE>(values, num_values, bound, op, matches); \
191
5
        break
192
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_BOOLEAN);
193
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_TINYINT);
194
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_SMALLINT);
195
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_LARGEINT);
196
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DATE);
197
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DATETIME);
198
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DATEV2);
199
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DATETIMEV2);
200
1
        EXECUTE_TOPN_RAW_SCALAR(TYPE_TIMESTAMP_NS);
201
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_TIMESTAMPTZ);
202
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_TIME);
203
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_TIMEV2);
204
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DECIMAL32);
205
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DECIMAL64);
206
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DECIMALV2);
207
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DECIMAL128I);
208
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_DECIMAL256);
209
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_IPV4);
210
0
        EXECUTE_TOPN_RAW_SCALAR(TYPE_IPV6);
211
4
        EXECUTE_TOPN_RAW_SCALAR(TYPE_UUID);
212
0
#undef EXECUTE_TOPN_RAW_SCALAR
213
0
    default:
214
0
        return Status::NotSupported("TopN raw fixed-width type {} is unsupported",
215
0
                                    type_to_string(primitive_type));
216
50
    }
217
50
    return Status::OK();
218
50
}
219
220
bool VTopNPred::can_execute_on_raw_binary_values(const DataTypePtr& data_type,
221
48
                                                 int column_id) const {
222
48
    if (_predicate == nullptr || data_type == nullptr || _children.size() != 1) {
223
0
        return false;
224
0
    }
225
48
    const auto raw_type = remove_nullable(data_type);
226
48
    const auto raw_primitive_type = raw_type->get_primitive_type();
227
48
    if (!topn_binary_type(raw_primitive_type)) {
228
41
        return false;
229
41
    }
230
7
    const auto slot = std::dynamic_pointer_cast<VSlotRef>(_children[0]);
231
7
    if (slot == nullptr || slot->column_id() != column_id || slot->data_type() == nullptr) {
232
0
        return false;
233
0
    }
234
7
    const auto slot_primitive_type = remove_nullable(slot->data_type())->get_primitive_type();
235
7
    return (is_string_type(raw_primitive_type) && is_string_type(slot_primitive_type)) ||
236
7
           (raw_primitive_type == TYPE_VARBINARY && slot_primitive_type == TYPE_VARBINARY);
237
7
}
238
239
Status VTopNPred::execute_on_raw_binary_values(const StringRef* values, size_t num_values,
240
                                               const DataTypePtr& data_type, int column_id,
241
2
                                               uint8_t* matches) const {
242
2
    if (!can_execute_on_raw_binary_values(data_type, column_id)) {
243
0
        return Status::NotSupported("TopN predicate cannot evaluate raw binary values");
244
0
    }
245
2
    DORIS_CHECK(values != nullptr || num_values == 0);
246
2
    DORIS_CHECK(matches != nullptr || num_values == 0);
247
2
    const Field bound = _predicate->get_value();
248
2
    if (bound.is_null()) {
249
0
        return Status::OK();
250
0
    }
251
2
    const auto primitive_type = remove_nullable(data_type)->get_primitive_type();
252
2
    StringRef bound_ref;
253
2
    if (is_string_type(primitive_type) && is_string_type(bound.get_type())) {
254
1
        const auto& value = bound.get<TYPE_STRING>();
255
1
        bound_ref = StringRef(value.data(), value.size());
256
1
    } else if (primitive_type == TYPE_VARBINARY && bound.get_type() == TYPE_VARBINARY) {
257
1
        bound_ref = bound.get<TYPE_VARBINARY>().to_string_ref();
258
1
    } else {
259
0
        return Status::InternalError("TopN bound type {} does not match raw binary type {}",
260
0
                                     type_to_string(bound.get_type()),
261
0
                                     type_to_string(primitive_type));
262
0
    }
263
2
    const auto op = _predicate->is_asc() ? RawComparisonOp::LE : RawComparisonOp::GE;
264
8
    for (size_t row = 0; row < num_values; ++row) {
265
6
        matches[row] &= topn_string_matches(values[row].compare(bound_ref), op) ? 1 : 0;
266
6
    }
267
2
    return Status::OK();
268
2
}
269
270
192
bool VTopNPred::can_evaluate_dictionary_filter() const {
271
192
    if (_predicate == nullptr || _predicate->nulls_first() || _children.size() != 1) {
272
1
        return false;
273
1
    }
274
191
    const auto slot = std::dynamic_pointer_cast<VSlotRef>(_children[0]);
275
191
    if (slot == nullptr || slot->data_type() == nullptr) {
276
0
        return false;
277
0
    }
278
191
    const auto primitive_type = remove_nullable(slot->data_type())->get_primitive_type();
279
191
    return topn_raw_value_size(primitive_type) != 0 || topn_binary_type(primitive_type);
280
191
}
281
282
7
ZoneMapFilterResult VTopNPred::evaluate_dictionary_filter(const DictionaryEvalContext& ctx) const {
283
7
    if (!can_evaluate_dictionary_filter()) {
284
0
        return ZoneMapFilterResult::kUnsupported;
285
0
    }
286
7
    const auto slot = std::dynamic_pointer_cast<VSlotRef>(_children[0]);
287
7
    DORIS_CHECK(slot != nullptr);
288
7
    const auto* dictionary = ctx.slot(slot->column_id());
289
7
    if (dictionary == nullptr ||
290
7
        !expr_zonemap::data_types_compatible(dictionary->data_type, slot->data_type())) {
291
0
        return ZoneMapFilterResult::kUnsupported;
292
0
    }
293
7
    const Field bound = _predicate->get_value();
294
7
    if (bound.is_null()) {
295
3
        return ZoneMapFilterResult::kMayMatch;
296
3
    }
297
4
    const auto primitive_type = remove_nullable(dictionary->data_type)->get_primitive_type();
298
4
    if (!expr_zonemap::field_types_compatible(bound.get_type(), primitive_type)) {
299
0
        return ZoneMapFilterResult::kUnsupported;
300
0
    }
301
8
    for (const Field& value : dictionary->values) {
302
8
        if (value.is_null()) {
303
0
            continue;
304
0
        }
305
8
        if (!expr_zonemap::field_types_compatible(value.get_type(), primitive_type)) {
306
0
            return ZoneMapFilterResult::kUnsupported;
307
0
        }
308
8
        if ((_predicate->is_asc() && value <= bound) || (!_predicate->is_asc() && value >= bound)) {
309
2
            return ZoneMapFilterResult::kMayMatch;
310
2
        }
311
8
    }
312
2
    return ZoneMapFilterResult::kNoMatch;
313
4
}
314
315
} // namespace doris