Coverage Report

Created: 2026-07-30 02:46

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