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