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 84f0eefaa66 [Feat](Nereids) support show databases command (#48941)
84f0eefaa66 is described below

commit 84f0eefaa6606f7c53ab9b386b51813db5ca0659
Author: Jensen <czjour...@163.com>
AuthorDate: Wed Mar 19 11:51:10 2025 +0800

    [Feat](Nereids) support show databases command (#48941)
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |   2 +-
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  20 +++
 .../apache/doris/nereids/trees/plans/PlanType.java |   1 +
 .../trees/plans/commands/ShowDatabasesCommand.java | 165 +++++++++++++++++++++
 .../trees/plans/visitor/CommandVisitor.java        |   5 +
 .../plans/commands/ShowDatabasesCommandTest.java   |  82 ++++++++++
 .../java/org/apache/doris/qe/StmtExecutorTest.java |  11 --
 7 files changed, 274 insertions(+), 12 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 7abf3e8b4ad..4af3af59ac2 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
@@ -342,6 +342,7 @@ supportedShowStatement
     | SHOW FULL? TABLES ((FROM | IN) database=multipartIdentifier)? wildWhere? 
     #showTables
     | SHOW FULL? VIEWS ((FROM | IN) database=multipartIdentifier)? wildWhere?  
     #showViews
     | SHOW TABLE STATUS ((FROM | IN) database=multipartIdentifier)? wildWhere? 
     #showTableStatus
+    | SHOW (DATABASES | SCHEMAS) (FROM catalog=identifier)? wildWhere?         
     #showDatabases
     ;
 
 supportedLoadStatement
@@ -387,7 +388,6 @@ unsupportedShowStatement
     | SHOW CREATE statementScope? FUNCTION functionIdentifier
         LEFT_PAREN functionArguments? RIGHT_PAREN
         ((FROM | IN) database=multipartIdentifier)?                            
     #showCreateFunction
-    | SHOW (DATABASES | SCHEMAS) (FROM catalog=identifier)? wildWhere?         
     #showDatabases
     | SHOW FULL? (COLUMNS | FIELDS) (FROM | IN) tableName=multipartIdentifier
         ((FROM | IN) database=multipartIdentifier)? wildWhere?                 
     #showColumns
     | SHOW LOAD WARNINGS ((((FROM | IN) database=multipartIdentifier)?
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 62574ee0f68..18c0f00dc7e 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
@@ -629,6 +629,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.ShowDataCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowDataSkewCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowDataTypesCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowDatabaseIdCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowDatabasesCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowDeleteCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowDiagnoseTabletCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.ShowDynamicPartitionCommand;
@@ -6073,6 +6074,25 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         return new ShowTabletIdCommand(tabletId);
     }
 
+    @Override
+    public LogicalPlan visitShowDatabases(DorisParser.ShowDatabasesContext 
ctx) {
+        String ctlName = null;
+        if (ctx.catalog != null) {
+            ctlName = ctx.catalog.getText();
+        }
+
+        if (ctx.wildWhere() != null) {
+            if (ctx.wildWhere().LIKE() != null) {
+                return new ShowDatabasesCommand(ctlName,
+                        
stripQuotes(ctx.wildWhere().STRING_LITERAL().getText()), null);
+            } else {
+                Expression expr = (Expression) 
ctx.wildWhere().expression().accept(this);
+                return new ShowDatabasesCommand(ctlName, null, expr);
+            }
+        }
+        return new ShowDatabasesCommand(ctlName, null, null);
+    }
+
     @Override
     public LogicalPlan visitDescribeTable(DorisParser.DescribeTableContext 
ctx) {
         TableNameInfo tableName = new 
TableNameInfo(visitMultipartIdentifier(ctx.multipartIdentifier()));
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 f3c670e9017..f61752ce934 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
@@ -235,6 +235,7 @@ public enum PlanType {
     SHOW_CREATE_REPOSITORY_COMMAND,
     SHOW_CREATE_TABLE_COMMAND,
     SHOW_CREATE_VIEW_COMMAND,
+    SHOW_DATABASES_COMMAND,
     SHOW_DATABASE_ID_COMMAND,
     SHOW_DATA_COMMAND,
     SHOW_DATA_SKEW_COMMAND,
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowDatabasesCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowDatabasesCommand.java
new file mode 100644
index 00000000000..5a296cb8c16
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowDatabasesCommand.java
@@ -0,0 +1,165 @@
+// 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.catalog.Column;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.InfoSchemaDb;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.cluster.ClusterNamespace;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.CaseSensibility;
+import org.apache.doris.common.PatternMatcher;
+import org.apache.doris.common.PatternMatcherWrapper;
+import org.apache.doris.datasource.CatalogIf;
+import org.apache.doris.datasource.InternalCatalog;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.nereids.analyzer.UnboundSlot;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import 
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.commands.info.AliasInfo;
+import org.apache.doris.nereids.trees.plans.commands.info.TableNameInfo;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.nereids.util.Utils;
+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.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * ShowDatabasesCommand
+ */
+public class ShowDatabasesCommand extends ShowCommand {
+    private static final String DB_COL = "Database";
+    private static final String ORI_DB_COL = "SCHEMA_NAME";
+    private static final ShowResultSetMetaData META_DATA = 
ShowResultSetMetaData.builder()
+                    .addColumn(new Column(DB_COL, 
ScalarType.createVarchar(20)))
+                    .build();
+
+    private String catalog;
+    private final String likePattern;
+    private final Expression whereClause;
+
+    /**
+     * ShowDatabasesCommand
+     */
+    public ShowDatabasesCommand(String catalog, String likePattern, Expression 
whereClause) {
+        super(PlanType.SHOW_DATABASES_COMMAND);
+        this.catalog = catalog;
+        this.likePattern = likePattern;
+        this.whereClause = whereClause;
+    }
+
+    /**
+     * validate
+     */
+    public void validate(ConnectContext ctx) throws AnalysisException {
+        if (Strings.isNullOrEmpty(catalog)) {
+            catalog = ctx.getDefaultCatalog();
+            if (Strings.isNullOrEmpty(catalog)) {
+                catalog = InternalCatalog.INTERNAL_CATALOG_NAME;
+            }
+        }
+    }
+
+    /**
+     * replaceColumnNameVisitor
+     * replace column name to real column name
+     */
+    private static class ReplaceColumnNameVisitor extends 
DefaultExpressionRewriter<Void> {
+        @Override
+        public Expression visitUnboundSlot(UnboundSlot slot, Void context) {
+            if 
(slot.getName().toLowerCase(Locale.ROOT).equals(DB_COL.toLowerCase(Locale.ROOT)))
 {
+                return UnboundSlot.quoted(ORI_DB_COL);
+            }
+            return slot;
+        }
+    }
+
+    private ShowResultSet execute(ConnectContext ctx, StmtExecutor executor, 
String whereClause) {
+        List<AliasInfo> selectList = new ArrayList<>();
+        selectList.add(AliasInfo.of(ORI_DB_COL, DB_COL));
+
+        TableNameInfo fullTblName = new TableNameInfo(catalog, 
InfoSchemaDb.DATABASE_NAME, "schemata");
+
+        LogicalPlan plan = Utils.buildLogicalPlan(selectList, fullTblName, 
whereClause);
+        List<List<String>> rows = Utils.executePlan(ctx, executor, plan);
+        return new ShowResultSet(META_DATA, rows);
+    }
+
+    @Override
+    public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) 
throws Exception {
+        validate(ctx);
+        if (whereClause != null) {
+            Expression rewrited = whereClause.accept(new 
ReplaceColumnNameVisitor(), null);
+            String whereCondition = " WHERE `CATALOG_NAME` = '" + catalog
+                    + "' AND " + rewrited.toSql();
+            return execute(ctx, executor, whereCondition);
+        }
+        List<List<String>> rows = Lists.newArrayList();
+        CatalogIf catalogIf = ctx.getCatalog(catalog);
+        if (catalogIf == null) {
+            throw new AnalysisException("No catalog found with name " + 
catalog);
+        }
+        List<String> dbNames = catalogIf.getDbNames();
+        PatternMatcher matcher = null;
+        if (likePattern != null) {
+            matcher = PatternMatcherWrapper.createMysqlPattern(likePattern,
+                    CaseSensibility.DATABASE.getCaseSensibility());
+        }
+        Set<String> dbNameSet = Sets.newTreeSet();
+        for (String fullName : dbNames) {
+            final String db = ClusterNamespace.getNameFromFullName(fullName);
+            // Filter dbname
+            if (matcher != null && !matcher.match(db)) {
+                continue;
+            }
+
+            if 
(!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(), 
catalog,
+                    fullName, PrivPredicate.SHOW)) {
+                continue;
+            }
+
+            dbNameSet.add(db);
+        }
+
+        for (String dbName : dbNameSet) {
+            rows.add(Lists.newArrayList(dbName));
+        }
+
+        rows.sort(Comparator.comparing(x -> x.get(0)));
+        return new ShowResultSet(META_DATA, rows);
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitShowDatabasesCommand(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 0844700f942..2a16d81ad20 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
@@ -126,6 +126,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.ShowDataCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowDataSkewCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowDataTypesCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowDatabaseIdCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowDatabasesCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowDeleteCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowDiagnoseTabletCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.ShowDynamicPartitionCommand;
@@ -853,6 +854,10 @@ public interface CommandVisitor<R, C> {
         return visitCommand(showTableStatusCommand, context);
     }
 
+    default R visitShowDatabasesCommand(ShowDatabasesCommand 
showDatabasesCommand, C context) {
+        return visitCommand(showDatabasesCommand, context);
+    }
+
     default R visitShowTableCommand(ShowTableCommand showTableCommand, C 
context) {
         return visitCommand(showTableCommand, context);
     }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowDatabasesCommandTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowDatabasesCommandTest.java
new file mode 100644
index 00000000000..384aa9f68ac
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowDatabasesCommandTest.java
@@ -0,0 +1,82 @@
+// 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.backup.CatalogMocker;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.datasource.CatalogMgr;
+import org.apache.doris.datasource.InternalCatalog;
+import org.apache.doris.mysql.privilege.AccessControllerManager;
+import org.apache.doris.nereids.analyzer.UnboundSlot;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
+import org.apache.doris.qe.ConnectContext;
+
+import mockit.Expectations;
+import mockit.Mocked;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ShowDatabasesCommandTest {
+    private static final String internalCtl = 
InternalCatalog.INTERNAL_CATALOG_NAME;
+
+    @Mocked
+    private Env env;
+    @Mocked
+    private AccessControllerManager accessManager;
+    @Mocked
+    private ConnectContext ctx;
+    @Mocked
+    private InternalCatalog catalog;
+    @Mocked
+    private CatalogMgr catalogMgr;
+
+    @Test
+    void testValidate() {
+        new Expectations() {
+            {
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getAccessManager();
+                minTimes = 0;
+                result = accessManager;
+
+                env.getCatalogMgr();
+                minTimes = 0;
+                result = catalogMgr;
+
+                catalogMgr.getCatalog(anyString);
+                minTimes = 0;
+                result = catalog;
+            }
+        };
+        EqualTo equalTo = new EqualTo(new UnboundSlot("schema_name"),
+                new StringLiteral(CatalogMocker.TEST_DB_NAME));
+
+        // normal
+        ShowDatabasesCommand command = new ShowDatabasesCommand(internalCtl, 
null, equalTo);
+        Assertions.assertDoesNotThrow(() -> command.validate(ctx));
+
+        // catalog is null
+        ShowDatabasesCommand command2 = new ShowDatabasesCommand(null, null, 
equalTo);
+        Assertions.assertDoesNotThrow(() -> command2.validate(ctx));
+    }
+}
+
diff --git a/fe/fe-core/src/test/java/org/apache/doris/qe/StmtExecutorTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/qe/StmtExecutorTest.java
index 8ab187315d8..d484b53bff5 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/qe/StmtExecutorTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/qe/StmtExecutorTest.java
@@ -30,7 +30,6 @@ import org.apache.doris.analysis.SetStmt;
 import org.apache.doris.analysis.ShowAuthorStmt;
 import org.apache.doris.analysis.ShowStmt;
 import org.apache.doris.analysis.SqlParser;
-import org.apache.doris.analysis.StatementBase;
 import org.apache.doris.analysis.UseStmt;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.common.Config;
@@ -544,16 +543,6 @@ public class StmtExecutorTest {
         Assert.assertEquals(QueryState.MysqlStateType.OK, 
state.getStateType());
     }
 
-    @Test
-    public void testStmtWithUserInfo(@Mocked StatementBase stmt, @Mocked 
ConnectContext context) throws Exception {
-        StmtExecutor stmtExecutor = new StmtExecutor(ctx, stmt);
-        Deencapsulation.setField(stmtExecutor, "parsedStmt", null);
-        Deencapsulation.setField(stmtExecutor, "originStmt", new 
OriginStatement("show databases;", 0));
-        stmtExecutor.execute();
-        StatementBase newstmt = Deencapsulation.getField(stmtExecutor, 
"parsedStmt");
-        Assert.assertNotNull(newstmt.getUserInfo());
-    }
-
     @Test
     public void testSetFail(@Mocked SetStmt setStmt, @Mocked SqlParser parser,
             @Mocked SetExecutor executor) throws Exception {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to