Coverage Report

Created: 2026-07-29 18:07

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