diff --git a/be/src/vec/CMakeLists.txt b/be/src/vec/CMakeLists.txt index 2bf3f245a1..98ef93e7a0 100644 --- a/be/src/vec/CMakeLists.txt +++ b/be/src/vec/CMakeLists.txt @@ -216,6 +216,7 @@ set(VEC_FILES functions/array/function_array_count.cpp functions/function_map.cpp functions/function_struct.cpp + functions/function_struct_element.cpp exprs/table_function/vexplode_json_array.cpp functions/math.cpp functions/function_bitmap.cpp diff --git a/be/src/vec/data_types/data_type.h b/be/src/vec/data_types/data_type.h index 6b10084e50..7a26b9908a 100644 --- a/be/src/vec/data_types/data_type.h +++ b/be/src/vec/data_types/data_type.h @@ -344,6 +344,9 @@ inline bool is_array(const DataTypePtr& data_type) { inline bool is_map(const DataTypePtr& data_type) { return WhichDataType(data_type).is_map(); } +inline bool is_struct(const DataTypePtr& data_type) { + return WhichDataType(data_type).is_struct(); +} inline bool is_nothing(const DataTypePtr& data_type) { return WhichDataType(data_type).is_nothing(); } diff --git a/be/src/vec/data_types/data_type_struct.cpp b/be/src/vec/data_types/data_type_struct.cpp index 663f7af949..0e7028ff0a 100644 --- a/be/src/vec/data_types/data_type_struct.cpp +++ b/be/src/vec/data_types/data_type_struct.cpp @@ -472,7 +472,9 @@ const char* DataTypeStruct::deserialize(const char* buf, IColumn* column, void DataTypeStruct::to_pb_column_meta(PColumnMeta* col_meta) const { IDataType::to_pb_column_meta(col_meta); for (size_t i = 0; i < elems.size(); ++i) { - elems[i]->to_pb_column_meta(col_meta->add_children()); + auto child = col_meta->add_children(); + child->set_name(names[i]); + elems[i]->to_pb_column_meta(child); } } diff --git a/be/src/vec/functions/function_struct_element.cpp b/be/src/vec/functions/function_struct_element.cpp new file mode 100644 index 0000000000..4b98522ba6 --- /dev/null +++ b/be/src/vec/functions/function_struct_element.cpp @@ -0,0 +1,134 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include +#include + +#include +#include +#include +#include + +#include "common/status.h" +#include "vec/columns/column.h" +#include "vec/columns/column_const.h" +#include "vec/columns/column_string.h" +#include "vec/columns/column_struct.h" +#include "vec/core/block.h" +#include "vec/data_types/data_type.h" +#include "vec/data_types/data_type_nothing.h" +#include "vec/data_types/data_type_nullable.h" +#include "vec/data_types/data_type_struct.h" +#include "vec/functions/function.h" +#include "vec/functions/function_helpers.h" +#include "vec/functions/simple_function_factory.h" + +namespace doris::vectorized { + +class FunctionStructElement : public IFunction { +public: + static constexpr auto name = "struct_element"; + static FunctionPtr create() { return std::make_shared(); } + + // Get function name. + String get_name() const override { return name; } + + bool use_default_implementation_for_nulls() const override { return true; } + + bool use_default_implementation_for_constants() const override { return true; } + + size_t get_number_of_arguments() const override { return 2; } + + ColumnNumbers get_arguments_that_are_always_constant() const override { return {1}; } + + DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { + DCHECK(is_struct(remove_nullable(arguments[0]))) + << "First argument for function: " << name + << " should be DataTypeStruct but it has type " << arguments[0]->get_name() << "."; + DCHECK(is_integer(arguments[1]) || is_string(arguments[1])) + << "Second argument for function: " << name + << " should be Int or String but it has type " << arguments[1]->get_name() << "."; + // Due to the inability to get the actual value of the index column + // in function's build stage, we directly return nothing here. + // Todo(xy): Is there any good way to return right type? + return make_nullable(std::make_shared()); + } + + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, + size_t result, size_t input_rows_count) override { + auto struct_type = check_and_get_data_type( + block.get_by_position(arguments[0]).type.get()); + auto struct_col = check_and_get_column( + block.get_by_position(arguments[0]).column.get()); + if (!struct_col || !struct_type) { + return Status::RuntimeError( + fmt::format("unsupported types for function {}({}, {})", get_name(), + block.get_by_position(arguments[0]).type->get_name(), + block.get_by_position(arguments[1]).type->get_name())); + } + + auto index_column = block.get_by_position(arguments[1]).column; + auto index_type = block.get_by_position(arguments[1]).type; + size_t index; + Status res = get_element_index(*struct_type, index_column, index_type, &index); + if (res == Status::OK()) { + ColumnPtr res_column = struct_col->get_column_ptr(index); + block.replace_by_position(result, res_column->clone_resized(res_column->size())); + return res; + } + return res; + } + +private: + Status get_element_index(const DataTypeStruct& struct_type, const ColumnPtr& index_column, + const DataTypePtr& index_type, size_t* result) const { + size_t index; + if (is_integer(index_type)) { + index = index_column->get_int(0); + size_t limit = struct_type.get_elements().size() + 1; + if (index < 1 || index >= limit) { + return Status::RuntimeError( + fmt::format("Index out of bound for function {}: index {} should base from " + "1 and less than {}.", + get_name(), index, limit)); + } + index -= 1; // the index start from 1 + } else if (is_string(index_type)) { + std::string field_name = index_column->get_data_at(0).to_string(); + std::optional pos = struct_type.try_get_position_by_name(field_name); + if (!pos.has_value()) { + return Status::RuntimeError( + fmt::format("Element not found for function {}: name {} not found in {}.", + get_name(), field_name, struct_type.get_name())); + } + index = pos.value(); + } else { + return Status::RuntimeError( + fmt::format("Argument not supported for function {}: second arg type {} should " + "be int or string.", + get_name(), index_type->get_name())); + } + *result = index; + return Status::OK(); + } +}; + +void register_function_struct_element(SimpleFunctionFactory& factory) { + factory.register_function(); +} + +} // namespace doris::vectorized diff --git a/be/src/vec/functions/simple_function_factory.h b/be/src/vec/functions/simple_function_factory.h index c38534d943..d19b2a9f38 100644 --- a/be/src/vec/functions/simple_function_factory.h +++ b/be/src/vec/functions/simple_function_factory.h @@ -85,6 +85,7 @@ void register_function_fake(SimpleFunctionFactory& factory); void register_function_array(SimpleFunctionFactory& factory); void register_function_map(SimpleFunctionFactory& factory); void register_function_struct(SimpleFunctionFactory& factory); +void register_function_struct_element(SimpleFunctionFactory& factory); void register_function_geo(SimpleFunctionFactory& factory); void register_function_multi_string_position(SimpleFunctionFactory& factory); void register_function_multi_string_search(SimpleFunctionFactory& factory); @@ -260,6 +261,7 @@ public: register_function_array(instance); register_function_map(instance); register_function_struct(instance); + register_function_struct_element(instance); register_function_geo(instance); register_function_url(instance); register_function_multi_string_position(instance); diff --git a/docs/en/docs/sql-manual/sql-functions/struct-functions/struct_element.md b/docs/en/docs/sql-manual/sql-functions/struct-functions/struct_element.md new file mode 100644 index 0000000000..5fef24bab6 --- /dev/null +++ b/docs/en/docs/sql-manual/sql-functions/struct-functions/struct_element.md @@ -0,0 +1,93 @@ +--- +{ + "title": "struct_element", + "language": "en" +} +--- + + + +## struct_element + + + +struct_element + + + +### description + +Function allows getting a field from a struct. + +#### Syntax + +``` +struct_element(struct, n/s) +``` + +#### Arguments + +``` +struct - The input struct column. If null, null will be returned. +n - The position of field,starting from 1,only supports constants. +s - The name of field,only supports constants. +``` + +#### Returned value + +Returns the specified field column, of any type. + +### notice + +`Only supported in vectorized engine` + +### example + +``` +mysql> select struct_element(named_struct('f1', 1, 'f2', 'a'), 'f2'); ++--------------------------------------------------------+ +| struct_element(named_struct('f1', 1, 'f2', 'a'), 'f2') | ++--------------------------------------------------------+ +| a | ++--------------------------------------------------------+ +1 row in set (0.03 sec) + +mysql> select struct_element(named_struct('f1', 1, 'f2', 'a'), 1); ++-----------------------------------------------------+ +| struct_element(named_struct('f1', 1, 'f2', 'a'), 1) | ++-----------------------------------------------------+ +| 1 | ++-----------------------------------------------------+ +1 row in set (0.02 sec) + +mysql> select struct_col, struct_element(struct_col, 'f1') from test_struct; ++-------------------------------------------------+-------------------------------------+ +| struct_col | struct_element(`struct_col `, 'f1') | ++-------------------------------------------------+-------------------------------------+ +| {1, 2, 3, 4, 5} | 1 | +| {1, 1000, 10000000, 100000000000, 100000000000} | 1 | +| {5, 4, 3, 2, 1} | 5 | +| NULL | NULL | +| {1, NULL, 3, NULL, 5} | 1 | ++-------------------------------------------------+-------------------------------------+ +9 rows in set (0.01 sec) +``` + +### keywords + +STRUCT, ELEMENT, STRUCT_ELEMENT \ No newline at end of file diff --git a/docs/sidebars.json b/docs/sidebars.json index 04e6dca9ec..b1598a44d3 100644 --- a/docs/sidebars.json +++ b/docs/sidebars.json @@ -499,7 +499,8 @@ "label": "Struct Functions", "items": [ "sql-manual/sql-functions/struct-functions/struct", - "sql-manual/sql-functions/struct-functions/named_struct" + "sql-manual/sql-functions/struct-functions/named_struct", + "sql-manual/sql-functions/struct-functions/struct_element" ] }, { diff --git a/docs/zh-CN/docs/sql-manual/sql-functions/struct-functions/struct_element.md b/docs/zh-CN/docs/sql-manual/sql-functions/struct-functions/struct_element.md new file mode 100644 index 0000000000..6df76cf69b --- /dev/null +++ b/docs/zh-CN/docs/sql-manual/sql-functions/struct-functions/struct_element.md @@ -0,0 +1,93 @@ +--- +{ + "title": "struct_element", + "language": "zh-CN" +} +--- + + + +## struct_element + + + +struct_element + + + +### description + +返回struct数据列内的某一field + +#### Syntax + +``` +struct_element(struct, n/s) +``` + +#### Arguments + +``` +struct - 输入的struct列,如果是null,则返回null +n - field的位置,起始位置从1开始,仅支持常量 +s - field的名字,仅支持常量 +``` + +#### Returned value + +返回指定的field列,类型为任意类型 + +### notice + +`只支持在向量化引擎中使用。` + +### example + +``` +mysql> select struct_element(named_struct('f1', 1, 'f2', 'a'), 'f2'); ++--------------------------------------------------------+ +| struct_element(named_struct('f1', 1, 'f2', 'a'), 'f2') | ++--------------------------------------------------------+ +| a | ++--------------------------------------------------------+ +1 row in set (0.03 sec) + +mysql> select struct_element(named_struct('f1', 1, 'f2', 'a'), 1); ++-----------------------------------------------------+ +| struct_element(named_struct('f1', 1, 'f2', 'a'), 1) | ++-----------------------------------------------------+ +| 1 | ++-----------------------------------------------------+ +1 row in set (0.02 sec) + +mysql> select struct_col, struct_element(struct_col, 'f1') from test_struct; ++-------------------------------------------------+-------------------------------------+ +| struct_col | struct_element(`struct_col `, 'f1') | ++-------------------------------------------------+-------------------------------------+ +| {1, 2, 3, 4, 5} | 1 | +| {1, 1000, 10000000, 100000000000, 100000000000} | 1 | +| {5, 4, 3, 2, 1} | 5 | +| NULL | NULL | +| {1, NULL, 3, NULL, 5} | 1 | ++-------------------------------------------------+-------------------------------------+ +9 rows in set (0.01 sec) +``` + +### keywords + +STRUCT, ELEMENT, STRUCT_ELEMENT \ No newline at end of file diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/AnyElementType.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/AnyElementType.java new file mode 100644 index 0000000000..37faeaa196 --- /dev/null +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/AnyElementType.java @@ -0,0 +1,35 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.catalog; + +/** + * Describes a AnyElementType type, used for SQL function return type, + * NOT used for table column type. + */ +public class AnyElementType extends AnyType { + + @Override + public boolean equals(Object obj) { + return obj instanceof AnyElementType; + } + + @Override + public boolean matchesType(Type t) { + return true; + } +} diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/AnyStructType.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/AnyStructType.java new file mode 100644 index 0000000000..8db214d0da --- /dev/null +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/AnyStructType.java @@ -0,0 +1,35 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.catalog; + +/** + * Describes a AnyStructType type, used for SQL function return type, + * NOT used for table column type. + */ +public class AnyStructType extends AnyType { + + @Override + public boolean equals(Object obj) { + return obj instanceof AnyStructType; + } + + @Override + public boolean matchesType(Type t) { + return t instanceof AnyStructType || t instanceof AnyElementType || t.isStructType(); + } +} diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/ArrayType.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/ArrayType.java index 8ae4efdcee..d3bd642a6d 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/catalog/ArrayType.java +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/ArrayType.java @@ -80,6 +80,10 @@ public class ArrayType extends Type { return true; } + if (t.isAnyType()) { + return t.matchesType(this); + } + if (!t.isArrayType()) { return false; } diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/MapType.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/MapType.java index a1000b9058..8b7a83bc93 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/catalog/MapType.java +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/MapType.java @@ -109,6 +109,10 @@ public class MapType extends Type { return true; } + if (t.isAnyType()) { + return t.matchesType(this); + } + if (!t.isMapType()) { return false; } diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java index 9493b6d6ae..925a48280c 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java @@ -823,6 +823,9 @@ public class ScalarType extends Type { if (equals(t)) { return true; } + if (t.isAnyType()) { + return t.matchesType(this); + } if (!t.isScalarType()) { return false; } diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/StructType.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/StructType.java index bbf6a87f01..bd1efec99b 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/catalog/StructType.java +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/StructType.java @@ -46,9 +46,6 @@ public class StructType extends Type { @SerializedName(value = "fieldMap") private final HashMap fieldMap = Maps.newHashMap(); - //@SerializedName(value = "positionToField") - //private final HashMap positionToField = Maps.newHashMap(); - @SerializedName(value = "fields") private final ArrayList fields; @@ -58,7 +55,6 @@ public class StructType extends Type { for (int i = 0; i < this.fields.size(); ++i) { this.fields.get(i).setPosition(i); fieldMap.put(this.fields.get(i).getName().toLowerCase(), this.fields.get(i)); - //positionToField.put(this.fields.get(i).getPosition(), this.fields.get(i)); } } @@ -138,7 +134,6 @@ public class StructType extends Type { field.setPosition(fields.size()); fields.add(field); fieldMap.put(field.getName().toLowerCase(), field); - //positionToField.put(field.getPosition(), field); } public ArrayList getFields() { @@ -149,14 +144,9 @@ public class StructType extends Type { return fieldMap.get(fieldName.toLowerCase()); } - //public StructField getField(int position) { - // return positionToField.get(position); - //} - public void clearFields() { fields.clear(); fieldMap.clear(); - //positionToField.clear(); } @Override @@ -170,6 +160,10 @@ public class StructType extends Type { return true; } + if (t.isAnyType()) { + return t.matchesType(this); + } + if (!t.isStructType()) { return false; } diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java index 9785988f31..55f386d428 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java @@ -112,7 +112,8 @@ public abstract class Type { new StructField("generic_struct", new ScalarType(PrimitiveType.NULL_TYPE)))); public static final StructType STRUCT = new StructType(); public static final VariantType VARIANT = new VariantType(); - public static final AnyType ANY_TYPE = new AnyType(); + public static final AnyType ANY_STRUCT_TYPE = new AnyStructType(); + public static final AnyType ANY_ELEMENT_TYPE = new AnyElementType(); private static final Logger LOG = LogManager.getLogger(Type.class); private static final ArrayList integerTypes; @@ -568,6 +569,10 @@ public abstract class Type { return this instanceof StructType; } + public boolean isAnyType() { + return this instanceof AnyType; + } + public boolean isDate() { return isScalarType(PrimitiveType.DATE); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java index ec2884d0b6..4267d3586f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java @@ -1590,6 +1590,31 @@ public class FunctionCallExpr extends Expr { } } + if (fn.getFunctionName().getFunction().equals("struct_element")) { + if (children.size() < 2) { + throw new AnalysisException(fnName.getFunction() + " needs two parameters: " + this.toSql()); + } + if (getChild(0).type instanceof StructType) { + StructType s = ((StructType) children.get(0).type); + if (getChild(1) instanceof StringLiteral) { + String fieldName = children.get(1).getStringValue(); + if (s.getField(fieldName) == null) { + throw new AnalysisException( + "the specified field name " + fieldName + " was not found: " + this.toSql()); + } + } else if (getChild(1) instanceof IntLiteral) { + int pos = (int) ((IntLiteral) children.get(1)).getValue(); + if (pos < 1 || pos > s.getFields().size()) { // the index start from 1 + throw new AnalysisException( + "the specified field index out of bound: " + this.toSql()); + } + } else { + throw new AnalysisException( + "struct_element only allows constant int or string second parameter: " + this.toSql()); + } + } + } + if (isAggregateFunction()) { final String functionName = fnName.getFunction(); // subexprs must not contain aggregates @@ -1796,6 +1821,15 @@ public class FunctionCallExpr extends Expr { this.type = new StructType(newFields); } else if (fnName.getFunction().equalsIgnoreCase("topn_array")) { this.type = new ArrayType(children.get(0).getType()); + } else if (fnName.getFunction().equalsIgnoreCase("struct_element")) { + if (children.get(1) instanceof StringLiteral) { + String fieldName = children.get(1).getStringValue(); + fn.setReturnType(((StructType) children.get(0).type).getField(fieldName).getType()); + } else if (children.get(1) instanceof IntLiteral) { + int pos = (int) ((IntLiteral) children.get(1)).getValue(); + fn.setReturnType(((StructType) children.get(0).type).getFields().get(pos - 1).getType()); + } + this.type = fn.getReturnType(); } else if (fnName.getFunction().equalsIgnoreCase("array_distinct") || fnName.getFunction() .equalsIgnoreCase("array_remove") || fnName.getFunction().equalsIgnoreCase("array_sort") || fnName.getFunction().equalsIgnoreCase("array_reverse_sort") diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java index a879c14ae3..6abff46b6f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java @@ -37,6 +37,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -1155,11 +1156,20 @@ public class FunctionSet { } public Function resolveInferenceFunction(Function inferenceFunction, Function requestFunction) { - Type[] args = requestFunction.getArgs(); - Type newRetType = FunctionTypeDeducers.deduce(inferenceFunction.functionName(), args); + Type[] inputArgs = requestFunction.getArgs(); + Type[] inferArgs = inferenceFunction.getArgs(); + Type[] newTypes = Arrays.copyOf(inputArgs, inputArgs.length); + for (int i = 0; i < inferArgs.length; ++i) { + Type inferType = inferArgs[i]; + Type inputType = inputArgs[i]; + if (!inferType.isAnyType()) { + newTypes[i] = inputType; + } + } + Type newRetType = FunctionTypeDeducers.deduce(inferenceFunction.functionName(), newTypes); if (newRetType != null && inferenceFunction instanceof ScalarFunction) { ScalarFunction f = (ScalarFunction) inferenceFunction; - return new ScalarFunction(f.getFunctionName(), Lists.newArrayList(f.getArgs()), newRetType, f.hasVarArgs(), + return new ScalarFunction(f.getFunctionName(), Lists.newArrayList(newTypes), newRetType, f.hasVarArgs(), f.getSymbolName(), f.getBinaryType(), f.isUserVisible(), f.isVectorized(), f.getNullableMode()); } return null; diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionTypeDeducers.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionTypeDeducers.java index 0e547cdcc3..80c78248ac 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionTypeDeducers.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionTypeDeducers.java @@ -30,7 +30,7 @@ public class FunctionTypeDeducers { public static final ImmutableMap DEDUCERS = ImmutableMap.builder() .put("named_struct", new NamedStructDeducer()) - .put("element_at", new ElementAtDeducer()) + .put("struct_element", new StructElementDeducer()) .build(); public static Type deduce(String fnName, Type[] args) { @@ -53,10 +53,12 @@ public class FunctionTypeDeducers { } } - public static class ElementAtDeducer implements TypeDeducer { + public static class StructElementDeducer implements TypeDeducer { @Override public Type deduce(Type[] args) { - // todo(xy) + if (args[0] instanceof StructType) { + return Type.ANY_ELEMENT_TYPE; + } return null; } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/catalog/FunctionSetTest.java b/fe/fe-core/src/test/java/org/apache/doris/catalog/FunctionSetTest.java index 737a3f9b40..8dbfe7e31c 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/catalog/FunctionSetTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/FunctionSetTest.java @@ -59,7 +59,7 @@ public class FunctionSetTest { TemplateType type1 = new TemplateType("T"); TemplateType type2 = new TemplateType("T"); functionSet.addBuiltinBothScalaAndVectorized(ScalarFunction.createBuiltin( - "test_a", Type.ANY_TYPE, Lists.newArrayList(type1, type2), false, + "test_a", Type.ANY_ELEMENT_TYPE, Lists.newArrayList(type1, type2), false, "", "", "", true)); Type[] argTypes = {ArrayType.create(), ScalarType.INT}; Function desc = new Function(new FunctionName("test_a"), Arrays.asList(argTypes), ScalarType.INVALID, false); diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py index 464d665390..61eb7c39ab 100644 --- a/gensrc/script/doris_builtins_functions.py +++ b/gensrc/script/doris_builtins_functions.py @@ -86,7 +86,9 @@ visible_functions = { # struct functions "struct": [ [['struct'], 'STRUCT', ['TYPES'], 'ALWAYS_NOT_NULLABLE', ['TYPES...']], - [['named_struct'], 'ANY_TYPE', ['TYPES'], 'ALWAYS_NOT_NULLABLE', ['TYPES...']] + [['named_struct'], 'ANY_STRUCT_TYPE', ['TYPES'], 'ALWAYS_NOT_NULLABLE', ['TYPES...']], + [['struct_element'], 'ANY_ELEMENT_TYPE', ['ANY_STRUCT_TYPE', 'INT'], 'ALWAYS_NULLABLE', ['TYPES...']], + [['struct_element'], 'ANY_ELEMENT_TYPE', ['ANY_STRUCT_TYPE', 'VARCHAR'], 'ALWAYS_NULLABLE', ['TYPES...']] ], # array functions diff --git a/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions.out b/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions.out index 995c9cc80f..ffce5c3e19 100644 Binary files a/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions.out and b/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions.out differ diff --git a/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.out b/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.out index e64e28b768..17e391b283 100644 --- a/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.out +++ b/regression-test/data/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.out @@ -26,3 +26,9 @@ -- !sql -- {NULL, NULL, NULL} +-- !sql -- +1 + +-- !sql -- +10000000000 + diff --git a/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions.groovy b/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions.groovy index ca1da4410f..aa519ca7f1 100644 --- a/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions.groovy +++ b/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions.groovy @@ -42,4 +42,9 @@ suite("test_struct_functions") { sql """ INSERT INTO ${tableName} VALUES(6,NULL,NULL,NULL,{"NULL",'null',NULL}) """ qt_select_all "SELECT * FROM ${tableName} ORDER BY k1" + + qt_select_struct_element_1 "SELECT struct_element(k2,'f1'),struct_element(k2,'f2'),struct_element(k2,'f3'),struct_element(k2,'f4'),struct_element(k2,'f5') FROM ${tableName} ORDER BY k1" + qt_select_struct_element_2 "SELECT struct_element(k3,'f1'),struct_element(k3,'f2'),struct_element(k3,'f3') FROM ${tableName} ORDER BY k1" + qt_select_struct_element_3 "SELECT struct_element(k4,1),struct_element(k4,2),struct_element(k4,3),struct_element(k4,4) FROM ${tableName} ORDER BY k1" + qt_select_struct_element_4 "SELECT struct_element(k5,1),struct_element(k5,2),struct_element(k5,3) FROM ${tableName} ORDER BY k1" } diff --git a/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.groovy b/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.groovy index bb7512882e..a7ae109fd3 100644 --- a/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.groovy +++ b/regression-test/suites/query_p0/sql_functions/struct_functions/test_struct_functions_by_literal.groovy @@ -27,4 +27,7 @@ suite("test_struct_functions_by_literal") { qt_sql "select named_struct('f1', 1, 'f2', 1000, 'f3', 10000000000)" qt_sql "select named_struct('f1', 1, 'f2', 'doris', 'f3', 1.32)" qt_sql "select named_struct('f1', null, 'f2', null, 'f3', null)" + + qt_sql "select struct_element(named_struct('f1', 1, 'f2', 2, 'f3', 3), 'f1')" + qt_sql "select struct_element(named_struct('f1', 1, 'f2', 1000, 'f3', 10000000000), 3)" }