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 7633d8c6356 [Enhancement] (nereids) Implement showIndexStatsCommand in nereids (#48835) 7633d8c6356 is described below commit 7633d8c6356d1bbdabba661ccbdfbc71e63d6dc3 Author: Jensen <czjour...@163.com> AuthorDate: Mon Mar 17 21:33:19 2025 +0800 [Enhancement] (nereids) Implement showIndexStatsCommand in nereids (#48835) Issue Number: close #42716 --- .../antlr4/org/apache/doris/nereids/DorisParser.g4 | 2 +- .../doris/nereids/parser/LogicalPlanBuilder.java | 8 ++ .../apache/doris/nereids/trees/plans/PlanType.java | 1 + .../plans/commands/ShowIndexStatsCommand.java | 142 +++++++++++++++++++++ .../trees/plans/visitor/CommandVisitor.java | 5 + .../plans/commands/ShowIndexStatsCommandTest.java | 113 ++++++++++++++++ 6 files changed, 270 insertions(+), 1 deletion(-) 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 c2a8e4fd07f..241e43cd46f 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 @@ -742,6 +742,7 @@ supportedStatsStatement | ALTER TABLE name=multipartIdentifier (INDEX indexName=identifier)? MODIFY COLUMN columnName=identifier SET STATS LEFT_PAREN propertyItemList RIGHT_PAREN partitionSpec? #alterColumnStats + | SHOW INDEX STATS tableName=multipartIdentifier indexId=identifier #showIndexStats ; unsupportedStatsStatement @@ -754,7 +755,6 @@ unsupportedStatsStatement | SHOW TABLE STATS tableName=multipartIdentifier partitionSpec? columnList=identifierList? #showTableStats | SHOW TABLE STATS tableId=INTEGER_VALUE #showTableStats - | SHOW INDEX STATS tableName=multipartIdentifier indexId=identifier #showIndexStats | SHOW COLUMN CACHED? STATS tableName=multipartIdentifier columnList=identifierList? partitionSpec? #showColumnStats | SHOW ANALYZE TASK STATUS jobId=INTEGER_VALUE #showAnalyzeTask 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 f92fd5f391a..4830a72de31 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 @@ -631,6 +631,7 @@ 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.ShowGrantsCommand; +import org.apache.doris.nereids.trees.plans.commands.ShowIndexStatsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowLastInsertCommand; import org.apache.doris.nereids.trees.plans.commands.ShowLoadProfileCommand; import org.apache.doris.nereids.trees.plans.commands.ShowPartitionIdCommand; @@ -5951,6 +5952,13 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> { return new ShowQueuedAnalyzeJobsCommand(tableName, stateKey, stateValue); } + @Override + public LogicalPlan visitShowIndexStats(DorisParser.ShowIndexStatsContext ctx) { + TableNameInfo tableName = new TableNameInfo(visitMultipartIdentifier(ctx.tableName)); + String indexId = stripQuotes(ctx.indexId.getText()); + return new ShowIndexStatsCommand(tableName, indexId); + } + @Override public LogicalPlan visitShowTableStatus(DorisParser.ShowTableStatusContext ctx) { String ctlName = null; 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 fe1a5d8eb76..b1d66432c6a 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 @@ -246,6 +246,7 @@ public enum PlanType { SHOW_DATA_TYPES_COMMAND, SHOW_FRONTENDS_COMMAND, SHOW_GRANTS_COMMAND, + SHOW_INDEX_STATS_COMMAND, SHOW_LAST_INSERT_COMMAND, SHOW_LOAD_PROFILE_COMMAND, SHOW_PARTITIONID_COMMAND, diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowIndexStatsCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowIndexStatsCommand.java new file mode 100644 index 00000000000..3178ca19a04 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowIndexStatsCommand.java @@ -0,0 +1,142 @@ +// 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.DatabaseIf; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.OlapTable; +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.catalog.TableIf; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.datasource.CatalogIf; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.commands.info.TableNameInfo; +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 org.apache.doris.statistics.TableStatsMeta; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import java.util.List; + +/** + * ShowIndexStatsCommand + */ +public class ShowIndexStatsCommand extends ShowCommand { + private static final ImmutableList<String> INDEX_TITLE_NAMES = + new ImmutableList.Builder<String>() + .add("table_name") + .add("index_name") + .add("analyze_row_count") + .add("report_row_count") + .add("report_row_count_for_nereids") + .build(); + private final TableNameInfo tableNameInfo; + private final String indexName; + private TableIf table; + + /** + * Constructor + */ + public ShowIndexStatsCommand(TableNameInfo tableNameInfo, String indexName) { + super(PlanType.SHOW_INDEX_STATS_COMMAND); + this.tableNameInfo = tableNameInfo; + this.indexName = indexName; + } + + /** + * validate + */ + public void validate(ConnectContext ctx) throws AnalysisException { + tableNameInfo.analyze(ctx); + + CatalogIf<DatabaseIf> catalog = Env.getCurrentEnv().getCatalogMgr().getCatalog(tableNameInfo.getCtl()); + if (catalog == null) { + ErrorReport.reportAnalysisException(String.format("Catalog: %s not exists", tableNameInfo.getCtl())); + } + DatabaseIf<TableIf> db = catalog.getDb(tableNameInfo.getDb()).orElse(null); + if (db == null) { + ErrorReport.reportAnalysisException(String.format("DB: %s not exists", tableNameInfo.getDb())); + } + table = db.getTable(tableNameInfo.getTbl()).orElse(null); + if (table == null) { + ErrorReport.reportAnalysisException(String.format("Table: %s not exists", tableNameInfo.getTbl())); + } + if (!Env.getCurrentEnv().getAccessManager().checkTblPriv(ConnectContext.get(), + tableNameInfo.getCtl(), tableNameInfo.getDb(), tableNameInfo.getTbl(), PrivPredicate.SHOW)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR, "Permission denied", + ConnectContext.get().getQualifiedUser(), ConnectContext.get().getRemoteIP(), + tableNameInfo.getDb() + ": " + tableNameInfo.getTbl()); + } + } + + @Override + public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) throws Exception { + validate(ctx); + TableStatsMeta tableStats = Env.getCurrentEnv().getAnalysisManager().findTableStatsStatus(table.getId()); + return constructIndexResultSet(tableStats, table); + } + + /** + * constructIndexResultSet + */ + public ShowResultSet constructIndexResultSet(TableStatsMeta tableStatistic, TableIf table) { + List<List<String>> result = Lists.newArrayList(); + if (!(table instanceof OlapTable)) { + return new ShowResultSet(getMetaData(), result); + } + OlapTable olapTable = (OlapTable) table; + Long indexId = olapTable.getIndexIdByName(indexName); + if (indexId == null) { + throw new RuntimeException(String.format("Index %s not exist.", indexName)); + } + long rowCount = tableStatistic == null ? -1 : tableStatistic.getRowCount(olapTable.getIndexIdByName(indexName)); + List<String> row = Lists.newArrayList(); + row.add(table.getName()); + row.add(indexName); + row.add(String.valueOf(rowCount)); + row.add(String.valueOf(olapTable.getRowCountForIndex(indexId, false))); + row.add(String.valueOf(olapTable.getRowCountForIndex(indexId, true))); + result.add(row); + return new ShowResultSet(getMetaData(), result); + } + + /** + * getMetaData + */ + public ShowResultSetMetaData getMetaData() { + ShowResultSetMetaData.Builder builder = ShowResultSetMetaData.builder(); + for (String title : INDEX_TITLE_NAMES) { + builder.addColumn(new Column(title, ScalarType.createVarchar(30))); + } + return builder.build(); + } + + @Override + public <R, C> R accept(PlanVisitor<R, C> visitor, C context) { + return visitor.visitShowIndexStatsCommand(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 01ed6315fa3..b730790fd18 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 @@ -130,6 +130,7 @@ 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.ShowGrantsCommand; +import org.apache.doris.nereids.trees.plans.commands.ShowIndexStatsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowLastInsertCommand; import org.apache.doris.nereids.trees.plans.commands.ShowLoadProfileCommand; import org.apache.doris.nereids.trees.plans.commands.ShowPartitionIdCommand; @@ -848,6 +849,10 @@ public interface CommandVisitor<R, C> { return visitCommand(showTableCommand, context); } + default R visitShowIndexStatsCommand(ShowIndexStatsCommand showIndexStatsCommand, C context) { + return visitCommand(showIndexStatsCommand, context); + } + default R visitShowTabletIdCommand(ShowTabletIdCommand showTabletIdCommand, C context) { return visitCommand(showTabletIdCommand, context); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowIndexStatsCommandTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowIndexStatsCommandTest.java new file mode 100644 index 00000000000..c3d8d4e855d --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowIndexStatsCommandTest.java @@ -0,0 +1,113 @@ +// 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.Database; +import org.apache.doris.catalog.Env; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.datasource.InternalCatalog; +import org.apache.doris.mysql.privilege.AccessControllerManager; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.nereids.trees.plans.commands.info.TableNameInfo; +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 ShowIndexStatsCommandTest { + private static final String internalCtl = InternalCatalog.INTERNAL_CATALOG_NAME; + private static final String tableNotExist = "table_not_exist"; + @Mocked + private Env env; + @Mocked + private InternalCatalog catalog; + @Mocked + private AccessControllerManager accessManager; + @Mocked + private ConnectContext ctx; + private Database db; + + private void runBefore() throws Exception { + db = CatalogMocker.mockDb(); + new Expectations() { + { + Env.getCurrentEnv(); + minTimes = 0; + result = env; + + env.getCatalogMgr().getCatalog(anyString); + minTimes = 0; + result = catalog; + + catalog.getDb(anyString); + minTimes = 0; + result = db; + + env.getAccessManager(); + minTimes = 0; + result = accessManager; + + ConnectContext.get(); + minTimes = 0; + result = ctx; + + ctx.isSkipAuth(); + minTimes = 0; + result = true; + + accessManager.checkTblPriv(ctx, internalCtl, CatalogMocker.TEST_DB_NAME, CatalogMocker.TEST_TBL_NAME, + PrivPredicate.SHOW); + minTimes = 0; + result = true; + + accessManager.checkTblPriv(ctx, internalCtl, CatalogMocker.TEST_DB_NAME, CatalogMocker.TEST_TBL2_NAME, + PrivPredicate.SHOW); + minTimes = 0; + result = false; + } + }; + } + + @Test + public void testValidateNormal() throws Exception { + runBefore(); + TableNameInfo tableNameInfo = + new TableNameInfo(internalCtl, CatalogMocker.TEST_DB_NAME, CatalogMocker.TEST_TBL_NAME); + ShowIndexStatsCommand command = new ShowIndexStatsCommand(tableNameInfo, CatalogMocker.TEST_TBL_NAME); + Assertions.assertDoesNotThrow(() -> command.validate(ctx)); + } + + @Test + public void testValidateFail() throws Exception { + runBefore(); + TableNameInfo tableNameInfo = + new TableNameInfo(internalCtl, CatalogMocker.TEST_DB_NAME, tableNotExist); + ShowIndexStatsCommand command = new ShowIndexStatsCommand(tableNameInfo, tableNotExist); + Assertions.assertThrows(AnalysisException.class, () -> command.validate(ctx), + "Table: " + tableNotExist + " not exists"); + + TableNameInfo tableNameInfo2 = + new TableNameInfo(internalCtl, CatalogMocker.TEST_DB_NAME, CatalogMocker.TEST_TBL2_NAME); + ShowIndexStatsCommand command2 = new ShowIndexStatsCommand(tableNameInfo2, CatalogMocker.TEST_TBL2_NAME); + Assertions.assertThrows(AnalysisException.class, () -> command2.validate(ctx), + "Permission denied command denied to user 'null'@'null' for table 'test_db: test_tbl2'"); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org