Coverage Report

Created: 2026-10-31 00:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/doris/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
7
                                 VExprContext* context) {
52
7
    RETURN_IF_ERROR_OR_PREPARED(VExpr::prepare(state, desc, context));
53
54
7
    DCHECK_EQ(_children.size(), 1);
55
7
    auto child = _children[0];
56
7
    const auto& child_name = child->expr_name();
57
58
    // create a const string column
59
7
    _target_data_type = _data_type;
60
    // TODO(xy): support return struct type name
61
7
    _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
7
    _cast_param_data_type = _target_data_type;
65
66
7
    ColumnsWithTypeAndName argument_template;
67
7
    argument_template.reserve(2);
68
7
    argument_template.emplace_back(nullptr, child->data_type(), child_name);
69
7
    argument_template.emplace_back(nullptr, _cast_param_data_type, _target_data_type_name);
70
7
    _function = SimpleFunctionFactory::instance().get_function(function_name, argument_template,
71
7
                                                               _data_type, {});
72
73
7
    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
7
    VExpr::register_function_context(state, context);
78
7
    _expr_name = fmt::format("({} {}({}) TO {})", cast_name(), child_name,
79
7
                             child->data_type()->get_name(), _target_data_type_name);
80
7
    _prepare_finished = true;
81
7
    return Status::OK();
82
7
}
83
84
0
const DataTypePtr& VCastExpr::get_target_type() const {
85
0
    return _target_data_type;
86
0
}
87
88
doris::Status VCastExpr::open(doris::RuntimeState* state, VExprContext* context,
89
5
                              FunctionContext::FunctionStateScope scope) {
90
5
    DCHECK(_prepare_finished);
91
5
    for (auto& i : _children) {
92
5
        RETURN_IF_ERROR(i->open(state, context, scope));
93
5
    }
94
5
    if (_is_strict_cast) {
95
0
        context->fn_context(_fn_context_index)->set_enable_strict_mode(true);
96
0
    }
97
5
    RETURN_IF_ERROR(VExpr::init_function_context(state, context, scope, _function));
98
5
    if (scope == FunctionContext::FRAGMENT_LOCAL) {
99
5
        RETURN_IF_ERROR(VExpr::get_const_col(context, nullptr));
100
5
    }
101
5
    _open_finished = true;
102
5
    return Status::OK();
103
5
}
104
105
5
void VCastExpr::close(VExprContext* context, FunctionContext::FunctionStateScope scope) {
106
5
    VExpr::close_function_context(context, scope, _function);
107
5
    VExpr::close(context, scope);
108
5
}
109
110
Status VCastExpr::execute_column_impl(VExprContext* context, const Block* block,
111
                                      const Selector* selector, size_t count,
112
4
                                      ColumnPtr& result_column) const {
113
4
    DCHECK(_open_finished || block == nullptr) << _open_finished << _expr_name;
114
4
    if (is_const_and_have_executed()) { // const have executed in open function
115
0
        result_column = get_result_from_const(count);
116
0
        return Status::OK();
117
0
    }
118
    // for each child call execute
119
120
4
    ColumnPtr from_column;
121
4
    RETURN_IF_ERROR(_children[0]->execute_column(context, block, selector, count, from_column));
122
123
4
    Block temp_block;
124
4
    temp_block.insert({from_column, _children[0]->execute_type(block), _children[0]->expr_name()});
125
4
    temp_block.insert({nullptr, _data_type, _expr_name});
126
4
    RETURN_IF_ERROR(_function->execute(context->fn_context(_fn_context_index), temp_block, {0}, 1,
127
4
                                       temp_block.rows()));
128
129
4
    result_column = temp_block.get_by_position(1).column;
130
4
    DCHECK_EQ(result_column->size(), count);
131
4
    return Status::OK();
132
4
}
133
134
125
bool cast_error_code(const Status& st) {
135
    // Value conversion failures use INVALID_ARGUMENT (parsing, numeric/date range checks,
136
    // JSONB and complex elements) or ARITHMETIC_OVERFLOW_ERRROR (decimal conversions).
137
    // This predicate relies on producers classifying errors correctly: execution-contract
138
    // violations (including invalid column shapes) must use INTERNAL_ERROR, not these codes.
139
    // Likewise, value failures reported as INTERNAL_ERROR must be corrected at their producer;
140
    // accepting all INTERNAL_ERROR/RUNTIME_ERROR statuses would hide execution defects.
141
125
    return st.is<ErrorCode::INVALID_ARGUMENT>() || st.is<ErrorCode::ARITHMETIC_OVERFLOW_ERRROR>();
142
125
}
143
144
177
DataTypePtr TryCastExpr::original_cast_return_type() const {
145
177
    if (_original_cast_return_is_nullable) {
146
64
        return _data_type;
147
113
    } else {
148
113
        return remove_nullable(_data_type);
149
113
    }
150
177
}
151
152
Status TryCastExpr::execute_column_impl(VExprContext* context, const Block* block,
153
                                        const Selector* selector, size_t count,
154
89
                                        ColumnPtr& result_column) const {
155
89
    DCHECK(_open_finished || block == nullptr) << _open_finished << _expr_name;
156
89
    if (is_const_and_have_executed()) { // const have executed in open function
157
0
        result_column = get_result_from_const(count);
158
0
        return Status::OK();
159
0
    }
160
161
    // For try_cast, try to execute it in batches first.
162
163
    // execute child first
164
165
89
    ColumnPtr from_column;
166
89
    RETURN_IF_ERROR(_children[0]->execute_column(context, block, selector, count, from_column));
167
88
    auto from_type = _children[0]->execute_type(block);
168
169
    // prepare block
170
171
88
    Block temp_block;
172
88
    temp_block.insert({from_column, from_type, _children[0]->expr_name()});
173
88
    temp_block.insert({nullptr, original_cast_return_type(), _expr_name});
174
175
    // batch execute
176
88
    auto batch_exec_status = _function->execute(context->fn_context(_fn_context_index), temp_block,
177
88
                                                {0}, 1, temp_block.rows());
178
    // If batch is executed successfully,
179
    // it means that there is no error and it will be returned directly.
180
88
    if (batch_exec_status.ok()) {
181
11
        result_column = temp_block.get_by_position(1).column;
182
11
        result_column = make_nullable(result_column);
183
11
        return batch_exec_status;
184
11
    }
185
186
    // If there is an error that cannot be handled by try cast, it will be returned directly.
187
77
    if (!cast_error_code(batch_exec_status)) {
188
29
        return batch_exec_status;
189
29
    }
190
191
    // If there is an error that can be handled by try cast,
192
    // it will be converted into line execution.
193
48
    ColumnWithTypeAndName input_info {from_column, from_type, _children[0]->expr_name()};
194
48
    RETURN_IF_ERROR(single_row_execute(context, input_info, result_column));
195
48
    DCHECK_EQ(result_column->size(), count);
196
197
19
    return Status::OK();
198
48
}
199
200
Status TryCastExpr::single_row_execute(VExprContext* context,
201
                                       const ColumnWithTypeAndName& input_info,
202
48
                                       ColumnPtr& return_column) const {
203
48
    auto input_column = input_info.column;
204
48
    const auto& input_type = input_info.type;
205
48
    const auto& input_name = input_info.name;
206
48
    auto result_column = _data_type->create_column();
207
208
48
    auto& result_null_column = assert_cast<ColumnNullable&>(*result_column);
209
210
48
    const auto size = input_column->size();
211
108
    for (size_t row = 0; row < size; ++row) {
212
89
        Block single_row_block;
213
89
        single_row_block.insert({input_column->cut(row, 1), input_type, input_name});
214
89
        single_row_block.insert({nullptr, original_cast_return_type(), _expr_name});
215
216
89
        auto single_exec_status = _function->execute(context->fn_context(_fn_context_index),
217
89
                                                     single_row_block, {0}, 1, 1);
218
89
        if (single_exec_status.ok()) {
219
            // FunctionCast uses TRY_CAST's nullable return type even when the original CAST
220
            // is non-nullable. Normalize the actual result before copying the successful row.
221
41
            auto single_exec_column = make_nullable(
222
41
                    single_row_block.get_by_position(1).column->convert_to_full_column_if_const());
223
41
            DCHECK_EQ(single_exec_column->size(), 1);
224
41
            result_null_column.insert_from(*single_exec_column, 0);
225
48
        } else {
226
48
            if (!cast_error_code(single_exec_status)) {
227
29
                return single_exec_status;
228
29
            }
229
19
            result_null_column.insert_default();
230
19
        }
231
89
    }
232
19
    return_column = std::move(result_column);
233
19
    return Status::OK();
234
48
}
235
236
7
const std::string& VCastExpr::expr_name() const {
237
7
    return _expr_name;
238
7
}
239
240
0
std::string VCastExpr::debug_string() const {
241
0
    std::stringstream out;
242
0
    out << cast_name() << " Expr(CAST " << get_child(0)->data_type()->get_name() << " to "
243
0
        << _target_data_type->get_name() << "){";
244
0
    bool first = true;
245
0
    for (const auto& input_expr : children()) {
246
0
        if (first) {
247
0
            first = false;
248
0
        } else {
249
0
            out << ",";
250
0
        }
251
0
        out << input_expr->debug_string();
252
0
    }
253
0
    out << "}";
254
0
    return out.str();
255
0
}
256
257
#include "common/compile_check_end.h"
258
} // namespace doris