be/src/exprs/function/function_struct_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 <glog/logging.h> |
19 | | #include <stddef.h> |
20 | | |
21 | | #include <memory> |
22 | | #include <ostream> |
23 | | #include <string> |
24 | | #include <utility> |
25 | | |
26 | | #include "common/status.h" |
27 | | #include "core/block/block.h" |
28 | | #include "core/column/column.h" |
29 | | #include "core/column/column_const.h" |
30 | | #include "core/column/column_nullable.h" |
31 | | #include "core/column/column_string.h" |
32 | | #include "core/column/column_struct.h" |
33 | | #include "core/data_type/data_type.h" |
34 | | #include "core/data_type/data_type_nothing.h" |
35 | | #include "core/data_type/data_type_nullable.h" |
36 | | #include "core/data_type/data_type_struct.h" |
37 | | #include "exprs/function/function.h" |
38 | | #include "exprs/function/function_helpers.h" |
39 | | #include "exprs/function/simple_function_factory.h" |
40 | | |
41 | | namespace doris { |
42 | | |
43 | | class FunctionStructElement : public IFunction { |
44 | | public: |
45 | | static constexpr auto name = "struct_element"; |
46 | 4 | static FunctionPtr create() { return std::make_shared<FunctionStructElement>(); } |
47 | | |
48 | | // Get function name. |
49 | 1 | String get_name() const override { return name; } |
50 | | |
51 | 2 | size_t get_number_of_arguments() const override { return 2; } |
52 | | |
53 | 1 | ColumnNumbers get_arguments_that_are_always_constant() const override { return {1}; } |
54 | | |
55 | 2 | DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) const override { |
56 | 2 | DCHECK(arguments[0].type->get_primitive_type() == TYPE_STRUCT) |
57 | 0 | << "First argument for function: " << name |
58 | 0 | << " should be DataTypeStruct but it has type " << arguments[0].type->get_name() |
59 | 0 | << "."; |
60 | 2 | DCHECK(is_int_or_bool(arguments[1].type->get_primitive_type()) || |
61 | 0 | is_string_type(arguments[1].type->get_primitive_type())) |
62 | 0 | << "Second argument for function: " << name |
63 | 0 | << " should be Int or String but it has type " << arguments[1].type->get_name() |
64 | 0 | << "."; |
65 | | |
66 | 2 | const auto* struct_type = check_and_get_data_type<DataTypeStruct>(arguments[0].type.get()); |
67 | | |
68 | 2 | auto index_type = arguments[1].type; |
69 | 2 | const auto& index_column = arguments[1].column; |
70 | 2 | if (!index_column) { |
71 | 0 | throw doris::Exception( |
72 | 0 | ErrorCode::INTERNAL_ERROR, |
73 | 0 | "Function {}: second argument column is nullptr, but it should not be.", |
74 | 0 | get_name()); |
75 | 0 | } |
76 | 2 | size_t index; |
77 | 2 | auto st = get_element_index(*struct_type, index_column, index_type, &index); |
78 | 2 | if (!st.ok()) { |
79 | | // will handle nullptr outside |
80 | 0 | return nullptr; |
81 | 0 | } |
82 | | // The struct_element is marked as AlwaysNullable in fe. |
83 | 2 | return make_nullable(struct_type->get_elements()[index]); |
84 | 2 | } |
85 | | |
86 | | Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, |
87 | 1 | uint32_t result, size_t input_rows_count) const override { |
88 | 1 | const auto* struct_type = check_and_get_data_type<DataTypeStruct>( |
89 | 1 | block.get_by_position(arguments[0]).type.get()); |
90 | 1 | const auto* struct_col = check_and_get_column<ColumnStruct>( |
91 | 1 | block.get_by_position(arguments[0]).column.get()); |
92 | 1 | if (!struct_col || !struct_type) { |
93 | 0 | return Status::RuntimeError( |
94 | 0 | fmt::format("unsupported types for function {}({}, {})", get_name(), |
95 | 0 | block.get_by_position(arguments[0]).type->get_name(), |
96 | 0 | block.get_by_position(arguments[1]).type->get_name())); |
97 | 0 | } |
98 | | |
99 | 1 | auto index_column = block.get_by_position(arguments[1]).column; |
100 | 1 | auto index_type = block.get_by_position(arguments[1]).type; |
101 | 1 | size_t index; |
102 | 1 | RETURN_IF_ERROR(get_element_index(*struct_type, index_column, index_type, &index)); |
103 | 1 | ColumnPtr res_column = struct_col->get_column_ptr(index); |
104 | 1 | ColumnPtr ele_column = res_column->clone_resized(res_column->size()); |
105 | | //This function must return a ColumnNullable column, so it is necessary to convert the result column into ColumnNullable. |
106 | 1 | block.replace_by_position(result, make_nullable(ele_column)); |
107 | 1 | return Status::OK(); |
108 | 1 | } |
109 | | |
110 | | private: |
111 | | Status get_element_index(const DataTypeStruct& struct_type, const ColumnPtr& index_column, |
112 | 3 | const DataTypePtr& index_type, size_t* result) const { |
113 | 3 | size_t index; |
114 | 3 | if (is_int_or_bool(index_type->get_primitive_type())) { |
115 | 0 | index = index_column->get_int(0); |
116 | 0 | size_t limit = struct_type.get_elements().size() + 1; |
117 | 0 | if (index < 1 || index >= limit) { |
118 | 0 | return Status::RuntimeError( |
119 | 0 | fmt::format("Index out of bound for function {}: index {} should base from " |
120 | 0 | "1 and less than {}.", |
121 | 0 | get_name(), index, limit)); |
122 | 0 | } |
123 | 0 | index -= 1; // the index start from 1 |
124 | 3 | } else if (is_string_type(index_type->get_primitive_type())) { |
125 | 3 | std::string field_name = index_column->get_data_at(0).to_string(); |
126 | 3 | std::optional<size_t> pos = struct_type.try_get_position_by_name(field_name); |
127 | 3 | if (!pos.has_value()) { |
128 | 0 | return Status::RuntimeError( |
129 | 0 | fmt::format("Element not found for function {}: name {} not found in {}.", |
130 | 0 | get_name(), field_name, struct_type.get_name())); |
131 | 0 | } |
132 | 3 | index = pos.value(); |
133 | 3 | } else { |
134 | 0 | return Status::RuntimeError( |
135 | 0 | fmt::format("Argument not supported for function {}: second arg type {} should " |
136 | 0 | "be int or string.", |
137 | 0 | get_name(), index_type->get_name())); |
138 | 0 | } |
139 | 3 | *result = index; |
140 | 3 | return Status::OK(); |
141 | 3 | } |
142 | | }; |
143 | | |
144 | 1 | void register_function_struct_element(SimpleFunctionFactory& factory) { |
145 | 1 | factory.register_function<FunctionStructElement>(); |
146 | 1 | } |
147 | | |
148 | | } // namespace doris |