From ca10205137134c4971e0f8a658d739d84bf150fc Mon Sep 17 00:00:00 2001 From: caiconghui <55968745+caiconghui@users.noreply.github.com> Date: Thu, 28 Jan 2021 10:52:37 +0800 Subject: [PATCH] [Function] Support show create function statement (#5197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Function]Support show create function stmt Co-authored-by: caiconghui [蔡聪辉] --- .../Data Manipulation/SHOW CREATE FUNCTION.md | 43 ++++++++ .../Data Manipulation/SHOW CREATE FUNCTION.md | 43 ++++++++ fe/fe-core/src/main/cup/sql_parser.cup | 5 + .../doris/analysis/DropFunctionStmt.java | 2 +- .../analysis/ShowCreateFunctionStmt.java | 103 ++++++++++++++++++ .../doris/catalog/AggregateFunction.java | 24 ++-- .../org/apache/doris/catalog/Database.java | 16 +++ .../org/apache/doris/catalog/Function.java | 18 +-- .../apache/doris/catalog/ScalarFunction.java | 22 +++- .../org/apache/doris/qe/ShowExecutor.java | 20 ++++ 10 files changed, 264 insertions(+), 32 deletions(-) create mode 100644 docs/en/sql-reference/sql-statements/Data Manipulation/SHOW CREATE FUNCTION.md create mode 100644 docs/zh-CN/sql-reference/sql-statements/Data Manipulation/SHOW CREATE FUNCTION.md create mode 100644 fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateFunctionStmt.java diff --git a/docs/en/sql-reference/sql-statements/Data Manipulation/SHOW CREATE FUNCTION.md b/docs/en/sql-reference/sql-statements/Data Manipulation/SHOW CREATE FUNCTION.md new file mode 100644 index 0000000000..51410168a3 --- /dev/null +++ b/docs/en/sql-reference/sql-statements/Data Manipulation/SHOW CREATE FUNCTION.md @@ -0,0 +1,43 @@ +--- +{ + "title": "SHOW CREATE FUNCTION", + "language": "en" +} +--- + + + +# SHOW CREATE FUNCTION +## description + The statement is used to show the creation statement of user-defined function + grammar: + SHOW CREATE FUNTION function_name(arg_type [, ...]) [FROM db_name]]; + + Description: + `function_name`: the name of the function to be displayed + `arg_type`: the parameter list of the function to be displayed + If you do not specify db_name, use the current default db + +## example + 1. Show the creation statement of the specified function under the default db + SHOW CREATE FUNCTION my_add(INT, INT) + +## keyword + SHOW,CREATE,FUNCTION \ No newline at end of file diff --git a/docs/zh-CN/sql-reference/sql-statements/Data Manipulation/SHOW CREATE FUNCTION.md b/docs/zh-CN/sql-reference/sql-statements/Data Manipulation/SHOW CREATE FUNCTION.md new file mode 100644 index 0000000000..cacc894262 --- /dev/null +++ b/docs/zh-CN/sql-reference/sql-statements/Data Manipulation/SHOW CREATE FUNCTION.md @@ -0,0 +1,43 @@ +--- +{ + "title": "SHOW CREATE FUNCTION", + "language": "zh-CN" +} +--- + + + +# SHOW CREATE FUNCTION +## description + 该语句用于展示用户自定义函数的创建语句 + 语法: + SHOW CREATE FUNTION function_name(arg_type [, ...]) [FROM db_name]]; + + 说明: + `function_name`: 要展示的函数名称 + `arg_type`: 要展示的函数的参数列表 + 如果不指定 db_name,使用当前默认 db + +## example + 1. 展示默认db下指定函数的创建语句 + SHOW CREATE FUNCTION my_add(INT, INT) + +## keyword + SHOW,CREATE,FUNCTION \ No newline at end of file diff --git a/fe/fe-core/src/main/cup/sql_parser.cup b/fe/fe-core/src/main/cup/sql_parser.cup index 555db66685..62d363e68f 100644 --- a/fe/fe-core/src/main/cup/sql_parser.cup +++ b/fe/fe-core/src/main/cup/sql_parser.cup @@ -2261,6 +2261,11 @@ show_param ::= {: RESULT = new ShowCreateDbStmt(db); :} + /* Create Function */ + | KW_CREATE KW_FUNCTION function_name:functionName LPAREN func_args_def:args RPAREN opt_db:dbName + {: + RESULT = new ShowCreateFunctionStmt(dbName, functionName, args); + :} /* Cluster */ | KW_CLUSTERS {: diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/DropFunctionStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/DropFunctionStmt.java index c29cd2c265..c0125d4073 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/DropFunctionStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/DropFunctionStmt.java @@ -60,7 +60,7 @@ public class DropFunctionStmt extends DdlStmt { @Override public String toSql() { StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("DROP FUNCTION ").append(functionName); + stringBuilder.append("DROP FUNCTION ").append(functionName).append(argsDef); return stringBuilder.toString(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateFunctionStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateFunctionStmt.java new file mode 100644 index 0000000000..2519d796cb --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateFunctionStmt.java @@ -0,0 +1,103 @@ +// 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.analysis; + +import com.google.common.base.Strings; +import org.apache.doris.catalog.Catalog; +import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.FunctionSearchDesc; +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.cluster.ClusterNamespace; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.UserException; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.ShowResultSetMetaData; + +public class ShowCreateFunctionStmt extends ShowStmt { + private static final ShowResultSetMetaData META_DATA = + ShowResultSetMetaData.builder() + .addColumn(new Column("Function Signature", ScalarType.createVarchar(256))) + .addColumn(new Column("Create Function", ScalarType.createVarchar(1024))) + .build(); + private String dbName; + private final FunctionName functionName; + private final FunctionArgsDef argsDef; + + // set after analyzed + private FunctionSearchDesc function; + + public ShowCreateFunctionStmt(String dbName, FunctionName functionName, FunctionArgsDef argsDef) { + this.dbName = dbName; + this.functionName = functionName; + this.argsDef = argsDef; + } + + public String getDbName() { + return dbName; + } + + public FunctionName getFunctionName() { return functionName; } + + public FunctionSearchDesc getFunction() { return function; } + + @Override + public void analyze(Analyzer analyzer) throws UserException { + super.analyze(analyzer); + + if (Strings.isNullOrEmpty(dbName)) { + dbName = analyzer.getDefaultDb(); + if (Strings.isNullOrEmpty(dbName)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_DB_ERROR); + } + } else { + dbName = ClusterNamespace.getFullName(getClusterName(), dbName); + } + + // analyze function name + functionName.analyze(analyzer); + + // check operation privilege + if (!Catalog.getCurrentCatalog().getAuth().checkDbPriv(ConnectContext.get(), dbName, PrivPredicate.SHOW)) { + ErrorReport.reportAnalysisException( + ErrorCode.ERR_DB_ACCESS_DENIED, ConnectContext.get().getQualifiedUser(), dbName); + } + // analyze arguments + argsDef.analyze(analyzer); + function = new FunctionSearchDesc(functionName, argsDef.getArgTypes(), argsDef.isVariadic()); + } + + @Override + public String toSql() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("SHOW CREATE FUNCTION ").append(functionName).append(argsDef) + .append(" IN ").append(dbName); + return stringBuilder.toString(); + } + + @Override + public String toString() { + return toSql(); + } + + @Override + public ShowResultSetMetaData getMetaData() { + return META_DATA; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/AggregateFunction.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/AggregateFunction.java index ed8ce6b1f5..4d5ec3dbd5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/AggregateFunction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/AggregateFunction.java @@ -361,21 +361,27 @@ public class AggregateFunction extends Function { if (ifNotExists) { sb.append("IF NOT EXISTS "); } - sb.append(dbName() + "." + signatureString() + "\n") - .append(" RETURNS " + getReturnType() + "\n"); + + sb.append(signatureString()).append(" RETURNS " + getReturnType()); if (getIntermediateType() != null) { - sb.append(" INTERMEDIATE " + getIntermediateType() + "\n"); + sb.append(" INTERMEDIATE " + getIntermediateType()); } - sb.append(" LOCATION '" + getLocation() + "'\n") - .append(" UPDATE_FN='" + getUpdateFnSymbol() + "'\n") - .append(" INIT_FN='" + getInitFnSymbol() + "'\n") - .append(" MERGE_FN='" + getMergeFnSymbol() + "'\n"); + + sb.append(" PROPERTIES (") + .append("\n \"INIT_FN\"=\"" + getUpdateFnSymbol() + "\"") + .append(",\n \"UPDATE_FN\"=\"" + getInitFnSymbol() + "\"") + .append(",\n \"MERGE_FN\"=\"" + getMergeFnSymbol() + "\""); if (getSerializeFnSymbol() != null) { - sb.append(" SERIALIZE_FN='" + getSerializeFnSymbol() + "'\n"); + sb.append(",\n \"SERIALIZE_FN\"=\"" + getSerializeFnSymbol() + "\""); } if (getFinalizeFnSymbol() != null) { - sb.append(" FINALIZE_FN='" + getFinalizeFnSymbol() + "'\n"); + sb.append(",\n \"FINALIZE_FN\"=\"" + getFinalizeFnSymbol() + "\""); } + + sb.append(",\n \"OBJECT_FILE\"=") + .append("\"" + (getLocation() == null ? "" : getLocation().toString()) + "\""); + sb.append(",\n \"MD5\"=").append("\"" + getChecksum() + "\""); + sb.append("\n);"); return sb.toString(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java index d9fe637c60..3de097ef99 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java @@ -19,6 +19,7 @@ package org.apache.doris.catalog; import org.apache.doris.catalog.Table.TableType; import org.apache.doris.cluster.ClusterNamespace; +import org.apache.doris.common.AnalysisException; import org.apache.doris.common.Config; import org.apache.doris.common.DdlException; import org.apache.doris.common.FeConstants; @@ -712,6 +713,21 @@ public class Database extends MetaObject implements Writable { return Function.getFunction(fns, desc, mode); } + public synchronized Function getFunction(FunctionSearchDesc function) throws AnalysisException { + String functionName = function.getName().getFunction(); + List existFuncs = name2Function.get(functionName); + if (existFuncs == null) { + throw new AnalysisException("Unknown function, function=" + function.toString()); + } + + for (Function existFunc : existFuncs) { + if (function.isIdentical(existFunc)) { + return existFunc; + } + } + throw new AnalysisException("Unknown function, function=" + function.toString()); + } + public synchronized List getFunctions() { List functions = Lists.newArrayList(); for (Map.Entry> entry : name2Function.entrySet()) { 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 425c39c76b..930f9ec201 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 @@ -659,22 +659,6 @@ public class Function implements Writable { return function; } - public String getSignature() { - StringBuilder sb = new StringBuilder(); - sb.append(name.getFunction()).append("("); - for (int i = 0; i < argTypes.length; ++i) { - if (i != 0) { - sb.append(','); - } - sb.append(argTypes[i].getPrimitiveType().toString()); - } - if (hasVarArgs) { - sb.append(", ..."); - } - sb.append(")"); - return sb.toString(); - } - public String getProperties() { return ""; } @@ -683,7 +667,7 @@ public class Function implements Writable { List row = Lists.newArrayList(); if (isVerbose) { // signature - row.add(getSignature()); + row.add(signatureString()); // return type row.add(getReturnType().getPrimitiveType().toString()); // function type diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/ScalarFunction.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/ScalarFunction.java index 5eff1e82d6..5f836741fc 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/ScalarFunction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/ScalarFunction.java @@ -256,11 +256,23 @@ public class ScalarFunction extends Function { @Override public String toSql(boolean ifNotExists) { StringBuilder sb = new StringBuilder("CREATE FUNCTION "); - if (ifNotExists) sb.append("IF NOT EXISTS "); - sb.append(dbName() + "." + signatureString() + "\n") - .append(" RETURNS " + getReturnType() + "\n") - .append(" LOCATION '" + getLocation() + "'\n") - .append(" SYMBOL='" + getSymbolName() + "'\n"); + if (ifNotExists) { + sb.append("IF NOT EXISTS "); + } + sb.append(signatureString()) + .append(" RETURNS " + getReturnType()) + .append(" PROPERTIES ("); + sb.append("\n \"SYMBOL\"=").append("\"" + getSymbolName() + "\""); + if (getPrepareFnSymbol() != null) { + sb.append(",\n \"PREPARE_FN\"=").append("\"" + getPrepareFnSymbol() + "\""); + } + if (getCloseFnSymbol() != null) { + sb.append(",\n \"CLOSE_FN\"=").append("\"" + getCloseFnSymbol() + "\""); + } + sb.append(",\n \"OBJECT_FILE\"=") + .append("\"" + (getLocation() == null ? "" : getLocation().toString()) + "\""); + sb.append(",\n \"MD5\"=").append("\"" + getChecksum() + "\""); + sb.append("\n);"); return sb.toString(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java index 41dd674285..9062a9207e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java @@ -32,6 +32,7 @@ import org.apache.doris.analysis.ShowClusterStmt; import org.apache.doris.analysis.ShowCollationStmt; import org.apache.doris.analysis.ShowColumnStmt; import org.apache.doris.analysis.ShowCreateDbStmt; +import org.apache.doris.analysis.ShowCreateFunctionStmt; import org.apache.doris.analysis.ShowCreateTableStmt; import org.apache.doris.analysis.ShowDataStmt; import org.apache.doris.analysis.ShowDbStmt; @@ -192,6 +193,8 @@ public class ShowExecutor { handleShowEngines(); } else if (stmt instanceof ShowFunctionsStmt) { handleShowFunctions(); + } else if (stmt instanceof ShowCreateFunctionStmt) { + handleShowCreateFunction(); } else if (stmt instanceof ShowVariablesStmt) { handleShowVariables(); } else if (stmt instanceof ShowColumnStmt) { @@ -360,6 +363,23 @@ public class ShowExecutor { resultSet = new ShowResultSet(showMetaData, resultRowSet); } + // Handle show create function + private void handleShowCreateFunction() throws AnalysisException { + ShowCreateFunctionStmt showCreateFunctionStmt = (ShowCreateFunctionStmt) stmt; + Database db = ctx.getCatalog().getDb(showCreateFunctionStmt.getDbName()); + if (db == null) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_BAD_DB_ERROR, showCreateFunctionStmt.getDbName()); + } + + Function function = db.getFunction(showCreateFunctionStmt.getFunction()); + List> resultRowSet = Lists.newArrayList(); + List resultRow = Lists.newArrayList(); + resultRow.add(function.signatureString()); + resultRow.add(function.toSql(false)); + resultRowSet.add(resultRow); + resultSet = new ShowResultSet(showCreateFunctionStmt.getMetaData(), resultRowSet); + } + private void handleShowProc() throws AnalysisException { ShowProcStmt showProcStmt = (ShowProcStmt) stmt; ShowResultSetMetaData metaData = showProcStmt.getMetaData();