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 9ce55d1a0de [Enhancement] (nereids) implement cleanQueryStatsCommand in nereids (#49120) 9ce55d1a0de is described below commit 9ce55d1a0de0ad5900120ce2fbcd8fa5366c6f87 Author: yaoxiao <yx136264...@163.com> AuthorDate: Wed Apr 30 11:23:58 2025 +0800 [Enhancement] (nereids) implement cleanQueryStatsCommand in nereids (#49120) Issue Number: #42571, #42572 --- .../antlr4/org/apache/doris/nereids/DorisParser.g4 | 10 +- .../doris/nereids/parser/LogicalPlanBuilder.java | 16 ++ .../apache/doris/nereids/trees/plans/PlanType.java | 1 + .../plans/commands/CleanQueryStatsCommand.java | 176 +++++++++++++++++++++ .../trees/plans/commands/info/TableNameInfo.java | 13 ++ .../trees/plans/visitor/CommandVisitor.java | 5 + .../plans/commands/CleanQueryStatsCommandTest.java | 108 +++++++++++++ 7 files changed, 322 insertions(+), 7 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 7679e7354d9..8b3bd5b2d3a 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 @@ -81,7 +81,6 @@ unsupportedStatement | unsupportedGrantRevokeStatement | unsupportedAdminStatement | unsupportedCancelStatement - | unsupportedCleanStatement | unsupportedRefreshStatement | unsupportedLoadStatement | unsupportedShowStatement @@ -530,18 +529,15 @@ supportedRefreshStatement supportedCleanStatement : CLEAN ALL PROFILE #cleanAllProfile | CLEAN LABEL label=identifier? (FROM | IN) database=identifier #cleanLabel + | CLEAN QUERY STATS ((FOR database=identifier) + | ((FROM | IN) table=multipartIdentifier)) #cleanQueryStats + | CLEAN ALL QUERY STATS #cleanAllQueryStats ; unsupportedRefreshStatement : REFRESH LDAP (ALL | (FOR user=identifierOrText)) #refreshLdap ; -unsupportedCleanStatement - : CLEAN QUERY STATS ((FOR database=identifier) - | ((FROM | IN) table=multipartIdentifier)) #cleanQueryStats - | CLEAN ALL QUERY STATS #cleanAllQueryStats - ; - supportedCancelStatement : CANCEL LOAD ((FROM | IN) database=identifier)? wildWhere? #cancelLoad | CANCEL EXPORT ((FROM | IN) database=identifier)? wildWhere? #cancelExport 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 ab97bcdc989..853826595f9 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 @@ -562,6 +562,7 @@ import org.apache.doris.nereids.trees.plans.commands.CancelLoadCommand; import org.apache.doris.nereids.trees.plans.commands.CancelMTMVTaskCommand; import org.apache.doris.nereids.trees.plans.commands.CancelWarmUpJobCommand; import org.apache.doris.nereids.trees.plans.commands.CleanAllProfileCommand; +import org.apache.doris.nereids.trees.plans.commands.CleanQueryStatsCommand; import org.apache.doris.nereids.trees.plans.commands.Command; import org.apache.doris.nereids.trees.plans.commands.Constraint; import org.apache.doris.nereids.trees.plans.commands.CopyIntoCommand; @@ -6647,6 +6648,21 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> { properties); } + @Override + public LogicalPlan visitCleanAllQueryStats(DorisParser.CleanAllQueryStatsContext ctx) { + return new CleanQueryStatsCommand(); + } + + @Override + public LogicalPlan visitCleanQueryStats(DorisParser.CleanQueryStatsContext ctx) { + if (ctx.database != null) { + return new CleanQueryStatsCommand(ctx.identifier().getText()); + } else { + TableNameInfo tableNameInfo = new TableNameInfo(visitMultipartIdentifier(ctx.table)); + return new CleanQueryStatsCommand(tableNameInfo); + } + } + @Override public LogicalPlan visitStopDataSyncJob(DorisParser.StopDataSyncJobContext ctx) { List<String> nameParts = visitMultipartIdentifier(ctx.name); 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 70ef5d782be..08bfb08b035 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 @@ -339,6 +339,7 @@ public enum PlanType { DROP_EXPIRED_STATS_COMMAND, ALTER_TABLE_STATS_COMMAND, ALTER_COLUMN_STATS_COMMAND, + CLEAN_QUERY_STATS_COMMAND, PAUSE_DATA_SYNC_JOB_COMMAND, RESUME_DATA_SYNC_JOB_COMMAND, STOP_DATA_SYNC_JOB_COMMAND, diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CleanQueryStatsCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CleanQueryStatsCommand.java new file mode 100644 index 00000000000..d8f9d8a182b --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CleanQueryStatsCommand.java @@ -0,0 +1,176 @@ +// 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.analysis.CleanQueryStatsStmt; +import org.apache.doris.analysis.StmtType; +import org.apache.doris.catalog.DatabaseIf; +import org.apache.doris.catalog.Env; +import org.apache.doris.cluster.ClusterNamespace; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.DdlException; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +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.persist.CleanQueryStatsInfo; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.StmtExecutor; + +import org.apache.commons.lang3.StringUtils; + +/** + * CLEAN ALL QUERY STATS; + * CLEAN DATABASE QUERY STATS FROM db; + * CLEAN TABLE QUERY STATS FROM db.table; + */ +public class CleanQueryStatsCommand extends Command implements ForwardWithSync { + private String dbName; + private TableNameInfo tableNameInfo; + private Scope scope; + + /** + * CLEAN ALL QUERY STATS + */ + public CleanQueryStatsCommand() { + super(PlanType.CLEAN_QUERY_STATS_COMMAND); + this.scope = Scope.ALL; + this.tableNameInfo = null; + this.dbName = null; + } + + /** + * CLEAN DATABASE QUERY STATS FROM db; + */ + public CleanQueryStatsCommand(String dbName) { + super(PlanType.CLEAN_QUERY_STATS_COMMAND); + this.dbName = dbName; + this.tableNameInfo = null; + this.scope = Scope.DB; + } + + /** + * CLEAN TABLE QUERY STATS FROM db.table; + */ + public CleanQueryStatsCommand(TableNameInfo tableNameInfo) { + super(PlanType.CLEAN_QUERY_STATS_COMMAND); + this.dbName = null; + this.tableNameInfo = tableNameInfo; + this.scope = Scope.TABLE; + } + + @Override + public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { + validate(ctx); + handleCleanQueryStatsCommand(ctx); + } + + /** + * validate + */ + public void validate(ConnectContext ctx) throws AnalysisException { + switch (scope) { + case ALL: + if (!Env.getCurrentEnv().getAccessManager() + .checkGlobalPriv(ConnectContext.get(), PrivPredicate.ADMIN)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, + "CLEAN ALL QUERY STATS"); + } + break; + case DB: + if (StringUtils.isEmpty(dbName)) { + dbName = ctx.getDatabase(); + } + if (StringUtils.isEmpty(dbName)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_DB_ERROR); + } + + Env.getCurrentEnv().getCurrentCatalog().getDbOrAnalysisException(dbName); + if (!Env.getCurrentEnv().getAccessManager() + .checkDbPriv(ConnectContext.get(), ctx.getCurrentCatalog().getName(), dbName, + PrivPredicate.ALTER)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, + "CLEAN DATABASE QUERY STATS FOR " + ClusterNamespace.getNameFromFullName(dbName)); + } + break; + case TABLE: + tableNameInfo.analyze(ctx); + dbName = tableNameInfo.getDb(); + if (StringUtils.isEmpty(dbName)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_DB_ERROR); + } + DatabaseIf db = Env.getCurrentEnv().getCurrentCatalog().getDbOrAnalysisException(dbName); + db.getTableOrAnalysisException(tableNameInfo.getTbl()); + if (!Env.getCurrentEnv().getAccessManager() + .checkTblPriv(ConnectContext.get(), tableNameInfo.getCtl(), dbName, tableNameInfo.getTbl(), + PrivPredicate.ALTER)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, + "CLEAN TABLE QUERY STATS FROM " + tableNameInfo); + } + break; + default: + throw new IllegalStateException("Unexpected value: " + scope); + } + } + + private void handleCleanQueryStatsCommand(ConnectContext ctx) throws DdlException { + CleanQueryStatsInfo cleanQueryStatsInfo; + Env env = ctx.getEnv(); + switch (scope) { + case ALL: + cleanQueryStatsInfo = new CleanQueryStatsInfo( + translateToLegacyScope(Scope.ALL), env.getCurrentCatalog().getName(), null, null); + break; + case DB: + cleanQueryStatsInfo = new CleanQueryStatsInfo( + translateToLegacyScope(Scope.DB), env.getCurrentCatalog().getName(), tableNameInfo.getDb(), null); + break; + case TABLE: + cleanQueryStatsInfo = new CleanQueryStatsInfo( + translateToLegacyScope(Scope.TABLE), env.getCurrentCatalog().getName(), tableNameInfo.getDb(), + tableNameInfo.getTbl()); + break; + default: + throw new DdlException("Unknown scope: " + scope); + } + env.cleanQueryStats(cleanQueryStatsInfo); + } + + @Override + public <R, C> R accept(PlanVisitor<R, C> visitor, C context) { + return visitor.visitCleanQueryStatsCommand(this, context); + } + + /** + * Scope of clean query stats + */ + public enum Scope { + ALL, DB, TABLE + } + + private CleanQueryStatsStmt.Scope translateToLegacyScope(Scope scope) { + return CleanQueryStatsStmt.Scope.valueOf(scope.name()); + } + + @Override + public StmtType stmtType() { + return StmtType.CLEAN; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/TableNameInfo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/TableNameInfo.java index d184c330116..bc951cb7fc4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/TableNameInfo.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/TableNameInfo.java @@ -196,6 +196,19 @@ public class TableNameInfo implements Writable { tbl = fromJson.tbl; } + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + if (ctl != null && !ctl.equals(InternalCatalog.INTERNAL_CATALOG_NAME)) { + stringBuilder.append(ctl).append("."); + } + if (db != null) { + stringBuilder.append(db).append("."); + } + stringBuilder.append(tbl); + return stringBuilder.toString(); + } + /** * equals */ 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 ff96b0ae75e..92dae110281 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 @@ -47,6 +47,7 @@ import org.apache.doris.nereids.trees.plans.commands.CancelLoadCommand; import org.apache.doris.nereids.trees.plans.commands.CancelMTMVTaskCommand; import org.apache.doris.nereids.trees.plans.commands.CancelWarmUpJobCommand; import org.apache.doris.nereids.trees.plans.commands.CleanAllProfileCommand; +import org.apache.doris.nereids.trees.plans.commands.CleanQueryStatsCommand; import org.apache.doris.nereids.trees.plans.commands.Command; import org.apache.doris.nereids.trees.plans.commands.CopyIntoCommand; import org.apache.doris.nereids.trees.plans.commands.CreateCatalogCommand; @@ -968,6 +969,10 @@ public interface CommandVisitor<R, C> { return visitCommand(alterColumnStatsCommand, context); } + default R visitCleanQueryStatsCommand(CleanQueryStatsCommand cleanQueryStatsCommand, C context) { + return visitCommand(cleanQueryStatsCommand, context); + } + default R visitPauseDataSyncJobCommand(PauseDataSyncJobCommand pauseDataSyncJobCommand, C context) { return visitCommand(pauseDataSyncJobCommand, context); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/CleanQueryStatsCommandTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/CleanQueryStatsCommandTest.java new file mode 100644 index 00000000000..eae35f9f2c5 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/CleanQueryStatsCommandTest.java @@ -0,0 +1,108 @@ +// 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.Env; +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 org.apache.doris.utframe.TestWithFeService; + +import mockit.Expectations; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +public class CleanQueryStatsCommandTest extends TestWithFeService { + private static final String internalCtl = InternalCatalog.INTERNAL_CATALOG_NAME; + private ConnectContext connectContext; + private Env env; + private AccessControllerManager accessControllerManager; + + public void runBefore() throws IOException { + connectContext = createDefaultCtx(); + env = Env.getCurrentEnv(); + accessControllerManager = env.getAccessManager(); + } + + @Test + public void testAllNormal() throws IOException { + runBefore(); + new Expectations() { + { + connectContext.isSkipAuth(); + minTimes = 0; + result = true; + + accessControllerManager.checkGlobalPriv(connectContext, PrivPredicate.ADMIN); + minTimes = 0; + result = true; + } + }; + CleanQueryStatsCommand command = new CleanQueryStatsCommand(); + Assertions.assertDoesNotThrow(() -> command.validate(connectContext)); + } + + @Test + public void testDB() throws Exception { + runBefore(); + connectContext.setDatabase("test_db"); + //normal + new Expectations() { + { + connectContext.isSkipAuth(); + minTimes = 0; + result = true; + + accessControllerManager.checkDbPriv(connectContext, internalCtl, "test_db", PrivPredicate.ALTER); + minTimes = 0; + result = true; + } + }; + CleanQueryStatsCommand command = new CleanQueryStatsCommand("test_db"); + Assertions.assertDoesNotThrow(() -> command.validate(connectContext)); + } + + @Test + public void testTbl() throws Exception { + runBefore(); + createDatabase("test_db"); + createTable("create table test_db.test_tbl\n" + "(k1 int, k2 int)\n" + + "duplicate key(k1)\n" + "partition by range(k2)\n" + "(partition p1 values less than(\"10\"))\n" + + "distributed by hash(k2) buckets 1\n" + "properties('replication_num' = '1'); "); + TableNameInfo tableNameInfo = new TableNameInfo("test_db", "test_tbl"); + connectContext.setDatabase("test_db"); + //normal + new Expectations() { + { + connectContext.isSkipAuth(); + minTimes = 0; + result = true; + + accessControllerManager.checkTblPriv(connectContext, tableNameInfo, PrivPredicate.ALTER); + minTimes = 0; + result = true; + } + }; + CleanQueryStatsCommand command = new CleanQueryStatsCommand(tableNameInfo); + Assertions.assertDoesNotThrow(() -> command.validate(connectContext)); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org