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 0cab080a3f5 [Enhancement] (nereids) implement cancelBuildIndexCommand 
in nereids (#49631)
0cab080a3f5 is described below

commit 0cab080a3f5b5e2043b85ca59fcf237c21e61d75
Author: yaoxiao <[email protected]>
AuthorDate: Mon May 19 09:42:42 2025 +0800

    [Enhancement] (nereids) implement cancelBuildIndexCommand in nereids 
(#49631)
    
    Issue Number: close #42560
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |  10 +-
 .../apache/doris/alter/SchemaChangeHandler.java    |  60 ++++++++++++
 .../main/java/org/apache/doris/catalog/Env.java    |   8 ++
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  11 +++
 .../apache/doris/nereids/trees/plans/PlanType.java |   1 +
 .../plans/commands/CancelBuildIndexCommand.java    | 106 +++++++++++++++++++++
 .../trees/plans/visitor/CommandVisitor.java        |   5 +
 .../commands/CancelBuildIndexCommandTest.java      |  87 +++++++++++++++++
 8 files changed, 283 insertions(+), 5 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 eeefdcc5e65..2c58279d677 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
@@ -558,16 +558,16 @@ supportedCancelStatement
     : CANCEL LOAD ((FROM | IN) database=identifier)? wildWhere?                
     #cancelLoad
     | CANCEL EXPORT ((FROM | IN) database=identifier)? wildWhere?              
     #cancelExport
     | CANCEL WARM UP JOB wildWhere?                                            
     #cancelWarmUpJob
-    ;
-
-unsupportedCancelStatement
-    : CANCEL ALTER TABLE (ROLLUP | (MATERIALIZED VIEW) | COLUMN)
+    | CANCEL ALTER TABLE (ROLLUP | (MATERIALIZED VIEW) | COLUMN)
         FROM tableName=multipartIdentifier (LEFT_PAREN jobIds+=INTEGER_VALUE
             (COMMA jobIds+=INTEGER_VALUE)* RIGHT_PAREN)?                       
     #cancelAlterTable
     | CANCEL BUILD INDEX ON tableName=multipartIdentifier
         (LEFT_PAREN jobIds+=INTEGER_VALUE
             (COMMA jobIds+=INTEGER_VALUE)* RIGHT_PAREN)?                       
     #cancelBuildIndex
-    | CANCEL DECOMMISSION BACKEND hostPorts+=STRING_LITERAL
+    ;
+
+unsupportedCancelStatement
+    : CANCEL DECOMMISSION BACKEND hostPorts+=STRING_LITERAL
         (COMMA hostPorts+=STRING_LITERAL)*                                     
     #cancelDecommisionBackend
     | CANCEL BACKUP ((FROM | IN) database=identifier)?                         
     #cancelBackup
     | CANCEL RESTORE ((FROM | IN) database=identifier)?                        
     #cancelRestore
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java 
b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
index 5e742d11caa..caa16f0ac20 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
@@ -86,6 +86,7 @@ import org.apache.doris.common.util.Util;
 import org.apache.doris.datasource.InternalCatalog;
 import org.apache.doris.mysql.privilege.PrivPredicate;
 import org.apache.doris.nereids.trees.plans.commands.AlterCommand;
+import org.apache.doris.nereids.trees.plans.commands.CancelBuildIndexCommand;
 import org.apache.doris.nereids.trees.plans.commands.info.ColumnDefinition;
 import org.apache.doris.persist.AlterLightSchemaChangeInfo;
 import org.apache.doris.persist.RemoveAlterJobV2OperationLog;
@@ -2635,6 +2636,65 @@ public class SchemaChangeHandler extends AlterHandler {
         }
     }
 
+    public void cancelIndexJob(CancelBuildIndexCommand command) throws 
DdlException {
+        String dbName = command.getDbName();
+        String tableName = command.getTableName();
+        Preconditions.checkState(!Strings.isNullOrEmpty(dbName));
+        Preconditions.checkState(!Strings.isNullOrEmpty(tableName));
+
+        Database db = 
Env.getCurrentInternalCatalog().getDbOrDdlException(dbName);
+
+        List<IndexChangeJob> jobList = new ArrayList<>();
+
+        Table olapTable = db.getTableOrDdlException(tableName, 
Table.TableType.OLAP);
+        olapTable.writeLock();
+        try {
+            // find from index change jobs first
+            if (command.getAlterJobIdList() != null
+                    && command.getAlterJobIdList().size() > 0) {
+                for (Long jobId : command.getAlterJobIdList()) {
+                    IndexChangeJob job = indexChangeJobs.get(jobId);
+                    if (job == null) {
+                        continue;
+                    }
+                    jobList.add(job);
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("add build index job {} on table {} for 
specific id", jobId, tableName);
+                    }
+                }
+            } else {
+                for (IndexChangeJob job : indexChangeJobs.values()) {
+                    if (!job.isDone() && job.getTableId() == 
olapTable.getId()) {
+                        jobList.add(job);
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("add build index job {} on table {} for 
all", job.getJobId(), tableName);
+                        }
+                    }
+                }
+            }
+        } finally {
+            olapTable.writeUnlock();
+        }
+
+        // alter job v2's cancel must be called outside the table lock
+        if (jobList.size() > 0) {
+            for (IndexChangeJob job : jobList) {
+                long jobId = job.getJobId();
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("cancel build index job {} on table {}", jobId, 
tableName);
+                }
+                if (!job.cancel("user cancelled")) {
+                    LOG.warn("cancel build index job {} on table {} failed", 
jobId, tableName);
+                    throw new DdlException("Job can not be cancelled. State: " 
+ job.getJobState());
+                } else {
+                    LOG.info("cancel build index job {} on table {} success", 
jobId, tableName);
+                }
+            }
+        } else {
+            throw new DdlException("No job to cancel for Table[" + tableName + 
"]");
+        }
+    }
+
     private void cancelIndexJob(CancelAlterTableStmt cancelAlterTableStmt) 
throws DdlException {
         String dbName = cancelAlterTableStmt.getDbName();
         String tableName = cancelAlterTableStmt.getTableName();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
index cfebc422759..da07550bda7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
@@ -207,6 +207,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.AdminSetReplicaStatusComman
 import org.apache.doris.nereids.trees.plans.commands.AlterSystemCommand;
 import org.apache.doris.nereids.trees.plans.commands.AlterTableCommand;
 import org.apache.doris.nereids.trees.plans.commands.AnalyzeCommand;
+import org.apache.doris.nereids.trees.plans.commands.CancelBuildIndexCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.CreateMaterializedViewCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.DropCatalogRecycleBinCommand.IdType;
 import org.apache.doris.nereids.trees.plans.commands.TruncateTableCommand;
@@ -4972,6 +4973,13 @@ public class Env {
         this.alter.processDropMaterializedView(stmt);
     }
 
+    /*
+     * used for handling CancelIndexCommand
+     */
+    public void cancelBuildIndex(CancelBuildIndexCommand command) throws 
DdlException {
+        this.getSchemaChangeHandler().cancelIndexJob(command);
+    }
+
     /*
      * used for handling CancelAlterStmt (for client is the CANCEL ALTER
      * command). including SchemaChangeHandler and RollupHandler
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 85f20b41250..f6456eea7fd 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
@@ -567,6 +567,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.AlterWorkloadPolicyCommand;
 import org.apache.doris.nereids.trees.plans.commands.AnalyzeDatabaseCommand;
 import org.apache.doris.nereids.trees.plans.commands.AnalyzeTableCommand;
 import org.apache.doris.nereids.trees.plans.commands.CallCommand;
+import org.apache.doris.nereids.trees.plans.commands.CancelBuildIndexCommand;
 import org.apache.doris.nereids.trees.plans.commands.CancelExportCommand;
 import org.apache.doris.nereids.trees.plans.commands.CancelJobTaskCommand;
 import org.apache.doris.nereids.trees.plans.commands.CancelLoadCommand;
@@ -7055,6 +7056,16 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         return new KillAnalyzeJobCommand(jobId);
     }
 
+    @Override
+    public LogicalPlan 
visitCancelBuildIndex(DorisParser.CancelBuildIndexContext ctx) {
+        TableNameInfo tableNameInfo = new 
TableNameInfo(visitMultipartIdentifier(ctx.tableName));
+        List<Long> jobIs = new ArrayList<>();
+        for (Token token : ctx.jobIds) {
+            jobIs.add(Long.parseLong(token.getText()));
+        }
+        return new CancelBuildIndexCommand(tableNameInfo, jobIs);
+    }
+
     @Override
     public PasswordOptions 
visitPasswordOption(DorisParser.PasswordOptionContext ctx) {
         int historyPolicy = PasswordOptions.UNSET;
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 413f4777ea1..cf44afa6e45 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
@@ -370,6 +370,7 @@ public enum PlanType {
     TRANSACTION_ROLLBACK_COMMAND,
     KILL_ANALYZE_JOB_COMMAND,
     DROP_ANALYZE_JOB_COMMAND,
+    CANCEL_BUILD_INDEX_COMMAND,
     CREATE_USER_COMMAND,
     CREATE_RESOURCE_COMMAND,
     CREATE_DATA_SYNC_JOB_COMMAND,
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CancelBuildIndexCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CancelBuildIndexCommand.java
new file mode 100644
index 00000000000..ef72a506424
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CancelBuildIndexCommand.java
@@ -0,0 +1,106 @@
+// 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.StmtType;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.util.Util;
+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.StmtExecutor;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * CancelBuildIndexCommand
+ */
+public class CancelBuildIndexCommand extends CancelCommand {
+    private final TableNameInfo tableNameInfo;
+    private final List<Long> alterJobIdList;
+
+    /**
+     * CancelBuildIndexCommand
+     */
+    public CancelBuildIndexCommand(TableNameInfo tableNameInfo,
+                                   List<Long> alterJobIdList) {
+        super(PlanType.CANCEL_BUILD_INDEX_COMMAND);
+        Objects.requireNonNull(tableNameInfo, "tableNameInfo is null");
+        Objects.requireNonNull(alterJobIdList, "alterJobIdList is null");
+        this.tableNameInfo = tableNameInfo;
+        this.alterJobIdList = alterJobIdList;
+    }
+
+    public String getDbName() {
+        return tableNameInfo.getDb();
+    }
+
+    public String getTableName() {
+        return tableNameInfo.getTbl();
+    }
+
+    public List<Long> getAlterJobIdList() {
+        return alterJobIdList;
+    }
+
+    @Override
+    public void run(ConnectContext ctx, StmtExecutor executor) throws 
Exception {
+        validate(ctx);
+        ctx.getEnv().cancelBuildIndex(this);
+    }
+
+    /**
+     * validate
+     */
+    public void validate(ConnectContext ctx) throws AnalysisException {
+        tableNameInfo.analyze(ctx);
+        // disallow external catalog
+        Util.prohibitExternalCatalog(tableNameInfo.getCtl(), 
this.getClass().getSimpleName());
+
+        if (FeConstants.runningUnitTest) {
+            return;
+        }
+        // check access
+        if (!Env.getCurrentEnv().getAccessManager()
+                .checkTblPriv(ConnectContext.get(), tableNameInfo.getCtl(), 
tableNameInfo.getDb(),
+                tableNameInfo.getTbl(),
+                PrivPredicate.ALTER)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR, 
"CANCEL ALTER TABLE",
+                    ConnectContext.get().getQualifiedUser(),
+                    ConnectContext.get().getRemoteIP(),
+                    tableNameInfo.getDb() + ": " + tableNameInfo.getTbl());
+        }
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitCancelBuildIndexCommand(this, context);
+    }
+
+    @Override
+    public StmtType stmtType() {
+        return StmtType.CANCEL;
+    }
+}
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 b032d033794..32190e1a0f1 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
@@ -45,6 +45,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.AlterViewCommand;
 import org.apache.doris.nereids.trees.plans.commands.AlterWorkloadGroupCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.AlterWorkloadPolicyCommand;
 import org.apache.doris.nereids.trees.plans.commands.CallCommand;
+import org.apache.doris.nereids.trees.plans.commands.CancelBuildIndexCommand;
 import org.apache.doris.nereids.trees.plans.commands.CancelExportCommand;
 import org.apache.doris.nereids.trees.plans.commands.CancelJobTaskCommand;
 import org.apache.doris.nereids.trees.plans.commands.CancelLoadCommand;
@@ -1091,6 +1092,10 @@ public interface CommandVisitor<R, C> {
         return visitCommand(dropAnalyzeJobCommand, context);
     }
 
+    default R visitCancelBuildIndexCommand(CancelBuildIndexCommand 
cancelBuildIndexCommand, C context) {
+        return visitCommand(cancelBuildIndexCommand, context);
+    }
+
     default R visitCreateUserCommand(CreateUserCommand createUserCommand, C 
context) {
         return visitCommand(createUserCommand, context);
     }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/CancelBuildIndexCommandTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/CancelBuildIndexCommandTest.java
new file mode 100644
index 00000000000..e6b7e8e4e77
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/CancelBuildIndexCommandTest.java
@@ -0,0 +1,87 @@
+// 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.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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CancelBuildIndexCommandTest {
+    private static final String internalCtl = 
InternalCatalog.INTERNAL_CATALOG_NAME;
+    @Mocked
+    private Env env;
+    @Mocked
+    private ConnectContext connectContext;
+    @Mocked
+    private AccessControllerManager accessControllerManager;
+
+    private final String dbName = "test_db";
+    private final String tblName = "test_tbl";
+
+    private void runBefore() {
+        new Expectations() {
+            {
+                Env.getCurrentEnv();
+                minTimes = 0;
+                result = env;
+
+                env.getAccessManager();
+                minTimes = 0;
+                result = accessControllerManager;
+
+                ConnectContext.get();
+                minTimes = 0;
+                result = connectContext;
+
+                connectContext.isSkipAuth();
+                minTimes = 0;
+                result = true;
+
+                accessControllerManager.checkTblPriv(connectContext, 
internalCtl, dbName, tblName, PrivPredicate.ALTER);
+                minTimes = 0;
+                result = true;
+            }
+        };
+    }
+
+    @Test
+    public void testValidateNormal() throws Exception {
+        runBefore();
+        TableNameInfo tableNameInfo = new TableNameInfo(dbName, tblName);
+        List<Long> alterJobIdList = new ArrayList<>();
+        CancelBuildIndexCommand command = new 
CancelBuildIndexCommand(tableNameInfo, alterJobIdList);
+        Assertions.assertDoesNotThrow(() -> command.validate(connectContext));
+
+        TableNameInfo tableNameInfo02 = new TableNameInfo("hive", dbName, 
tblName);
+        CancelBuildIndexCommand command02 = new 
CancelBuildIndexCommand(tableNameInfo02, alterJobIdList);
+        Assertions.assertThrows(AnalysisException.class, () -> 
command02.validate(connectContext),
+                "External catalog 'hive' is not allowed in 
'CancelAlterTableCommand'");
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to