Coverage Report

Created: 2026-09-15 15:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/function/function_variant_element.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 <span>
19
20
#include "common/status.h"
21
#include "core/assert_cast.h"
22
#include "core/block/block.h"
23
#include "core/column/column.h"
24
#include "core/column/column_nullable.h"
25
#include "core/column/column_string.h"
26
#include "core/column/variant_v2/column_variant_v2.h"
27
#include "core/data_type/data_type.h"
28
#include "core/data_type/data_type_nullable.h"
29
#include "core/data_type/data_type_number.h"
30
#include "core/data_type/data_type_string.h"
31
#include "core/data_type/data_type_variant.h"
32
#include "core/string_ref.h"
33
#include "exprs/function/function.h"
34
#include "exprs/function/function_helpers.h"
35
#include "exprs/function/function_variant_element_v2.h"
36
#include "exprs/function/simple_function_factory.h"
37
38
namespace doris {
39
40
class FunctionVariantElement : public IFunction {
41
public:
42
    static constexpr auto name = "element_at";
43
2
    static FunctionPtr create() { return std::make_shared<FunctionVariantElement>(); }
44
45
    // Get function name.
46
2
    String get_name() const override { return name; }
47
48
1
    bool use_default_implementation_for_nulls() const override { return false; }
49
50
1
    size_t get_number_of_arguments() const override { return 2; }
51
52
0
    ColumnNumbers get_arguments_that_are_always_constant() const override { return {1}; }
53
54
1
    DataTypes get_variadic_argument_types_impl() const override {
55
1
        return {std::make_shared<DataTypeVariant>(), std::make_shared<DataTypeString>()};
56
1
    }
57
58
1
    DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
59
1
        DCHECK_EQ(arguments[0]->get_primitive_type(), TYPE_VARIANT)
60
0
                << "First argument for function: " << name
61
0
                << " should be DataTypeVariant but it has type " << arguments[0]->get_name() << ".";
62
1
        const PrimitiveType index_type = remove_nullable(arguments[1])->get_primitive_type();
63
1
        DCHECK(is_string_type(index_type) || is_int_or_bool(index_type))
64
0
                << "Second argument for function: " << name
65
0
                << " should be String or Integer but it has type " << arguments[1]->get_name()
66
0
                << ".";
67
1
        auto arg_variant = remove_nullable(arguments[0]);
68
1
        return make_nullable(std::move(arg_variant));
69
1
    }
70
71
    // Keep physical-column dispatch in one entry point so nullable handling stays shared.
72
    // NOLINTNEXTLINE(readability-function-size)
73
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
74
0
                        uint32_t result, size_t input_rows_count) const override {
75
0
        const ColumnPtr materialized =
76
0
                block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
77
0
        const IColumn* physical = materialized.get();
78
0
        std::span<const uint8_t> outer_nulls;
79
0
        if (const auto* nullable = check_and_get_column<ColumnNullable>(physical)) {
80
0
            outer_nulls = nullable->get_null_map_data();
81
0
            physical = &nullable->get_nested_column();
82
0
        }
83
0
        if (const auto* variant_v2 = check_and_get_column<ColumnVariantV2>(physical)) {
84
0
            if (block.empty()) {
85
0
                block.replace_by_position(result, ColumnNullable::create(ColumnVariantV2::create(),
86
0
                                                                         ColumnUInt8::create()));
87
0
                return Status::OK();
88
0
            }
89
90
0
            auto replace_with_all_null_result = [&]() {
91
0
                auto null_values = ColumnVariantV2::create();
92
0
                null_values->insert_many_defaults(variant_v2->size());
93
0
                block.replace_by_position(
94
0
                        result, ColumnNullable::create(std::move(null_values),
95
0
                                                       ColumnUInt8::create(variant_v2->size(), 1)));
96
0
            };
97
0
            const auto& index_argument = block.get_by_position(arguments[1]);
98
0
            const ColumnPtr materialized_index =
99
0
                    index_argument.column->convert_to_full_column_if_const();
100
0
            const IColumn* index_column = materialized_index.get();
101
0
            if (index_column->is_null_at(0)) {
102
0
                replace_with_all_null_result();
103
0
                return Status::OK();
104
0
            }
105
0
            if (const auto* nullable = check_and_get_column<ColumnNullable>(*index_column)) {
106
0
                index_column = &nullable->get_nested_column();
107
0
            }
108
109
0
            std::optional<VariantElementV2PathSegment> segment;
110
0
            const PrimitiveType index_type =
111
0
                    remove_nullable(index_argument.type)->get_primitive_type();
112
0
            if (is_string_type(index_type)) {
113
0
                segment = VariantElementV2PathSegment::object_key(index_column->get_data_at(0));
114
0
            } else if (is_int_or_bool(index_type)) {
115
0
                const int64_t sql_index = index_column->get_int(0);
116
0
                if (sql_index == 0) {
117
0
                    replace_with_all_null_result();
118
0
                    return Status::OK();
119
0
                }
120
0
                segment = VariantElementV2PathSegment::array_index(sql_index > 0 ? sql_index - 1
121
0
                                                                                 : sql_index);
122
0
            } else {
123
0
                return Status::RuntimeError("unsupported index type {} for function {}",
124
0
                                            index_argument.type->get_name(), get_name());
125
0
            }
126
0
            std::unique_ptr<ResolvedVariantElementV2Path> path;
127
0
            RETURN_IF_ERROR(resolve_variant_element_v2_path(std::span(&*segment, 1), &path));
128
0
            ColumnPtr result_column;
129
0
            RETURN_IF_ERROR(
130
0
                    extract_variant_element_v2(*variant_v2, *path, outer_nulls, &result_column));
131
0
            block.replace_by_position(result, std::move(result_column));
132
0
            return Status::OK();
133
0
        }
134
135
0
        return Status::RuntimeError("element_at requires ColumnVariantV2, got {}",
136
0
                                    physical->get_name());
137
0
    }
138
};
139
140
class FunctionVariantElementByInteger final : public FunctionVariantElement {
141
public:
142
    static constexpr auto name = FunctionVariantElement::name;
143
3
    static FunctionPtr create() { return std::make_shared<FunctionVariantElementByInteger>(); }
144
145
1
    DataTypes get_variadic_argument_types_impl() const override {
146
1
        return {std::make_shared<DataTypeVariant>(), std::make_shared<DataTypeInt64>()};
147
1
    }
148
};
149
150
1
void register_function_variant_element(SimpleFunctionFactory& factory) {
151
1
    factory.register_function<FunctionVariantElement>();
152
1
    factory.register_function<FunctionVariantElementByInteger>();
153
1
}
154
155
} // namespace doris