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

Reply via email to