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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]