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