Coverage Report

Created: 2026-07-21 21:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/function/function_variant_parse.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 <cstdint>
19
20
#include "common/config.h"
21
#include "common/exception.h"
22
#include "core/assert_cast.h"
23
#include "core/column/column_nullable.h"
24
#include "core/column/column_string.h"
25
#include "core/column/column_variant.h"
26
#include "core/column/variant_v2/column_variant_v2.h"
27
#include "core/data_type/data_type_nullable.h"
28
#include "core/data_type/data_type_string.h"
29
#include "core/data_type/data_type_variant.h"
30
#include "core/data_type/data_type_variant_v2.h"
31
#include "exec/common/variant_util.h"
32
#include "exprs/function/parse/variant_string_parse.h"
33
#include "exprs/function/simple_function_factory.h"
34
#include "util/json/json_parser.h"
35
36
namespace doris {
37
namespace {
38
39
template <bool ERROR_TO_NULL>
40
class FunctionVariantParse final : public IFunction {
41
public:
42
    static constexpr auto name =
43
            ERROR_TO_NULL ? "parse_to_variant_error_to_null" : "parse_to_variant";
44
45
29
    static FunctionPtr create() { return std::make_shared<FunctionVariantParse>(); }
function_variant_parse.cpp:_ZN5doris12_GLOBAL__N_120FunctionVariantParseILb0EE6createEv
Line
Count
Source
45
17
    static FunctionPtr create() { return std::make_shared<FunctionVariantParse>(); }
function_variant_parse.cpp:_ZN5doris12_GLOBAL__N_120FunctionVariantParseILb1EE6createEv
Line
Count
Source
45
12
    static FunctionPtr create() { return std::make_shared<FunctionVariantParse>(); }
46
47
2
    String get_name() const override { return name; }
function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb0EE8get_nameB5cxx11Ev
Line
Count
Source
47
1
    String get_name() const override { return name; }
function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb1EE8get_nameB5cxx11Ev
Line
Count
Source
47
1
    String get_name() const override { return name; }
48
0
    size_t get_number_of_arguments() const override { return 1; }
Unexecuted instantiation: function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb0EE23get_number_of_argumentsEv
Unexecuted instantiation: function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb1EE23get_number_of_argumentsEv
49
23
    bool use_default_implementation_for_nulls() const override { return false; }
function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb0EE36use_default_implementation_for_nullsEv
Line
Count
Source
49
14
    bool use_default_implementation_for_nulls() const override { return false; }
function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb1EE36use_default_implementation_for_nullsEv
Line
Count
Source
49
9
    bool use_default_implementation_for_nulls() const override { return false; }
50
25
    bool skip_return_type_check() const override { return true; }
function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb0EE22skip_return_type_checkEv
Line
Count
Source
50
15
    bool skip_return_type_check() const override { return true; }
function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb1EE22skip_return_type_checkEv
Line
Count
Source
50
10
    bool skip_return_type_check() const override { return true; }
51
52
0
    DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
53
0
        auto result = std::make_shared<DataTypeVariant>();
54
0
        return ERROR_TO_NULL || arguments[0]->is_nullable() ? make_nullable(std::move(result))
55
0
                                                            : std::move(result);
56
0
    }
Unexecuted instantiation: function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb0EE20get_return_type_implERKSt6vectorISt10shared_ptrIKNS_9IDataTypeEESaIS7_EE
Unexecuted instantiation: function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb1EE20get_return_type_implERKSt6vectorISt10shared_ptrIKNS_9IDataTypeEESaIS7_EE
57
58
    // Keep strict/error-to-null, SQL-null, and legacy/V2 parse transitions in one auditable state machine.
59
    // NOLINTNEXTLINE(readability-function-size,readability-function-cognitive-complexity)
60
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
61
23
                        uint32_t result, size_t input_rows_count) const override {
62
23
        const ColumnPtr source =
63
23
                block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
64
23
        const ColumnString* strings = nullptr;
65
23
        const NullMap* input_nulls = nullptr;
66
23
        if (const auto* nullable = check_and_get_column<ColumnNullable>(source.get())) {
67
3
            strings = &assert_cast<const ColumnString&>(nullable->get_nested_column());
68
3
            input_nulls = &nullable->get_null_map_data();
69
20
        } else {
70
20
            strings = &assert_cast<const ColumnString&>(*source);
71
20
        }
72
23
        DORIS_CHECK_EQ(strings->size(), input_rows_count);
73
74
23
        const bool result_is_nullable = block.get_by_position(result).type->is_nullable();
75
23
        DORIS_CHECK_EQ(result_is_nullable, ERROR_TO_NULL || input_nulls != nullptr);
76
23
        auto result_nulls = ColumnUInt8::create(input_rows_count, uint8_t {0});
77
78
23
        const IDataType* result_type = remove_nullable(block.get_by_position(result).type).get();
79
23
        if (const auto* variant_type = dynamic_cast<const DataTypeVariant*>(result_type)) {
80
4
            const int32_t max_subcolumns_count = variant_type->variant_max_subcolumns_count();
81
4
            const bool enable_doc_mode = variant_type->enable_doc_mode();
82
4
            auto values = ColumnVariant::create(max_subcolumns_count, enable_doc_mode);
83
4
            ParseConfig parse_config;
84
4
            parse_config.check_duplicate_json_path =
85
4
                    config::variant_enable_duplicate_json_path_check;
86
4
            JsonParser parser;
87
4
            const StringRef empty_json("", 0);
88
4
            if constexpr (!ERROR_TO_NULL) {
89
7
                for (size_t row = 0; row < input_rows_count; ++row) {
90
5
                    const bool input_is_null = input_nulls != nullptr && (*input_nulls)[row] != 0;
91
5
                    try {
92
5
                        variant_util::parse_json_to_variant(
93
5
                                *values, input_is_null ? empty_json : strings->get_data_at(row),
94
5
                                &parser, parse_config);
95
5
                    } catch (const Exception& exception) {
96
1
                        const Status status = exception.to_status();
97
1
                        if (!status.is<ErrorCode::INVALID_ARGUMENT>()) {
98
0
                            return status;
99
0
                        }
100
1
                        return Status::InvalidArgument(
101
1
                                "Parse json document failed at row {}, error: {}", row,
102
1
                                status.to_string());
103
1
                    }
104
4
                    if (input_is_null) {
105
1
                        result_nulls->get_data()[row] = 1;
106
1
                    }
107
4
                }
108
3
            } else {
109
4
                for (size_t row = 0; row < input_rows_count; ++row) {
110
3
                    const bool input_is_null = input_nulls != nullptr && (*input_nulls)[row] != 0;
111
3
                    auto one_row = ColumnVariant::create(max_subcolumns_count, enable_doc_mode);
112
3
                    try {
113
3
                        variant_util::parse_json_to_variant(
114
3
                                *one_row, input_is_null ? empty_json : strings->get_data_at(row),
115
3
                                &parser, parse_config);
116
3
                        one_row->finalize();
117
3
                    } catch (const Exception& exception) {
118
1
                        const Status status = exception.to_status();
119
1
                        if (!status.is<ErrorCode::INVALID_ARGUMENT>()) {
120
0
                            return status;
121
0
                        }
122
1
                        one_row = ColumnVariant::create(max_subcolumns_count, enable_doc_mode);
123
1
                        variant_util::parse_json_to_variant(*one_row, empty_json, &parser,
124
1
                                                            parse_config);
125
1
                        one_row->finalize();
126
1
                        result_nulls->get_data()[row] = 1;
127
1
                    }
128
3
                    if (input_is_null) {
129
0
                        result_nulls->get_data()[row] = 1;
130
0
                    }
131
3
                    values->insert_range_from(*one_row, 0, 1);
132
3
                }
133
1
            }
134
3
            values->finalize();
135
136
4
            ColumnPtr output;
137
4
            if (result_is_nullable) {
138
2
                output = ColumnNullable::create(std::move(values), std::move(result_nulls));
139
2
            } else {
140
2
                output = std::move(values);
141
2
            }
142
4
            DORIS_CHECK_EQ(output->size(), input_rows_count);
143
4
            block.replace_by_position(result, std::move(output));
144
4
            return Status::OK();
145
4
        }
146
19
        DORIS_CHECK(dynamic_cast<const DataTypeVariantV2*>(result_type) != nullptr);
147
148
19
        JsonStringToVariantEncoder encoder(JsonToVariantOptions::current_config());
149
19
        const StringRef null_json("null", 4);
150
47
        for (size_t row = 0; row < input_rows_count; ++row) {
151
28
            if (input_nulls != nullptr && (*input_nulls)[row] != 0) {
152
2
                encoder.add_json(null_json);
153
2
                result_nulls->get_data()[row] = 1;
154
2
                continue;
155
2
            }
156
157
26
            const Status status = encoder.try_add_json(strings->get_data_at(row));
158
26
            if (status.ok()) {
159
16
                continue;
160
16
            }
161
10
            if constexpr (!ERROR_TO_NULL) {
162
5
                return Status::InvalidArgument("Parse json document failed at row {}, error: {}",
163
5
                                               row, status.to_string());
164
5
            }
165
0
            encoder.add_json(null_json);
166
10
            result_nulls->get_data()[row] = 1;
167
10
        }
168
169
19
        VariantBatchBuilder encoded = encoder.finish_batch();
170
19
        auto values = ColumnVariantV2::create();
171
19
        values->insert_encoded_batch(encoded);
172
173
19
        ColumnPtr output;
174
19
        if (result_is_nullable) {
175
10
            output = ColumnNullable::create(std::move(values), std::move(result_nulls));
176
10
        } else {
177
9
            output = std::move(values);
178
9
        }
179
19
        DORIS_CHECK_EQ(output->size(), input_rows_count);
180
19
        block.replace_by_position(result, std::move(output));
181
19
        return Status::OK();
182
23
    }
function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb0EE12execute_implEPNS_15FunctionContextERNS_5BlockERKSt6vectorIjSaIjEEjm
Line
Count
Source
61
14
                        uint32_t result, size_t input_rows_count) const override {
62
14
        const ColumnPtr source =
63
14
                block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
64
14
        const ColumnString* strings = nullptr;
65
14
        const NullMap* input_nulls = nullptr;
66
14
        if (const auto* nullable = check_and_get_column<ColumnNullable>(source.get())) {
67
3
            strings = &assert_cast<const ColumnString&>(nullable->get_nested_column());
68
3
            input_nulls = &nullable->get_null_map_data();
69
11
        } else {
70
11
            strings = &assert_cast<const ColumnString&>(*source);
71
11
        }
72
14
        DORIS_CHECK_EQ(strings->size(), input_rows_count);
73
74
14
        const bool result_is_nullable = block.get_by_position(result).type->is_nullable();
75
14
        DORIS_CHECK_EQ(result_is_nullable, ERROR_TO_NULL || input_nulls != nullptr);
76
14
        auto result_nulls = ColumnUInt8::create(input_rows_count, uint8_t {0});
77
78
14
        const IDataType* result_type = remove_nullable(block.get_by_position(result).type).get();
79
14
        if (const auto* variant_type = dynamic_cast<const DataTypeVariant*>(result_type)) {
80
3
            const int32_t max_subcolumns_count = variant_type->variant_max_subcolumns_count();
81
3
            const bool enable_doc_mode = variant_type->enable_doc_mode();
82
3
            auto values = ColumnVariant::create(max_subcolumns_count, enable_doc_mode);
83
3
            ParseConfig parse_config;
84
3
            parse_config.check_duplicate_json_path =
85
3
                    config::variant_enable_duplicate_json_path_check;
86
3
            JsonParser parser;
87
3
            const StringRef empty_json("", 0);
88
3
            if constexpr (!ERROR_TO_NULL) {
89
7
                for (size_t row = 0; row < input_rows_count; ++row) {
90
5
                    const bool input_is_null = input_nulls != nullptr && (*input_nulls)[row] != 0;
91
5
                    try {
92
5
                        variant_util::parse_json_to_variant(
93
5
                                *values, input_is_null ? empty_json : strings->get_data_at(row),
94
5
                                &parser, parse_config);
95
5
                    } catch (const Exception& exception) {
96
1
                        const Status status = exception.to_status();
97
1
                        if (!status.is<ErrorCode::INVALID_ARGUMENT>()) {
98
0
                            return status;
99
0
                        }
100
1
                        return Status::InvalidArgument(
101
1
                                "Parse json document failed at row {}, error: {}", row,
102
1
                                status.to_string());
103
1
                    }
104
4
                    if (input_is_null) {
105
1
                        result_nulls->get_data()[row] = 1;
106
1
                    }
107
4
                }
108
            } else {
109
                for (size_t row = 0; row < input_rows_count; ++row) {
110
                    const bool input_is_null = input_nulls != nullptr && (*input_nulls)[row] != 0;
111
                    auto one_row = ColumnVariant::create(max_subcolumns_count, enable_doc_mode);
112
                    try {
113
                        variant_util::parse_json_to_variant(
114
                                *one_row, input_is_null ? empty_json : strings->get_data_at(row),
115
                                &parser, parse_config);
116
                        one_row->finalize();
117
                    } catch (const Exception& exception) {
118
                        const Status status = exception.to_status();
119
                        if (!status.is<ErrorCode::INVALID_ARGUMENT>()) {
120
                            return status;
121
                        }
122
                        one_row = ColumnVariant::create(max_subcolumns_count, enable_doc_mode);
123
                        variant_util::parse_json_to_variant(*one_row, empty_json, &parser,
124
                                                            parse_config);
125
                        one_row->finalize();
126
                        result_nulls->get_data()[row] = 1;
127
                    }
128
                    if (input_is_null) {
129
                        result_nulls->get_data()[row] = 1;
130
                    }
131
                    values->insert_range_from(*one_row, 0, 1);
132
                }
133
            }
134
2
            values->finalize();
135
136
3
            ColumnPtr output;
137
3
            if (result_is_nullable) {
138
1
                output = ColumnNullable::create(std::move(values), std::move(result_nulls));
139
2
            } else {
140
2
                output = std::move(values);
141
2
            }
142
3
            DORIS_CHECK_EQ(output->size(), input_rows_count);
143
3
            block.replace_by_position(result, std::move(output));
144
3
            return Status::OK();
145
3
        }
146
11
        DORIS_CHECK(dynamic_cast<const DataTypeVariantV2*>(result_type) != nullptr);
147
148
11
        JsonStringToVariantEncoder encoder(JsonToVariantOptions::current_config());
149
11
        const StringRef null_json("null", 4);
150
28
        for (size_t row = 0; row < input_rows_count; ++row) {
151
17
            if (input_nulls != nullptr && (*input_nulls)[row] != 0) {
152
2
                encoder.add_json(null_json);
153
2
                result_nulls->get_data()[row] = 1;
154
2
                continue;
155
2
            }
156
157
15
            const Status status = encoder.try_add_json(strings->get_data_at(row));
158
15
            if (status.ok()) {
159
10
                continue;
160
10
            }
161
5
            if constexpr (!ERROR_TO_NULL) {
162
5
                return Status::InvalidArgument("Parse json document failed at row {}, error: {}",
163
5
                                               row, status.to_string());
164
5
            }
165
0
            encoder.add_json(null_json);
166
5
            result_nulls->get_data()[row] = 1;
167
5
        }
168
169
11
        VariantBatchBuilder encoded = encoder.finish_batch();
170
11
        auto values = ColumnVariantV2::create();
171
11
        values->insert_encoded_batch(encoded);
172
173
11
        ColumnPtr output;
174
11
        if (result_is_nullable) {
175
2
            output = ColumnNullable::create(std::move(values), std::move(result_nulls));
176
9
        } else {
177
9
            output = std::move(values);
178
9
        }
179
        DORIS_CHECK_EQ(output->size(), input_rows_count);
180
11
        block.replace_by_position(result, std::move(output));
181
11
        return Status::OK();
182
14
    }
function_variant_parse.cpp:_ZNK5doris12_GLOBAL__N_120FunctionVariantParseILb1EE12execute_implEPNS_15FunctionContextERNS_5BlockERKSt6vectorIjSaIjEEjm
Line
Count
Source
61
9
                        uint32_t result, size_t input_rows_count) const override {
62
9
        const ColumnPtr source =
63
9
                block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
64
9
        const ColumnString* strings = nullptr;
65
9
        const NullMap* input_nulls = nullptr;
66
9
        if (const auto* nullable = check_and_get_column<ColumnNullable>(source.get())) {
67
0
            strings = &assert_cast<const ColumnString&>(nullable->get_nested_column());
68
0
            input_nulls = &nullable->get_null_map_data();
69
9
        } else {
70
9
            strings = &assert_cast<const ColumnString&>(*source);
71
9
        }
72
9
        DORIS_CHECK_EQ(strings->size(), input_rows_count);
73
74
9
        const bool result_is_nullable = block.get_by_position(result).type->is_nullable();
75
9
        DORIS_CHECK_EQ(result_is_nullable, ERROR_TO_NULL || input_nulls != nullptr);
76
9
        auto result_nulls = ColumnUInt8::create(input_rows_count, uint8_t {0});
77
78
9
        const IDataType* result_type = remove_nullable(block.get_by_position(result).type).get();
79
9
        if (const auto* variant_type = dynamic_cast<const DataTypeVariant*>(result_type)) {
80
1
            const int32_t max_subcolumns_count = variant_type->variant_max_subcolumns_count();
81
1
            const bool enable_doc_mode = variant_type->enable_doc_mode();
82
1
            auto values = ColumnVariant::create(max_subcolumns_count, enable_doc_mode);
83
1
            ParseConfig parse_config;
84
1
            parse_config.check_duplicate_json_path =
85
1
                    config::variant_enable_duplicate_json_path_check;
86
1
            JsonParser parser;
87
1
            const StringRef empty_json("", 0);
88
            if constexpr (!ERROR_TO_NULL) {
89
                for (size_t row = 0; row < input_rows_count; ++row) {
90
                    const bool input_is_null = input_nulls != nullptr && (*input_nulls)[row] != 0;
91
                    try {
92
                        variant_util::parse_json_to_variant(
93
                                *values, input_is_null ? empty_json : strings->get_data_at(row),
94
                                &parser, parse_config);
95
                    } catch (const Exception& exception) {
96
                        const Status status = exception.to_status();
97
                        if (!status.is<ErrorCode::INVALID_ARGUMENT>()) {
98
                            return status;
99
                        }
100
                        return Status::InvalidArgument(
101
                                "Parse json document failed at row {}, error: {}", row,
102
                                status.to_string());
103
                    }
104
                    if (input_is_null) {
105
                        result_nulls->get_data()[row] = 1;
106
                    }
107
                }
108
1
            } else {
109
4
                for (size_t row = 0; row < input_rows_count; ++row) {
110
3
                    const bool input_is_null = input_nulls != nullptr && (*input_nulls)[row] != 0;
111
3
                    auto one_row = ColumnVariant::create(max_subcolumns_count, enable_doc_mode);
112
3
                    try {
113
3
                        variant_util::parse_json_to_variant(
114
3
                                *one_row, input_is_null ? empty_json : strings->get_data_at(row),
115
3
                                &parser, parse_config);
116
3
                        one_row->finalize();
117
3
                    } catch (const Exception& exception) {
118
1
                        const Status status = exception.to_status();
119
1
                        if (!status.is<ErrorCode::INVALID_ARGUMENT>()) {
120
0
                            return status;
121
0
                        }
122
1
                        one_row = ColumnVariant::create(max_subcolumns_count, enable_doc_mode);
123
1
                        variant_util::parse_json_to_variant(*one_row, empty_json, &parser,
124
1
                                                            parse_config);
125
1
                        one_row->finalize();
126
1
                        result_nulls->get_data()[row] = 1;
127
1
                    }
128
3
                    if (input_is_null) {
129
0
                        result_nulls->get_data()[row] = 1;
130
0
                    }
131
3
                    values->insert_range_from(*one_row, 0, 1);
132
3
                }
133
1
            }
134
1
            values->finalize();
135
136
1
            ColumnPtr output;
137
1
            if (result_is_nullable) {
138
1
                output = ColumnNullable::create(std::move(values), std::move(result_nulls));
139
1
            } else {
140
0
                output = std::move(values);
141
0
            }
142
1
            DORIS_CHECK_EQ(output->size(), input_rows_count);
143
1
            block.replace_by_position(result, std::move(output));
144
1
            return Status::OK();
145
1
        }
146
8
        DORIS_CHECK(dynamic_cast<const DataTypeVariantV2*>(result_type) != nullptr);
147
148
8
        JsonStringToVariantEncoder encoder(JsonToVariantOptions::current_config());
149
8
        const StringRef null_json("null", 4);
150
19
        for (size_t row = 0; row < input_rows_count; ++row) {
151
11
            if (input_nulls != nullptr && (*input_nulls)[row] != 0) {
152
0
                encoder.add_json(null_json);
153
0
                result_nulls->get_data()[row] = 1;
154
0
                continue;
155
0
            }
156
157
11
            const Status status = encoder.try_add_json(strings->get_data_at(row));
158
11
            if (status.ok()) {
159
6
                continue;
160
6
            }
161
            if constexpr (!ERROR_TO_NULL) {
162
                return Status::InvalidArgument("Parse json document failed at row {}, error: {}",
163
                                               row, status.to_string());
164
            }
165
5
            encoder.add_json(null_json);
166
5
            result_nulls->get_data()[row] = 1;
167
5
        }
168
169
8
        VariantBatchBuilder encoded = encoder.finish_batch();
170
8
        auto values = ColumnVariantV2::create();
171
8
        values->insert_encoded_batch(encoded);
172
173
8
        ColumnPtr output;
174
8
        if (result_is_nullable) {
175
8
            output = ColumnNullable::create(std::move(values), std::move(result_nulls));
176
8
        } else {
177
0
            output = std::move(values);
178
0
        }
179
        DORIS_CHECK_EQ(output->size(), input_rows_count);
180
8
        block.replace_by_position(result, std::move(output));
181
8
        return Status::OK();
182
9
    }
183
};
184
185
using FunctionParseToVariant = FunctionVariantParse<false>;
186
using FunctionParseToVariantErrorToNull = FunctionVariantParse<true>;
187
188
} // namespace
189
190
1
void register_function_variant_parse(SimpleFunctionFactory& factory) {
191
1
    factory.register_function<FunctionParseToVariant>();
192
1
    factory.register_function<FunctionParseToVariantErrorToNull>();
193
1
}
194
195
} // namespace doris