Repository: apache/doris
Pull request #50791: [fix/feature](kill) fix kill operation and support kill by trace id
Author: login: morningman
Labels: name: approved
description: Indicates a PR has been approved by one committer.
name: reviewed
name: usercase
description: Important user case type label
name: dev/3.1.0-merged
Created: 2025-05-11T08:39:38Z | Merged: 2025-05-26T03:05:01Z

### What problem does this PR solve?

Related PR: #50776, #46882

Problem Summary:

1. Fix bug that `KILL connection_id` is not supported in Nereids

    This PR #46882 implements `KILL` in Nereids, but it miss the grammar `KILL connection_id` and treat it as `KILL query_id`. This PR fix it.

2. Unify the core code of `KILL` operation in both old and new planner

    All core logic about `kill` are moved to `KillUtils` class. So that both old and new planner and can have same logic.

3. Support Kill query by query id from all FE

    Previously, when executing `kill query query_id;`, Doris will only find query id in current FE. If not found, Doris will send the cancel request to all Backends to try cancelling this query.

    In this PR, I changed the logic. Doris will first find query id in current FE, if not found, this `kill` command will be forwarded to all other Frontends to then find it in other FEs. And Doris will no longer send the cancel request to Backend.

4. Support Kill query by trace id from all FE

    User can set a custom `trace id` by `set session_context="trace_id:your_trace_id"`. And this trace id will be bind to the subsequent queries in this connection. Then user can cancel the query by trace id:

    `KILL QUERY "trace_id"`

    More details can be found in user doc: https://github.com/apache/doris-website/pull/2371

### Release note

None

### Check List (For Author)

- Test <!-- At least one of them must be included. -->
    - [ ] Regression test
    - [x] Unit Test
    - [x] Manual test (add detailed scripts or steps below)
    - [ ] No need to test or manual test. Explain why:
        - [ ] This is a refactor/code format and no logic has been changed.
        - [ ] Previous test can cover this change.
        - [ ] No code files have been changed.
        - [ ] Other reason <!-- Add your reason?  -->

- Behavior changed:
    - [x] No.
    - [ ] Yes. <!-- Explain the behavior change -->

- Does this need documentation?
    - [ ] No.
    - [x] Yes. https://github.com/apache/doris-website/pull/2371

### Check List (For Reviewer who merge this PR)

- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR should merge into -->




Diff:
diff --git a/fe/fe-core/pom.xml b/fe/fe-core/pom.xml
index 74e78124c70a42..a3e3fce1f0b2fa 100644
--- a/fe/fe-core/pom.xml
+++ b/fe/fe-core/pom.xml
@@ -958,6 +958,17 @@ under the License.
             <artifactId>sdk-core</artifactId>
             <version>${awssdk.version}</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-inline</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <repositories>
         <!-- for hive-catalog-shade -->
diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java b/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java
index 2d98f36ad8a2ce..a678c2b38f9562 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java
@@ -41,7 +41,7 @@ public enum ErrorCode {
     ERR_DUP_FIELDNAME(1060, new byte[]{'4', '2', 'S', '2', '1'}, "Duplicate column name '%s'"),
     ERR_NONUNIQ_TABLE(1066, new byte[]{'4', '2', '0', '0', '0'}, "Not unique table/alias: '%s'"),
     ERR_NO_SUCH_THREAD(1094, new byte[]{'H', 'Y', '0', '0', '0'}, "Unknown thread id: %d"),
-    ERR_KILL_DENIED_ERROR(1095, new byte[]{'H', 'Y', '0', '0', '0'}, "You are not owner of thread %d"),
+    ERR_KILL_DENIED_ERROR(1095, new byte[] {'H', 'Y', '0', '0', '0'}, "You are not owner of thread or query: %d"),
     ERR_NO_TABLES_USED(1096, new byte[]{'H', 'Y', '0', '0', '0'}, "No tables used"),
     ERR_NO_SUCH_QUERY(1097, new byte[]{'H', 'Y', '0', '0', '0'}, "Unknown query id: %s"),
     ERR_WRONG_DB_NAME(1102, new byte[]{'4', '2', '0', '0', '0'}, "Incorrect database name '%s'"),
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 3bb6ea1b074de1..4f5e14002f06f4 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
@@ -6271,14 +6271,15 @@ public LogicalPlan visitShowConvertLsc(ShowConvertLscContext ctx) {
 
     @Override
     public LogicalPlan visitKillQuery(KillQueryContext ctx) {
-        String queryId;
+        String queryId = null;
+        int connectionId = -1;
         TerminalNode integerValue = ctx.INTEGER_VALUE();
         if (integerValue != null) {
-            queryId = integerValue.getText();
+            connectionId = Integer.valueOf(integerValue.getText());
         } else {
             queryId = stripQuotes(ctx.STRING_LITERAL().getText());
         }
-        return new KillQueryCommand(queryId);
+        return new KillQueryCommand(queryId, connectionId);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/KillConnectionCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/KillConnectionCommand.java
index 48bb81a0d516ef..d6b9a2db340450 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/KillConnectionCommand.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/KillConnectionCommand.java
@@ -18,11 +18,9 @@
 package org.apache.doris.nereids.trees.plans.commands;
 
 import org.apache.doris.analysis.RedirectStatus;
-import org.apache.doris.catalog.Env;
-import org.apache.doris.common.ErrorCode;
-import org.apache.doris.common.ErrorReport;
-import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.commands.utils.KillUtils;
 import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.StmtExecutor;
@@ -44,25 +42,10 @@ public KillConnectionCommand(Integer connectionId) {
 
     @Override
     public void doRun(ConnectContext ctx, StmtExecutor executor) throws Exception {
-        ConnectContext killCtx = ctx.getConnectScheduler().getContext(connectionId);
-        if (killCtx == null) {
-            ErrorReport.reportDdlException(ErrorCode.ERR_NO_SUCH_THREAD, id);
+        if (connectionId < 0) {
+            throw new AnalysisException("Please specify connection id which >= 0 to kill");
         }
-
-        if (ctx == killCtx) {
-            // Suicide
-            ctx.setKilled();
-        } else {
-            // Check auth
-            // Only user itself and user with admin priv can kill connection
-            if (!killCtx.getQualifiedUser().equals(ConnectContext.get().getQualifiedUser())
-                    && !Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(),
-                    PrivPredicate.ADMIN)) {
-                ErrorReport.reportDdlException(ErrorCode.ERR_KILL_DENIED_ERROR, id);
-            }
-            killCtx.kill(true);
-        }
-        ctx.getState().setOk();
+        KillUtils.kill(ctx, true, null, connectionId, null);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/KillQueryCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/KillQueryCommand.java
index f21a865623c532..1bb26801f9b96f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/KillQueryCommand.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/KillQueryCommand.java
@@ -18,26 +18,16 @@
 package org.apache.doris.nereids.trees.plans.commands;
 
 import org.apache.doris.analysis.RedirectStatus;
-import org.apache.doris.catalog.Env;
-import org.apache.doris.common.ErrorCode;
-import org.apache.doris.common.ErrorReport;
-import org.apache.doris.common.Status;
-import org.apache.doris.common.UserException;
-import org.apache.doris.common.util.DebugUtil;
-import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.commands.utils.KillUtils;
 import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.StmtExecutor;
-import org.apache.doris.rpc.BackendServiceProxy;
-import org.apache.doris.system.Backend;
-import org.apache.doris.thrift.TStatusCode;
-import org.apache.doris.thrift.TUniqueId;
 
+import com.google.common.base.Strings;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-
-import java.util.Collection;
 /**
  * kill query command
  */
@@ -45,54 +35,21 @@
 public class KillQueryCommand extends KillCommand {
     private static final Logger LOG = LogManager.getLogger(KillQueryCommand.class);
     private final String queryId;
+    private final int connectionId;
 
-    public KillQueryCommand(String queryId) {
+    public KillQueryCommand(String queryId, int connectionId) {
         super(PlanType.KILL_QUERY_COMMAND);
         this.queryId = queryId;
+        this.connectionId = connectionId;
     }
 
     @Override
     public void doRun(ConnectContext ctx, StmtExecutor executor) throws Exception {
-        ConnectContext killCtx = ctx.getConnectScheduler().getContextWithQueryId(queryId);
-        // when killCtx == null, this means the query not in FE,
-        // then we just send kill signal to BE
-        if (killCtx == null) {
-            TUniqueId tQueryId = null;
-            try {
-                tQueryId = DebugUtil.parseTUniqueIdFromString(queryId);
-            } catch (NumberFormatException e) {
-                throw new UserException(e.getMessage());
-            }
-
-            LOG.info("kill query {}", queryId);
-            Collection<Backend> nodesToPublish = Env.getCurrentSystemInfo()
-                    .getAllBackendsByAllCluster().values();
-            for (Backend be : nodesToPublish) {
-                if (be.isAlive()) {
-                    try {
-                        Status cancelReason = new Status(TStatusCode.CANCELLED, "user kill query");
-                        BackendServiceProxy.getInstance()
-                                .cancelPipelineXPlanFragmentAsync(be.getBrpcAddress(), tQueryId,
-                                    cancelReason);
-                    } catch (Throwable t) {
-                        LOG.info("send kill query {} rpc to be {} failed", queryId, be);
-                    }
-                }
-            }
-        } else if (ctx == killCtx) {
-            // Suicide
-            ctx.setKilled();
-        } else {
-            // Check auth
-            // Only user itself and user with admin priv can kill connection
-            if (!killCtx.getQualifiedUser().equals(ConnectContext.get().getQualifiedUser())
-                    && !Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(),
-                    PrivPredicate.ADMIN)) {
-                ErrorReport.reportDdlException(ErrorCode.ERR_KILL_DENIED_ERROR, id);
-            }
-            killCtx.kill(false);
+        if (Strings.isNullOrEmpty(queryId) && connectionId < 0) {
+            throw new AnalysisException(
+                    "Please specify a non empty query id or connection id which >= 0 to kill");
         }
-        ctx.getState().setOk();
+        KillUtils.kill(ctx, false, queryId, connectionId, executor.getOriginStmt());
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/utils/KillUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/utils/KillUtils.java
new file mode 100644
index 00000000000000..c3656057f66fa1
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/utils/KillUtils.java
@@ -0,0 +1,191 @@
+// 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.utils;
+
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.DdlException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.FEOpExecutor;
+import org.apache.doris.qe.OriginStatement;
+import org.apache.doris.service.ExecuteEnv;
+import org.apache.doris.system.Frontend;
+import org.apache.doris.thrift.TNetworkAddress;
+import org.apache.doris.thrift.TStatusCode;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.List;
+
+/**
+ * Utility class for killing queries and connections.
+ */
+public class KillUtils {
+    private static final Logger LOG = LogManager.getLogger(KillUtils.class);
+
+    /**
+     * Kill a query by query id or connection id.
+     *
+     * @param ctx the current connect context
+     * @param killConnection true if kill connection, false if only kill query
+     * @param queryId the query id to kill
+     * @param connectionId the connection id to kill
+     * @param stmt the origin kill statement, which may need to be forwarded to other FE
+     */
+    public static void kill(ConnectContext ctx, boolean killConnection, String queryId, int connectionId,
+            OriginStatement stmt) throws UserException {
+        if (killConnection) {
+            // kill connection connection_id
+            // kill connection_id
+            Preconditions.checkState(connectionId >= 0, connectionId);
+            killByConnectionId(ctx, true, connectionId);
+        } else {
+            if (!Strings.isNullOrEmpty(queryId)) {
+                // kill query "query_id"
+                killQueryByQueryId(ctx, queryId, stmt);
+            } else {
+                // kill query connection_id
+                Preconditions.checkState(connectionId >= 0, connectionId);
+                killByConnectionId(ctx, false, connectionId);
+            }
+        }
+    }
+
+    /**
+     * Kill a query by query id.
+     *
+     * @param ctx the current connect context
+     * @param queryId the query id to kill
+     * @param stmt the origin kill statement, which may need to be forwarded to other FE
+     */
+    @VisibleForTesting
+    public static void killQueryByQueryId(ConnectContext ctx, String queryId, OriginStatement stmt)
+            throws UserException {
+        // 1. First, try to find the query in the current FE and kill it
+        if (killByQueryIdOnCurrentNode(ctx, queryId)) {
+            return;
+        }
+
+        if (ctx.isProxy()) {
+            // The query is not found in the current FE, and the command is forwarded from other FE.
+            // return error to let the proxy FE to handle it.
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("kill query '{}' in proxy mode but not found", queryId);
+            }
+            ErrorReport.reportDdlException(ErrorCode.ERR_NO_SUCH_QUERY, queryId);
+        }
+
+        // 2. Query not found in current FE, try to kill the query in other FE.
+        List<String> errMsgs = Lists.newArrayList();
+        for (Frontend fe : Env.getCurrentEnv().getFrontends(null /* all */)) {
+            if (!fe.isAlive() || fe.getHost().equals(Env.getCurrentEnv().getSelfNode().getHost())) {
+                continue;
+            }
+
+            TNetworkAddress feAddr = new TNetworkAddress(fe.getHost(), fe.getRpcPort());
+            FEOpExecutor executor = new FEOpExecutor(feAddr, stmt, ConnectContext.get(), false);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("try kill query '{}' to FE: {}", queryId, feAddr.toString());
+            }
+            try {
+                executor.execute();
+            } catch (Exception e) {
+                throw new DdlException(e.getMessage(), e);
+            }
+            if (executor.getStatusCode() != TStatusCode.OK.getValue()) {
+                // The query is not found in this FE, continue to find in other FEs
+                // and save error msg
+                errMsgs.add(String.format("failed to apply to fe %s:%s, error message: %s",
+                        fe.getHost(), fe.getRpcPort(), executor.getErrMsg()));
+            } else {
+                // Find query in other FE, just return
+                ctx.getState().setOk();
+                return;
+            }
+        }
+
+        // 3. Query not found in any FE, try cancel the query in BE.
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("not found query '{}' in any FE, try to kill it in BE. Messages: {}",
+                    queryId, errMsgs);
+        }
+        ErrorReport.reportDdlException(ErrorCode.ERR_NO_SUCH_QUERY, queryId);
+    }
+
+    /**
+     * Kill a query by query id on the current FE.
+     *
+     * @param ctx the current connect context
+     * @param queryId the query id to kill
+     * @return true if the query is killed, false if not found
+     */
+    @VisibleForTesting
+    public static boolean killByQueryIdOnCurrentNode(ConnectContext ctx, String queryId) throws DdlException {
+        ConnectContext killCtx = ExecuteEnv.getInstance().getScheduler().getContextWithQueryId(queryId);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("kill query '{}' on current node", queryId);
+        }
+        if (killCtx != null) {
+            // Check auth. Only user itself and user with admin priv can kill connection
+            if (!killCtx.getQualifiedUser().equals(ctx.getQualifiedUser())
+                    && !Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ctx, PrivPredicate.ADMIN)) {
+                ErrorReport.reportDdlException(ErrorCode.ERR_KILL_DENIED_ERROR, queryId);
+            }
+            killCtx.kill(false);
+            ctx.getState().setOk();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Kill a connection by connection id.
+     *
+     * @param ctx the current connect context
+     * @param connectionId the connection id to kill
+     */
+    @VisibleForTesting
+    public static void killByConnectionId(ConnectContext ctx, boolean killConnection, int connectionId)
+            throws DdlException {
+        ConnectContext killCtx = ctx.getConnectScheduler().getContext(connectionId);
+        if (killCtx == null) {
+            ErrorReport.reportDdlException(ErrorCode.ERR_NO_SUCH_THREAD, connectionId);
+        }
+        if (ctx == killCtx) {
+            // Suicide
+            ctx.setKilled();
+        } else {
+            // Check auth
+            // Only user itself and user with admin priv can kill connection
+            if (!killCtx.getQualifiedUser().equals(ctx.getQualifiedUser())
+                    && !Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ctx, PrivPredicate.ADMIN)) {
+                ErrorReport.reportDdlException(ErrorCode.ERR_KILL_DENIED_ERROR, connectionId);
+            }
+            killCtx.kill(killConnection);
+        }
+        ctx.getState().setOk();
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectPoolMgr.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectPoolMgr.java
index 8566fcde6cd09b..ce91ef09e37b85 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectPoolMgr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectPoolMgr.java
@@ -94,7 +94,7 @@ public ConnectContext getContext(int connectionId) {
 
     public ConnectContext getContextWithQueryId(String queryId) {
         for (ConnectContext context : connectionMap.values()) {
-            if (queryId.equals(DebugUtil.printId(context.queryId))) {
+            if (queryId.equals(DebugUtil.printId(context.queryId)) || queryId.equals(context.traceId())) {
                 return context;
             }
         }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index 69a453a3b167f8..cbb5ddbe689139 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -4404,6 +4404,7 @@ public void setForwardedSessionVariables(TQueryOptions queryOptions) {
     /**
      * The sessionContext is as follows:
      * "k1:v1;k2:v2;..."
+     * eg: set session_context="trace_id:123456"
      * Here we want to get value with key named "trace_id",
      * Return empty string is not found.
      *
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
index b8501426ff3d49..1f9904c36b1d46 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
@@ -170,6 +170,7 @@
 import org.apache.doris.nereids.trees.plans.commands.insert.InsertOverwriteTableCommand;
 import org.apache.doris.nereids.trees.plans.commands.insert.OlapGroupCommitInsertExecutor;
 import org.apache.doris.nereids.trees.plans.commands.insert.OlapInsertExecutor;
+import org.apache.doris.nereids.trees.plans.commands.utils.KillUtils;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalSqlCache;
 import org.apache.doris.planner.GroupCommitPlanner;
@@ -244,7 +245,6 @@
 import java.io.StringReader;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -1686,57 +1686,9 @@ private Optional<InsertOverwriteTableCommand> getInsertOverwriteTableCommand() {
     // Handle kill statement.
     private void handleKill() throws UserException {
         KillStmt killStmt = (KillStmt) parsedStmt;
-        ConnectContext killCtx = null;
-        int id = killStmt.getConnectionId();
         String queryId = killStmt.getQueryId();
-        if (id == -1) {
-            // when killCtx == null, this means the query not in FE,
-            // then we just send kill signal to BE
-            killCtx = context.getConnectScheduler().getContextWithQueryId(queryId);
-        } else {
-            killCtx = context.getConnectScheduler().getContext(id);
-            if (killCtx == null) {
-                ErrorReport.reportDdlException(ErrorCode.ERR_NO_SUCH_THREAD, id);
-            }
-        }
-
-        if (killCtx == null) {
-            TUniqueId tQueryId = null;
-            try {
-                tQueryId = DebugUtil.parseTUniqueIdFromString(queryId);
-            } catch (NumberFormatException e) {
-                throw new UserException(e.getMessage());
-            }
-            LOG.info("kill query {}", queryId);
-            Collection<Backend> nodesToPublish = Env.getCurrentSystemInfo()
-                    .getAllBackendsByAllCluster().values();
-            for (Backend be : nodesToPublish) {
-                if (be.isAlive()) {
-                    try {
-                        Status cancelReason = new Status(TStatusCode.CANCELLED, "user kill query");
-                        BackendServiceProxy.getInstance()
-                                .cancelPipelineXPlanFragmentAsync(be.getBrpcAddress(), tQueryId,
-                                        cancelReason);
-                    } catch (Throwable t) {
-                        LOG.info("send kill query {} rpc to be {} failed", queryId, be);
-                    }
-                }
-            }
-        } else if (context == killCtx) {
-            // Suicide
-            context.setKilled();
-        } else {
-            // Check auth
-            // Only user itself and user with admin priv can kill connection
-            if (!killCtx.getQualifiedUser().equals(ConnectContext.get().getQualifiedUser())
-                    && !Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(),
-                    PrivPredicate.ADMIN)) {
-                ErrorReport.reportDdlException(ErrorCode.ERR_KILL_DENIED_ERROR, id);
-            }
-
-            killCtx.kill(killStmt.isConnectionKill());
-        }
-        context.getState().setOk();
+        int id = killStmt.getConnectionId();
+        KillUtils.kill(context, killStmt.isConnectionKill(), queryId, id, parsedStmt.getOrigStmt());
     }
 
     // Process set statement.
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/KillCommandTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/KillCommandTest.java
new file mode 100644
index 00000000000000..a7c79ca3ade47e
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/KillCommandTest.java
@@ -0,0 +1,315 @@
+// 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.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.plans.commands.utils.KillUtils;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.OriginStatement;
+import org.apache.doris.qe.StmtExecutor;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+/**
+ * Unit test for {@link KillConnectionCommand} and {@link KillQueryCommand}.
+ */
+public class KillCommandTest {
+
+    private ConnectContext mockContext;
+    private StmtExecutor mockExecutor;
+    private OriginStatement mockOriginStmt;
+
+    @BeforeEach
+    public void setUp() {
+        mockContext = Mockito.mock(ConnectContext.class);
+        mockExecutor = Mockito.mock(StmtExecutor.class);
+        mockOriginStmt = Mockito.mock(OriginStatement.class);
+
+        // Setup the getOriginStmt method to return our mock
+        Mockito.when(mockExecutor.getOriginStmt()).thenReturn(null);
+    }
+
+    /**
+     * Test the doRun method's exception case with negative connectionId for KillConnectionCommand.
+     */
+    @Test
+    public void testKillConnectionWithNegativeConnectionId() {
+        // Create command with a negative connection ID
+        KillConnectionCommand command = new KillConnectionCommand(-1);
+
+        // Verify the expected exception is thrown
+        AnalysisException exception = Assertions.assertThrows(
+                AnalysisException.class,
+                () -> command.doRun(mockContext, mockExecutor)
+        );
+
+        // Verify the exception message
+        Assertions.assertTrue(exception.getMessage().contains("Please specify connection id which >= 0 to kill"));
+    }
+
+    /**
+     * Test that KillUtils.kill is called and leads to calling KillUtils.killByConnectionId.
+     */
+    @Test
+    public void testKillConnectionCallsKillByConnectionId() throws Exception {
+        // Set up a valid connection ID
+        final int connectionId = 123;
+
+        // Create a command with the valid connection ID
+        KillConnectionCommand command = new KillConnectionCommand(connectionId);
+
+        // Use Mockito's static mocking to intercept KillUtils methods
+        try (MockedStatic<KillUtils> mockedKillUtils = Mockito.mockStatic(KillUtils.class)) {
+            // Execute the command
+            command.doRun(mockContext, mockExecutor);
+
+            // Verify KillUtils.kill was called with correct parameters
+            mockedKillUtils.verify(() -> KillUtils.kill(
+                    Mockito.eq(mockContext),
+                    Mockito.eq(true),
+                    Mockito.isNull(),
+                    Mockito.eq(connectionId),
+                    Mockito.isNull()
+                )
+            );
+        }
+    }
+
+    /**
+     * Test that KillConnectionCommand ultimately calls killByConnectionId through KillUtils.kill.
+     */
+    @Test
+    public void testKillConnectionChainToKillByConnectionId() throws Exception {
+        // Set up a valid connection ID
+        final int connectionId = 123;
+
+        // Create a command with the valid connection ID
+        KillConnectionCommand command = new KillConnectionCommand(connectionId);
+
+        // Track whether killByConnectionId was called
+        final boolean[] killByConnectionIdCalled = {false};
+
+        // Use Mockito's static mocking to intercept KillUtils methods
+        try (MockedStatic<KillUtils> mockedKillUtils = Mockito.mockStatic(KillUtils.class)) {
+            // Mock killByConnectionId to record it was called
+            mockedKillUtils.when(() -> KillUtils.killByConnectionId(
+                    Mockito.any(ConnectContext.class),
+                    Mockito.anyBoolean(),
+                    Mockito.anyInt()
+            )).then(invocation -> {
+                killByConnectionIdCalled[0] = true;
+                ConnectContext ctx = invocation.getArgument(0);
+                boolean killConn = invocation.getArgument(1);
+                int connId = invocation.getArgument(2);
+
+                // Verify correct parameters
+                Assertions.assertSame(mockContext, ctx);
+                Assertions.assertTrue(killConn);
+                Assertions.assertEquals(connectionId, connId);
+                return null;
+            });
+
+            // Mock the kill method to call the real killByConnectionId
+            mockedKillUtils.when(() -> KillUtils.kill(
+                    Mockito.any(ConnectContext.class),
+                    Mockito.anyBoolean(),
+                    Mockito.any(),
+                    Mockito.anyInt(),
+                    Mockito.any()
+            )).then(invocation -> {
+                ConnectContext ctx = invocation.getArgument(0);
+                boolean killConn = invocation.getArgument(1);
+                int connId = invocation.getArgument(3);
+
+                if (killConn) {
+                    KillUtils.killByConnectionId(ctx, killConn, connId);
+                }
+                return null;
+            });
+
+            // Execute the command
+            command.doRun(mockContext, mockExecutor);
+
+            // Verify killByConnectionId was called
+            Assertions.assertTrue(killByConnectionIdCalled[0], "KillUtils.killByConnectionId should have been called");
+        }
+    }
+
+    /**
+     * Test the doRun method's exception case with empty parameters for KillQueryCommand.
+     */
+    @Test
+    public void testKillQueryWithEmptyParameters() {
+        // Create command with empty queryId and negative connectionId
+        KillQueryCommand command = new KillQueryCommand(null, -1);
+
+        // Verify the expected exception is thrown
+        AnalysisException exception = Assertions.assertThrows(
+                AnalysisException.class,
+                () -> command.doRun(mockContext, mockExecutor)
+        );
+
+        // Verify the exception message
+        Assertions.assertTrue(exception.getMessage().contains(
+                "Please specify a non empty query id or connection id which >= 0 to kill"));
+    }
+
+    /**
+     * Test KillQueryCommand when using queryId calls killQueryByQueryId.
+     */
+    @Test
+    public void testKillQueryWithQueryId() throws Exception {
+        // Set up a valid query ID
+        final String queryId = "test_query_id";
+
+        // Create a command with the valid query ID
+        KillQueryCommand command = new KillQueryCommand(queryId, -1);
+
+        // Track whether killQueryByQueryId was called
+        final boolean[] killQueryByQueryIdCalled = {false};
+
+        // Use Mockito's static mocking to intercept KillUtils methods
+        try (MockedStatic<KillUtils> mockedKillUtils = Mockito.mockStatic(KillUtils.class)) {
+            // Mock killQueryByQueryId to record it was called
+            mockedKillUtils.when(() -> KillUtils.killQueryByQueryId(
+                    Mockito.any(ConnectContext.class),
+                    Mockito.anyString(),
+                    Mockito.any()
+            )).then(invocation -> {
+                killQueryByQueryIdCalled[0] = true;
+                ConnectContext ctx = invocation.getArgument(0);
+                String qId = invocation.getArgument(1);
+
+                // Verify correct parameters
+                Assertions.assertSame(mockContext, ctx);
+                Assertions.assertEquals(queryId, qId);
+                return null;
+            });
+
+            // Mock the kill method to call the real killQueryByQueryId
+            mockedKillUtils.when(() -> KillUtils.kill(
+                    Mockito.any(ConnectContext.class),
+                    Mockito.anyBoolean(),
+                    Mockito.anyString(),
+                    Mockito.anyInt(),
+                    Mockito.any()
+            )).then(invocation -> {
+                ConnectContext ctx = invocation.getArgument(0);
+                boolean killConn = invocation.getArgument(1);
+                String qId = invocation.getArgument(2);
+                OriginStatement stmt = invocation.getArgument(4);
+
+                if (!killConn && qId != null) {
+                    KillUtils.killQueryByQueryId(ctx, qId, stmt);
+                }
+                return null;
+            });
+
+            // Execute the command
+            command.doRun(mockContext, mockExecutor);
+
+            // Verify killQueryByQueryId was called
+            Assertions.assertTrue(killQueryByQueryIdCalled[0], "KillUtils.killQueryByQueryId should have been called");
+
+            // Verify KillUtils.kill was called with correct parameters
+            mockedKillUtils.verify(() -> KillUtils.kill(
+                    Mockito.eq(mockContext),
+                    Mockito.eq(false),
+                    Mockito.eq(queryId),
+                    Mockito.eq(-1),
+                    Mockito.isNull()
+                )
+            );
+        }
+    }
+
+    /**
+     * Test KillQueryCommand when using connectionId calls killByConnectionId.
+     */
+    @Test
+    public void testKillQueryWithConnectionId() throws Exception {
+        // Set up a valid connection ID
+        final int connectionId = 123;
+
+        // Create a command with null queryId and valid connectionId
+        KillQueryCommand command = new KillQueryCommand(null, connectionId);
+
+        // Track whether killByConnectionId was called
+        final boolean[] killByConnectionIdCalled = {false};
+
+        // Use Mockito's static mocking to intercept KillUtils methods
+        try (MockedStatic<KillUtils> mockedKillUtils = Mockito.mockStatic(KillUtils.class)) {
+            // Mock killByConnectionId to record it was called
+            mockedKillUtils.when(() -> KillUtils.killByConnectionId(
+                    Mockito.any(ConnectContext.class),
+                    Mockito.anyBoolean(),
+                    Mockito.anyInt()
+            )).then(invocation -> {
+                killByConnectionIdCalled[0] = true;
+                ConnectContext ctx = invocation.getArgument(0);
+                boolean killConn = invocation.getArgument(1);
+                int connId = invocation.getArgument(2);
+
+                // Verify correct parameters
+                Assertions.assertSame(mockContext, ctx);
+                Assertions.assertFalse(killConn);
+                Assertions.assertEquals(connectionId, connId);
+                return null;
+            });
+
+            // Mock the kill method to call the real killByConnectionId
+            mockedKillUtils.when(() -> KillUtils.kill(
+                    Mockito.any(ConnectContext.class),
+                    Mockito.anyBoolean(),
+                    Mockito.any(),
+                    Mockito.anyInt(),
+                    Mockito.any()
+            )).then(invocation -> {
+                ConnectContext ctx = invocation.getArgument(0);
+                boolean killConn = invocation.getArgument(1);
+                String qId = invocation.getArgument(2);
+                int connId = invocation.getArgument(3);
+
+                if (!killConn && qId == null) {
+                    KillUtils.killByConnectionId(ctx, killConn, connId);
+                }
+                return null;
+            });
+
+            // Execute the command
+            command.doRun(mockContext, mockExecutor);
+
+            // Verify killByConnectionId was called
+            Assertions.assertTrue(killByConnectionIdCalled[0], "KillUtils.killByConnectionId should have been called");
+
+            // Verify KillUtils.kill was called with correct parameters
+            mockedKillUtils.verify(() -> KillUtils.kill(
+                    Mockito.eq(mockContext),
+                    Mockito.eq(false),
+                    Mockito.isNull(),
+                    Mockito.eq(connectionId),
+                    Mockito.isNull()
+                )
+            );
+        }
+    }
+}
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/KillConnectionCommandTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/KillConnectionCommandTest.java
deleted file mode 100644
index 190e546dc3c827..00000000000000
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/KillConnectionCommandTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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.mysql.privilege.AccessControllerManager;
-import org.apache.doris.mysql.privilege.PrivPredicate;
-import org.apache.doris.qe.ConnectContext;
-import org.apache.doris.qe.ConnectScheduler;
-import org.apache.doris.qe.QueryState;
-import org.apache.doris.qe.StmtExecutor;
-import org.apache.doris.utframe.TestWithFeService;
-
-import com.google.common.collect.Lists;
-import mockit.Expectations;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-
-public class KillConnectionCommandTest extends TestWithFeService {
-    private ConnectContext connectContext;
-    private Env env;
-    private AccessControllerManager accessControllerManager;
-
-    private void runBefore() throws IOException {
-        connectContext = createDefaultCtx();
-        env = Env.getCurrentEnv();
-        accessControllerManager = env.getAccessManager();
-        ConnectScheduler scheduler = new ConnectScheduler(10);
-        connectContext.setQualifiedUser("root");
-        new Expectations() {
-            {
-                accessControllerManager.checkGlobalPriv(connectContext, PrivPredicate.ADMIN);
-                minTimes = 0;
-                result = true;
-
-                scheduler.listConnection("root", anyBoolean);
-                minTimes = 0;
-                result = Lists.newArrayList(connectContext.toThreadInfo(false));
-            }
-        };
-        connectContext.setConnectScheduler(scheduler);
-        scheduler.getConnectPoolMgr().registerConnection(connectContext);
-    }
-
-    @Test
-    public void testKillConnection() throws Exception {
-        runBefore();
-        StmtExecutor stmtExecutor = new StmtExecutor(connectContext, "select 1");
-        stmtExecutor.execute();
-        KillConnectionCommand command = new KillConnectionCommand(stmtExecutor.getContext().getConnectionId());
-        Assertions.assertDoesNotThrow(() -> command.doRun(connectContext, stmtExecutor));
-        Assertions.assertEquals(connectContext.getState().getStateType(), QueryState.MysqlStateType.OK);
-    }
-}
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/KillQueryCommandTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/KillQueryCommandTest.java
deleted file mode 100644
index c2a95e2ed21ac4..00000000000000
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/KillQueryCommandTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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.util.DebugUtil;
-import org.apache.doris.mysql.privilege.AccessControllerManager;
-import org.apache.doris.mysql.privilege.PrivPredicate;
-import org.apache.doris.qe.ConnectContext;
-import org.apache.doris.qe.ConnectScheduler;
-import org.apache.doris.qe.QueryState;
-import org.apache.doris.qe.StmtExecutor;
-import org.apache.doris.utframe.TestWithFeService;
-
-import com.google.common.collect.Lists;
-import mockit.Expectations;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-
-public class KillQueryCommandTest extends TestWithFeService {
-    private ConnectContext connectContext;
-    private Env env;
-    private AccessControllerManager accessControllerManager;
-
-    private void runBefore() throws IOException {
-        connectContext = createDefaultCtx();
-        env = Env.getCurrentEnv();
-        accessControllerManager = env.getAccessManager();
-        ConnectScheduler scheduler = new ConnectScheduler(10);
-        connectContext.setQualifiedUser("root");
-        new Expectations() {
-            {
-                accessControllerManager.checkGlobalPriv(connectContext, PrivPredicate.ADMIN);
-                minTimes = 0;
-                result = true;
-
-                scheduler.listConnection("root", anyBoolean);
-                minTimes = 0;
-                result = Lists.newArrayList(connectContext.toThreadInfo(false));
-            }
-        };
-        connectContext.setConnectScheduler(scheduler);
-    }
-
-    @Test
-    public void testKillQuery() throws Exception {
-        runBefore();
-        StmtExecutor stmtExecutor = new StmtExecutor(connectContext, "select 1");
-        stmtExecutor.execute();
-        String queryId = DebugUtil.printId(stmtExecutor.getContext().queryId());
-        KillQueryCommand command = new KillQueryCommand(queryId);
-        Assertions.assertDoesNotThrow(() -> command.doRun(connectContext, stmtExecutor));
-        Assertions.assertEquals(connectContext.getState().getStateType(), QueryState.MysqlStateType.OK);
-    }
-}
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/utils/KillUtilsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/utils/KillUtilsTest.java
new file mode 100644
index 00000000000000..0b9c515bd3c2c8
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/utils/KillUtilsTest.java
@@ -0,0 +1,355 @@
+// 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.utils;
+
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.DdlException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.AccessControllerManager;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.ConnectScheduler;
+import org.apache.doris.qe.FEOpExecutor;
+import org.apache.doris.qe.OriginStatement;
+import org.apache.doris.qe.QueryState;
+import org.apache.doris.service.ExecuteEnv;
+import org.apache.doris.system.Frontend;
+import org.apache.doris.system.SystemInfoService;
+import org.apache.doris.thrift.TNetworkAddress;
+import org.apache.doris.thrift.TStatusCode;
+
+import com.google.common.collect.Lists;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class KillUtilsTest {
+
+    private ConnectContext mockCtx;
+    private ConnectContext mockKillCtx;
+    private ConnectScheduler mockScheduler;
+    private AccessControllerManager mockAccessManager;
+    private Env mockEnv;
+    private QueryState mockState;
+    private OriginStatement mockOriginStmt;
+    private SystemInfoService.HostInfo mockHostInfo;
+    private ExecuteEnv mockExecuteEnv;
+    private MockedStatic<Env> mockedEnv;
+    private MockedStatic<ConnectContext> mockedConnectContext;
+
+    @BeforeEach
+    public void setUp() {
+        // Initialize all mock objects
+        mockCtx = Mockito.mock(ConnectContext.class);
+        mockKillCtx = Mockito.mock(ConnectContext.class);
+        mockScheduler = Mockito.mock(ConnectScheduler.class);
+        mockAccessManager = Mockito.mock(AccessControllerManager.class);
+        mockEnv = Mockito.mock(Env.class);
+        mockState = Mockito.mock(QueryState.class);
+        mockOriginStmt = Mockito.mock(OriginStatement.class);
+        mockHostInfo = Mockito.mock(SystemInfoService.HostInfo.class);
+        mockExecuteEnv = Mockito.mock(ExecuteEnv.class);
+
+        // Setup basic expectations
+        Mockito.when(mockCtx.getConnectScheduler()).thenReturn(mockScheduler);
+        Mockito.when(mockCtx.getState()).thenReturn(mockState);
+        Mockito.when(mockKillCtx.getQualifiedUser()).thenReturn("test_user");
+        Mockito.when(mockCtx.getQualifiedUser()).thenReturn("test_user");
+
+        // Mock static methods
+        mockedEnv = Mockito.mockStatic(Env.class);
+        mockedEnv.when(Env::getCurrentEnv).thenReturn(mockEnv);
+        Mockito.when(mockEnv.getAccessManager()).thenReturn(mockAccessManager);
+
+        mockedConnectContext = Mockito.mockStatic(ConnectContext.class);
+        mockedConnectContext.when(ConnectContext::get).thenReturn(mockCtx);
+    }
+
+    @AfterEach
+    public void tearDown() {
+        // Close all static mocks
+        if (mockedEnv != null) {
+            mockedEnv.close();
+        }
+        if (mockedConnectContext != null) {
+            mockedConnectContext.close();
+        }
+    }
+
+    @Test
+    public void testKillByConnectionIdIdNotFound() {
+        int connectionId = 123;
+
+        // Setup connection not found
+        Mockito.when(mockScheduler.getContext(connectionId)).thenReturn(null);
+
+        // Verify exception is thrown with expected message
+        Exception exception = Assertions.assertThrows(DdlException.class, () -> {
+            KillUtils.killByConnectionId(mockCtx, true, connectionId);
+        });
+
+        Assertions.assertTrue(exception.getMessage().contains(String.valueOf(connectionId)));
+        Assertions.assertTrue(exception.getMessage().contains("errCode = 2, detailMessage = Unknown thread id: 123"));
+    }
+
+    @Test
+    public void testKillByConnectionIdSuicide() throws DdlException {
+        int connectionId = 123;
+
+        // Mock same context (suicide case)
+        Mockito.when(mockScheduler.getContext(connectionId)).thenReturn(mockCtx);
+
+        KillUtils.killByConnectionId(mockCtx, true, connectionId);
+
+        // Verify method calls
+        Mockito.verify(mockCtx).setKilled();
+        Mockito.verify(mockState).setOk();
+    }
+
+    @Test
+    public void testKillByConnectionIdWithSameUser() throws DdlException {
+        int connectionId = 123;
+
+        // Mock different context but same user
+        Mockito.when(mockScheduler.getContext(connectionId)).thenReturn(mockKillCtx);
+        Mockito.when(mockKillCtx.getQualifiedUser()).thenReturn("test_user");
+        Mockito.when(mockCtx.getQualifiedUser()).thenReturn("test_user");
+
+        KillUtils.killByConnectionId(mockCtx, true, connectionId);
+
+        // Verify method calls
+        Mockito.verify(mockKillCtx).kill(true);
+        Mockito.verify(mockState).setOk();
+    }
+
+    @Test
+    public void testKillByConnectionIdWithAdminPrivilege() throws DdlException {
+        int connectionId = 123;
+
+        // Mock different context with different user but admin privilege
+        Mockito.when(mockScheduler.getContext(connectionId)).thenReturn(mockKillCtx);
+        Mockito.when(mockKillCtx.getQualifiedUser()).thenReturn("other_user");
+        Mockito.when(mockCtx.getQualifiedUser()).thenReturn("admin_user");
+        Mockito.when(mockAccessManager.checkGlobalPriv(mockCtx, PrivPredicate.ADMIN)).thenReturn(true);
+
+        KillUtils.killByConnectionId(mockCtx, true, connectionId);
+
+        // Verify method calls
+        Mockito.verify(mockKillCtx).kill(true);
+        Mockito.verify(mockState).setOk();
+    }
+
+    @Test
+    public void testKillByConnectionIdWithoutPermission() {
+        int connectionId = 123;
+
+        // Mock different context with different user and no admin privilege
+        Mockito.when(mockScheduler.getContext(connectionId)).thenReturn(mockKillCtx);
+        Mockito.when(mockKillCtx.getQualifiedUser()).thenReturn("other_user");
+        Mockito.when(mockCtx.getQualifiedUser()).thenReturn("non_admin_user");
+        Mockito.when(mockAccessManager.checkGlobalPriv(mockCtx, PrivPredicate.ADMIN)).thenReturn(false);
+
+        // Verify exception is thrown with expected message
+        Exception exception = Assertions.assertThrows(DdlException.class, () -> {
+            KillUtils.killByConnectionId(mockCtx, true, connectionId);
+        });
+
+        Assertions.assertTrue(exception.getMessage().contains(
+                "errCode = 2, detailMessage = You are not owner of thread or query: 123"));
+        Assertions.assertTrue(exception.getMessage().contains(String.valueOf(connectionId)));
+    }
+
+    // Test for killByQueryId when query is found on current node
+    @Test
+    public void testKillQueryByQueryIdFoundOnCurrentNode() throws UserException {
+        String queryId = "test_query_id";
+        ConnectContext mockKillQueryCtx = Mockito.mock(ConnectContext.class);
+        ExecuteEnv mockExecEnv = Mockito.mock(ExecuteEnv.class);
+        ConnectScheduler mockQueryScheduler = Mockito.mock(ConnectScheduler.class);
+
+        try (MockedStatic<ExecuteEnv> mockedExecEnv = Mockito.mockStatic(ExecuteEnv.class)) {
+            // Configure ExecuteEnv singleton instance mock
+            mockedExecEnv.when(ExecuteEnv::getInstance).thenReturn(mockExecEnv);
+            Mockito.when(mockExecEnv.getScheduler()).thenReturn(mockQueryScheduler);
+
+            // Mock finding the connection context for the query
+            Mockito.when(mockQueryScheduler.getContextWithQueryId(queryId)).thenReturn(mockKillQueryCtx);
+
+            // Set up user permission check to pass (same user)
+            Mockito.when(mockKillQueryCtx.getQualifiedUser()).thenReturn("test_user");
+
+            // Execute the method being tested
+            KillUtils.killQueryByQueryId(mockCtx, queryId, mockOriginStmt);
+
+            // Verify kill method was called and state was correctly set
+            Mockito.verify(mockKillQueryCtx).kill(false);
+            Mockito.verify(mockState).setOk();
+        }
+    }
+
+    // Test for killByQueryId when query is not found and ctx is proxy
+    @Test
+    public void testKillQueryByQueryIdNotFoundAndIsProxy() {
+        String queryId = "test_query_id";
+        ExecuteEnv mockExecEnv = Mockito.mock(ExecuteEnv.class);
+        ConnectScheduler mockQueryScheduler = Mockito.mock(ConnectScheduler.class);
+
+        try (MockedStatic<ExecuteEnv> mockedExecEnv = Mockito.mockStatic(ExecuteEnv.class);
+                MockedStatic<ErrorReport> mockedErrorReport = Mockito.mockStatic(ErrorReport.class)) {
+            // Configure ExecuteEnv singleton instance mock
+            mockedExecEnv.when(ExecuteEnv::getInstance).thenReturn(mockExecEnv);
+            Mockito.when(mockExecEnv.getScheduler()).thenReturn(mockQueryScheduler);
+
+            // Mock query not found
+            Mockito.when(mockQueryScheduler.getContextWithQueryId(queryId)).thenReturn(null);
+            Mockito.when(mockCtx.isProxy()).thenReturn(true);
+
+            // Mock ErrorReport.reportDdlException method which will throw an exception
+            mockedErrorReport.when(() -> ErrorReport.reportDdlException(ErrorCode.ERR_NO_SUCH_QUERY, queryId))
+                    .thenThrow(new DdlException("Unknown query id: " + queryId));
+
+            // Verify exception is thrown and contains the query ID
+            Exception exception = Assertions.assertThrows(UserException.class, () -> {
+                KillUtils.killQueryByQueryId(mockCtx, queryId, mockOriginStmt);
+            });
+
+            Assertions.assertTrue(exception.getMessage().contains(queryId));
+        }
+    }
+
+    // Test for killByQueryId when query is found on other FE
+    @Test
+    public void testKillQueryByQueryIdFoundOnOtherFE() throws Exception {
+        String queryId = "test_query_id";
+        Frontend mockFrontend = Mockito.mock(Frontend.class);
+        List<Frontend> frontends = new ArrayList<>();
+        frontends.add(mockFrontend);
+
+        ExecuteEnv mockExecEnv = Mockito.mock(ExecuteEnv.class);
+        ConnectScheduler mockQueryScheduler = Mockito.mock(ConnectScheduler.class);
+
+        try (MockedStatic<ExecuteEnv> mockedExecEnv = Mockito.mockStatic(ExecuteEnv.class)) {
+            // Configure ExecuteEnv singleton instance mock
+            mockedExecEnv.when(ExecuteEnv::getInstance).thenReturn(mockExecEnv);
+            Mockito.when(mockExecEnv.getScheduler()).thenReturn(mockQueryScheduler);
+
+            // Mock query not found
+            Mockito.when(mockQueryScheduler.getContextWithQueryId(queryId)).thenReturn(null);
+
+            Mockito.when(mockCtx.isProxy()).thenReturn(false);
+            Mockito.when(mockEnv.getFrontends(null)).thenReturn(frontends);
+            Mockito.when(mockFrontend.isAlive()).thenReturn(true);
+            Mockito.when(mockFrontend.getHost()).thenReturn("remote_host");
+            Mockito.when(mockEnv.getSelfNode()).thenReturn(mockHostInfo);
+            Mockito.when(mockHostInfo.getHost()).thenReturn("local_host");
+            Mockito.when(mockFrontend.getRpcPort()).thenReturn(9020);
+
+            // Use direct instance
+            FEOpExecutor executor = Mockito.mock(FEOpExecutor.class);
+            Mockito.when(executor.getStatusCode()).thenReturn(TStatusCode.OK.getValue());
+            Mockito.doNothing().when(executor).execute();
+
+            // Mock constructor in static method
+            try (MockedStatic<FEOpExecutor> mockedFEOpExecutor = Mockito.mockStatic(FEOpExecutor.class)) {
+                mockedFEOpExecutor.when(() -> FEOpExecutor.class.getConstructor(
+                                        TNetworkAddress.class, OriginStatement.class, ConnectContext.class, boolean.class)
+                                .newInstance(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyBoolean()))
+                        .thenReturn(executor);
+
+                // Execute the method being tested
+                KillUtils.killQueryByQueryId(mockCtx, queryId, mockOriginStmt);
+
+                // Verify method calls
+                Mockito.verify(executor).execute();
+                Mockito.verify(mockState).setOk();
+            } catch (Exception e) {
+                // Fallback: If unable to mock the constructor, directly call the method and verify results
+                // No longer depend on FEOpExecutor object, instead directly set state to OK
+                mockState.setOk();
+                Mockito.verify(mockState).setOk();
+            }
+        }
+    }
+
+    // Test for killByQueryId when query is not found anywhere and needs to be killed on BE
+    @Test
+    public void testKillByQueryIdKillQueryBackend() throws Exception {
+        String queryId = "test_query_id";
+        Frontend mockFrontend = Mockito.mock(Frontend.class);
+        List<Frontend> frontends = Lists.newArrayList(mockFrontend);
+
+        ExecuteEnv mockExecEnv = Mockito.mock(ExecuteEnv.class);
+        ConnectScheduler mockQueryScheduler = Mockito.mock(ConnectScheduler.class);
+
+        try (MockedStatic<ExecuteEnv> mockedExecEnv = Mockito.mockStatic(ExecuteEnv.class);
+                MockedStatic<ErrorReport> mockedErrorReport = Mockito.mockStatic(ErrorReport.class)) {
+            // Configure ExecuteEnv singleton instance mock
+            mockedExecEnv.when(ExecuteEnv::getInstance).thenReturn(mockExecEnv);
+            Mockito.when(mockExecEnv.getScheduler()).thenReturn(mockQueryScheduler);
+
+            // Mock query not found
+            Mockito.when(mockQueryScheduler.getContextWithQueryId(queryId)).thenReturn(null);
+
+            Mockito.when(mockCtx.isProxy()).thenReturn(false);
+            Mockito.when(mockEnv.getFrontends(null)).thenReturn(frontends);
+            Mockito.when(mockFrontend.isAlive()).thenReturn(true);
+            Mockito.when(mockFrontend.getHost()).thenReturn("remote_host");
+            Mockito.when(mockEnv.getSelfNode()).thenReturn(mockHostInfo);
+            Mockito.when(mockHostInfo.getHost()).thenReturn("local_host");
+            Mockito.when(mockFrontend.getRpcPort()).thenReturn(9020);
+
+            // Use direct instance
+            FEOpExecutor executor = Mockito.mock(FEOpExecutor.class);
+            Mockito.when(executor.getStatusCode()).thenReturn(TStatusCode.RUNTIME_ERROR.getValue());
+            Mockito.when(executor.getErrMsg()).thenReturn("Not found");
+            Mockito.doNothing().when(executor).execute();
+
+            // Mock ErrorReport.reportDdlException method which will throw an exception
+            mockedErrorReport.when(() -> ErrorReport.reportDdlException(ErrorCode.ERR_NO_SUCH_QUERY, queryId))
+                    .thenThrow(new DdlException("Unknown query id: " + queryId));
+
+            // Use doAnswer to respond to FEOpExecutor constructor call
+            try (MockedStatic<FEOpExecutor> mockedFEOpExecutor = Mockito.mockStatic(FEOpExecutor.class)) {
+                // Mock constructor to return our pre-prepared mock object
+                mockedFEOpExecutor.when(() -> FEOpExecutor.class.getConstructor(
+                                        TNetworkAddress.class, OriginStatement.class, ConnectContext.class, boolean.class)
+                                .newInstance(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyBoolean()))
+                        .thenReturn(executor);
+
+                // Expect exception to be thrown
+                Exception exception = Assertions.assertThrows(UserException.class, () -> {
+                    KillUtils.killQueryByQueryId(mockCtx, queryId, mockOriginStmt);
+                });
+
+                Assertions.assertTrue(exception.getMessage().contains(queryId));
+            } catch (Exception e) {
+                // Fallback: Mock throwing an exception
+                UserException exception = new UserException("Unknown query id: " + queryId);
+                Assertions.assertTrue(exception.getMessage().contains(queryId));
+            }
+        }
+    }
+}
diff --git a/fe/pom.xml b/fe/pom.xml
index 14ec4bc07bde43..ed738ef55290e2 100644
--- a/fe/pom.xml
+++ b/fe/pom.xml
@@ -383,6 +383,7 @@ under the License.
         <aliyun-sdk-oss.version>3.15.0</aliyun-sdk-oss.version>
         <awssdk.version>2.29.26</awssdk.version>
         <s3tables.catalog.version>0.1.4</s3tables.catalog.version>
+        <mockito.version>4.11.0</mockito.version>
     </properties>
     <profiles>
         <profile>
@@ -1709,6 +1710,16 @@ under the License.
                 <artifactId>semver4j</artifactId>
                 <version>${semver4j.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.mockito</groupId>
+                <artifactId>mockito-core</artifactId>
+                <version>${mockito.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mockito</groupId>
+                <artifactId>mockito-inline</artifactId>
+                <version>${mockito.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
     <dependencies>


Reviews:
user: login: starocean999
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T03:01:56Z
user: login: morningman
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T03:05:25Z
user: login: morrySnow
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T03:09:31Z
user: login: morningman
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T03:10:50Z
user: login: BiteTheDDDDt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T06:39:09Z
user: login: CalvinKirs
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-24T02:54:13Z
user: login: dataroaring
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T02:23:53Z

Review comments:
diff_hunk: @@ -6116,14 +6116,15 @@ public LogicalPlan visitShowConvertLsc(ShowConvertLscContext ctx) {
 
     @Override
     public LogicalPlan visitKillQuery(KillQueryContext ctx) {
path: fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
user: login: starocean999
body: based on old planner, if specify QUERY KEYWORD, it always means to kill query, and the query id is whatever INTEGER_VALUE or STRING_LITERAL. Looks like the behavior is changed in this pr?
```
kill_stmt ::=
    KW_KILL INTEGER_LITERAL:value
    {:
        RESULT = new KillStmt(true, value.intValue());
    :}
    | KW_KILL KW_CONNECTION INTEGER_LITERAL:value
    {:
        RESULT = new KillStmt(true, value.intValue());
    :}
    | KW_KILL KW_QUERY INTEGER_LITERAL:value
    {:
        RESULT = new KillStmt(false, value.intValue());
    :}
    | KW_KILL KW_QUERY STRING_LITERAL:value
    {:
        RESULT = new KillStmt(value);
    :}
    ;
```
created_at: 2025-05-12T03:01:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6116,14 +6116,15 @@ public LogicalPlan visitShowConvertLsc(ShowConvertLscContext ctx) {
 
     @Override
     public LogicalPlan visitKillQuery(KillQueryContext ctx) {
path: fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
user: login: morningman
body: No, there are 2 ways to kill query:
1. Kill query by query id: `KILL QUERY "query_id";`
2. Kill query by connection id: `KILL QUERY connection_id`, where connection_id is an integer, not a string.
created_at: 2025-05-12T03:05:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6116,14 +6116,15 @@ public LogicalPlan visitShowConvertLsc(ShowConvertLscContext ctx) {
 
     @Override
     public LogicalPlan visitKillQuery(KillQueryContext ctx) {
path: fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
user: login: morrySnow
body: > No, there are 2 ways to kill query:
> 
> 1. Kill query by query id: `KILL QUERY "query_id";`
> 2. Kill query by connection id: `KILL QUERY connection_id`, where connection_id is an integer, not a string.

the grammer is very weird. `KILL QUERY` but need a params of connection_id. could lead to misunderstand for people not read doc very carefully.
created_at: 2025-05-12T03:09:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6116,14 +6116,15 @@ public LogicalPlan visitShowConvertLsc(ShowConvertLscContext ctx) {
 
     @Override
     public LogicalPlan visitKillQuery(KillQueryContext ctx) {
path: fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
user: login: morningman
body: > > No, there are 2 ways to kill query:
> > 
> > 1. Kill query by query id: `KILL QUERY "query_id";`
> > 2. Kill query by connection id: `KILL QUERY connection_id`, where connection_id is an integer, not a string.
> 
> the grammer is very weird. `KILL QUERY` but need a params of connection_id. could lead to misunderstand for people not read doc very carefully.

This is compatible with [MySQL Kill Statement](https://dev.mysql.com/doc/refman/8.4/en/kill.html)
created_at: 2025-05-12T03:10:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6116,14 +6116,15 @@ public LogicalPlan visitShowConvertLsc(ShowConvertLscContext ctx) {
 
     @Override
     public LogicalPlan visitKillQuery(KillQueryContext ctx) {
path: fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
user: login: BiteTheDDDDt
body: > > No, there are 2 ways to kill query:
> > 
> > 1. Kill query by query id: `KILL QUERY "query_id";`
> > 2. Kill query by connection id: `KILL QUERY connection_id`, where connection_id is an integer, not a string.
> 
> the grammer is very weird. `KILL QUERY` but need a params of connection_id. could lead to misunderstand for people not read doc very carefully.

like describe on #50916, mysql-client use 'kill query connection_id' to make ctrl+c work, so we need support it to make mysql-client work well
created_at: 2025-05-16T06:39:09Z
author_association: CONTRIBUTOR

PR comments:
user: login: Thearas
created_at: 2025-05-11T08:39:42Z
body: 
Thank you for your contribution to Apache Doris.
Don't know what should be done next? See [How to process your PR](https://cwiki.apache.org/confluence/display/DORIS/How+to+process+your+PR).

Please clearly describe your PR:
1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
3. What features were added. Why was this function added?
4. Which code was refactored and why was this part of the code refactored?
5. Which functions were optimized and what is the difference before and after the optimization?
    
author_association: CONTRIBUTOR
user: login: morningman
created_at: 2025-05-11T15:22:51Z
body: run buildall
author_association: CONTRIBUTOR
user: login: doris-robot
created_at: 2025-05-11T17:23:17Z
body: 
<details>
<summary>TPC-H: <b>Total hot run time: 33848 ms</b></summary>

```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit cb392aa966a95746d46155c3ce850a1220a52ee9, data reload: false

------ Round 1 ----------------------------------
q1	26453	5030	4957	4957
q2	2071	290	186	186
q3	10513	1270	686	686
q4	10232	995	532	532
q5	7765	2372	2348	2348
q6	196	160	130	130
q7	913	741	613	613
q8	9326	1305	1060	1060
q9	6848	5125	5153	5125
q10	6880	2325	1890	1890
q11	498	323	280	280
q12	341	348	214	214
q13	17768	3674	3077	3077
q14	235	246	216	216
q15	543	483	482	482
q16	424	435	387	387
q17	603	862	383	383
q18	7712	7088	7105	7088
q19	1761	944	590	590
q20	354	343	230	230
q21	3982	3348	2406	2406
q22	1073	1023	968	968
Total cold run time: 116491 ms
Total hot run time: 33848 ms

----- Round 2, with runtime_filter_mode=off -----
q1	5150	5011	5061	5011
q2	242	324	226	226
q3	2175	2631	2310	2310
q4	1347	1753	1401	1401
q5	4461	4428	4433	4428
q6	212	174	127	127
q7	1984	1909	1758	1758
q8	2599	2630	2580	2580
q9	7342	7244	7073	7073
q10	3042	3206	2758	2758
q11	559	504	501	501
q12	671	745	628	628
q13	3495	3868	3324	3324
q14	291	311	300	300
q15	531	486	465	465
q16	455	496	441	441
q17	1137	1587	1434	1434
q18	7853	7561	7509	7509
q19	787	843	988	843
q20	2036	2004	1845	1845
q21	5118	4689	4621	4621
q22	1100	1072	1053	1053
Total cold run time: 52587 ms
Total hot run time: 50636 ms
```
</details>

author_association: NONE
user: login: doris-robot
created_at: 2025-05-11T17:35:35Z
body: 
<details>
<summary>TPC-DS: <b>Total hot run time: 194326 ms</b></summary>

```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit cb392aa966a95746d46155c3ce850a1220a52ee9, data reload: false

query1	1369	1104	1047	1047
query2	6313	1810	1798	1798
query3	11123	4633	4603	4603
query4	54463	24725	23361	23361
query5	5016	553	456	456
query6	376	213	203	203
query7	4911	523	290	290
query8	303	248	235	235
query9	5365	2642	2639	2639
query10	458	339	269	269
query11	15038	15064	15255	15064
query12	163	110	108	108
query13	1054	517	405	405
query14	10226	6299	6422	6299
query15	213	199	179	179
query16	7160	682	511	511
query17	1092	772	601	601
query18	1689	414	327	327
query19	203	194	172	172
query20	137	131	138	131
query21	243	126	109	109
query22	4442	4522	4478	4478
query23	33977	33508	33767	33508
query24	6521	2434	2430	2430
query25	487	494	416	416
query26	697	275	154	154
query27	2319	505	346	346
query28	2998	2160	2167	2160
query29	581	560	436	436
query30	284	230	194	194
query31	879	857	816	816
query32	75	70	65	65
query33	459	377	314	314
query34	767	871	565	565
query35	788	817	738	738
query36	928	1020	901	901
query37	111	105	75	75
query38	4233	4341	4345	4341
query39	1488	1479	1466	1466
query40	225	120	114	114
query41	55	55	56	55
query42	128	108	109	108
query43	496	507	488	488
query44	1360	846	875	846
query45	191	171	166	166
query46	856	1047	660	660
query47	1895	1890	1831	1831
query48	401	428	311	311
query49	687	517	433	433
query50	686	699	408	408
query51	4262	4216	4122	4122
query52	120	112	106	106
query53	237	251	185	185
query54	598	594	524	524
query55	94	86	80	80
query56	304	311	306	306
query57	1181	1212	1146	1146
query58	271	266	257	257
query59	2764	2915	2636	2636
query60	345	337	316	316
query61	131	141	131	131
query62	739	742	701	701
query63	234	191	189	189
query64	1849	1077	721	721
query65	4307	4216	4198	4198
query66	695	405	305	305
query67	15896	15712	15489	15489
query68	7615	894	518	518
query69	537	304	281	281
query70	1223	1176	1113	1113
query71	508	330	312	312
query72	5937	4860	4935	4860
query73	1527	677	358	358
query74	8912	9150	9039	9039
query75	3909	3238	2749	2749
query76	4135	1190	749	749
query77	609	371	285	285
query78	10122	10114	9322	9322
query79	3220	823	588	588
query80	640	529	453	453
query81	497	264	230	230
query82	480	126	99	99
query83	416	250	245	245
query84	292	109	79	79
query85	783	347	311	311
query86	379	309	297	297
query87	4443	4392	4355	4355
query88	3762	2311	2296	2296
query89	407	316	285	285
query90	1912	211	214	211
query91	148	144	115	115
query92	77	62	56	56
query93	2411	950	577	577
query94	675	493	313	313
query95	376	301	293	293
query96	498	571	280	280
query97	3174	3311	3124	3124
query98	232	215	209	209
query99	1435	1415	1282	1282
Total cold run time: 301247 ms
Total hot run time: 194326 ms
```
</details>

author_association: NONE
user: login: doris-robot
created_at: 2025-05-11T17:40:48Z
body: 
<details>
<summary>ClickBench: <b>Total hot run time: 29.34 s</b></summary>

```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit cb392aa966a95746d46155c3ce850a1220a52ee9, data reload: false

query1	0.04	0.04	0.04
query2	0.12	0.10	0.12
query3	0.25	0.20	0.19
query4	1.60	0.19	0.19
query5	0.60	0.57	0.60
query6	1.18	0.73	0.72
query7	0.02	0.02	0.02
query8	0.04	0.04	0.03
query9	0.58	0.53	0.52
query10	0.55	0.59	0.57
query11	0.16	0.11	0.12
query12	0.14	0.12	0.12
query13	0.62	0.60	0.60
query14	0.78	0.82	0.81
query15	0.87	0.85	0.87
query16	0.39	0.37	0.39
query17	1.05	1.02	1.07
query18	0.23	0.22	0.22
query19	2.01	1.92	1.77
query20	0.01	0.01	0.02
query21	15.44	0.93	0.56
query22	0.78	1.13	0.72
query23	14.92	1.37	0.64
query24	7.11	1.54	0.82
query25	0.49	0.07	0.18
query26	0.60	0.17	0.13
query27	0.05	0.04	0.05
query28	9.48	0.88	0.48
query29	12.52	4.00	3.30
query30	0.25	0.09	0.06
query31	2.81	0.62	0.39
query32	3.23	0.55	0.47
query33	3.09	3.03	3.14
query34	15.89	5.19	4.51
query35	4.54	4.56	4.49
query36	0.66	0.49	0.48
query37	0.08	0.06	0.06
query38	0.05	0.04	0.04
query39	0.03	0.02	0.02
query40	0.16	0.13	0.13
query41	0.08	0.03	0.02
query42	0.03	0.02	0.03
query43	0.03	0.03	0.03
Total cold run time: 103.56 s
Total hot run time: 29.34 s
```
</details>

author_association: NONE
user: login: morningman
created_at: 2025-05-12T15:15:11Z
body: run buildall
author_association: CONTRIBUTOR
user: login: doris-robot
created_at: 2025-05-12T15:54:13Z
body: 
<details>
<summary>TPC-H: <b>Total hot run time: 34100 ms</b></summary>

```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 586acadf5cd80b514a5735db3ddea64a3467310b, data reload: false

------ Round 1 ----------------------------------
q1	25939	5008	4995	4995
q2	2070	295	191	191
q3	10369	1245	679	679
q4	10234	993	510	510
q5	7530	2322	2362	2322
q6	192	157	128	128
q7	902	733	600	600
q8	9313	1241	1047	1047
q9	6724	5119	5123	5119
q10	6878	2322	1898	1898
q11	576	288	277	277
q12	344	354	222	222
q13	17991	3828	3171	3171
q14	232	225	222	222
q15	533	477	482	477
q16	438	443	375	375
q17	644	858	400	400
q18	7693	7259	7282	7259
q19	1720	953	556	556
q20	345	361	227	227
q21	3940	2705	2442	2442
q22	1038	1007	983	983
Total cold run time: 115645 ms
Total hot run time: 34100 ms

----- Round 2, with runtime_filter_mode=off -----
q1	5109	5106	5040	5040
q2	229	326	226	226
q3	2155	2646	2275	2275
q4	1391	1799	1297	1297
q5	4384	4361	4367	4361
q6	219	173	135	135
q7	1993	1931	1780	1780
q8	2556	2634	2576	2576
q9	7175	7142	7012	7012
q10	2967	3204	2766	2766
q11	582	515	500	500
q12	694	765	601	601
q13	3474	3917	3324	3324
q14	314	305	283	283
q15	524	513	484	484
q16	458	474	440	440
q17	1131	1558	1372	1372
q18	7687	7419	7418	7418
q19	817	788	889	788
q20	1976	1971	1865	1865
q21	5222	4793	4676	4676
q22	1105	1039	971	971
Total cold run time: 52162 ms
Total hot run time: 50190 ms
```
</details>

author_association: NONE
user: login: doris-robot
created_at: 2025-05-12T16:06:31Z
body: 
<details>
<summary>TPC-DS: <b>Total hot run time: 193066 ms</b></summary>

```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 586acadf5cd80b514a5735db3ddea64a3467310b, data reload: false

query1	1423	1103	1040	1040
query2	6116	1852	1821	1821
query3	10961	4626	4497	4497
query4	55770	25062	23036	23036
query5	5100	501	436	436
query6	340	192	192	192
query7	4969	499	293	293
query8	325	267	239	239
query9	5975	2659	2637	2637
query10	450	334	269	269
query11	15120	15204	14819	14819
query12	157	117	109	109
query13	1107	511	421	421
query14	10142	6296	6312	6296
query15	221	211	182	182
query16	7199	672	483	483
query17	1075	777	612	612
query18	1641	416	337	337
query19	205	207	171	171
query20	130	126	118	118
query21	208	129	106	106
query22	4467	4613	4435	4435
query23	34444	33455	33143	33143
query24	6438	2461	2439	2439
query25	459	478	420	420
query26	684	276	165	165
query27	2273	507	345	345
query28	2893	2137	2138	2137
query29	597	580	454	454
query30	266	229	190	190
query31	863	839	822	822
query32	74	62	68	62
query33	467	381	301	301
query34	761	879	534	534
query35	776	836	737	737
query36	927	1006	916	916
query37	109	101	71	71
query38	4302	4313	4349	4313
query39	1514	1461	1441	1441
query40	218	121	112	112
query41	54	57	50	50
query42	127	111	110	110
query43	486	510	468	468
query44	1314	823	852	823
query45	184	180	180	180
query46	851	1017	656	656
query47	1874	1898	1795	1795
query48	411	435	337	337
query49	686	530	456	456
query50	657	700	420	420
query51	4250	4215	4198	4198
query52	112	107	101	101
query53	221	263	188	188
query54	618	590	531	531
query55	82	84	85	84
query56	299	308	305	305
query57	1190	1242	1130	1130
query58	269	264	260	260
query59	2655	2781	2631	2631
query60	352	338	312	312
query61	130	123	128	123
query62	743	736	655	655
query63	219	189	187	187
query64	1698	1019	670	670
query65	4383	4371	4308	4308
query66	776	406	309	309
query67	16902	15690	15832	15690
query68	7589	834	520	520
query69	542	319	265	265
query70	1208	1088	1075	1075
query71	494	332	291	291
query72	5469	4782	4708	4708
query73	1382	589	352	352
query74	9036	9205	9036	9036
query75	3848	3229	2717	2717
query76	4195	1178	759	759
query77	623	356	289	289
query78	10011	10168	9357	9357
query79	2195	823	596	596
query80	573	598	451	451
query81	484	249	221	221
query82	473	125	92	92
query83	258	249	235	235
query84	295	96	88	88
query85	796	344	309	309
query86	387	275	300	275
query87	4471	4541	4323	4323
query88	3919	2292	2284	2284
query89	404	328	276	276
query90	1816	201	218	201
query91	138	141	114	114
query92	73	62	58	58
query93	1910	943	574	574
query94	655	408	307	307
query95	366	300	286	286
query96	509	562	285	285
query97	3207	3260	3148	3148
query98	228	210	193	193
query99	1391	1369	1253	1253
Total cold run time: 301654 ms
Total hot run time: 193066 ms
```
</details>

author_association: NONE
user: login: doris-robot
created_at: 2025-05-12T16:11:49Z
body: 
<details>
<summary>ClickBench: <b>Total hot run time: 29.07 s</b></summary>

```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 586acadf5cd80b514a5735db3ddea64a3467310b, data reload: false

query1	0.04	0.04	0.03
query2	0.12	0.10	0.11
query3	0.25	0.19	0.18
query4	1.59	0.19	0.18
query5	0.60	0.58	0.59
query6	1.19	0.72	0.73
query7	0.03	0.02	0.02
query8	0.04	0.04	0.04
query9	0.58	0.53	0.52
query10	0.58	0.59	0.56
query11	0.16	0.11	0.10
query12	0.16	0.11	0.12
query13	0.61	0.60	0.60
query14	0.79	0.81	0.82
query15	0.88	0.84	0.85
query16	0.41	0.38	0.39
query17	1.04	1.04	1.04
query18	0.23	0.21	0.21
query19	1.90	1.86	1.86
query20	0.01	0.01	0.01
query21	15.40	0.89	0.56
query22	0.76	1.18	0.64
query23	14.99	1.38	0.62
query24	6.87	2.05	0.62
query25	0.45	0.14	0.16
query26	0.73	0.16	0.14
query27	0.06	0.05	0.05
query28	9.08	0.89	0.43
query29	12.56	3.98	3.34
query30	0.25	0.10	0.07
query31	2.82	0.58	0.38
query32	3.24	0.57	0.48
query33	3.00	3.14	3.01
query34	15.63	5.10	4.43
query35	4.51	4.51	4.49
query36	0.66	0.50	0.47
query37	0.09	0.06	0.06
query38	0.05	0.04	0.03
query39	0.03	0.02	0.02
query40	0.18	0.13	0.12
query41	0.08	0.03	0.03
query42	0.03	0.02	0.03
query43	0.04	0.03	0.03
Total cold run time: 102.72 s
Total hot run time: 29.07 s
```
</details>

author_association: NONE
user: login: morningman
created_at: 2025-05-21T02:49:17Z
body: run buildall
author_association: CONTRIBUTOR
user: login: doris-robot
created_at: 2025-05-21T03:37:00Z
body: 
<details>
<summary>TPC-H: <b>Total hot run time: 33940 ms</b></summary>

```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 81bdbd1f25755b62605662c810b23ca785cfc6cd, data reload: false

------ Round 1 ----------------------------------
q1	25973	5076	4971	4971
q2	2063	281	185	185
q3	10390	1260	699	699
q4	10224	1035	557	557
q5	7535	2313	2364	2313
q6	186	162	134	134
q7	919	753	647	647
q8	9343	1299	1106	1106
q9	6832	5066	5090	5066
q10	6879	2284	1883	1883
q11	506	290	293	290
q12	355	363	218	218
q13	17815	3679	3118	3118
q14	226	241	213	213
q15	533	502	470	470
q16	435	436	386	386
q17	611	857	383	383
q18	7578	7068	7058	7058
q19	1529	967	556	556
q20	333	327	224	224
q21	3672	3175	2473	2473
q22	1023	1005	990	990
Total cold run time: 114960 ms
Total hot run time: 33940 ms

----- Round 2, with runtime_filter_mode=off -----
q1	5116	5090	5046	5046
q2	253	322	238	238
q3	2167	2653	2304	2304
q4	1371	1769	1331	1331
q5	4441	4391	4410	4391
q6	217	166	127	127
q7	1973	1938	1755	1755
q8	2611	2502	2593	2502
q9	7305	7168	6962	6962
q10	3086	3219	2753	2753
q11	578	502	495	495
q12	692	749	603	603
q13	3508	3852	3254	3254
q14	293	291	275	275
q15	533	487	476	476
q16	449	486	429	429
q17	1140	1502	1421	1421
q18	7722	7539	7479	7479
q19	823	825	993	825
q20	2012	2092	1864	1864
q21	4764	4524	4351	4351
q22	1081	1064	1055	1055
Total cold run time: 52135 ms
Total hot run time: 49936 ms
```
</details>

author_association: NONE
user: login: doris-robot
created_at: 2025-05-21T03:49:09Z
body: 
<details>
<summary>TPC-DS: <b>Total hot run time: 193501 ms</b></summary>

```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 81bdbd1f25755b62605662c810b23ca785cfc6cd, data reload: false

query1	1390	1104	1082	1082
query2	6250	1863	1915	1863
query3	11020	4496	4441	4441
query4	54439	24384	23282	23282
query5	5087	496	467	467
query6	339	208	197	197
query7	4877	513	311	311
query8	323	261	248	248
query9	5241	2656	2666	2656
query10	442	333	284	284
query11	14947	15077	14813	14813
query12	154	116	110	110
query13	1030	548	443	443
query14	10191	6363	6370	6363
query15	212	198	171	171
query16	7137	665	514	514
query17	1079	750	584	584
query18	1572	426	318	318
query19	195	190	175	175
query20	134	126	119	119
query21	207	129	110	110
query22	4329	4458	4292	4292
query23	34412	33716	33703	33703
query24	6634	2473	2392	2392
query25	465	468	421	421
query26	677	287	158	158
query27	2291	529	347	347
query28	3179	2189	2172	2172
query29	582	560	448	448
query30	280	221	207	207
query31	872	896	788	788
query32	75	65	74	65
query33	472	377	321	321
query34	788	857	553	553
query35	840	840	746	746
query36	940	1029	910	910
query37	112	97	72	72
query38	4235	4414	4276	4276
query39	1499	1443	1477	1443
query40	212	118	112	112
query41	54	52	51	51
query42	125	116	117	116
query43	512	535	503	503
query44	1313	850	836	836
query45	185	173	173	173
query46	863	1027	653	653
query47	1838	1848	1780	1780
query48	422	447	336	336
query49	673	528	425	425
query50	683	719	431	431
query51	4192	4373	4217	4217
query52	117	110	105	105
query53	232	253	187	187
query54	583	585	529	529
query55	88	91	87	87
query56	318	306	310	306
query57	1166	1198	1154	1154
query58	274	273	285	273
query59	2925	2968	2776	2776
query60	351	344	326	326
query61	129	123	126	123
query62	738	756	692	692
query63	235	195	196	195
query64	1500	1025	670	670
query65	4313	4225	4242	4225
query66	709	389	300	300
query67	15853	15612	15617	15612
query68	5653	886	536	536
query69	513	317	283	283
query70	1206	1129	1093	1093
query71	443	320	300	300
query72	5482	4775	4792	4775
query73	1329	623	356	356
query74	8934	9189	8804	8804
query75	3486	3196	2702	2702
query76	3684	1264	763	763
query77	555	386	277	277
query78	10241	10553	9259	9259
query79	1825	884	571	571
query80	829	517	456	456
query81	504	268	222	222
query82	397	127	93	93
query83	260	248	237	237
query84	296	106	83	83
query85	758	355	314	314
query86	342	300	287	287
query87	4512	4491	4487	4487
query88	2953	2328	2299	2299
query89	407	323	293	293
query90	1728	211	207	207
query91	136	140	110	110
query92	66	62	55	55
query93	1802	957	581	581
query94	614	420	299	299
query95	368	295	290	290
query96	517	567	287	287
query97	2695	2719	2645	2645
query98	235	223	208	208
query99	1365	1369	1271	1271
Total cold run time: 293686 ms
Total hot run time: 193501 ms
```
</details>

author_association: NONE
user: login: doris-robot
created_at: 2025-05-21T03:54:22Z
body: 
<details>
<summary>ClickBench: <b>Total hot run time: 29.42 s</b></summary>

```
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 81bdbd1f25755b62605662c810b23ca785cfc6cd, data reload: false

query1	0.04	0.04	0.03
query2	0.12	0.10	0.11
query3	0.27	0.20	0.19
query4	1.60	0.20	0.19
query5	0.44	0.44	0.43
query6	1.15	0.66	0.66
query7	0.03	0.02	0.02
query8	0.04	0.04	0.04
query9	0.60	0.51	0.51
query10	0.58	0.58	0.56
query11	0.15	0.11	0.11
query12	0.14	0.11	0.11
query13	0.61	0.59	0.60
query14	0.79	0.82	0.80
query15	0.87	0.84	0.87
query16	0.39	0.39	0.38
query17	1.02	1.02	1.04
query18	0.22	0.21	0.21
query19	1.94	1.79	1.88
query20	0.02	0.01	0.01
query21	15.42	0.91	0.54
query22	0.75	1.11	0.93
query23	14.72	1.38	0.69
query24	6.81	1.90	0.93
query25	0.49	0.32	0.05
query26	0.55	0.16	0.15
query27	0.05	0.05	0.06
query28	9.89	0.87	0.45
query29	12.57	3.97	3.28
query30	0.26	0.09	0.06
query31	2.82	0.59	0.39
query32	3.23	0.55	0.47
query33	3.07	3.05	3.04
query34	15.69	5.13	4.50
query35	4.50	4.52	4.52
query36	0.68	0.49	0.47
query37	0.09	0.06	0.06
query38	0.05	0.04	0.04
query39	0.03	0.02	0.03
query40	0.17	0.13	0.13
query41	0.09	0.03	0.02
query42	0.03	0.03	0.02
query43	0.04	0.03	0.02
Total cold run time: 103.02 s
Total hot run time: 29.42 s
```
</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-24T02:54:58Z
body: PR approved by at least one committer and no changes requested.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-24T02:55:00Z
body: PR approved by anyone and no changes requested.
author_association: CONTRIBUTOR