Coverage Report

Created: 2026-07-29 15:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/vectorized_fn_call.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/vectorized_fn_call.h"
19
20
#include <fmt/compile.h>
21
#include <fmt/format.h>
22
#include <fmt/ranges.h> // IWYU pragma: keep
23
#include <gen_cpp/Opcodes_types.h>
24
#include <gen_cpp/Types_types.h>
25
26
#include <memory>
27
#include <optional>
28
#include <ostream>
29
#include <set>
30
31
#include "common/config.h"
32
#include "common/exception.h"
33
#include "common/logging.h"
34
#include "common/status.h"
35
#include "common/utils.h"
36
#include "core/assert_cast.h"
37
#include "core/block/block.h"
38
#include "core/block/column_numbers.h"
39
#include "core/column/column.h"
40
#include "core/column/column_array.h"
41
#include "core/column/column_nullable.h"
42
#include "core/column/column_vector.h"
43
#include "core/data_type/data_type.h"
44
#include "core/data_type/data_type_agg_state.h"
45
#include "core/types.h"
46
#include "exec/common/util.hpp"
47
#include "exec/pipeline/pipeline_task.h"
48
#include "exprs/function/array/function_array_distance.h"
49
#include "exprs/function/function_agg_state.h"
50
#include "exprs/function/function_fake.h"
51
#include "exprs/function/function_java_udf.h"
52
#include "exprs/function/function_python_udf.h"
53
#include "exprs/function/function_rpc.h"
54
#include "exprs/function/simple_function_factory.h"
55
#include "exprs/function_context.h"
56
#include "exprs/varray_literal.h"
57
#include "exprs/vcast_expr.h"
58
#include "exprs/vexpr_context.h"
59
#include "exprs/virtual_slot_ref.h"
60
#include "exprs/vliteral.h"
61
#include "runtime/runtime_state.h"
62
#include "storage/index/ann/ann_index.h"
63
#include "storage/index/ann/ann_index_iterator.h"
64
#include "storage/index/ann/ann_search_params.h"
65
#include "storage/index/index_reader.h"
66
#include "storage/index/zone_map/zonemap_eval_context.h"
67
#include "storage/segment/column_reader.h"
68
#include "storage/segment/virtual_column_iterator.h"
69
#include "util/simd/parquet_kernels.h"
70
71
namespace doris {
72
class RowDescriptor;
73
class RuntimeState;
74
class TExprNode;
75
} // namespace doris
76
77
namespace doris {
78
79
const std::string AGG_STATE_SUFFIX = "_state";
80
81
// Now left child is a function call, we need to check if it is a distance function
82
const static std::set<std::string> DISTANCE_FUNCS = {L2DistanceApproximate::name,
83
                                                     InnerProductApproximate::name};
84
const static std::set<TExprOpcode::type> OPS_FOR_ANN_RANGE_SEARCH = {
85
        TExprOpcode::GE, TExprOpcode::LE, TExprOpcode::LE, TExprOpcode::GT, TExprOpcode::LT};
86
87
namespace {
88
89
using simd::RawComparisonOp;
90
91
109k
std::optional<RawComparisonOp> raw_comparison_op(std::string_view function_name, bool reverse) {
92
109k
    RawComparisonOp op;
93
109k
    if (function_name == "eq" || function_name == "eq_for_null") {
94
5.46k
        op = RawComparisonOp::EQ;
95
103k
    } else if (function_name == "ne") {
96
142
        op = RawComparisonOp::NE;
97
103k
    } else if (function_name == "lt") {
98
2.04k
        op = RawComparisonOp::LT;
99
101k
    } else if (function_name == "le") {
100
46.5k
        op = RawComparisonOp::LE;
101
54.9k
    } else if (function_name == "gt") {
102
3.20k
        op = RawComparisonOp::GT;
103
51.7k
    } else if (function_name == "ge") {
104
51.7k
        op = RawComparisonOp::GE;
105
51.7k
    } else {
106
30
        return std::nullopt;
107
30
    }
108
109k
    if (!reverse || op == RawComparisonOp::EQ || op == RawComparisonOp::NE) {
109
109k
        return op;
110
109k
    }
111
18.4E
    switch (op) {
112
1
    case RawComparisonOp::LT:
113
1
        return RawComparisonOp::GT;
114
0
    case RawComparisonOp::LE:
115
0
        return RawComparisonOp::GE;
116
0
    case RawComparisonOp::GT:
117
0
        return RawComparisonOp::LT;
118
0
    case RawComparisonOp::GE:
119
0
        return RawComparisonOp::LE;
120
0
    case RawComparisonOp::EQ:
121
0
    case RawComparisonOp::NE:
122
0
        break;
123
18.4E
    }
124
0
    __builtin_unreachable();
125
18.4E
}
126
127
2.41k
bool raw_string_comparison_matches(int comparison, RawComparisonOp op) {
128
2.41k
    switch (op) {
129
1.25k
    case RawComparisonOp::EQ:
130
1.25k
        return comparison == 0;
131
634
    case RawComparisonOp::NE:
132
634
        return comparison != 0;
133
156
    case RawComparisonOp::LT:
134
156
        return comparison < 0;
135
104
    case RawComparisonOp::LE:
136
104
        return comparison <= 0;
137
140
    case RawComparisonOp::GT:
138
140
        return comparison > 0;
139
118
    case RawComparisonOp::GE:
140
118
        return comparison >= 0;
141
2.41k
    }
142
0
    __builtin_unreachable();
143
2.41k
}
144
145
template <typename T, PrimitiveType PT>
146
void execute_raw_comparison(const uint8_t* values, size_t num_values, const Field& literal,
147
15.0k
                            RawComparisonOp op, uint8_t* matches) {
148
15.0k
    const T rhs = literal.get<PT>();
149
15.0k
    simd::raw_compare(values, num_values, rhs, op, matches);
150
15.0k
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_122execute_raw_comparisonIiLNS_13PrimitiveTypeE5EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
147
14.7k
                            RawComparisonOp op, uint8_t* matches) {
148
14.7k
    const T rhs = literal.get<PT>();
149
14.7k
    simd::raw_compare(values, num_values, rhs, op, matches);
150
14.7k
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_122execute_raw_comparisonIlLNS_13PrimitiveTypeE6EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
147
179
                            RawComparisonOp op, uint8_t* matches) {
148
179
    const T rhs = literal.get<PT>();
149
179
    simd::raw_compare(values, num_values, rhs, op, matches);
150
179
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_122execute_raw_comparisonIfLNS_13PrimitiveTypeE8EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
147
8
                            RawComparisonOp op, uint8_t* matches) {
148
8
    const T rhs = literal.get<PT>();
149
8
    simd::raw_compare(values, num_values, rhs, op, matches);
150
8
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_122execute_raw_comparisonIdLNS_13PrimitiveTypeE9EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
147
84
                            RawComparisonOp op, uint8_t* matches) {
148
84
    const T rhs = literal.get<PT>();
149
84
    simd::raw_compare(values, num_values, rhs, op, matches);
150
84
}
151
152
template <typename T>
153
171k
bool raw_scalar_comparison_matches(const T& lhs, const T& rhs, RawComparisonOp op) {
154
171k
    switch (op) {
155
770
    case RawComparisonOp::EQ:
156
770
        return lhs == rhs;
157
0
    case RawComparisonOp::NE:
158
0
        return lhs != rhs;
159
320
    case RawComparisonOp::LT:
160
320
        return lhs < rhs;
161
151k
    case RawComparisonOp::LE:
162
151k
        return lhs <= rhs;
163
18.0k
    case RawComparisonOp::GT:
164
18.0k
        return lhs > rhs;
165
938
    case RawComparisonOp::GE:
166
938
        return lhs >= rhs;
167
171k
    }
168
0
    __builtin_unreachable();
169
171k
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesIhEEbRKT_S4_NS_4simd15RawComparisonOpE
Line
Count
Source
153
178
bool raw_scalar_comparison_matches(const T& lhs, const T& rhs, RawComparisonOp op) {
154
178
    switch (op) {
155
178
    case RawComparisonOp::EQ:
156
178
        return lhs == rhs;
157
0
    case RawComparisonOp::NE:
158
0
        return lhs != rhs;
159
0
    case RawComparisonOp::LT:
160
0
        return lhs < rhs;
161
0
    case RawComparisonOp::LE:
162
0
        return lhs <= rhs;
163
0
    case RawComparisonOp::GT:
164
0
        return lhs > rhs;
165
0
    case RawComparisonOp::GE:
166
0
        return lhs >= rhs;
167
178
    }
168
0
    __builtin_unreachable();
169
178
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesIaEEbRKT_S4_NS_4simd15RawComparisonOpE
Line
Count
Source
153
42
bool raw_scalar_comparison_matches(const T& lhs, const T& rhs, RawComparisonOp op) {
154
42
    switch (op) {
155
36
    case RawComparisonOp::EQ:
156
36
        return lhs == rhs;
157
0
    case RawComparisonOp::NE:
158
0
        return lhs != rhs;
159
0
    case RawComparisonOp::LT:
160
0
        return lhs < rhs;
161
0
    case RawComparisonOp::LE:
162
0
        return lhs <= rhs;
163
6
    case RawComparisonOp::GT:
164
6
        return lhs > rhs;
165
0
    case RawComparisonOp::GE:
166
0
        return lhs >= rhs;
167
42
    }
168
0
    __builtin_unreachable();
169
42
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesIsEEbRKT_S4_NS_4simd15RawComparisonOpE
Line
Count
Source
153
194
bool raw_scalar_comparison_matches(const T& lhs, const T& rhs, RawComparisonOp op) {
154
194
    switch (op) {
155
194
    case RawComparisonOp::EQ:
156
194
        return lhs == rhs;
157
0
    case RawComparisonOp::NE:
158
0
        return lhs != rhs;
159
0
    case RawComparisonOp::LT:
160
0
        return lhs < rhs;
161
0
    case RawComparisonOp::LE:
162
0
        return lhs <= rhs;
163
0
    case RawComparisonOp::GT:
164
0
        return lhs > rhs;
165
0
    case RawComparisonOp::GE:
166
0
        return lhs >= rhs;
167
194
    }
168
0
    __builtin_unreachable();
169
194
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesInEEbRKT_S4_NS_4simd15RawComparisonOpE
Line
Count
Source
153
204
bool raw_scalar_comparison_matches(const T& lhs, const T& rhs, RawComparisonOp op) {
154
204
    switch (op) {
155
204
    case RawComparisonOp::EQ:
156
204
        return lhs == rhs;
157
0
    case RawComparisonOp::NE:
158
0
        return lhs != rhs;
159
0
    case RawComparisonOp::LT:
160
0
        return lhs < rhs;
161
0
    case RawComparisonOp::LE:
162
0
        return lhs <= rhs;
163
0
    case RawComparisonOp::GT:
164
0
        return lhs > rhs;
165
0
    case RawComparisonOp::GE:
166
0
        return lhs >= rhs;
167
204
    }
168
0
    __builtin_unreachable();
169
204
}
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesINS_16VecDateTimeValueEEEbRKT_S5_NS_4simd15RawComparisonOpE
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesINS_11DateV2ValueINS_15DateV2ValueTypeEEEEEbRKT_S7_NS_4simd15RawComparisonOpE
Line
Count
Source
153
151k
bool raw_scalar_comparison_matches(const T& lhs, const T& rhs, RawComparisonOp op) {
154
151k
    switch (op) {
155
48
    case RawComparisonOp::EQ:
156
48
        return lhs == rhs;
157
0
    case RawComparisonOp::NE:
158
0
        return lhs != rhs;
159
58
    case RawComparisonOp::LT:
160
58
        return lhs < rhs;
161
151k
    case RawComparisonOp::LE:
162
151k
        return lhs <= rhs;
163
40
    case RawComparisonOp::GT:
164
40
        return lhs > rhs;
165
90
    case RawComparisonOp::GE:
166
90
        return lhs >= rhs;
167
151k
    }
168
0
    __builtin_unreachable();
169
151k
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesINS_11DateV2ValueINS_19DateTimeV2ValueTypeEEEEEbRKT_S7_NS_4simd15RawComparisonOpE
Line
Count
Source
153
886
bool raw_scalar_comparison_matches(const T& lhs, const T& rhs, RawComparisonOp op) {
154
886
    switch (op) {
155
66
    case RawComparisonOp::EQ:
156
66
        return lhs == rhs;
157
0
    case RawComparisonOp::NE:
158
0
        return lhs != rhs;
159
262
    case RawComparisonOp::LT:
160
262
        return lhs < rhs;
161
124
    case RawComparisonOp::LE:
162
124
        return lhs <= rhs;
163
28
    case RawComparisonOp::GT:
164
28
        return lhs > rhs;
165
406
    case RawComparisonOp::GE:
166
406
        return lhs >= rhs;
167
886
    }
168
0
    __builtin_unreachable();
169
886
}
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesINS_16TimestampTzValueEEEbRKT_S5_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesIdEEbRKT_S4_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesINS_7DecimalIiEEEEbRKT_S6_NS_4simd15RawComparisonOpE
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesINS_7DecimalIlEEEEbRKT_S6_NS_4simd15RawComparisonOpE
Line
Count
Source
153
18.5k
bool raw_scalar_comparison_matches(const T& lhs, const T& rhs, RawComparisonOp op) {
154
18.5k
    switch (op) {
155
44
    case RawComparisonOp::EQ:
156
44
        return lhs == rhs;
157
0
    case RawComparisonOp::NE:
158
0
        return lhs != rhs;
159
0
    case RawComparisonOp::LT:
160
0
        return lhs < rhs;
161
12
    case RawComparisonOp::LE:
162
12
        return lhs <= rhs;
163
18.0k
    case RawComparisonOp::GT:
164
18.0k
        return lhs > rhs;
165
442
    case RawComparisonOp::GE:
166
442
        return lhs >= rhs;
167
18.5k
    }
168
0
    __builtin_unreachable();
169
18.5k
}
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesINS_14DecimalV2ValueEEEbRKT_S5_NS_4simd15RawComparisonOpE
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesINS_12Decimal128V3EEEbRKT_S5_NS_4simd15RawComparisonOpE
Line
Count
Source
153
4
bool raw_scalar_comparison_matches(const T& lhs, const T& rhs, RawComparisonOp op) {
154
4
    switch (op) {
155
0
    case RawComparisonOp::EQ:
156
0
        return lhs == rhs;
157
0
    case RawComparisonOp::NE:
158
0
        return lhs != rhs;
159
0
    case RawComparisonOp::LT:
160
0
        return lhs < rhs;
161
0
    case RawComparisonOp::LE:
162
0
        return lhs <= rhs;
163
4
    case RawComparisonOp::GT:
164
4
        return lhs > rhs;
165
0
    case RawComparisonOp::GE:
166
0
        return lhs >= rhs;
167
4
    }
168
0
    __builtin_unreachable();
169
4
}
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesINS_7DecimalIN4wide7integerILm256EiEEEEEEbRKT_S9_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesIjEEbRKT_S4_NS_4simd15RawComparisonOpE
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129raw_scalar_comparison_matchesIoEEbRKT_S4_NS_4simd15RawComparisonOpE
170
171
template <PrimitiveType PT>
172
void execute_raw_scalar_comparison(const uint8_t* values, size_t num_values, const Field& literal,
173
1.02k
                                   RawComparisonOp op, uint8_t* matches) {
174
1.02k
    using T = typename PrimitiveTypeTraits<PT>::CppType;
175
1.02k
    const T& rhs = literal.get<PT>();
176
172k
    for (size_t row = 0; row < num_values; ++row) {
177
171k
        T lhs;
178
171k
        std::memcpy(&lhs, values + row * sizeof(T), sizeof(T));
179
171k
        matches[row] &= raw_scalar_comparison_matches(lhs, rhs, op) ? 1 : 0;
180
171k
    }
181
1.02k
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE2EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
173
56
                                   RawComparisonOp op, uint8_t* matches) {
174
56
    using T = typename PrimitiveTypeTraits<PT>::CppType;
175
56
    const T& rhs = literal.get<PT>();
176
234
    for (size_t row = 0; row < num_values; ++row) {
177
178
        T lhs;
178
178
        std::memcpy(&lhs, values + row * sizeof(T), sizeof(T));
179
178
        matches[row] &= raw_scalar_comparison_matches(lhs, rhs, op) ? 1 : 0;
180
178
    }
181
56
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE3EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
173
11
                                   RawComparisonOp op, uint8_t* matches) {
174
11
    using T = typename PrimitiveTypeTraits<PT>::CppType;
175
11
    const T& rhs = literal.get<PT>();
176
53
    for (size_t row = 0; row < num_values; ++row) {
177
42
        T lhs;
178
42
        std::memcpy(&lhs, values + row * sizeof(T), sizeof(T));
179
42
        matches[row] &= raw_scalar_comparison_matches(lhs, rhs, op) ? 1 : 0;
180
42
    }
181
11
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE4EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
173
14
                                   RawComparisonOp op, uint8_t* matches) {
174
14
    using T = typename PrimitiveTypeTraits<PT>::CppType;
175
14
    const T& rhs = literal.get<PT>();
176
208
    for (size_t row = 0; row < num_values; ++row) {
177
194
        T lhs;
178
194
        std::memcpy(&lhs, values + row * sizeof(T), sizeof(T));
179
194
        matches[row] &= raw_scalar_comparison_matches(lhs, rhs, op) ? 1 : 0;
180
194
    }
181
14
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE7EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
173
6
                                   RawComparisonOp op, uint8_t* matches) {
174
6
    using T = typename PrimitiveTypeTraits<PT>::CppType;
175
6
    const T& rhs = literal.get<PT>();
176
210
    for (size_t row = 0; row < num_values; ++row) {
177
204
        T lhs;
178
204
        std::memcpy(&lhs, values + row * sizeof(T), sizeof(T));
179
204
        matches[row] &= raw_scalar_comparison_matches(lhs, rhs, op) ? 1 : 0;
180
204
    }
181
6
}
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE11EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE12EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE25EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
173
256
                                   RawComparisonOp op, uint8_t* matches) {
174
256
    using T = typename PrimitiveTypeTraits<PT>::CppType;
175
256
    const T& rhs = literal.get<PT>();
176
152k
    for (size_t row = 0; row < num_values; ++row) {
177
151k
        T lhs;
178
151k
        std::memcpy(&lhs, values + row * sizeof(T), sizeof(T));
179
151k
        matches[row] &= raw_scalar_comparison_matches(lhs, rhs, op) ? 1 : 0;
180
151k
    }
181
256
}
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE26EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
173
520
                                   RawComparisonOp op, uint8_t* matches) {
174
520
    using T = typename PrimitiveTypeTraits<PT>::CppType;
175
520
    const T& rhs = literal.get<PT>();
176
1.40k
    for (size_t row = 0; row < num_values; ++row) {
177
886
        T lhs;
178
886
        std::memcpy(&lhs, values + row * sizeof(T), sizeof(T));
179
886
        matches[row] &= raw_scalar_comparison_matches(lhs, rhs, op) ? 1 : 0;
180
886
    }
181
520
}
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE42EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE27EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE28EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE29EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
173
157
                                   RawComparisonOp op, uint8_t* matches) {
174
157
    using T = typename PrimitiveTypeTraits<PT>::CppType;
175
157
    const T& rhs = literal.get<PT>();
176
18.6k
    for (size_t row = 0; row < num_values; ++row) {
177
18.5k
        T lhs;
178
18.5k
        std::memcpy(&lhs, values + row * sizeof(T), sizeof(T));
179
18.5k
        matches[row] &= raw_scalar_comparison_matches(lhs, rhs, op) ? 1 : 0;
180
18.5k
    }
181
157
}
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE20EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE30EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Line
Count
Source
173
4
                                   RawComparisonOp op, uint8_t* matches) {
174
4
    using T = typename PrimitiveTypeTraits<PT>::CppType;
175
4
    const T& rhs = literal.get<PT>();
176
8
    for (size_t row = 0; row < num_values; ++row) {
177
4
        T lhs;
178
4
        std::memcpy(&lhs, values + row * sizeof(T), sizeof(T));
179
4
        matches[row] &= raw_scalar_comparison_matches(lhs, rhs, op) ? 1 : 0;
180
4
    }
181
4
}
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE35EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE36EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
Unexecuted instantiation: vectorized_fn_call.cpp:_ZN5doris12_GLOBAL__N_129execute_raw_scalar_comparisonILNS_13PrimitiveTypeE37EEEvPKhmRKNS_5FieldENS_4simd15RawComparisonOpEPh
182
183
106k
size_t raw_comparison_value_size(PrimitiveType primitive_type) {
184
106k
    switch (primitive_type) {
185
0
#define RETURN_RAW_COMPARISON_SIZE(TYPE) \
186
105k
    case TYPE:                           \
187
105k
        return sizeof(typename PrimitiveTypeTraits<TYPE>::CppType)
188
339
        RETURN_RAW_COMPARISON_SIZE(TYPE_BOOLEAN);
189
63
        RETURN_RAW_COMPARISON_SIZE(TYPE_TINYINT);
190
69
        RETURN_RAW_COMPARISON_SIZE(TYPE_SMALLINT);
191
99.2k
        RETURN_RAW_COMPARISON_SIZE(TYPE_INT);
192
894
        RETURN_RAW_COMPARISON_SIZE(TYPE_BIGINT);
193
21
        RETURN_RAW_COMPARISON_SIZE(TYPE_LARGEINT);
194
37
        RETURN_RAW_COMPARISON_SIZE(TYPE_FLOAT);
195
291
        RETURN_RAW_COMPARISON_SIZE(TYPE_DOUBLE);
196
3
        RETURN_RAW_COMPARISON_SIZE(TYPE_DATE);
197
3
        RETURN_RAW_COMPARISON_SIZE(TYPE_DATETIME);
198
1.14k
        RETURN_RAW_COMPARISON_SIZE(TYPE_DATEV2);
199
2.89k
        RETURN_RAW_COMPARISON_SIZE(TYPE_DATETIMEV2);
200
3
        RETURN_RAW_COMPARISON_SIZE(TYPE_TIMESTAMPTZ);
201
3
        RETURN_RAW_COMPARISON_SIZE(TYPE_TIMEV2);
202
3
        RETURN_RAW_COMPARISON_SIZE(TYPE_DECIMAL32);
203
723
        RETURN_RAW_COMPARISON_SIZE(TYPE_DECIMAL64);
204
3
        RETURN_RAW_COMPARISON_SIZE(TYPE_DECIMALV2);
205
83
        RETURN_RAW_COMPARISON_SIZE(TYPE_DECIMAL128I);
206
3
        RETURN_RAW_COMPARISON_SIZE(TYPE_DECIMAL256);
207
3
        RETURN_RAW_COMPARISON_SIZE(TYPE_IPV4);
208
3
        RETURN_RAW_COMPARISON_SIZE(TYPE_IPV6);
209
0
#undef RETURN_RAW_COMPARISON_SIZE
210
842
    default:
211
842
        return 0;
212
106k
    }
213
106k
}
214
215
} // namespace
216
217
788k
VectorizedFnCall::VectorizedFnCall(const TExprNode& node) : VExpr(node) {
218
788k
    _function_name = _fn.name.function_name;
219
788k
}
220
221
Status VectorizedFnCall::prepare(RuntimeState* state, const RowDescriptor& desc,
222
851k
                                 VExprContext* context) {
223
851k
    RETURN_IF_ERROR_OR_PREPARED(VExpr::prepare(state, desc, context));
224
851k
    ColumnsWithTypeAndName argument_template;
225
851k
    argument_template.reserve(_children.size());
226
1.66M
    for (auto child : _children) {
227
1.66M
        if (child->is_literal()) {
228
            // For some functions, he needs some literal columns to derive the return type.
229
798k
            auto literal_node = std::dynamic_pointer_cast<VLiteral>(child);
230
798k
            argument_template.emplace_back(literal_node->get_column_ptr(), child->data_type(),
231
798k
                                           child->expr_name());
232
871k
        } else {
233
871k
            argument_template.emplace_back(nullptr, child->data_type(), child->expr_name());
234
871k
        }
235
1.66M
    }
236
237
851k
    _expr_name = fmt::format("VectorizedFnCall[{}](arguments={},return={})", _fn.name.function_name,
238
851k
                             get_child_names(), _data_type->get_name());
239
851k
    if (_fn.binary_type == TFunctionBinaryType::RPC) {
240
0
        _function = FunctionRPC::create(_fn, argument_template, _data_type);
241
851k
    } else if (_fn.binary_type == TFunctionBinaryType::JAVA_UDF) {
242
528
        if (config::enable_java_support) {
243
527
            if (_fn.is_udtf_function) {
244
                // fake function. it's no use and can't execute.
245
56
                auto builder =
246
56
                        std::make_shared<DefaultFunctionBuilder>(FunctionFake<UDTFImpl>::create());
247
56
                _function = builder->build(argument_template, std::make_shared<DataTypeUInt8>());
248
471
            } else {
249
471
                _function = JavaFunctionCall::create(_fn, argument_template, _data_type);
250
471
            }
251
527
        } else {
252
1
            return Status::InternalError(
253
1
                    "Java UDF is not enabled, you can change be config enable_java_support to true "
254
1
                    "and restart be.");
255
1
        }
256
850k
    } else if (_fn.binary_type == TFunctionBinaryType::PYTHON_UDF) {
257
670
        if (config::enable_python_udf_support) {
258
669
            if (_fn.is_udtf_function) {
259
                // fake function. it's no use and can't execute.
260
                // Python UDTF is executed via PythonUDTFFunction in table function path
261
285
                auto builder =
262
285
                        std::make_shared<DefaultFunctionBuilder>(FunctionFake<UDTFImpl>::create());
263
285
                _function = builder->build(argument_template, std::make_shared<DataTypeUInt8>());
264
384
            } else {
265
384
                _function = PythonFunctionCall::create(_fn, argument_template, _data_type);
266
384
                LOG(INFO) << fmt::format(
267
384
                        "create python function call: {}, runtime version: {}, function code: {}",
268
384
                        _fn.name.function_name, _fn.runtime_version, _fn.function_code);
269
384
            }
270
669
        } else {
271
1
            return Status::InternalError(
272
1
                    "Python UDF is not enabled, you can change be config enable_python_udf_support "
273
1
                    "to true and restart be.");
274
1
        }
275
850k
    } else if (_fn.binary_type == TFunctionBinaryType::AGG_STATE) {
276
753
        DataTypes argument_types;
277
1.10k
        for (auto column : argument_template) {
278
1.10k
            argument_types.emplace_back(column.type);
279
1.10k
        }
280
281
753
        if (match_suffix(_fn.name.function_name, AGG_STATE_SUFFIX)) {
282
752
            if (_data_type->is_nullable()) {
283
0
                return Status::InternalError("State function's return type must be not nullable");
284
0
            }
285
752
            if (_data_type->get_primitive_type() != PrimitiveType::TYPE_AGG_STATE) {
286
0
                return Status::InternalError(
287
0
                        "State function's return type must be agg_state but get {}",
288
0
                        _data_type->get_family_name());
289
0
            }
290
752
            _function = FunctionAggState::create(
291
752
                    argument_types, _data_type,
292
752
                    assert_cast<const DataTypeAggState*>(_data_type.get())->get_nested_function());
293
752
        } else {
294
1
            return Status::InternalError("Function {} is not endwith '_state'", _fn.signature);
295
1
        }
296
849k
    } else {
297
        // get the function. won't prepare function.
298
849k
        _function = SimpleFunctionFactory::instance().get_function(
299
849k
                _fn.name.function_name, argument_template, _data_type,
300
849k
                {.new_version_unix_timestamp = state->query_options().new_version_unix_timestamp,
301
849k
                 .new_version_bitmap_op_count =
302
849k
                         state->query_options().__isset.new_version_bitmap_op_count &&
303
849k
                         state->query_options().new_version_bitmap_op_count},
304
849k
                state->be_exec_version());
305
849k
    }
306
851k
    if (_function == nullptr) {
307
2
        return Status::InternalError("Could not find function {}, arg {} return {} ",
308
2
                                     _fn.name.function_name, get_child_type_names(),
309
2
                                     _data_type->get_name());
310
2
    }
311
851k
    VExpr::register_function_context(state, context);
312
851k
    _function_name = _fn.name.function_name;
313
851k
    _prepare_finished = true;
314
315
851k
    FunctionContext* fn_ctx = context->fn_context(_fn_context_index);
316
851k
    if (fn().__isset.dict_function) {
317
95
        fn_ctx->set_dict_function(fn().dict_function);
318
95
    }
319
851k
    return Status::OK();
320
851k
}
321
322
Status VectorizedFnCall::open(RuntimeState* state, VExprContext* context,
323
2.19M
                              FunctionContext::FunctionStateScope scope) {
324
2.19M
    DCHECK(_prepare_finished);
325
4.17M
    for (auto& i : _children) {
326
4.17M
        RETURN_IF_ERROR(i->open(state, context, scope));
327
4.17M
    }
328
2.19M
    RETURN_IF_ERROR(VExpr::init_function_context(state, context, scope, _function));
329
2.19M
    if (scope == FunctionContext::FRAGMENT_LOCAL) {
330
851k
        RETURN_IF_ERROR(VExpr::get_const_col(context, nullptr));
331
851k
    }
332
2.18M
    _open_finished = true;
333
2.18M
    return Status::OK();
334
2.19M
}
335
336
2.19M
void VectorizedFnCall::close(VExprContext* context, FunctionContext::FunctionStateScope scope) {
337
2.19M
    VExpr::close_function_context(context, scope, _function);
338
2.19M
    VExpr::close(context, scope);
339
2.19M
}
340
341
12.7k
Status VectorizedFnCall::evaluate_inverted_index(VExprContext* context, uint32_t segment_num_rows) {
342
12.7k
    if (get_num_children() < 1) {
343
        // score() and similar 0-children virtual column functions don't need
344
        // inverted index evaluation; return OK to skip gracefully.
345
89
        return Status::OK();
346
89
    }
347
12.6k
    return _evaluate_inverted_index(context, _function, segment_num_rows);
348
12.7k
}
349
350
37.9k
ZoneMapFilterResult VectorizedFnCall::evaluate_zonemap_filter(const ZoneMapEvalContext& ctx) const {
351
37.9k
    return _function->evaluate_zonemap_filter(ctx, _children);
352
37.9k
}
353
354
130k
bool VectorizedFnCall::can_evaluate_zonemap_filter() const {
355
130k
    return _function != nullptr && !_function->is_blockable() &&
356
130k
           _function->can_evaluate_zonemap_filter(_children);
357
130k
}
358
359
ZoneMapFilterResult VectorizedFnCall::evaluate_dictionary_filter(
360
650
        const DictionaryEvalContext& ctx) const {
361
650
    return _function->evaluate_dictionary_filter(ctx, _children);
362
650
}
363
364
18.8k
bool VectorizedFnCall::can_evaluate_dictionary_filter() const {
365
18.8k
    return _function != nullptr && !_function->is_blockable() &&
366
18.8k
           _function->can_evaluate_dictionary_filter(_children);
367
18.8k
}
368
369
ZoneMapFilterResult VectorizedFnCall::evaluate_bloom_filter(
370
10
        const BloomFilterEvalContext& ctx) const {
371
10
    return _function->evaluate_bloom_filter(ctx, _children);
372
10
}
373
374
9.74k
bool VectorizedFnCall::can_evaluate_bloom_filter() const {
375
9.74k
    return _function != nullptr && !_function->is_blockable() &&
376
9.74k
           _function->can_evaluate_bloom_filter(_children);
377
9.74k
}
378
379
Status VectorizedFnCall::_do_execute(VExprContext* context, const Block* block,
380
                                     const Selector* selector, size_t count,
381
629k
                                     ColumnPtr& result_column, ColumnPtr* arg_column) const {
382
629k
    if (is_const_and_have_executed()) { // const have executed in open function
383
28.0k
        result_column = get_result_from_const(count);
384
28.0k
        return Status::OK();
385
28.0k
    }
386
601k
    if (fast_execute(context, selector, count, result_column)) {
387
665
        return Status::OK();
388
665
    }
389
600k
    DBUG_EXECUTE_IF("VectorizedFnCall.must_in_slow_path", {
390
600k
        if (get_child(0)->is_slot_ref()) {
391
600k
            auto debug_col_name = DebugPoints::instance()->get_debug_param_or_default<std::string>(
392
600k
                    "VectorizedFnCall.must_in_slow_path", "column_name", "");
393
394
600k
            std::vector<std::string> column_names;
395
600k
            boost::split(column_names, debug_col_name, boost::algorithm::is_any_of(","));
396
397
600k
            auto* column_slot_ref = assert_cast<VSlotRef*>(get_child(0).get());
398
600k
            std::string column_name = column_slot_ref->expr_name();
399
600k
            auto it = std::find(column_names.begin(), column_names.end(), column_name);
400
600k
            if (it == column_names.end()) {
401
600k
                return Status::Error<ErrorCode::INTERNAL_ERROR>(
402
600k
                        "column {} should in slow path while VectorizedFnCall::execute.",
403
600k
                        column_name);
404
600k
            }
405
600k
        }
406
600k
    })
407
600k
    DCHECK(_open_finished || block == nullptr) << debug_string();
408
409
600k
    Block temp_block;
410
600k
    ColumnNumbers args(_children.size());
411
412
1.73M
    for (int i = 0; i < _children.size(); ++i) {
413
1.13M
        ColumnPtr tmp_arg_column;
414
1.13M
        RETURN_IF_ERROR(
415
1.13M
                _children[i]->execute_column(context, block, selector, count, tmp_arg_column));
416
1.13M
        auto arg_type = _children[i]->execute_type(block);
417
1.13M
        temp_block.insert({tmp_arg_column, arg_type, _children[i]->expr_name()});
418
1.13M
        args[i] = i;
419
420
1.13M
        if (arg_column != nullptr && i == 0) {
421
19.4k
            *arg_column = tmp_arg_column;
422
19.4k
        }
423
1.13M
    }
424
425
600k
    uint32_t num_columns_without_result = temp_block.columns();
426
    // prepare a column to save result
427
600k
    temp_block.insert({nullptr, _data_type, _expr_name});
428
429
600k
    DBUG_EXECUTE_IF("VectorizedFnCall.wait_before_execute", {
430
600k
        auto possibility = DebugPoints::instance()->get_debug_param_or_default<double>(
431
600k
                "VectorizedFnCall.wait_before_execute", "possibility", 0);
432
600k
        if (random_bool_slow(possibility)) {
433
600k
            LOG(WARNING) << "VectorizedFnCall::execute sleep 30s";
434
600k
            sleep(30);
435
600k
        }
436
600k
    });
437
438
600k
    RETURN_IF_ERROR(_function->execute(context->fn_context(_fn_context_index), temp_block, args,
439
600k
                                       num_columns_without_result, count));
440
600k
    result_column = temp_block.get_by_position(num_columns_without_result).column;
441
600k
    DCHECK_EQ(result_column->size(), count);
442
600k
    RETURN_IF_ERROR(result_column->column_self_check());
443
600k
    return Status::OK();
444
600k
}
445
446
0
size_t VectorizedFnCall::estimate_memory(const size_t rows) {
447
0
    if (is_const_and_have_executed()) { // const have execute in open function
448
0
        return 0;
449
0
    }
450
451
0
    size_t estimate_size = 0;
452
0
    for (auto& child : _children) {
453
0
        estimate_size += child->estimate_memory(rows);
454
0
    }
455
456
0
    if (_data_type->have_maximum_size_of_value()) {
457
0
        estimate_size += rows * _data_type->get_size_of_value_in_memory();
458
0
    } else {
459
0
        estimate_size += rows * 512; /// FIXME: estimated value...
460
0
    }
461
0
    return estimate_size;
462
0
}
463
464
Status VectorizedFnCall::execute_runtime_filter(VExprContext* context, const Block* block,
465
                                                const uint8_t* __restrict filter, size_t count,
466
                                                ColumnPtr& result_column,
467
19.4k
                                                ColumnPtr* arg_column) const {
468
19.4k
    return _do_execute(context, block, nullptr, count, result_column, arg_column);
469
19.4k
}
470
471
Status VectorizedFnCall::execute_column_impl(VExprContext* context, const Block* block,
472
                                             const Selector* selector, size_t count,
473
610k
                                             ColumnPtr& result_column) const {
474
610k
    return _do_execute(context, block, selector, count, result_column, nullptr);
475
610k
}
476
477
bool VectorizedFnCall::can_execute_on_raw_fixed_values(const DataTypePtr& data_type,
478
90.6k
                                                       int column_id) const {
479
90.6k
    if (data_type == nullptr || !raw_comparison_op(_function_name, false).has_value()) {
480
18
        return false;
481
18
    }
482
90.6k
    auto slot_literal = expr_zonemap::extract_slot_and_literal(_children);
483
90.6k
    if (!slot_literal.has_value() || slot_literal->slot_index != column_id ||
484
90.6k
        slot_literal->literal.is_null()) {
485
2
        return false;
486
2
    }
487
90.6k
    const auto raw_type = remove_nullable(data_type);
488
90.6k
    if (!remove_nullable(slot_literal->slot_type)->equals(*raw_type) ||
489
90.6k
        !remove_nullable(slot_literal->literal_type)->equals(*raw_type)) {
490
2
        return false;
491
2
    }
492
90.6k
    return raw_comparison_value_size(raw_type->get_primitive_type()) != 0;
493
90.6k
}
494
495
Status VectorizedFnCall::execute_on_raw_fixed_values(const uint8_t* values, size_t num_values,
496
                                                     size_t value_width,
497
                                                     const DataTypePtr& data_type, int column_id,
498
16.0k
                                                     uint8_t* matches) const {
499
16.0k
    if (!can_execute_on_raw_fixed_values(data_type, column_id)) {
500
0
        return Status::NotSupported("Expression {} cannot evaluate raw fixed-width values",
501
0
                                    expr_name());
502
0
    }
503
16.0k
    DORIS_CHECK(values != nullptr || num_values == 0);
504
16.0k
    DORIS_CHECK(matches != nullptr || num_values == 0);
505
16.0k
    const auto slot_literal = expr_zonemap::extract_slot_and_literal(_children);
506
16.0k
    DORIS_CHECK(slot_literal.has_value());
507
16.0k
    const auto op = raw_comparison_op(_function_name, slot_literal->literal_on_left);
508
16.0k
    DORIS_CHECK(op.has_value());
509
16.0k
    const auto primitive_type = remove_nullable(data_type)->get_primitive_type();
510
16.0k
    const size_t expected_width = raw_comparison_value_size(primitive_type);
511
16.0k
    if (value_width != expected_width) {
512
0
        return Status::Corruption("Raw expression width {} does not match expected {}", value_width,
513
0
                                  expected_width);
514
0
    }
515
16.0k
    switch (primitive_type) {
516
14.7k
    case TYPE_INT:
517
14.7k
        execute_raw_comparison<int32_t, TYPE_INT>(values, num_values, slot_literal->literal, *op,
518
14.7k
                                                  matches);
519
14.7k
        break;
520
179
    case TYPE_BIGINT:
521
179
        execute_raw_comparison<int64_t, TYPE_BIGINT>(values, num_values, slot_literal->literal, *op,
522
179
                                                     matches);
523
179
        break;
524
8
    case TYPE_FLOAT:
525
8
        execute_raw_comparison<float, TYPE_FLOAT>(values, num_values, slot_literal->literal, *op,
526
8
                                                  matches);
527
8
        break;
528
84
    case TYPE_DOUBLE:
529
84
        execute_raw_comparison<double, TYPE_DOUBLE>(values, num_values, slot_literal->literal, *op,
530
84
                                                    matches);
531
84
        break;
532
0
#define EXECUTE_RAW_SCALAR_COMPARISON(TYPE)                                                 \
533
1.02k
    case TYPE:                                                                              \
534
1.02k
        execute_raw_scalar_comparison<TYPE>(values, num_values, slot_literal->literal, *op, \
535
1.02k
                                            matches);                                       \
536
1.02k
        break
537
56
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_BOOLEAN);
538
11
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_TINYINT);
539
14
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_SMALLINT);
540
6
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_LARGEINT);
541
0
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_DATE);
542
0
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_DATETIME);
543
256
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_DATEV2);
544
520
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_DATETIMEV2);
545
0
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_TIMESTAMPTZ);
546
0
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_TIMEV2);
547
0
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_DECIMAL32);
548
157
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_DECIMAL64);
549
0
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_DECIMALV2);
550
4
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_DECIMAL128I);
551
0
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_DECIMAL256);
552
0
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_IPV4);
553
0
        EXECUTE_RAW_SCALAR_COMPARISON(TYPE_IPV6);
554
0
#undef EXECUTE_RAW_SCALAR_COMPARISON
555
0
    default:
556
0
        __builtin_unreachable();
557
16.0k
    }
558
16.0k
    return Status::OK();
559
16.0k
}
560
561
bool VectorizedFnCall::can_execute_on_raw_binary_values(const DataTypePtr& data_type,
562
10.4k
                                                        int column_id) const {
563
10.4k
    if (data_type == nullptr || !is_string_type(remove_nullable(data_type)->get_primitive_type()) ||
564
10.4k
        !raw_comparison_op(_function_name, false).has_value()) {
565
8.28k
        return false;
566
8.28k
    }
567
2.13k
    const auto slot_literal = expr_zonemap::extract_slot_and_literal(_children);
568
2.13k
    return slot_literal.has_value() && slot_literal->slot_index == column_id &&
569
2.13k
           !slot_literal->literal.is_null() &&
570
2.13k
           is_string_type(remove_nullable(slot_literal->slot_type)->get_primitive_type()) &&
571
2.13k
           is_string_type(remove_nullable(slot_literal->literal_type)->get_primitive_type());
572
10.4k
}
573
574
Status VectorizedFnCall::execute_on_raw_binary_values(const StringRef* values, size_t num_values,
575
                                                      const DataTypePtr& data_type, int column_id,
576
389
                                                      uint8_t* matches) const {
577
389
    if (!can_execute_on_raw_binary_values(data_type, column_id)) {
578
0
        return Status::NotSupported("Expression {} cannot evaluate raw binary values", expr_name());
579
0
    }
580
389
    DORIS_CHECK(values != nullptr || num_values == 0);
581
389
    DORIS_CHECK(matches != nullptr || num_values == 0);
582
389
    const auto slot_literal = expr_zonemap::extract_slot_and_literal(_children);
583
389
    DORIS_CHECK(slot_literal.has_value());
584
389
    const auto op = raw_comparison_op(_function_name, slot_literal->literal_on_left);
585
389
    DORIS_CHECK(op.has_value());
586
389
    const auto& literal = slot_literal->literal.get<TYPE_STRING>();
587
389
    const StringRef literal_ref(literal.data(), literal.size());
588
2.79k
    for (size_t row = 0; row < num_values; ++row) {
589
2.41k
        matches[row] &=
590
2.41k
                raw_string_comparison_matches(values[row].compare(literal_ref), *op) ? 1 : 0;
591
2.41k
    }
592
389
    return Status::OK();
593
389
}
594
595
13.6k
bool VectorizedFnCall::can_execute_on_null_map(const DataTypePtr& data_type, int column_id) const {
596
13.6k
    if (data_type == nullptr) {
597
0
        return false;
598
0
    }
599
13.6k
    if (_children.size() == 1 &&
600
13.6k
        (_function_name == "is_null_pred" || _function_name == "is_not_null_pred")) {
601
1.59k
        const auto slot = std::dynamic_pointer_cast<VSlotRef>(_children[0]);
602
1.59k
        return slot != nullptr && slot->column_id() == column_id;
603
1.59k
    }
604
12.0k
    if (_function_name == "eq_for_null") {
605
0
        const auto slot_literal = expr_zonemap::extract_slot_and_literal(_children);
606
0
        return slot_literal.has_value() && slot_literal->slot_index == column_id &&
607
0
               slot_literal->literal.is_null();
608
0
    }
609
12.0k
    return false;
610
12.0k
}
611
612
Status VectorizedFnCall::execute_on_null_map(const uint8_t* null_map, size_t num_values,
613
                                             const DataTypePtr& data_type, int column_id,
614
5
                                             uint8_t* matches) const {
615
5
    if (!can_execute_on_null_map(data_type, column_id)) {
616
0
        return Status::NotSupported("Expression {} cannot evaluate a NULL map", expr_name());
617
0
    }
618
5
    DORIS_CHECK(null_map != nullptr || num_values == 0);
619
5
    DORIS_CHECK(matches != nullptr || num_values == 0);
620
5
    const bool keep_nulls = _function_name == "is_null_pred" || _function_name == "eq_for_null";
621
28
    for (size_t row = 0; row < num_values; ++row) {
622
23
        matches[row] &= (null_map[row] != 0) == keep_nulls ? 1 : 0;
623
23
    }
624
5
    return Status::OK();
625
5
}
626
627
390k
const std::string& VectorizedFnCall::expr_name() const {
628
390k
    return _expr_name;
629
390k
}
630
631
202
std::string VectorizedFnCall::function_name() const {
632
202
    return _function_name;
633
202
}
634
635
676
std::string VectorizedFnCall::debug_string() const {
636
676
    std::stringstream out;
637
676
    out << "VectorizedFn[";
638
676
    out << _expr_name;
639
676
    out << "]{";
640
676
    bool first = true;
641
1.34k
    for (const auto& input_expr : children()) {
642
1.34k
        if (first) {
643
674
            first = false;
644
674
        } else {
645
673
            out << ",";
646
673
        }
647
1.34k
        out << "\n" << input_expr->debug_string();
648
1.34k
    }
649
676
    out << "}";
650
676
    return out.str();
651
676
}
652
653
0
std::string VectorizedFnCall::debug_string(const std::vector<VectorizedFnCall*>& agg_fns) {
654
0
    std::stringstream out;
655
0
    out << "[";
656
0
    for (int i = 0; i < agg_fns.size(); ++i) {
657
0
        out << (i == 0 ? "" : " ") << agg_fns[i]->debug_string();
658
0
    }
659
0
    out << "]";
660
0
    return out.str();
661
0
}
662
663
2.56k
bool VectorizedFnCall::can_push_down_to_index() const {
664
2.56k
    return _function->can_push_down_to_index();
665
2.56k
}
666
667
233k
bool VectorizedFnCall::is_deterministic() const {
668
233k
    static const std::set<std::string> NON_DETERMINISTIC_FUNCTIONS = {
669
233k
            "random", "rand", "random_bytes", "uuid", "uuid_numeric"};
670
233k
    return !NON_DETERMINISTIC_FUNCTIONS.contains(_function_name) && VExpr::is_deterministic();
671
233k
}
672
673
143k
bool VectorizedFnCall::is_safe_to_execute_on_selected_rows() const {
674
143k
    static const std::set<std::string> TOTAL_PREDICATE_FUNCTIONS = {
675
143k
            "eq", "ne", "lt", "le", "gt", "ge", "in", "not_in", "is_null_pred", "is_not_null_pred"};
676
    // Selected-row execution may hide data-dependent errors in rows rejected by an earlier
677
    // predicate. Keep function calls unsafe by default and opt in only operations that are total
678
    // for their input domain; child checks then reject expressions such as gt(mod(x, -1), 0).
679
143k
    return TOTAL_PREDICATE_FUNCTIONS.contains(_function_name) &&
680
143k
           VExpr::is_safe_to_execute_on_selected_rows();
681
143k
}
682
683
0
bool VectorizedFnCall::equals(const VExpr& other) {
684
0
    const auto* other_ptr = dynamic_cast<const VectorizedFnCall*>(&other);
685
0
    if (!other_ptr) {
686
0
        return false;
687
0
    }
688
0
    if (this->_function_name != other_ptr->_function_name) {
689
0
        return false;
690
0
    }
691
0
    if (get_num_children() != other_ptr->get_num_children()) {
692
0
        return false;
693
0
    }
694
0
    for (uint16_t i = 0; i < get_num_children(); i++) {
695
0
        if (!this->get_child(i)->equals(*other_ptr->get_child(i))) {
696
0
            return false;
697
0
        }
698
0
    }
699
0
    return true;
700
0
}
701
702
/*
703
 * For ANN range search we expect a comparison expression (LE/LT/GE/GT) whose left side is either:
704
 *   1) a vector distance function call, or
705
 *   2) a cast/virtual slot that unwraps to the function call when the planner promotes float to
706
 *      double literals.
707
 *
708
 * Visually the logical tree looks like:
709
 *
710
 *   FunctionCall(LE/LT/GE/GT)
711
 *   |----------------
712
 *   |               |
713
 *   |               |
714
 *   VirtualSlotRef* Float32Literal/Float64Literal
715
 *   |
716
 *   |
717
 *   Cast(Float -> Double)*
718
 *   |
719
 *   FunctionCall(distance)
720
 *   |----------------
721
 *   |               |
722
 *   |               |
723
 *   SlotRef         ArrayLiteral/Cast(String as Array<FLOAT>)
724
 *
725
 * Items marked with * are optional and depend on literal types/virtual column usage. The helper
726
 * below normalizes the shape and validates distance function, slot, and constant vector inputs.
727
 */
728
729
void VectorizedFnCall::prepare_ann_range_search(
730
        const doris::VectorSearchUserParams& user_params,
731
14.1k
        segment_v2::AnnRangeSearchRuntime& range_search_runtime, bool& suitable_for_ann_index) {
732
14.1k
    if (!suitable_for_ann_index) {
733
0
        return;
734
0
    }
735
736
14.1k
    if (OPS_FOR_ANN_RANGE_SEARCH.find(this->op()) == OPS_FOR_ANN_RANGE_SEARCH.end()) {
737
11.4k
        suitable_for_ann_index = false;
738
11.4k
        return;
739
11.4k
    }
740
741
2.71k
    auto mark_unsuitable = [&](const std::string& reason) {
742
2.66k
        suitable_for_ann_index = false;
743
18.4E
        VLOG_DEBUG << "ANN range search skipped: " << reason;
744
2.66k
    };
745
746
2.71k
    range_search_runtime.is_le_or_lt =
747
2.71k
            (this->op() == TExprOpcode::LE || this->op() == TExprOpcode::LT);
748
749
2.71k
    DCHECK(_children.size() == 2);
750
751
2.71k
    auto left_child = get_child(0);
752
2.71k
    auto right_child = get_child(1);
753
754
    // ========== Step 1: Check left child - must be a distance function ==========
755
2.71k
    auto get_virtual_expr = [&](const VExprSPtr& expr,
756
3.57k
                                std::shared_ptr<VirtualSlotRef>& slot_ref) -> VExprSPtr {
757
3.57k
        auto virtual_ref = std::dynamic_pointer_cast<VirtualSlotRef>(expr);
758
3.57k
        if (virtual_ref != nullptr) {
759
118
            DCHECK(virtual_ref->get_virtual_column_expr() != nullptr);
760
118
            slot_ref = virtual_ref;
761
118
            return virtual_ref->get_virtual_column_expr();
762
118
        }
763
3.45k
        return expr;
764
3.57k
    };
765
766
2.71k
    std::shared_ptr<VirtualSlotRef> vir_slot_ref;
767
2.71k
    auto normalized_left = get_virtual_expr(left_child, vir_slot_ref);
768
769
    // Try to find the distance function call, it may be wrapped in a Cast(Float->Double)
770
2.71k
    std::shared_ptr<VectorizedFnCall> function_call =
771
2.71k
            std::dynamic_pointer_cast<VectorizedFnCall>(normalized_left);
772
2.71k
    bool has_float_to_double_cast = false;
773
774
2.71k
    if (function_call == nullptr) {
775
        // Check if it's a Cast expression wrapping a function call
776
1.18k
        auto cast_expr = std::dynamic_pointer_cast<VCastExpr>(normalized_left);
777
1.18k
        if (cast_expr == nullptr) {
778
313
            mark_unsuitable("Left child is neither a function call nor a cast expression.");
779
313
            return;
780
313
        }
781
875
        has_float_to_double_cast = true;
782
875
        auto normalized_cast_child = get_virtual_expr(cast_expr->get_child(0), vir_slot_ref);
783
875
        function_call = std::dynamic_pointer_cast<VectorizedFnCall>(normalized_cast_child);
784
875
        if (function_call == nullptr) {
785
833
            mark_unsuitable("Left child of cast is not a function call.");
786
833
            return;
787
833
        }
788
875
    }
789
790
    // Check if it's a supported distance function
791
1.56k
    if (DISTANCE_FUNCS.find(function_call->_function_name) == DISTANCE_FUNCS.end()) {
792
1.51k
        mark_unsuitable(fmt::format("Left child is not a supported distance function: {}",
793
1.51k
                                    function_call->_function_name));
794
1.51k
        return;
795
1.51k
    }
796
797
    // Strip the _approximate suffix to get metric type
798
50
    std::string metric_name = function_call->_function_name;
799
50
    metric_name = metric_name.substr(0, metric_name.size() - 12);
800
50
    range_search_runtime.metric_type = segment_v2::string_to_metric(metric_name);
801
802
    // ========== Step 2: Validate distance function arguments ==========
803
    // Identify the slot ref child and the constant query array child (ArrayLiteral or CAST to array)
804
50
    Int32 idx_of_slot_ref = -1;
805
50
    Int32 idx_of_array_expr = -1;
806
94
    auto classify_child = [&](const VExprSPtr& child, UInt16 index) {
807
94
        if (idx_of_slot_ref == -1 && std::dynamic_pointer_cast<VSlotRef>(child) != nullptr) {
808
47
            idx_of_slot_ref = index;
809
47
            return;
810
47
        }
811
47
        if (idx_of_array_expr == -1 &&
812
47
            (std::dynamic_pointer_cast<VArrayLiteral>(child) != nullptr ||
813
47
             std::dynamic_pointer_cast<VCastExpr>(child) != nullptr)) {
814
40
            idx_of_array_expr = index;
815
40
        }
816
47
    };
817
818
144
    for (UInt16 i = 0; i < function_call->get_num_children(); ++i) {
819
94
        classify_child(function_call->get_child(i), i);
820
94
    }
821
822
50
    if (idx_of_slot_ref == -1 || idx_of_array_expr == -1) {
823
7
        mark_unsuitable("slot ref or array literal/cast is missing.");
824
7
        return;
825
7
    }
826
827
43
    auto slot_ref = std::dynamic_pointer_cast<VSlotRef>(
828
43
            function_call->get_child(static_cast<UInt16>(idx_of_slot_ref)));
829
43
    range_search_runtime.src_col_idx = slot_ref->column_id();
830
43
    range_search_runtime.dst_col_idx = vir_slot_ref == nullptr ? -1 : vir_slot_ref->column_id();
831
832
    // Materialize the constant array expression and validate its shape and types
833
43
    auto array_expr = function_call->get_child(static_cast<UInt16>(idx_of_array_expr));
834
43
    auto extract_result = extract_query_vector(array_expr);
835
43
    if (!extract_result.has_value()) {
836
0
        mark_unsuitable("Failed to extract query vector from constant array expression.");
837
0
        return;
838
0
    }
839
43
    range_search_runtime.query_value = extract_result.value();
840
43
    range_search_runtime.dim = range_search_runtime.query_value->size();
841
842
    // ========== Step 3: Check right child - must be a float/double literal ==========
843
43
    auto right_literal = std::dynamic_pointer_cast<VLiteral>(right_child);
844
43
    if (right_literal == nullptr) {
845
1
        mark_unsuitable("Right child is not a literal.");
846
1
        return;
847
1
    }
848
849
    // Handle nullable literal gracefully - just mark as unsuitable instead of crash
850
42
    if (right_literal->is_nullable()) {
851
0
        mark_unsuitable("Right literal is nullable, not supported for ANN range search.");
852
0
        return;
853
0
    }
854
855
42
    auto right_type = right_literal->get_data_type();
856
42
    PrimitiveType right_primitive = right_type->get_primitive_type();
857
42
    const bool float32_literal = right_primitive == PrimitiveType::TYPE_FLOAT;
858
42
    const bool float64_literal = right_primitive == PrimitiveType::TYPE_DOUBLE;
859
860
42
    if (!float32_literal && !float64_literal) {
861
0
        mark_unsuitable("Right child is not a Float32Literal or Float64Literal.");
862
0
        return;
863
0
    }
864
865
    // Validate consistency: if we have Cast(Float->Double), right must be double literal
866
42
    if (has_float_to_double_cast && !float64_literal) {
867
0
        mark_unsuitable("Cast expression expects double literal on right side.");
868
0
        return;
869
0
    }
870
871
    // Extract radius value
872
42
    auto right_col = right_literal->get_column_ptr()->convert_to_full_column_if_const();
873
42
    if (float32_literal) {
874
7
        const ColumnFloat32* cf32_right = assert_cast<const ColumnFloat32*>(right_col.get());
875
7
        range_search_runtime.radius = cf32_right->get_data()[0];
876
35
    } else {
877
35
        const ColumnFloat64* cf64_right = assert_cast<const ColumnFloat64*>(right_col.get());
878
35
        range_search_runtime.radius = static_cast<float>(cf64_right->get_data()[0]);
879
35
    }
880
881
    // ========== Done: Mark as suitable for ANN range search ==========
882
42
    range_search_runtime.is_ann_range_search = true;
883
42
    range_search_runtime.user_params = user_params;
884
42
    VLOG_DEBUG << fmt::format("Ann range search params: {}", range_search_runtime.to_string());
885
42
    return;
886
42
}
887
888
Status VectorizedFnCall::evaluate_ann_range_search(
889
        const segment_v2::AnnRangeSearchRuntime& range_search_runtime,
890
        const std::vector<std::unique_ptr<segment_v2::IndexIterator>>& cid_to_index_iterators,
891
        const std::vector<ColumnId>& idx_to_cid,
892
        const std::vector<std::unique_ptr<segment_v2::ColumnIterator>>& column_iterators,
893
        size_t rows_of_segment, roaring::Roaring& row_bitmap,
894
        segment_v2::AnnIndexStats& ann_index_stats, bool enable_result_cache,
895
11.7k
        AnnRangeSearchEvaluationResult& evaluation_result) {
896
11.7k
    evaluation_result = {};
897
11.7k
    if (range_search_runtime.is_ann_range_search == false) {
898
11.6k
        return Status::OK();
899
11.6k
    }
900
901
18.4E
    VLOG_DEBUG << fmt::format("Try apply ann range search. Local search params: {}",
902
18.4E
                              range_search_runtime.to_string());
903
33
    size_t origin_num = row_bitmap.cardinality();
904
905
33
    const auto idx_in_block = range_search_runtime.src_col_idx;
906
33
    DCHECK_LT(idx_in_block, idx_to_cid.size())
907
0
            << "idx_in_block: " << idx_in_block << ", idx_to_cid.size(): " << idx_to_cid.size();
908
909
33
    ColumnId src_col_cid = idx_to_cid[idx_in_block];
910
33
    DCHECK(src_col_cid < cid_to_index_iterators.size());
911
33
    segment_v2::IndexIterator* index_iterator = cid_to_index_iterators[src_col_cid].get();
912
33
    if (index_iterator == nullptr) {
913
1
        VLOG_DEBUG << "ANN range search skipped: "
914
0
                   << fmt::format("No index iterator for column cid {}", src_col_cid);
915
1
        ;
916
1
        return Status::OK();
917
1
    }
918
919
32
    segment_v2::AnnIndexIterator* ann_index_iterator =
920
32
            dynamic_cast<segment_v2::AnnIndexIterator*>(index_iterator);
921
32
    if (ann_index_iterator == nullptr) {
922
0
        VLOG_DEBUG << "ANN range search skipped: "
923
0
                   << fmt::format("Column cid {} has no ANN index iterator", src_col_cid);
924
0
        return Status::OK();
925
0
    }
926
18.4E
    DCHECK(ann_index_iterator->get_reader(AnnIndexReaderType::ANN) != nullptr)
927
18.4E
            << "Ann index iterator should have reader. Column cid: " << src_col_cid;
928
32
    std::shared_ptr<AnnIndexReader> ann_index_reader = std::dynamic_pointer_cast<AnnIndexReader>(
929
32
            ann_index_iterator->get_reader(segment_v2::AnnIndexReaderType::ANN));
930
18.4E
    DCHECK(ann_index_reader != nullptr)
931
18.4E
            << "Ann index reader should not be null. Column cid: " << src_col_cid;
932
    // Check if metrics type is match.
933
32
    if (ann_index_reader->get_metric_type() != range_search_runtime.metric_type) {
934
0
        VLOG_DEBUG << "ANN range search skipped: "
935
0
                   << fmt::format("Metric type mismatch. Index={} Query={}",
936
0
                                  segment_v2::metric_to_string(ann_index_reader->get_metric_type()),
937
0
                                  segment_v2::metric_to_string(range_search_runtime.metric_type));
938
0
        return Status::OK();
939
0
    }
940
941
    // Check dimension if available (>0)
942
32
    const size_t index_dim = ann_index_reader->get_dimension();
943
37
    if (index_dim > 0 && index_dim != range_search_runtime.dim) {
944
7
        return Status::InvalidArgument(
945
7
                "Ann range search query dimension {} does not match index dimension {}",
946
7
                range_search_runtime.dim, index_dim);
947
7
    }
948
949
25
    const auto& user_params = range_search_runtime.user_params;
950
25
    if (user_params.should_fallback_ann_index_by_small_candidate(origin_num, rows_of_segment)) {
951
0
        VLOG_DEBUG << fmt::format(
952
0
                "Ann range search input rows {} reach small candidate threshold, "
953
0
                "rows_of_segment: {}, absolute_threshold: {}, percent_threshold: {}, "
954
0
                "will not use ann index to filter",
955
0
                origin_num, rows_of_segment, user_params.ann_index_candidate_rows_threshold,
956
0
                user_params.ann_index_candidate_rows_percent_threshold);
957
0
        ann_index_stats.fall_back_brute_force_cnt += 1;
958
0
        ann_index_stats.range_fallback_by_small_candidate_cnt += 1;
959
0
        ann_index_stats.range_fallback_small_candidate_rows += origin_num;
960
0
        return Status::OK();
961
0
    }
962
963
25
    auto stats = std::make_unique<segment_v2::AnnIndexStats>();
964
    // Track load index timing
965
25
    {
966
25
        SCOPED_TIMER(&(stats->load_index_costs_ns));
967
25
        if (!ann_index_iterator->try_load_index()) {
968
2
            VLOG_DEBUG << "ANN range search skipped: "
969
0
                       << fmt::format("Failed to load ANN index for column cid {}", src_col_cid);
970
2
            ann_index_stats.fall_back_brute_force_cnt += 1;
971
2
            return Status::OK();
972
2
        }
973
23
        double load_costs_ms = static_cast<double>(stats->load_index_costs_ns.value()) / 1000000.0;
974
23
        DorisMetrics::instance()->ann_index_load_costs_ms->increment(
975
23
                static_cast<int64_t>(load_costs_ms));
976
23
    }
977
978
0
    AnnRangeSearchParams params = range_search_runtime.to_range_search_params();
979
980
23
    params.roaring = &row_bitmap;
981
23
    params.enable_result_cache = enable_result_cache;
982
23
    DCHECK(params.roaring != nullptr);
983
23
    DCHECK(params.query_value != nullptr);
984
23
    segment_v2::AnnRangeSearchResult result;
985
23
    RETURN_IF_ERROR(ann_index_iterator->range_search(params, range_search_runtime.user_params,
986
23
                                                     &result, stats.get()));
987
988
23
#ifndef NDEBUG
989
23
    if (range_search_runtime.is_le_or_lt == false &&
990
23
        ann_index_reader->get_metric_type() == AnnIndexMetric::L2) {
991
7
        DCHECK(result.distance == nullptr) << "Should not have distance";
992
7
    }
993
23
    if (range_search_runtime.is_le_or_lt == true &&
994
23
        ann_index_reader->get_metric_type() == AnnIndexMetric::IP) {
995
4
        DCHECK(result.distance == nullptr);
996
4
    }
997
23
#endif
998
23
    DCHECK(result.roaring != nullptr);
999
23
    row_bitmap = *result.roaring;
1000
1001
    // Process virtual column
1002
23
    bool dist_fulfilled = false;
1003
23
    if (range_search_runtime.dst_col_idx >= 0) {
1004
        // Prepare materialization if we can use result from index.
1005
        // Typical situation: range search and operator is LE or LT.
1006
4
        if (result.distance != nullptr) {
1007
2
            DCHECK(result.row_ids != nullptr);
1008
2
            ColumnId dst_col_cid = idx_to_cid[range_search_runtime.dst_col_idx];
1009
2
            DCHECK(dst_col_cid < column_iterators.size());
1010
2
            DCHECK(column_iterators[dst_col_cid] != nullptr);
1011
2
            segment_v2::ColumnIterator* column_iterator = column_iterators[dst_col_cid].get();
1012
2
            DCHECK(column_iterator != nullptr);
1013
2
            segment_v2::VirtualColumnIterator* virtual_column_iterator =
1014
2
                    dynamic_cast<segment_v2::VirtualColumnIterator*>(column_iterator);
1015
2
            DCHECK(virtual_column_iterator != nullptr);
1016
            // Now convert distance to column
1017
2
            size_t size = result.roaring->cardinality();
1018
2
            auto distance_col = ColumnFloat32::create(size);
1019
2
            const float* src = result.distance.get();
1020
2
            float* dst = distance_col->get_data().data();
1021
15
            for (size_t i = 0; i < size; ++i) {
1022
13
                dst[i] = src[i];
1023
13
            }
1024
2
            virtual_column_iterator->prepare_materialization(std::move(distance_col),
1025
2
                                                             std::move(result.row_ids));
1026
2
            dist_fulfilled = true;
1027
2
        } else {
1028
            // Whether the ANN index should have produced distance depends on metric and operator:
1029
            //  - L2: distance is produced for LE/LT; not produced for GE/GT
1030
            //  - IP: distance is produced for GE/GT; not produced for LE/LT
1031
2
#ifndef NDEBUG
1032
2
            const bool should_have_distance =
1033
2
                    (range_search_runtime.is_le_or_lt &&
1034
2
                     range_search_runtime.metric_type == AnnIndexMetric::L2) ||
1035
2
                    (!range_search_runtime.is_le_or_lt &&
1036
2
                     range_search_runtime.metric_type == AnnIndexMetric::IP);
1037
            // If we expected distance but didn't get it, assert in debug to catch logic errors.
1038
2
            DCHECK(!should_have_distance) << "Expected distance from ANN index but got none";
1039
2
#endif
1040
2
        }
1041
19
    } else {
1042
        // Dest is not virtual column.
1043
19
        dist_fulfilled = true;
1044
19
    }
1045
1046
23
    evaluation_result.executed = true;
1047
23
    evaluation_result.dist_fulfilled = dist_fulfilled;
1048
18.4E
    VLOG_DEBUG << fmt::format(
1049
18.4E
            "Ann range search filtered {} rows, origin {} rows, virtual column is full-filled: {}",
1050
18.4E
            origin_num - row_bitmap.cardinality(), origin_num, dist_fulfilled);
1051
1052
23
    ann_index_stats = *stats;
1053
23
    return Status::OK();
1054
23
}
1055
1056
1.30M
double VectorizedFnCall::execute_cost() const {
1057
1.30M
    if (!_function) {
1058
0
        throw Exception(
1059
0
                Status::InternalError("Function is null in expression: {}", this->debug_string()));
1060
0
    }
1061
1.30M
    double cost = _function->execute_cost();
1062
2.60M
    for (const auto& child : _children) {
1063
2.60M
        cost += child->execute_cost();
1064
2.60M
    }
1065
1.30M
    return cost;
1066
1.30M
}
1067
1068
} // namespace doris