| #!/usr/bin/env python |
| # encoding: utf-8 |
| |
| # 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. |
| |
| """ |
| This module is doris builtin functions |
| """ |
| |
| import sys |
| import os |
| import errno |
| from string import Template |
| import doris_builtins_functions |
| |
| java_registry_preamble = '\ |
| // Licensed to the Apache Software Foundation (ASF) under one \n\ |
| // or more contributor license agreements. See the NOTICE file \n\ |
| // distributed with this work for additional information \n\ |
| // regarding copyright ownership. The ASF licenses this file \n\ |
| // to you under the Apache License, Version 2.0 (the \n\ |
| // "License"); you may not use this file except in compliance \n\ |
| // with the License. You may obtain a copy of the License at \n\ |
| // \n\ |
| // http://www.apache.org/licenses/LICENSE-2.0\n\ |
| // \n\ |
| // Unless required by applicable law or agreed to in writing, software\n\ |
| // distributed under the License is distributed on an "AS IS" BASIS,\n\ |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\ |
| // See the License for the specific language governing permissions and\n\ |
| // limitations under the License.\n\ |
| \n\ |
| // This is a generated file, DO NOT EDIT.\n\ |
| // To add new functions, see the generator at\n\ |
| // common/function-registry/gen_builtins_catalog.py or the function list at\n\ |
| // common/function-registry/doris_builtins_functions.py.\n\ |
| \n\ |
| 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.StructType;\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\ |
| import com.google.common.collect.Sets;\n\ |
| import java.util.Set;\n\ |
| \n\ |
| public class ScalarBuiltins { \n\ |
| public static void initBuiltins(FunctionSet functionSet) { \ |
| \n' |
| |
| java_registry_epilogue = '\ |
| }\n' |
| |
| FE_PATH = "../../../fe/fe-core/target/generated-sources/build/org/apache/doris/builtins/" |
| 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, template_types(optional) |
| |
| """ |
| generate fe data type, support nested ARRAY type. |
| for example: |
| in[TINYINT] --> out[Type.TINYINT] |
| in[INT] --> out[Type.INT] |
| 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, template_types): |
| # delete whitespace |
| str_type = str_type.replace(' ', '').replace('\t', '') |
| |
| # delete ellipsis dots |
| str_type = str_type.replace('...', '') |
| |
| # process template |
| if str_type in template_types: |
| return 'new TemplateType("{0}")'.format(str_type) |
| elif str_type + "..." in template_types: |
| return 'new TemplateType("{0}", true)'.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({0})'.format(generate_fe_datatype(template, template_types)) |
| elif str_type.startswith("MAP<"): |
| types = template.split(',', 2) |
| return 'new MapType({0}, {1})'.format(generate_fe_datatype(types[0], template_types), generate_fe_datatype(types[1], template_types)) |
| elif str_type.startswith("STRUCT<"): |
| types = template.split(',') |
| field_str = generate_fe_datatype(types[0], template_types) |
| for i in range(1, len(types)): |
| field_str += ", " + generate_fe_datatype(types[i], template_types) |
| return 'new StructType({0})'.format(field_str) |
| |
| # 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], 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], template_types) + "," + generate_fe_datatype(vec_type[2], template_types)+")" |
| if str_type == "DECIMALV2": |
| return "Type.MAX_DECIMALV2_TYPE" |
| if str_type == "DECIMAL32": |
| return "Type.DECIMAL32" |
| if str_type == "DECIMAL64": |
| return "Type.DECIMAL64" |
| if str_type == "DECIMAL128": |
| return "Type.DECIMAL128" |
| return "Type." + str_type |
| |
| """ |
| Order of params: |
| name, symbol, user_visible, prepare, close, nullable_mode, ret_type, has_var_args, args |
| """ |
| def generate_fe_entry(entry, name): |
| """add function |
| """ |
| java_output = "" |
| java_output += "\"" + name + "\"" |
| if entry["user_visible"]: |
| java_output += ", true" |
| else: |
| java_output += ", false" |
| java_output += ", Function.NullableMode." + entry["nullable_mode"] |
| 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] == "...": |
| entry["args"].pop() |
| java_output += ", true" |
| else: |
| java_output += ", false" |
| for arg in entry["args"]: |
| java_output += ", " + generate_fe_datatype(arg, entry["template_types"]) |
| return java_output |
| |
| # Generates the FE builtins init file that registers all the builtins. |
| def generate_fe_registry_init(filename): |
| """add function |
| """ |
| java_registry_file = open(filename, "w") |
| java_registry_file.write(java_registry_preamble) |
| |
| # Generate initialization calls for each category |
| for category, functions in doris_builtins_functions.visible_functions.items(): |
| java_registry_file.write(" init{0}Builtins(functionSet);\n".format(category.capitalize())) |
| |
| # add null_result_with_one_null_param_functions |
| java_registry_file.write(" Set<String> funcNames = Sets.newHashSet();\n") |
| for entry in doris_builtins_functions.null_result_with_one_null_param_functions: |
| java_registry_file.write(" funcNames.add(\"%s\");\n" % entry) |
| java_registry_file.write(" functionSet.buildNullResultWithOneNullParamFunction(funcNames);\n"); |
| |
| # add nondeterministic functions |
| java_registry_file.write(" Set<String> nondeterministicFuncNames = Sets.newHashSet();\n") |
| for entry in doris_builtins_functions.nondeterministic_functions: |
| java_registry_file.write(" nondeterministicFuncNames.add(\"%s\");\n" % entry) |
| java_registry_file.write(" functionSet.buildNondeterministicFunctions(nondeterministicFuncNames);\n"); |
| |
| # add null_result_with_one_null_param_functions |
| # java_registry_file.write(" funcNames = Sets.newHashSet();\n") |
| # for entry in doris_builtins_functions.null_result_with_one_null_param_functions: |
| # java_registry_file.write(" funcNames.add(\"%s\");\n" % entry) |
| # java_registry_file.write(" functionSet.buildNullResultWithOneNullParamFunction(funcNames);\n"); |
| |
| java_registry_file.write(" }\n") |
| java_registry_file.write("\n") |
| |
| # Generate functions for each category |
| for category, functions in doris_builtins_functions.visible_functions.items(): |
| generate_fe_category(category, functions, java_registry_file, True) |
| |
| java_registry_file.write(java_registry_epilogue) |
| java_registry_file.close() |
| |
| def generate_fe_category(category, functions, java_registry_file, user_visible): |
| java_registry_file.write(" private static void init{0}Builtins(FunctionSet functionSet) {{\n".format(category.capitalize())) |
| for function in functions: |
| assert len(function) >= 4, \ |
| "Invalid function entry in doris_builtins_functions.py:\n\t" + repr(function) |
| entry = {} |
| entry["sql_names"] = function[0] |
| entry["ret_type"] = function[1] |
| entry["args"] = function[2] |
| if function[3] != '': |
| entry['nullable_mode'] = function[3] |
| else: |
| entry['nullable_mode'] = 'DEPEND_ON_ARGUMENT' |
| |
| # process template |
| if len(function) >= 5: |
| entry["template_types"] = function[4] |
| else: |
| entry["template_types"] = [] |
| |
| entry["user_visible"] = user_visible |
| |
| for name in entry["sql_names"]: |
| java_output = generate_fe_entry(entry, name) |
| java_registry_file.write(" functionSet.addScalarAndVectorizedBuiltin(%s);\n" % java_output) |
| |
| java_registry_file.write(" }\n") |
| |
| |
| if __name__ == "__main__": |
| |
| try: |
| os.makedirs(FE_PATH) |
| except OSError as e: |
| if e.errno == errno.EEXIST: |
| pass |
| else: |
| raise |
| |
| generate_fe_registry_init(FE_PATH + "ScalarBuiltins.java") |