Coverage Report

Created: 2026-03-12 17:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/vcast_expr.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/vcast_expr.h"
19
20
#include <fmt/format.h>
21
#include <gen_cpp/Types_types.h>
22
#include <glog/logging.h>
23
24
#include <cstddef>
25
#include <memory>
26
#include <ostream>
27
28
#include "common/exception.h"
29
#include "common/status.h"
30
#include "core/assert_cast.h"
31
#include "core/block/block.h"
32
#include "core/block/column_with_type_and_name.h"
33
#include "core/block/columns_with_type_and_name.h"
34
#include "core/column/column.h"
35
#include "core/column/column_nullable.h"
36
#include "core/data_type/data_type_nullable.h"
37
#include "exprs/function/simple_function_factory.h"
38
#include "exprs/vexpr.h"
39
#include "exprs/vexpr_context.h"
40
#include "runtime/runtime_state.h"
41
42
namespace doris {
43
class RowDescriptor;
44
class RuntimeState;
45
} // namespace doris
46
47
namespace doris {
48
#include "common/compile_check_begin.h"
49
50
doris::Status VCastExpr::prepare(doris::RuntimeState* state, const doris::RowDescriptor& desc,
51
131k
                                 VExprContext* context) {
52
131k
    RETURN_IF_ERROR_OR_PREPARED(VExpr::prepare(state, desc, context));
53
54
131k
    DCHECK_EQ(_children.size(), 1);
55
131k
    auto child = _children[0];
56
131k
    const auto& child_name = child->expr_name();
57
58
    // create a const string column
59
131k
    _target_data_type = _data_type;
60
    // TODO(xy): support return struct type name
61
131k
    _target_data_type_name = _target_data_type->get_name();
62
    // Using typeindex to indicate the datatype, not using type name because
63
    // type name is not stable, but type index is stable and immutable
64
131k
    _cast_param_data_type = _target_data_type;
65
66
131k
    ColumnsWithTypeAndName argument_template;
67
131k
    argument_template.reserve(2);
68
131k
    argument_template.emplace_back(nullptr, child->data_type(), child_name);
69
131k
    argument_template.emplace_back(nullptr, _cast_param_data_type, _target_data_type_name);
70
131k
    _function = SimpleFunctionFactory::instance().get_function(function_name, argument_template,
71
131k
                                                               _data_type, {});
72
73
131k
    if (_function == nullptr) {
74
0
        return Status::NotSupported("Cast from {} to {} is not implemented",
75
0
                                    child->data_type()->get_name(), _target_data_type_name);
76
0
    }
77
131k
    VExpr::register_function_context(state, context);
78
131k
    _expr_name = fmt::format("({} {}({}) TO {})", cast_name(), child_name,
79
131k
                             child->data_type()->get_name(), _target_data_type_name);
80
131k
    _prepare_finished = true;
81
131k
    return Status::OK();
82
131k
}
83
84
6.58k
const DataTypePtr& VCastExpr::get_target_type() const {
85
6.58k
    return _target_data_type;
86
6.58k
}
87
88
doris::Status VCastExpr::open(doris::RuntimeState* state, VExprContext* context,
89
376k
                              FunctionContext::FunctionStateScope scope) {
90
376k
    DCHECK(_prepare_finished);
91
377k
    for (auto& i : _children) {
92
377k
        RETURN_IF_ERROR(i->open(state, context, scope));
93
377k
    }
94
376k
    RETURN_IF_ERROR(VExpr::init_function_context(state, context, scope, _function));
95
376k
    if (scope == FunctionContext::FRAGMENT_LOCAL) {
96
130k
        RETURN_IF_ERROR(VExpr::get_const_col(context, nullptr));
97
130k
    }
98
375k
    _open_finished = true;
99
375k
    return Status::OK();
100
376k
}
101
102
376k
void VCastExpr::close(VExprContext* context, FunctionContext::FunctionStateScope scope) {
103
376k
    VExpr::close_function_context(context, scope, _function);
104
376k
    VExpr::close(context, scope);
105
376k
}
106
107
Status VCastExpr::execute_column(VExprContext* context, const Block* block, Selector* selector,
108
289k
                                 size_t count, ColumnPtr& result_column) const {
109
18.4E
    DCHECK(_open_finished || block == nullptr) << _open_finished << _expr_name;
110
289k
    if (is_const_and_have_executed()) { // const have executed in open function
111
50.5k
        result_column = get_result_from_const(count);
112
50.5k
        return Status::OK();
113
50.5k
    }
114
    // for each child call execute
115
116
238k
    ColumnPtr from_column;
117
238k
    RETURN_IF_ERROR(_children[0]->execute_column(context, block, selector, count, from_column));
118
119
238k
    Block temp_block;
120
238k
    temp_block.insert({from_column, _children[0]->execute_type(block), _children[0]->expr_name()});
121
238k
    temp_block.insert({nullptr, _data_type, _expr_name});
122
238k
    RETURN_IF_ERROR(_function->execute(context->fn_context(_fn_context_index), temp_block, {0}, 1,
123
238k
                                       temp_block.rows()));
124
125
236k
    result_column = temp_block.get_by_position(1).column;
126
236k
    DCHECK_EQ(result_column->size(), count);
127
236k
    return Status::OK();
128
238k
}
129
130
21
bool cast_error_code(Status& st) {
131
    //There may be more error codes that need to be captured by try cast in the future.
132
21
    if (st.is<ErrorCode::INVALID_ARGUMENT>()) {
133
19
        return true;
134
19
    } else {
135
2
        return false;
136
2
    }
137
21
}
138
139
31
DataTypePtr TryCastExpr::original_cast_return_type() const {
140
31
    if (_original_cast_return_is_nullable) {
141
25
        return _data_type;
142
25
    } else {
143
6
        return remove_nullable(_data_type);
144
6
    }
145
31
}
146
147
Status TryCastExpr::execute_column(VExprContext* context, const Block* block, Selector* selector,
148
22
                                   size_t count, ColumnPtr& result_column) const {
149
22
    DCHECK(_open_finished || block == nullptr) << _open_finished << _expr_name;
150
22
    if (is_const_and_have_executed()) { // const have executed in open function
151
5
        result_column = get_result_from_const(count);
152
5
        return Status::OK();
153
5
    }
154
155
    // For try_cast, try to execute it in batches first.
156
157
    // execute child first
158
159
17
    ColumnPtr from_column;
160
17
    RETURN_IF_ERROR(_children[0]->execute_column(context, block, selector, count, from_column));
161
17
    auto from_type = _children[0]->execute_type(block);
162
163
    // prepare block
164
165
17
    Block temp_block;
166
17
    temp_block.insert({from_column, from_type, _children[0]->expr_name()});
167
17
    temp_block.insert({nullptr, original_cast_return_type(), _expr_name});
168
169
    // batch execute
170
17
    auto batch_exec_status = _function->execute(context->fn_context(_fn_context_index), temp_block,
171
17
                                                {0}, 1, temp_block.rows());
172
    // If batch is executed successfully,
173
    // it means that there is no error and it will be returned directly.
174
17
    if (batch_exec_status.ok()) {
175
6
        result_column = temp_block.get_by_position(1).column;
176
6
        result_column = make_nullable(result_column);
177
6
        return batch_exec_status;
178
6
    }
179
180
    // If there is an error that cannot be handled by try cast, it will be returned directly.
181
11
    if (!cast_error_code(batch_exec_status)) {
182
1
        return batch_exec_status;
183
1
    }
184
185
    // If there is an error that can be handled by try cast,
186
    // it will be converted into line execution.
187
10
    ColumnWithTypeAndName input_info {from_column, from_type, _children[0]->expr_name()};
188
    // distinguish whether the return value of the original cast is nullable
189
10
    if (_original_cast_return_is_nullable) {
190
9
        RETURN_IF_ERROR(single_row_execute<true>(context, input_info, result_column));
191
9
    } else {
192
1
        RETURN_IF_ERROR(single_row_execute<false>(context, input_info, result_column));
193
1
    }
194
    // wrap nullable
195
9
    result_column = make_nullable(result_column);
196
9
    DCHECK_EQ(result_column->size(), count);
197
198
9
    return Status::OK();
199
10
}
200
201
template <bool original_cast_reutrn_is_nullable>
202
Status TryCastExpr::single_row_execute(VExprContext* context,
203
                                       const ColumnWithTypeAndName& input_info,
204
10
                                       ColumnPtr& return_column) const {
205
10
    auto input_column = input_info.column;
206
10
    const auto& input_type = input_info.type;
207
10
    const auto& input_name = input_info.name;
208
10
    auto result_column = _data_type->create_column();
209
210
10
    ColumnNullable& result_null_column = assert_cast<ColumnNullable&>(*result_column);
211
212
10
    IColumn& result_nested_column = result_null_column.get_nested_column();
213
10
    auto& result_null_map_data = result_null_column.get_null_map_data();
214
215
10
    auto insert_from_single_row = [&](const IColumn& single_exec_column, size_t row) {
216
4
        DCHECK_EQ(single_exec_column.size(), 1);
217
4
        if constexpr (original_cast_reutrn_is_nullable) {
218
2
            result_null_column.insert_from(single_exec_column, 0);
219
2
        } else {
220
2
            DCHECK(!single_exec_column.is_nullable());
221
2
            result_nested_column.insert_from(single_exec_column, 0);
222
2
            result_null_map_data.push_back(0);
223
2
        }
224
4
    };
_ZZNK5doris11TryCastExpr18single_row_executeILb1EEENS_6StatusEPNS_12VExprContextERKNS_21ColumnWithTypeAndNameERNS_3COWINS_7IColumnEE13immutable_ptrIS9_EEENKUlRKS9_mE_clESF_m
Line
Count
Source
215
2
    auto insert_from_single_row = [&](const IColumn& single_exec_column, size_t row) {
216
2
        DCHECK_EQ(single_exec_column.size(), 1);
217
2
        if constexpr (original_cast_reutrn_is_nullable) {
218
2
            result_null_column.insert_from(single_exec_column, 0);
219
        } else {
220
            DCHECK(!single_exec_column.is_nullable());
221
            result_nested_column.insert_from(single_exec_column, 0);
222
            result_null_map_data.push_back(0);
223
        }
224
2
    };
_ZZNK5doris11TryCastExpr18single_row_executeILb0EEENS_6StatusEPNS_12VExprContextERKNS_21ColumnWithTypeAndNameERNS_3COWINS_7IColumnEE13immutable_ptrIS9_EEENKUlRKS9_mE_clESF_m
Line
Count
Source
215
2
    auto insert_from_single_row = [&](const IColumn& single_exec_column, size_t row) {
216
2
        DCHECK_EQ(single_exec_column.size(), 1);
217
        if constexpr (original_cast_reutrn_is_nullable) {
218
            result_null_column.insert_from(single_exec_column, 0);
219
2
        } else {
220
            DCHECK(!single_exec_column.is_nullable());
221
2
            result_nested_column.insert_from(single_exec_column, 0);
222
2
            result_null_map_data.push_back(0);
223
2
        }
224
2
    };
225
226
10
    auto insert_null = [&](size_t row) { result_null_column.insert_default(); };
_ZZNK5doris11TryCastExpr18single_row_executeILb1EEENS_6StatusEPNS_12VExprContextERKNS_21ColumnWithTypeAndNameERNS_3COWINS_7IColumnEE13immutable_ptrIS9_EEENKUlmE_clEm
Line
Count
Source
226
8
    auto insert_null = [&](size_t row) { result_null_column.insert_default(); };
_ZZNK5doris11TryCastExpr18single_row_executeILb0EEENS_6StatusEPNS_12VExprContextERKNS_21ColumnWithTypeAndNameERNS_3COWINS_7IColumnEE13immutable_ptrIS9_EEENKUlmE_clEm
Line
Count
Source
226
1
    auto insert_null = [&](size_t row) { result_null_column.insert_default(); };
227
228
10
    const auto size = input_column->size();
229
23
    for (size_t row = 0; row < size; ++row) {
230
14
        Block single_row_block;
231
14
        single_row_block.insert({input_column->cut(row, 1), input_type, input_name});
232
14
        single_row_block.insert({nullptr, original_cast_return_type(), _expr_name});
233
234
14
        auto single_exec_status = _function->execute(context->fn_context(_fn_context_index),
235
14
                                                     single_row_block, {0}, 1, 1);
236
14
        if (single_exec_status.ok()) {
237
4
            insert_from_single_row(*single_row_block.get_by_position(1).column, row);
238
10
        } else {
239
10
            if (!cast_error_code(single_exec_status)) {
240
1
                return single_exec_status;
241
1
            }
242
9
            insert_null(row);
243
9
        }
244
14
    }
245
9
    return_column = std::move(result_column);
246
9
    return Status::OK();
247
10
}
_ZNK5doris11TryCastExpr18single_row_executeILb1EEENS_6StatusEPNS_12VExprContextERKNS_21ColumnWithTypeAndNameERNS_3COWINS_7IColumnEE13immutable_ptrIS9_EE
Line
Count
Source
204
9
                                       ColumnPtr& return_column) const {
205
9
    auto input_column = input_info.column;
206
9
    const auto& input_type = input_info.type;
207
9
    const auto& input_name = input_info.name;
208
9
    auto result_column = _data_type->create_column();
209
210
9
    ColumnNullable& result_null_column = assert_cast<ColumnNullable&>(*result_column);
211
212
9
    IColumn& result_nested_column = result_null_column.get_nested_column();
213
9
    auto& result_null_map_data = result_null_column.get_null_map_data();
214
215
9
    auto insert_from_single_row = [&](const IColumn& single_exec_column, size_t row) {
216
9
        DCHECK_EQ(single_exec_column.size(), 1);
217
9
        if constexpr (original_cast_reutrn_is_nullable) {
218
9
            result_null_column.insert_from(single_exec_column, 0);
219
9
        } else {
220
9
            DCHECK(!single_exec_column.is_nullable());
221
9
            result_nested_column.insert_from(single_exec_column, 0);
222
9
            result_null_map_data.push_back(0);
223
9
        }
224
9
    };
225
226
9
    auto insert_null = [&](size_t row) { result_null_column.insert_default(); };
227
228
9
    const auto size = input_column->size();
229
19
    for (size_t row = 0; row < size; ++row) {
230
11
        Block single_row_block;
231
11
        single_row_block.insert({input_column->cut(row, 1), input_type, input_name});
232
11
        single_row_block.insert({nullptr, original_cast_return_type(), _expr_name});
233
234
11
        auto single_exec_status = _function->execute(context->fn_context(_fn_context_index),
235
11
                                                     single_row_block, {0}, 1, 1);
236
11
        if (single_exec_status.ok()) {
237
2
            insert_from_single_row(*single_row_block.get_by_position(1).column, row);
238
9
        } else {
239
9
            if (!cast_error_code(single_exec_status)) {
240
1
                return single_exec_status;
241
1
            }
242
8
            insert_null(row);
243
8
        }
244
11
    }
245
8
    return_column = std::move(result_column);
246
8
    return Status::OK();
247
9
}
_ZNK5doris11TryCastExpr18single_row_executeILb0EEENS_6StatusEPNS_12VExprContextERKNS_21ColumnWithTypeAndNameERNS_3COWINS_7IColumnEE13immutable_ptrIS9_EE
Line
Count
Source
204
1
                                       ColumnPtr& return_column) const {
205
1
    auto input_column = input_info.column;
206
1
    const auto& input_type = input_info.type;
207
1
    const auto& input_name = input_info.name;
208
1
    auto result_column = _data_type->create_column();
209
210
1
    ColumnNullable& result_null_column = assert_cast<ColumnNullable&>(*result_column);
211
212
1
    IColumn& result_nested_column = result_null_column.get_nested_column();
213
1
    auto& result_null_map_data = result_null_column.get_null_map_data();
214
215
1
    auto insert_from_single_row = [&](const IColumn& single_exec_column, size_t row) {
216
1
        DCHECK_EQ(single_exec_column.size(), 1);
217
1
        if constexpr (original_cast_reutrn_is_nullable) {
218
1
            result_null_column.insert_from(single_exec_column, 0);
219
1
        } else {
220
1
            DCHECK(!single_exec_column.is_nullable());
221
1
            result_nested_column.insert_from(single_exec_column, 0);
222
1
            result_null_map_data.push_back(0);
223
1
        }
224
1
    };
225
226
1
    auto insert_null = [&](size_t row) { result_null_column.insert_default(); };
227
228
1
    const auto size = input_column->size();
229
4
    for (size_t row = 0; row < size; ++row) {
230
3
        Block single_row_block;
231
3
        single_row_block.insert({input_column->cut(row, 1), input_type, input_name});
232
3
        single_row_block.insert({nullptr, original_cast_return_type(), _expr_name});
233
234
3
        auto single_exec_status = _function->execute(context->fn_context(_fn_context_index),
235
3
                                                     single_row_block, {0}, 1, 1);
236
3
        if (single_exec_status.ok()) {
237
2
            insert_from_single_row(*single_row_block.get_by_position(1).column, row);
238
2
        } else {
239
1
            if (!cast_error_code(single_exec_status)) {
240
0
                return single_exec_status;
241
0
            }
242
1
            insert_null(row);
243
1
        }
244
3
    }
245
1
    return_column = std::move(result_column);
246
1
    return Status::OK();
247
1
}
248
249
263k
const std::string& VCastExpr::expr_name() const {
250
263k
    return _expr_name;
251
263k
}
252
253
175
std::string VCastExpr::debug_string() const {
254
175
    std::stringstream out;
255
175
    out << cast_name() << " Expr(CAST " << get_child(0)->data_type()->get_name() << " to "
256
175
        << _target_data_type->get_name() << "){";
257
175
    bool first = true;
258
180
    for (const auto& input_expr : children()) {
259
180
        if (first) {
260
180
            first = false;
261
180
        } else {
262
0
            out << ",";
263
0
        }
264
180
        out << input_expr->debug_string();
265
180
    }
266
175
    out << "}";
267
175
    return out.str();
268
175
}
269
270
#include "common/compile_check_end.h"
271
} // namespace doris