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 <110876560+lsy3...@users.noreply.github.com>
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: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to