This is an automated email from the ASF dual-hosted git repository.
starocean999 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 5d2698924fc [Enhancement](nereids)support show functions (#49893)
5d2698924fc is described below
commit 5d2698924fc0a39db6bf5d18f0d6727d3f9b35ea
Author: lsy3993 <[email protected]>
AuthorDate: Mon Apr 14 14:28:34 2025 +0800
[Enhancement](nereids)support show functions (#49893)
---
.../antlr4/org/apache/doris/nereids/DorisParser.g4 | 4 +-
.../org/apache/doris/catalog/FunctionRegistry.java | 17 ++
.../doris/nereids/parser/LogicalPlanBuilder.java | 26 +++
.../apache/doris/nereids/trees/plans/PlanType.java | 1 +
.../trees/plans/commands/ShowFunctionsCommand.java | 236 +++++++++++++++++++++
.../trees/plans/visitor/CommandVisitor.java | 5 +
.../plans/commands/ShowFunctionsCommandTest.java | 190 +++++++++++++++++
.../show/test_nereids_show_functions.groovy | 92 ++++++++
8 files changed, 569 insertions(+), 2 deletions(-)
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index bf480cd099c..77a28ea6f8a 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -285,6 +285,8 @@ supportedShowStatement
| SHOW LAST INSERT
#showLastInsert
| SHOW ((CHAR SET) | CHARSET)
#showCharset
| SHOW DELETE ((FROM | IN) database=multipartIdentifier)?
#showDelete
+ | SHOW FULL? BUILTIN? FUNCTIONS
+ ((FROM | IN) database=multipartIdentifier)? (LIKE STRING_LITERAL)?
#showFunctions
| SHOW ALL? GRANTS
#showGrants
| SHOW GRANTS FOR userIdentify
#showGrantsForUser
| SHOW SYNC JOB ((FROM | IN) database=multipartIdentifier)?
#showSyncJob
@@ -415,8 +417,6 @@ unsupportedShowStatement
wildWhere? sortClause? limitClause?
#showPartitions
| SHOW RESOURCES wildWhere? sortClause? limitClause?
#showResources
| SHOW WORKLOAD GROUPS wildWhere?
#showWorkloadGroups
- | SHOW FULL? BUILTIN? FUNCTIONS
- ((FROM | IN) database=multipartIdentifier)? wildWhere?
#showFunctions
| SHOW GLOBAL FULL? FUNCTIONS wildWhere?
#showGlobalFunctions
| SHOW TYPECAST ((FROM | IN) database=multipartIdentifier)?
#showTypeCast
| SHOW (KEY | KEYS | INDEX | INDEXES)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionRegistry.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionRegistry.java
index dc11b5d02be..74fe1e38753 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionRegistry.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionRegistry.java
@@ -74,6 +74,18 @@ public class FunctionRegistry {
afterRegisterBuiltinFunctions(name2BuiltinBuilders);
}
+ public Map<String, List<FunctionBuilder>> getName2BuiltinBuilders() {
+ return name2BuiltinBuilders;
+ }
+
+ public String getGlobalFunctionDbName() {
+ return GLOBAL_FUNCTION;
+ }
+
+ public Map<String, Map<String, List<FunctionBuilder>>>
getName2UdfBuilders() {
+ return name2UdfBuilders;
+ }
+
// this function is used to test.
// for example, you can create child class of FunctionRegistry and clear
builtin functions or add more functions
// in this method
@@ -284,6 +296,11 @@ public class FunctionRegistry {
Map<String, List<FunctionBuilder>> builders =
name2UdfBuilders.getOrDefault(dbName, ImmutableMap.of());
builders.getOrDefault(name, Lists.newArrayList())
.removeIf(builder -> ((UdfBuilder)
builder).getArgTypes().equals(argTypes));
+
+ // the name will be used when show functions, so remove the name
when it's dropped
+ if (builders.getOrDefault(name, Lists.newArrayList()).isEmpty()) {
+ builders.remove(name);
+ }
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 8e8944035f7..f722344fa7f 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -329,6 +329,7 @@ import
org.apache.doris.nereids.DorisParser.ShowDynamicPartitionContext;
import org.apache.doris.nereids.DorisParser.ShowEncryptKeysContext;
import org.apache.doris.nereids.DorisParser.ShowEventsContext;
import org.apache.doris.nereids.DorisParser.ShowFrontendsContext;
+import org.apache.doris.nereids.DorisParser.ShowFunctionsContext;
import org.apache.doris.nereids.DorisParser.ShowGrantsContext;
import org.apache.doris.nereids.DorisParser.ShowGrantsForUserContext;
import org.apache.doris.nereids.DorisParser.ShowLastInsertContext;
@@ -652,6 +653,7 @@ import
org.apache.doris.nereids.trees.plans.commands.ShowDynamicPartitionCommand
import org.apache.doris.nereids.trees.plans.commands.ShowEncryptKeysCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowEventsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowFrontendsCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowFunctionsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowGrantsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowIndexStatsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowLastInsertCommand;
@@ -5373,6 +5375,30 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
return new ShowFrontendsCommand(detail);
}
+ @Override
+ public LogicalPlan visitShowFunctions(ShowFunctionsContext ctx) {
+ String dbName = null;
+ if (ctx.database != null) {
+ List<String> nameParts = visitMultipartIdentifier(ctx.database);
+ if (nameParts.size() == 1) {
+ dbName = nameParts.get(0);
+ } else if (nameParts.size() == 2) {
+ dbName = nameParts.get(1);
+ } else {
+ throw new AnalysisException("nameParts in analyze database
should be [ctl.]db");
+ }
+ }
+
+ boolean isVerbose = ctx.FULL() != null;
+ boolean isBuiltin = ctx.BUILTIN() != null;
+
+ String wild = null;
+ if (ctx.STRING_LITERAL() != null) {
+ wild = stripQuotes(ctx.STRING_LITERAL().getText());
+ }
+ return new ShowFunctionsCommand(dbName, isBuiltin, isVerbose, wild);
+ }
+
@Override
public LogicalPlan visitShowCreateDatabase(ShowCreateDatabaseContext ctx) {
List<String> nameParts = visitMultipartIdentifier(ctx.name);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
index 353cfb1788a..9bfdedb0e89 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
@@ -249,6 +249,7 @@ public enum PlanType {
SHOW_EVENTS_COMMAND,
SHOW_DATA_TYPES_COMMAND,
SHOW_FRONTENDS_COMMAND,
+ SHOW_FUNCTIONS_COMMAND,
SHOW_GRANTS_COMMAND,
SHOW_INDEX_STATS_COMMAND,
SHOW_LAST_INSERT_COMMAND,
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowFunctionsCommand.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowFunctionsCommand.java
new file mode 100644
index 00000000000..d5c003052df
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowFunctionsCommand.java
@@ -0,0 +1,236 @@
+// 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.nereids.trees.plans.commands;
+
+import org.apache.doris.analysis.SetType;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Database;
+import org.apache.doris.catalog.DatabaseIf;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.FunctionRegistry;
+import org.apache.doris.catalog.FunctionUtil;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.util.ListComparator;
+import org.apache.doris.common.util.OrderByPair;
+import org.apache.doris.common.util.Util;
+import org.apache.doris.datasource.InternalCatalog;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.nereids.trees.expressions.functions.FunctionBuilder;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.ShowResultSet;
+import org.apache.doris.qe.ShowResultSetMetaData;
+import org.apache.doris.qe.StmtExecutor;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * show functions command
+ */
+public class ShowFunctionsCommand extends ShowCommand {
+ private static final ShowResultSetMetaData META_DATA =
+ ShowResultSetMetaData.builder()
+ .addColumn(new Column("Signature", ScalarType.STRING))
+ .addColumn(new Column("Return Type", ScalarType.STRING))
+ .addColumn(new Column("Function Type", ScalarType.STRING))
+ .addColumn(new Column("Intermediate Type", ScalarType.STRING))
+ .addColumn(new Column("Properties", ScalarType.STRING))
+ .build();
+
+ private String dbName;
+ private boolean isBuiltin;
+ private boolean isVerbose;
+ private String likeCondition;
+ private SetType type = SetType.DEFAULT;
+
+ /**
+ * constructor
+ */
+ public ShowFunctionsCommand(String dbName, boolean isBuiltin, boolean
isVerbose, String likeCondition) {
+ super(PlanType.SHOW_FUNCTIONS_COMMAND);
+ this.dbName = dbName;
+ this.isBuiltin = isBuiltin;
+ this.isVerbose = isVerbose;
+ this.likeCondition = likeCondition;
+ }
+
+ /***
+ * get Info by nereids.
+ * To make the code in nereids more concise, all irrelevant information
here will use an empty string.
+ */
+ private List<Comparable> getInfo(boolean isVerbose, String funcName) {
+ List<Comparable> row = Lists.newArrayList();
+ if (isVerbose) {
+ // signature
+ row.add(funcName);
+ // return type
+ row.add("");
+ // function type
+ // intermediate type
+ row.add("");
+ row.add("");
+ // property
+ row.add("");
+ } else {
+ row.add(funcName);
+ }
+ return row;
+ }
+
+ @VisibleForTesting
+ protected boolean like(String funcName, String likeCondition) {
+ funcName = funcName.toLowerCase();
+ return funcName.matches(likeCondition.replace(".", "\\.").replace("?",
".").replace("%", ".*").toLowerCase());
+ }
+
+ /***
+ * get resultRowSet
+ */
+ @VisibleForTesting
+ protected List<List<String>> getResultRowSetByFunctions(List<String>
functions) {
+ List<List<String>> resultRowSet = Lists.newArrayList();
+ List<List<Comparable>> rowSet = Lists.newArrayList();
+ for (String function : functions) {
+ List<Comparable> row = getInfo(isVerbose, function);
+ // like predicate
+ if (likeCondition == null || like(function, likeCondition)) {
+ rowSet.add(row);
+ }
+ }
+
+ // sort function rows by first column asc
+ ListComparator<List<Comparable>> comparator = null;
+ OrderByPair orderByPair = new OrderByPair(0, false);
+ comparator = new ListComparator<>(orderByPair);
+ Collections.sort(rowSet, comparator);
+
+ Set<String> functionNameSet = new HashSet<>();
+ for (List<Comparable> row : rowSet) {
+ List<String> resultRow = Lists.newArrayList();
+ // if not verbose, remove duplicate function name
+ if (functionNameSet.contains(row.get(0).toString())) {
+ continue;
+ }
+ for (Comparable column : row) {
+ resultRow.add(column.toString());
+ }
+ resultRowSet.add(resultRow);
+ functionNameSet.add(resultRow.get(0));
+ }
+ return resultRowSet;
+ }
+
+ /***
+ * get resultRowSet
+ */
+ private List<List<String>> getResultRowSet(ConnectContext ctx) throws
AnalysisException {
+ List<String> functions = getFunctions(ctx);
+ return getResultRowSetByFunctions(functions);
+ }
+
+ /***
+ * get functions by nereids.
+ * All functions including builtin and udf are registered in
FunctionRegistry
+ */
+ @VisibleForTesting
+ protected List<String> getFunctions(ConnectContext ctx) throws
AnalysisException {
+ List<String> functions = Lists.newArrayList();
+ if (ctx == null || ctx.getEnv() == null ||
ctx.getEnv().getFunctionRegistry() == null) {
+ return functions;
+ }
+
+ FunctionRegistry functionRegistry = ctx.getEnv().getFunctionRegistry();
+ Map<String, Map<String, List<FunctionBuilder>>> udfFunctions =
functionRegistry.getName2UdfBuilders();
+ if (!FunctionUtil.isGlobalFunction(type)) {
+ Util.prohibitExternalCatalog(ctx.getDefaultCatalog(),
this.getClass().getSimpleName());
+ DatabaseIf db =
ctx.getCurrentCatalog().getDbOrAnalysisException(dbName);
+ if (db instanceof Database) {
+ Map<String, List<FunctionBuilder>> builtinFunctions =
functionRegistry.getName2BuiltinBuilders();
+ functions = isBuiltin ? new
ArrayList<>(builtinFunctions.keySet()) :
+ new ArrayList<>(udfFunctions.getOrDefault(dbName, new
HashMap<>()).keySet());
+ }
+ } else {
+ functions = new ArrayList<>(udfFunctions
+ .getOrDefault(functionRegistry.getGlobalFunctionDbName(), new
HashMap<>()).keySet());
+ }
+ return functions;
+ }
+
+ private static String reAcquireDbName(ConnectContext ctx, String dbName)
throws AnalysisException {
+ if (Strings.isNullOrEmpty(dbName)) {
+ dbName = ctx.getDatabase();
+ if (Strings.isNullOrEmpty(dbName)) {
+ ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_DB_ERROR);
+ }
+ }
+ return dbName;
+ }
+
+ public ShowResultSetMetaData getMetaData() {
+ if (isVerbose) {
+ return META_DATA;
+ }
+ return ShowResultSetMetaData.builder().addColumn(new Column("Function
Name", ScalarType.STRING)).build();
+ }
+
+ /**
+ * handle show functions
+ */
+ @VisibleForTesting
+ protected ShowResultSet handleShowFunctions(ConnectContext ctx,
StmtExecutor executor) throws Exception {
+ if (!FunctionUtil.isGlobalFunction(type)) {
+ this.dbName = reAcquireDbName(ctx, dbName);
+ }
+
+ if (!FunctionUtil.isGlobalFunction(type) &&
!Env.getCurrentEnv().getAccessManager()
+ .checkDbPriv(ConnectContext.get(),
InternalCatalog.INTERNAL_CATALOG_NAME, dbName, PrivPredicate.SHOW)) {
+
ErrorReport.reportAnalysisException(ErrorCode.ERR_DBACCESS_DENIED_ERROR,
+ ConnectContext.get().getQualifiedUser(), dbName);
+ }
+
+ List<List<String>> resultRowSet = getResultRowSet(ctx);
+ // Only success
+ ShowResultSetMetaData showMetaData = getMetaData();
+ return new ShowResultSet(showMetaData, resultRowSet);
+ }
+
+ @Override
+ public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor)
throws Exception {
+ return handleShowFunctions(ctx, executor);
+ }
+
+ @Override
+ public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+ return visitor.visitShowFunctionsCommand(this, context);
+ }
+
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
index 10a2d97eb73..a18f0aa418c 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
@@ -140,6 +140,7 @@ import
org.apache.doris.nereids.trees.plans.commands.ShowDynamicPartitionCommand
import org.apache.doris.nereids.trees.plans.commands.ShowEncryptKeysCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowEventsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowFrontendsCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowFunctionsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowGrantsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowIndexStatsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowLastInsertCommand;
@@ -648,6 +649,10 @@ public interface CommandVisitor<R, C> {
return visitCommand(showFrontendsCommand, context);
}
+ default R visitShowFunctionsCommand(ShowFunctionsCommand
showFunctionsCommand, C context) {
+ return visitCommand(showFunctionsCommand, context);
+ }
+
default R visitAdminRebalanceDiskCommand(AdminRebalanceDiskCommand
adminRebalanceDiskCommand, C context) {
return visitCommand(adminRebalanceDiskCommand, context);
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowFunctionsCommandTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowFunctionsCommandTest.java
new file mode 100644
index 00000000000..2be61c7289b
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowFunctionsCommandTest.java
@@ -0,0 +1,190 @@
+// 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.nereids.trees.plans.commands;
+
+import org.apache.doris.analysis.Analyzer;
+import org.apache.doris.analysis.CreateUserStmt;
+import org.apache.doris.analysis.GrantStmt;
+import org.apache.doris.analysis.TablePattern;
+import org.apache.doris.analysis.UserDesc;
+import org.apache.doris.analysis.UserIdentity;
+import org.apache.doris.catalog.AccessPrivilege;
+import org.apache.doris.catalog.AccessPrivilegeWithCols;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.DdlException;
+import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.Auth;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.utframe.TestWithFeService;
+
+import com.google.common.collect.Lists;
+import mockit.Mocked;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+public class ShowFunctionsCommandTest extends TestWithFeService {
+ private Auth auth;
+ @Mocked
+ private Analyzer analyzer;
+
+ @Override
+ protected void runBeforeAll() throws Exception {
+ createDatabase("test");
+ createDatabase("test_no_priv");
+ connectContext.setDatabase("test");
+ createFunction(
+ "CREATE ALIAS FUNCTION test.test_for_create_function(bigint)
WITH PARAMETER(id) AS CONCAT(LEFT(id,3),'****',RIGHT(id,4));");
+ }
+
+ @Test
+ void testGetFunctions() throws AnalysisException {
+ // test No database selected
+ ShowFunctionsCommand sf = new ShowFunctionsCommand("", true, false,
null);
+ connectContext.setDatabase("");
+ ShowFunctionsCommand finalSf = sf;
+ Assertions.assertThrows(AnalysisException.class, () ->
finalSf.getFunctions(connectContext));
+
+ // test for builtin functions
+ connectContext.setDatabase("test"); // reset database
+ sf = new ShowFunctionsCommand("test", true, false, null);
+ List<String> re1 = sf.getFunctions(connectContext);
+ Assertions.assertTrue(re1.size() > 100);
+
+ // test for not builtin functions
+ sf = new ShowFunctionsCommand("test", false, false, null);
+ List<String> re2 = sf.getFunctions(connectContext);
+ Assertions.assertEquals(1, re2.size());
+ Assertions.assertEquals("test_for_create_function", re2.get(0));
+
+ // test for full builtin functions
+ sf = new ShowFunctionsCommand("test", true, true, null);
+ List<String> re3 = sf.getFunctions(connectContext);
+ Assertions.assertTrue(re3.size() > 100);
+
+ // test for full not builtin functions
+ sf = new ShowFunctionsCommand("test", false, true, null);
+ List<String> re4 = sf.getFunctions(connectContext);
+ Assertions.assertEquals(1, re4.size());
+ }
+
+ @Test
+ void testGetResultRowSetByFunctions() throws AnalysisException {
+ // test for builtin functions
+ connectContext.setDatabase("test");
+ ShowFunctionsCommand sf = new ShowFunctionsCommand("test", true,
false, null);
+ List<String> func1 = sf.getFunctions(connectContext);
+ List<List<String>> re1 = sf.getResultRowSetByFunctions(func1);
+ Assertions.assertTrue(re1.size() > 100);
+
+ // test for not builtin functions
+ sf = new ShowFunctionsCommand("test", false, false, null);
+ List<String> func2 = sf.getFunctions(connectContext);
+ List<List<String>> re2 = sf.getResultRowSetByFunctions(func2);
+ Assertions.assertEquals(1, re2.get(0).size());
+ Assertions.assertEquals("test_for_create_function", re2.get(0).get(0));
+
+ // test for full builtin functions
+ sf = new ShowFunctionsCommand("test", true, true, null);
+ List<String> func3 = sf.getFunctions(connectContext);
+ List<List<String>> re3 = sf.getResultRowSetByFunctions(func3);
+ Assertions.assertTrue(re3.size() > 100);
+ Assertions.assertEquals("", re3.get(0).get(1));
+ Assertions.assertEquals("", re3.get(0).get(2));
+ Assertions.assertEquals("", re3.get(0).get(3));
+ Assertions.assertEquals("", re3.get(0).get(4));
+
+ // test for full not builtin functions
+ sf = new ShowFunctionsCommand("test", false, true, null);
+ List<String> func4 = sf.getFunctions(connectContext);
+ List<List<String>> re4 = sf.getResultRowSetByFunctions(func4);
+ Assertions.assertEquals(5, re4.get(0).size());
+ Assertions.assertEquals("test_for_create_function", re4.get(0).get(0));
+ Assertions.assertEquals("", re4.get(0).get(1));
+ Assertions.assertEquals("", re4.get(0).get(2));
+ Assertions.assertEquals("", re4.get(0).get(3));
+ Assertions.assertEquals("", re4.get(0).get(4));
+
+ // test for full not builtin functions with where condition
+ String where = "test_for_create_function%";
+ sf = new ShowFunctionsCommand("test", false, true, where);
+ List<String> func5 = sf.getFunctions(connectContext);
+ List<List<String>> re5 = sf.getResultRowSetByFunctions(func5);
+ Assertions.assertEquals(5, re5.get(0).size());
+ Assertions.assertEquals("test_for_create_function", re5.get(0).get(0));
+ Assertions.assertEquals("", re5.get(0).get(1));
+ Assertions.assertEquals("", re5.get(0).get(2));
+ Assertions.assertEquals("", re5.get(0).get(3));
+ Assertions.assertEquals("", re5.get(0).get(4));
+ }
+
+ @Test
+ void testLike() {
+ connectContext.setDatabase("test");
+ ShowFunctionsCommand sf = new ShowFunctionsCommand("test", true,
false, null);
+ Assertions.assertTrue(sf.like("test_for_create_function",
"test_for_create_function%"));
+ }
+
+ @Test
+ void testAuth() throws Exception {
+ auth = Env.getCurrentEnv().getAuth();
+ TablePattern tablePattern1 = new TablePattern("test", "*");
+ List<AccessPrivilegeWithCols> privileges1 = Lists
+ .newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV));
+ UserIdentity user1 = new UserIdentity("cmy", "%");
+ UserDesc userDesc = new UserDesc(user1, "12345", true);
+ CreateUserStmt createUserStmt = new CreateUserStmt(false, userDesc,
null);
+ try {
+ createUserStmt.analyze(analyzer);
+ } catch (UserException e) {
+ e.printStackTrace();
+ }
+
+ try {
+ auth.createUser(createUserStmt);
+ } catch (DdlException e) {
+ e.printStackTrace();
+ }
+
+ GrantStmt grantStmt = new GrantStmt(user1, null, tablePattern1,
privileges1);
+ try {
+ grantStmt.analyze(analyzer);
+ } catch (UserException e) {
+ e.printStackTrace();
+ }
+
+ try {
+ auth.grant(grantStmt);
+ } catch (DdlException e) {
+ e.printStackTrace();
+ }
+
+ ConnectContext ctx = ConnectContext.get();
+ ctx.setCurrentUserIdentity(user1);
+
+ // user1 have select privilege of db test
+ ShowFunctionsCommand sf = new ShowFunctionsCommand("test", true, true,
null);
+ sf.handleShowFunctions(ctx, null);
+
+ // but user1 have not select privilege of db test_no_priv
+ ShowFunctionsCommand sfNoPriv = new
ShowFunctionsCommand("test_no_priv", true, true, null);
+ Assertions.assertThrows(AnalysisException.class, () ->
sfNoPriv.handleShowFunctions(ctx, null));
+ }
+}
diff --git
a/regression-test/suites/nereids_p0/show/test_nereids_show_functions.groovy
b/regression-test/suites/nereids_p0/show/test_nereids_show_functions.groovy
new file mode 100644
index 00000000000..64dcb50de52
--- /dev/null
+++ b/regression-test/suites/nereids_p0/show/test_nereids_show_functions.groovy
@@ -0,0 +1,92 @@
+// 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.
+
+suite("test_nereids_show_functions") {
+ String dbName = "show_functions_db"
+ String functionName = "zzzyyyxxx"
+
+ sql "CREATE DATABASE IF NOT EXISTS ${dbName}"
+ sql """CREATE ALIAS FUNCTION ${dbName}.${functionName}(INT) WITH
PARAMETER(id) AS CONCAT(LEFT(id, 3), '****', RIGHT(id, 4));"""
+
+ checkNereidsExecute("use ${dbName}; show builtin functions;")
+ checkNereidsExecute("use ${dbName}; show builtin functions like 'ye%'")
+ checkNereidsExecute("use ${dbName}; show full builtin functions;")
+ checkNereidsExecute("use ${dbName}; show full builtin functions like
'ye%';")
+ checkNereidsExecute("use ${dbName}; show functions;")
+ checkNereidsExecute("use ${dbName}; show functions like
'${functionName}%'")
+ checkNereidsExecute("use ${dbName}; show full functions like
'${functionName}%';")
+ def res = sql """use ${dbName}; show builtin functions like '%yow%';"""
+ assertTrue(res.size() == 1)
+ def res1 = sql """use ${dbName}; show functions;"""
+ assertTrue(res1.size() == 1)
+ def res2 = sql """use ${dbName}; show functions like '${functionName}%';"""
+ assertTrue(res2.size() == 1)
+ // in nereids, each column of 'show full functions' is empty string,
except Signature.
+ def res3 = sql """use ${dbName}; show full functions like
'${functionName}%';"""
+ assertTrue(res3.size() == 1)
+ assertEquals(res3.get(0).get(0), functionName)
+ assertEquals(res3.get(0).get(1), "")
+ assertEquals(res3.get(0).get(2), "")
+ assertEquals(res3.get(0).get(3), "")
+ assertEquals(res3.get(0).get(4), "")
+
+ checkNereidsExecute("show builtin functions from ${dbName};")
+ checkNereidsExecute("show builtin functions from ${dbName} like 'ye%';")
+ checkNereidsExecute("show full builtin functions from ${dbName};")
+ checkNereidsExecute("show full builtin functions from ${dbName} like
'ye%';")
+ checkNereidsExecute("show functions from ${dbName};")
+ checkNereidsExecute("show functions from ${dbName} like
'${functionName}%';")
+ checkNereidsExecute("show full functions from ${dbName};")
+ checkNereidsExecute("show full functions from ${dbName} like
'${functionName}%';")
+ def res4 = sql """show builtin functions from ${dbName} like '%yow%';"""
+ assertTrue(res4.size() == 1)
+ def res5 = sql """show functions from ${dbName}"""
+ assertTrue(res5.size() == 1)
+ def res6 = sql """show functions from ${dbName} like '${functionName}%';"""
+ assertTrue(res6.size() == 1)
+ // in nereids, each column of 'show full functions' is empty string,
except Signature.
+ def res7 = sql """show full functions from ${dbName} like
'${functionName}%';"""
+ assertTrue(res7.size() == 1)
+ assertEquals(res7.get(0).get(0), functionName)
+ assertEquals(res7.get(0).get(1), "")
+ assertEquals(res7.get(0).get(2), "")
+ assertEquals(res7.get(0).get(3), "")
+ assertEquals(res7.get(0).get(4), "")
+
+ checkNereidsExecute("show builtin functions in ${dbName};")
+ checkNereidsExecute("show builtin functions in ${dbName} like 'ye%';")
+ checkNereidsExecute("show full builtin functions in ${dbName};")
+ checkNereidsExecute("show full builtin functions in ${dbName} like 'ye%';")
+ checkNereidsExecute("show functions in ${dbName};")
+ checkNereidsExecute("show functions in ${dbName} like '${functionName}%';")
+ checkNereidsExecute("show full functions in ${dbName};")
+ checkNereidsExecute("show full functions in ${dbName} like
'${functionName}%';")
+ def res8 = sql """show builtin functions in ${dbName} like '%yow%';"""
+ assertTrue(res8.size() == 1)
+ def res9 = sql """show functions in ${dbName}"""
+ assertTrue(res9.size() == 1)
+ def res10 = sql """show functions in ${dbName} like '${functionName}%';"""
+ assertTrue(res10.size() == 1)
+ // in nereids, each column of 'show full functions' is empty string,
except Signature.
+ def res11 = sql """show full functions in ${dbName} like
'${functionName}%';"""
+ assertTrue(res11.size() == 1)
+ assertEquals(res11.get(0).get(0), functionName)
+ assertEquals(res11.get(0).get(1), "")
+ assertEquals(res11.get(0).get(2), "")
+ assertEquals(res11.get(0).get(3), "")
+ assertEquals(res11.get(0).get(4), "")
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]