This is an automated email from the ASF dual-hosted git repository. xuyang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new 4b743061b4 [feature](function) support type template in SQL function (#17344) 4b743061b4 is described below commit 4b743061b45dbf5a5bd86f368271c55c0a3baaea Author: Kang <kxiao.ti...@gmail.com> AuthorDate: Wed Mar 8 10:51:31 2023 +0800 [feature](function) support type template in SQL function (#17344) A new way just like c++ template is proposed in this PR. The previous functions can be defined much simpler using template function. # map element extract template function [['element_at', '%element_extract%'], 'E', ['ARRAY<E>', 'BIGINT'], 'ALWAYS_NULLABLE', ['E']], # map element extract template function [['element_at', '%element_extract%'], 'V', ['MAP<K, V>', 'K'], 'ALWAYS_NULLABLE', ['K', 'V']], BTW, the plain type function is not affected and the legacy ARRAY_X MAP_K_V is still supported for compatability. --- .../java/org/apache/doris/catalog/ArrayType.java | 22 ++++ .../java/org/apache/doris/catalog/MapType.java | 33 ++++++ .../org/apache/doris/catalog/PrimitiveType.java | 1 + .../org/apache/doris/catalog/TemplateType.java | 132 +++++++++++++++++++++ .../main/java/org/apache/doris/catalog/Type.java | 17 +++ .../apache/doris/analysis/FunctionCallExpr.java | 59 +++++---- .../java/org/apache/doris/catalog/Function.java | 10 ++ .../java/org/apache/doris/catalog/FunctionSet.java | 67 +++++++++++ gensrc/script/doris_builtins_functions.py | 8 +- gensrc/script/gen_builtins_functions.py | 41 +++++-- .../stream_load/test_map_load_and_function.out | 53 ++++++--- .../stream_load/test_map_load_and_function.groovy | 33 +++++- 12 files changed, 413 insertions(+), 63 deletions(-) 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 6cc9162dc6..a28f3c0302 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 @@ -27,6 +27,7 @@ import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.gson.annotations.SerializedName; +import java.util.Map; import java.util.Objects; /** @@ -92,6 +93,27 @@ public class ArrayType extends Type { && (((ArrayType) t).containsNull || !containsNull); } + @Override + public boolean hasTemplateType() { + return itemType.hasTemplateType(); + } + + @Override + public Type specializeTemplateType(Type specificType, Map<String, Type> specializedTypeMap, + boolean useSpecializedType) throws TypeException { + if (!(specificType instanceof ArrayType)) { + throw new TypeException(specificType + " is not ArrayType"); + } + + ArrayType o = (ArrayType) specificType; + Type newItemType = itemType; + if (itemType.hasTemplateType()) { + newItemType = itemType.specializeTemplateType(o.itemType, specializedTypeMap, useSpecializedType); + } + + return new ArrayType(newItemType); + } + public static ArrayType create() { return new ArrayType(); } 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 6fd8da9c24..e72df77710 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 @@ -27,6 +27,7 @@ import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.gson.annotations.SerializedName; +import java.util.Map; import java.util.Objects; /** @@ -121,6 +122,38 @@ public class MapType extends Type { && (valueType.matchesType(((MapType) t).valueType)); } + @Override + public boolean hasTemplateType() { + return keyType.hasTemplateType() || valueType.hasTemplateType(); + } + + @Override + public Type specializeTemplateType(Type specificType, Map<String, Type> specializedTypeMap, + boolean useSpecializedType) throws TypeException { + if (!(specificType instanceof MapType)) { + throw new TypeException(specificType + " is not MapType"); + } + + MapType specificMapType = (MapType) specificType; + Type newKeyType = keyType; + if (keyType.hasTemplateType()) { + newKeyType = keyType.specializeTemplateType( + specificMapType.keyType, specializedTypeMap, useSpecializedType); + } + Type newValueType = valueType; + if (valueType.hasTemplateType()) { + newValueType = valueType.specializeTemplateType( + specificMapType.valueType, specializedTypeMap, useSpecializedType); + } + + Type newMapType = new MapType(newKeyType, newValueType); + if (Type.canCastTo(specificType, newMapType)) { + return newMapType; + } else { + throw new TypeException(specificType + " can not cast to specialize type " + newMapType); + } + } + @Override public String toString() { return toSql(0).toUpperCase(); diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java index c4d1a4e8d8..fdcbfaedef 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java @@ -72,6 +72,7 @@ public enum PrimitiveType { STRUCT("STRUCT", 16, TPrimitiveType.STRUCT), STRING("STRING", 16, TPrimitiveType.STRING), VARIANT("VARIANT", 24, TPrimitiveType.VARIANT), + TEMPLATE("TEMPLATE", -1, TPrimitiveType.INVALID_TYPE), // Unsupported scalar types. BINARY("BINARY", -1, TPrimitiveType.BINARY), ALL("ALL", -1, TPrimitiveType.INVALID_TYPE); diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/TemplateType.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/TemplateType.java new file mode 100644 index 0000000000..58f3fbe952 --- /dev/null +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/TemplateType.java @@ -0,0 +1,132 @@ +// 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; + +import org.apache.doris.thrift.TColumnType; +import org.apache.doris.thrift.TTypeDesc; + +import com.google.common.base.Strings; +import com.google.gson.annotations.SerializedName; + +import java.util.Map; + +/** + * Describes a TemplateType type, used for SQL function argument and return type, + * NOT used for table column type. + */ +public class TemplateType extends Type { + + @SerializedName(value = "name") + private final String name; + + public TemplateType(String name) { + this.name = name; + } + + @Override + public PrimitiveType getPrimitiveType() { + return PrimitiveType.TEMPLATE; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof TemplateType)) { + return false; + } + TemplateType o = (TemplateType) other; + return o.name.equals(name); + } + + @Override + public boolean matchesType(Type t) { + // not matches any type + return false; + } + + @Override + public boolean hasTemplateType() { + return true; + } + + @Override + public Type specializeTemplateType(Type specificType, Map<String, Type> specializedTypeMap, + boolean useSpecializedType) throws TypeException { + if (specificType.hasTemplateType() && !specificType.isNull()) { + throw new TypeException(specificType + " should not hasTemplateType"); + } + + Type specializedType = specializedTypeMap.get(name); + if (useSpecializedType) { + if (specializedType == null) { + throw new TypeException("template type " + name + " is not specialized yet"); + } + return specializedType; + } + + if (specializedType != null + && !specificType.equals(specializedType) + && !specificType.matchesType(specializedType) + && !Type.isImplicitlyCastable(specificType, specializedType, true) + && !Type.canCastTo(specificType, specializedType)) { + throw new TypeException( + String.format("can not specialize template type %s to %s since it's already specialized as %s", + name, specificType, specializedType)); + } + + if (specializedType == null) { + specializedTypeMap.put(name, specificType); + } + return specializedTypeMap.get(name); + } + + @Override + public String toSql(int depth) { + return name; + } + + @Override + public String toString() { + return toSql(0).toUpperCase(); + } + + @Override + protected String prettyPrint(int lpad) { + String leftPadding = Strings.repeat(" ", lpad); + return leftPadding + toSql(); + } + + @Override + public boolean supportSubType(Type subType) { + throw new RuntimeException("supportSubType not implementd for TemplateType"); + } + + @Override + public void toThrift(TTypeDesc container) { + throw new RuntimeException("can not call toThrift on TemplateType"); + } + + @Override + public TColumnType toColumnTypeThrift() { + throw new RuntimeException("can not call toColumnTypeThrift on TemplateType"); + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} 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 a4bd07d33a..4f7f5cb96e 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 @@ -39,6 +39,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -507,6 +508,21 @@ public abstract class Type { return isScalarType(PrimitiveType.DATEV2); } + public boolean hasTemplateType() { + return false; + } + + // return a new type without template type, by specialize tempalte type in this type + public Type specializeTemplateType(Type specificType, Map<String, Type> specializedTypeMap, + boolean useSpecializedType) throws TypeException { + if (hasTemplateType()) { + // throw exception by default, sub class should specialize tempalte type properly + throw new TypeException("specializeTemplateType not implemented"); + } else { + return this; + } + } + /** * Returns true if Impala supports this type in the metdata. It does not mean we * can manipulate data of this type. For tables that contain columns with these @@ -1560,6 +1576,7 @@ public abstract class Type { || t1 == PrimitiveType.TIMEV2 || t2 == PrimitiveType.TIMEV2 || t1 == PrimitiveType.MAP || t2 == PrimitiveType.MAP || t1 == PrimitiveType.STRUCT || t2 == PrimitiveType.STRUCT + || t1 == PrimitiveType.TEMPLATE || t2 == PrimitiveType.TEMPLATE || t1 == PrimitiveType.UNSUPPORTED || t2 == PrimitiveType.UNSUPPORTED || t1 == PrimitiveType.VARIANT || t2 == PrimitiveType.VARIANT) { continue; 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 9e101275a0..51b6b75cad 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 @@ -1369,37 +1369,34 @@ public class FunctionCallExpr extends Expr { } } - if (!fn.getFunctionName().getFunction().equals(ELEMENT_EXTRACT_FN_NAME)) { - Type[] args = fn.getArgs(); - if (args.length > 0) { - // Implicitly cast all the children to match the function if necessary - for (int i = 0; i < argTypes.length - orderByElements.size(); ++i) { - // For varargs, we must compare with the last type in callArgs.argTypes. - int ix = Math.min(args.length - 1, i); - if (fnName.getFunction().equalsIgnoreCase("money_format") - && children.get(0).getType().isDecimalV3() && args[ix].isDecimalV3()) { - continue; - } else if (fnName.getFunction().equalsIgnoreCase("array") - && (children.get(0).getType().isDecimalV3() && args[ix].isDecimalV3() - || children.get(0).getType().isDatetimeV2() && args[ix].isDatetimeV2())) { - continue; - } else if ((fnName.getFunction().equalsIgnoreCase("array_min") || fnName.getFunction() - .equalsIgnoreCase("array_max") || fnName.getFunction().equalsIgnoreCase("element_at")) - && (( - children.get(0).getType().isDecimalV3() && ((ArrayType) args[ix]).getItemType() - .isDecimalV3()) - || (children.get(0).getType().isDatetimeV2() - && ((ArrayType) args[ix]).getItemType().isDatetimeV2()) - || (children.get(0).getType().isDecimalV2() - && ((ArrayType) args[ix]).getItemType().isDecimalV2()))) { - continue; - } else if (!argTypes[i].matchesType(args[ix]) - && !(argTypes[i].isDateOrDateTime() && args[ix].isDateOrDateTime()) - && (!fn.getReturnType().isDecimalV3() - || (argTypes[i].isValid() && !argTypes[i].isDecimalV3() - && args[ix].isDecimalV3()))) { - uncheckedCastChild(args[ix], i); - } + Type[] args = fn.getArgs(); + if (args.length > 0) { + // Implicitly cast all the children to match the function if necessary + for (int i = 0; i < argTypes.length - orderByElements.size(); ++i) { + // For varargs, we must compare with the last type in callArgs.argTypes. + int ix = Math.min(args.length - 1, i); + if (fnName.getFunction().equalsIgnoreCase("money_format") + && children.get(0).getType().isDecimalV3() && args[ix].isDecimalV3()) { + continue; + } else if (fnName.getFunction().equalsIgnoreCase("array") + && (children.get(0).getType().isDecimalV3() && args[ix].isDecimalV3() + || children.get(0).getType().isDatetimeV2() && args[ix].isDatetimeV2())) { + continue; + } else if ((fnName.getFunction().equalsIgnoreCase("array_min") || fnName.getFunction() + .equalsIgnoreCase("array_max") || fnName.getFunction().equalsIgnoreCase("element_at")) + && (( + children.get(0).getType().isDecimalV3() && ((ArrayType) args[ix]).getItemType() + .isDecimalV3()) + || (children.get(0).getType().isDatetimeV2() + && ((ArrayType) args[ix]).getItemType().isDatetimeV2()) + || (children.get(0).getType().isDecimalV2() + && ((ArrayType) args[ix]).getItemType().isDecimalV2()))) { + continue; + } else if (!argTypes[i].matchesType(args[ix]) && !( + argTypes[i].isDateOrDateTime() && args[ix].isDateOrDateTime()) + && (!fn.getReturnType().isDecimalV3() + || (argTypes[i].isValid() && !argTypes[i].isDecimalV3() && args[ix].isDecimalV3()))) { + uncheckedCastChild(args[ix], i); } } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java index f1a608a9b8..8997b1bf32 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java @@ -814,4 +814,14 @@ public class Function implements Writable { throw new UserException("failed to serialize function: " + functionName(), t); } } + + public boolean hasTemplateArg() { + for (Type t : getArgs()) { + if (t.hasTemplateType()) { + return true; + } + } + + return false; + } } 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 0a4bed2789..cb501817b4 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 @@ -1223,6 +1223,34 @@ public class FunctionSet<T> { return null; } + List<Function> normalFunctions = Lists.newArrayList(); + List<Function> templateFunctions = Lists.newArrayList(); + for (Function fn : fns) { + if (fn.hasTemplateArg()) { + templateFunctions.add(fn); + } else { + normalFunctions.add(fn); + } + } + + // try normal functions first + Function fn = getFunction(desc, mode, normalFunctions); + if (fn != null) { + return fn; + } + + // then specialize template functions and try them + List<Function> specializedTemplateFunctions = Lists.newArrayList(); + for (Function f : templateFunctions) { + f = FunctionSet.specializeTemplateFunction(f, desc); + if (f != null) { + specializedTemplateFunctions.add(f); + } + } + return getFunction(desc, mode, specializedTemplateFunctions); + } + + private Function getFunction(Function desc, Function.CompareMode mode, List<Function> fns) { // First check for identical for (Function f : fns) { if (f.compare(desc, Function.CompareMode.IS_IDENTICAL)) { @@ -1262,6 +1290,45 @@ public class FunctionSet<T> { return null; } + public static Function specializeTemplateFunction(Function templateFunction, Function requestFunction) { + try { + boolean hasTemplateType = false; + LOG.debug("templateFunction signature: " + templateFunction.signatureString() + + " return: " + templateFunction.getReturnType()); + LOG.debug("requestFunction signature: " + requestFunction.signatureString() + + " return: " + requestFunction.getReturnType()); + Function specializedFunction = templateFunction; + if (templateFunction instanceof ScalarFunction) { + ScalarFunction f = (ScalarFunction) templateFunction; + specializedFunction = new ScalarFunction(f.getFunctionName(), Lists.newArrayList(f.getArgs()), + f.getReturnType(), f.hasVarArgs(), f.getSymbolName(), f.getBinaryType(), + f.isUserVisible(), f.isVectorized(), f.getNullableMode()); + } else { + // TODO(xk) + } + Type[] args = specializedFunction.getArgs(); + Map<String, Type> specializedTypeMap = Maps.newHashMap(); + for (int i = 0; i < args.length; i++) { + if (args[i].hasTemplateType()) { + hasTemplateType = true; + args[i] = args[i].specializeTemplateType(requestFunction.getArgs()[i], specializedTypeMap, false); + } + } + if (specializedFunction.getReturnType().hasTemplateType()) { + hasTemplateType = true; + specializedFunction.setReturnType( + specializedFunction.getReturnType().specializeTemplateType( + requestFunction.getReturnType(), specializedTypeMap, true)); + } + LOG.debug("specializedFunction signature: " + specializedFunction.signatureString() + + " return: " + specializedFunction.getReturnType()); + return hasTemplateType ? specializedFunction : templateFunction; + } catch (TypeException e) { + LOG.warn("specializeTemplateFunction exception", e); + return null; + } + } + /** * There are essential differences in the implementation of some functions for different * types params, which should be prohibited. diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py index f650e0a28a..b891b48513 100644 --- a/gensrc/script/doris_builtins_functions.py +++ b/gensrc/script/doris_builtins_functions.py @@ -25,7 +25,7 @@ # It contains all the meta data that describes the function. # The format is: -# [sql aliases], <return_type>, [<args>], <nullable mode> +# [sql aliases], <return_type>, [<args>], <nullable mode>, [template_types] # # 'sql aliases' are the function names that can be used from sql. There must be at least # one per function. @@ -33,6 +33,10 @@ # 'nullable mode' reflects whether the return value of the function is null. See @Function.NullableMode # for the specific mode and meaning. # +# 'template_types' is for template function just like C++. It is optional list. +# eg. [['element_at', '%element_extract%'], 'V', ['MAP<K, V>', 'K'], 'ALWAYS_NULLABLE', ['K', 'V']], +# 'K' and 'V' is type template and will be specialized at runtime in FE to match specific args. +# visible_functions = [ # Bit and Byte functions # For functions corresponding to builtin operators, we can reuse the implementations @@ -100,7 +104,7 @@ visible_functions = [ [['element_at', '%element_extract%'], 'STRING', ['ARRAY_STRING', 'BIGINT'], 'ALWAYS_NULLABLE'], # map element - [['element_at', '%element_extract%'], 'INT', ['MAP_STRING_INT', 'STRING'], 'ALWAYS_NULLABLE'], + [['element_at', '%element_extract%'], 'V', ['MAP<K, V>', 'K'], 'ALWAYS_NULLABLE', ['K', 'V']], [['arrays_overlap'], 'BOOLEAN', ['ARRAY_BOOLEAN', 'ARRAY_BOOLEAN'], 'ALWAYS_NULLABLE'], [['arrays_overlap'], 'BOOLEAN', ['ARRAY_TINYINT', 'ARRAY_TINYINT'], 'ALWAYS_NULLABLE'], diff --git a/gensrc/script/gen_builtins_functions.py b/gensrc/script/gen_builtins_functions.py index 28b8812142..2aa6c74fcd 100755 --- a/gensrc/script/gen_builtins_functions.py +++ b/gensrc/script/gen_builtins_functions.py @@ -54,6 +54,7 @@ package org.apache.doris.builtins;\n\ \n\ import org.apache.doris.catalog.ArrayType;\n\ import org.apache.doris.catalog.MapType;\n\ +import org.apache.doris.catalog.TemplateType;\n\ import org.apache.doris.catalog.Type;\n\ import org.apache.doris.catalog.Function;\n\ import org.apache.doris.catalog.FunctionSet;\n\ @@ -73,14 +74,14 @@ print(FE_PATH) # This contains all the metadata to describe all the builtins. # Each meta data entry is itself a map to store all the meta data -# - fn_name, ret_type, args, symbol, sql_names +# - fn_name, ret_type, args, symbol, sql_names, template_types(optional) meta_data_entries = [] # Read in the function and add it to the meta_data_entries map def add_function(fn_meta_data, user_visible): """add function """ - assert len(fn_meta_data) == 4, \ + assert len(fn_meta_data) >= 4, \ "Invalid function entry in doris_builtins_functions.py:\n\t" + repr(fn_meta_data) entry = {} entry["sql_names"] = fn_meta_data[0] @@ -91,6 +92,12 @@ def add_function(fn_meta_data, user_visible): else: entry['nullable_mode'] = 'DEPEND_ON_ARGUMENT' + # process template + if len(fn_meta_data) >= 5: + entry["template_types"] = fn_meta_data[4] + else: + entry["template_types"] = [] + entry["user_visible"] = user_visible meta_data_entries.append(entry) @@ -103,15 +110,35 @@ for example: in[ARRAY_INT] --> out[new ArrayType(Type.INT)] in[MAP_STRING_INT] --> out[new MapType(Type.STRING,Type.INT)] """ -def generate_fe_datatype(str_type): +def generate_fe_datatype(str_type, template_types): + # delete whitespace + str_type = str_type.replace(' ', '').replace('\t', '') + + # process template + if str_type in template_types: + return 'new TemplateType("{}")'.format(str_type) + + # process Array, Map, Struct template + template_start = str_type.find('<') + template_end = str_type.rfind('>') + if template_start >= 0 and template_end > 0: + # exclude <> + template = str_type[template_start + 1 : template_end] + if str_type.startswith("ARRAY<"): + return 'new ArrayType({})'.format(generate_fe_datatype(template, template_types)) + elif str_type.startswith("MAP<"): + types = template.split(',', 2) + return 'new MapType({}, {})'.format(generate_fe_datatype(types[0], template_types), generate_fe_datatype(types[1], template_types)) + + # lagacy Array, Map syntax if str_type.startswith("ARRAY_"): vec_type = str_type.split('_', 1); if len(vec_type) > 1 and vec_type[0] == "ARRAY": - return "new ArrayType(" + generate_fe_datatype(vec_type[1]) + ")" + return "new ArrayType(" + generate_fe_datatype(vec_type[1], template_types) + ")" if str_type.startswith("MAP_"): vec_type = str_type.split('_', 2) if len(vec_type) > 2 and vec_type[0] == "MAP": - return "new MapType(" + generate_fe_datatype(vec_type[1]) + "," + generate_fe_datatype(vec_type[2])+")" + return "new MapType(" + generate_fe_datatype(vec_type[1], template_types) + "," + generate_fe_datatype(vec_type[2], template_types)+")" if str_type == "DECIMALV2": return "Type.MAX_DECIMALV2_TYPE" if str_type == "DECIMAL32": @@ -136,7 +163,7 @@ def generate_fe_entry(entry, name): else: java_output += ", false" java_output += ", Function.NullableMode." + entry["nullable_mode"] - java_output += ", " + generate_fe_datatype(entry["ret_type"]) + java_output += ", " + generate_fe_datatype(entry["ret_type"], entry["template_types"]) # Check the last entry for varargs indicator. if entry["args"] and entry["args"][-1] == "...": @@ -145,7 +172,7 @@ def generate_fe_entry(entry, name): else: java_output += ", false" for arg in entry["args"]: - java_output += ", " + generate_fe_datatype(arg) + java_output += ", " + generate_fe_datatype(arg, entry["template_types"]) return java_output # Generates the FE builtins init file that registers all the builtins. diff --git a/regression-test/data/load_p0/stream_load/test_map_load_and_function.out b/regression-test/data/load_p0/stream_load/test_map_load_and_function.out index 1b7eb1f5a3..8e3b7905a4 100644 --- a/regression-test/data/load_p0/stream_load/test_map_load_and_function.out +++ b/regression-test/data/load_p0/stream_load/test_map_load_and_function.out @@ -1,5 +1,5 @@ -- This file is automatically generated. You should know what you did if you want to edit this --- !select -- +-- !select_all -- 1 \N 2 {' 11amory ':23, 'beat':20, ' clever ':66} 3 {'k1':31, 'k2':300} @@ -16,22 +16,37 @@ 15 {'':2, 'k2':0} 16 {null:null} --- !select -- -\N -\N -300 -\N -\N -400 -\N -\N -\N -\N -\N -\N -\N -\N -130 -0 -\N +-- !select_m -- +1 \N +2 \N +3 300 +4 \N +5 \N +6 400 +7 \N +8 \N +9 \N +10 \N +11 \N +12 \N +13 \N +15 0 +16 \N +17 \N +18 130 + +-- !select_m1 -- +1 100 200 \N + +-- !select_m2 -- +1 k1 k2 \N + +-- !select_m3 -- +1 v1 v2 \N + +-- !select_m4 -- +1 10000 20000 \N + +-- !select_m5 -- +1 100 200 \N diff --git a/regression-test/suites/load_p0/stream_load/test_map_load_and_function.groovy b/regression-test/suites/load_p0/stream_load/test_map_load_and_function.groovy index d796c08b2e..38e4d295ca 100644 --- a/regression-test/suites/load_p0/stream_load/test_map_load_and_function.groovy +++ b/regression-test/suites/load_p0/stream_load/test_map_load_and_function.groovy @@ -63,12 +63,37 @@ suite("test_map_load_and_function", "p0") { } // check result - qt_select "SELECT * FROM ${testTable} ORDER BY id" + qt_select_all "SELECT * FROM ${testTable} ORDER BY id" // insert into valid json rows - sql """INSERT INTO ${testTable} VALUES(12, NULL)""" - sql """INSERT INTO ${testTable} VALUES(13, {"k1":100, "k2": 130})""" + sql """INSERT INTO ${testTable} VALUES(17, NULL)""" + sql """INSERT INTO ${testTable} VALUES(18, {"k1":100, "k2": 130})""" // map element_at - qt_select "SELECT m['k2'] FROM ${testTable} ORDER BY id" + qt_select_m "SELECT id, m['k2'] FROM ${testTable} ORDER BY id" + + + testTable = "tbl_test_map2" + sql "DROP TABLE IF EXISTS ${testTable}" + sql """ + CREATE TABLE IF NOT EXISTS ${testTable} ( + id INT, + `m1` MAP<STRING, INT> NULL, + `m2` MAP<INT, STRING> NULL, + `m3` MAP<STRING, STRING> NULL, + `m4` MAP<INT, BIGINT> NULL, + `m5` MAP<BIGINT, INT> NULL + ) + DUPLICATE KEY(id) + DISTRIBUTED BY HASH(id) BUCKETS 10 + PROPERTIES("replication_num" = "1"); + """ + sql """INSERT INTO ${testTable} VALUES(1, {'k1':100, 'k2':200}, {100:'k1', 200:'k2'}, {'k1':'v1', 'k2':'v2'}, {100:10000, 200:20000}, {10000:100, 20000:200})""" + + // map element_at + qt_select_m1 "SELECT id, m1['k1'], m1['k2'], m1['nokey'] FROM ${testTable} ORDER BY id" + qt_select_m2 "SELECT id, m2[100], m2[200], m1[300] FROM ${testTable} ORDER BY id" + qt_select_m3 "SELECT id, m3['k1'], m3['k2'], m3['nokey'] FROM ${testTable} ORDER BY id" + qt_select_m4 "SELECT id, m4[100], m4[200], m4[300] FROM ${testTable} ORDER BY id" + qt_select_m5 "SELECT id, m5[10000], m5[20000], m5[30000] FROM ${testTable} ORDER BY id" } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org