This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 7e26f7b1724 branch-4.1: [feature](fe) Support ORDER BY and LIMIT 
clauses for UPDATE and DELETE commands #61681 (#61752)
7e26f7b1724 is described below

commit 7e26f7b1724b236be1c116ad96c91757229995ec
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Mar 26 18:16:31 2026 +0800

    branch-4.1: [feature](fe) Support ORDER BY and LIMIT clauses for UPDATE and 
DELETE commands #61681 (#61752)
    
    Cherry-picked from #61681
    
    Co-authored-by: morrySnow <[email protected]>
    Co-authored-by: Copilot <[email protected]>
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |   6 +-
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  43 +++-
 .../trees/plans/commands/DeleteFromCommand.java    |   4 +-
 .../plans/commands/DeleteFromUsingCommand.java     |  12 +-
 .../nereids/parser/LogicalPlanBuilderTest.java     | 251 +++++++++++++++++++++
 .../doris/nereids/parser/NereidsParserTest.java    |  79 +++++++
 .../data/delete_p0/test_delete_order_by_limit.out  |  61 +++++
 .../data/update/test_update_order_by_limit.out     |  85 +++++++
 .../delete_p0/test_delete_order_by_limit.groovy    | 178 +++++++++++++++
 .../update/test_update_order_by_limit.groovy       | 175 ++++++++++++++
 10 files changed, 886 insertions(+), 8 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 32cdb65a71e..eb11ce49cd1 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
@@ -151,11 +151,13 @@ supportedDmlStatement
     | explain? cte? UPDATE tableName=multipartIdentifier tableAlias
         SET updateAssignmentSeq
         fromClause?
-        whereClause?                                                   #update
+        whereClause?
+        queryOrganization                                              #update
     | explain? cte? DELETE FROM tableName=multipartIdentifier
         partitionSpec? tableAlias
         (USING relations)?
-        whereClause?                                                   #delete
+        whereClause?
+        queryOrganization                                              #delete
     | explain? cte? MERGE INTO targetTable=multipartIdentifier
         (AS? identifier)? USING srcRelation=relationPrimary
         ON expression
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 a5acc362365..13b4eac4308 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
@@ -600,6 +600,7 @@ import 
org.apache.doris.nereids.trees.expressions.literal.DateV2Literal;
 import org.apache.doris.nereids.trees.expressions.literal.DecimalLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.DecimalV3Literal;
 import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLikeLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.Interval;
 import org.apache.doris.nereids.trees.expressions.literal.LargeIntLiteral;
@@ -2037,6 +2038,8 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
             query = withRelations(query, ((FromRelationsContext) 
ctx.fromClause()).relations().relation());
         }
         query = withFilter(query, Optional.ofNullable(ctx.whereClause()));
+        query = withQueryOrganization(query, ctx.queryOrganization());
+        query = convertSortOrdinalsToUnboundSlot(query);
         String tableAlias = null;
         if (ctx.tableAlias().strictIdentifier() != null) {
             tableAlias = ctx.tableAlias().strictIdentifier().getText();
@@ -2067,8 +2070,11 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
             tableAlias = ctx.tableAlias().strictIdentifier().getText();
         }
 
+        boolean hasQueryOrganization = ctx.queryOrganization() != null
+                && (ctx.queryOrganization().sortClause() != null
+                        || ctx.queryOrganization().limitClause() != null);
         Command deleteCommand;
-        if (ctx.USING() == null && ctx.cte() == null) {
+        if (ctx.USING() == null && ctx.cte() == null && !hasQueryOrganization) 
{
             query = withFilter(query, Optional.ofNullable(ctx.whereClause()));
             deleteCommand = new DeleteFromCommand(tableName, tableAlias, 
partitionSpec.first,
                     partitionSpec.second, query);
@@ -2078,12 +2084,14 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
                 query = withRelations(query, ctx.relations().relation());
             }
             query = withFilter(query, Optional.ofNullable(ctx.whereClause()));
+            query = withQueryOrganization(query, ctx.queryOrganization());
+            query = convertSortOrdinalsToUnboundSlot(query);
             Optional<LogicalPlan> cte = Optional.empty();
             if (ctx.cte() != null) {
                 cte = Optional.ofNullable(withCte(query, ctx.cte()));
             }
             deleteCommand = new DeleteFromUsingCommand(tableName, tableAlias,
-                    partitionSpec.first, partitionSpec.second, query, cte);
+                    partitionSpec.first, partitionSpec.second, query, cte, 
hasQueryOrganization);
         }
         if (ctx.explain() != null) {
             return withExplain(deleteCommand, ctx.explain());
@@ -4379,6 +4387,37 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         });
     }
 
+    /**
+     * Convert IntegerLikeLiteral expressions in ORDER BY keys to UnboundSlot.
+     * In SELECT queries, ORDER BY with an integer is treated as an ordinal 
(position reference).
+     * In DELETE/UPDATE commands, there is no user-specified SELECT list, so 
ordinal resolution
+     * would be meaningless. Convert integer literals to UnboundSlot to 
prevent the ordinal
+     * interpretation by BindExpression.
+     */
+    private LogicalPlan convertSortOrdinalsToUnboundSlot(LogicalPlan plan) {
+        if (plan instanceof LogicalSort) {
+            LogicalSort<?> sort = (LogicalSort<?>) plan;
+            List<OrderKey> newOrderKeys = sort.getOrderKeys().stream()
+                    .map(key -> {
+                        if (key.getExpr() instanceof IntegerLikeLiteral) {
+                            return key.withExpression(
+                                    new UnboundSlot(String.valueOf(
+                                            ((IntegerLikeLiteral) 
key.getExpr()).getIntValue())));
+                        }
+                        return key;
+                    })
+                    .collect(ImmutableList.toImmutableList());
+            return sort.withOrderKeys(newOrderKeys);
+        } else if (plan instanceof LogicalLimit) {
+            LogicalPlan child = (LogicalPlan) ((LogicalLimit<?>) plan).child();
+            LogicalPlan newChild = convertSortOrdinalsToUnboundSlot(child);
+            if (newChild != child) {
+                return (LogicalPlan) ((LogicalLimit<?>) 
plan).withChildren(newChild);
+            }
+        }
+        return plan;
+    }
+
     private LogicalPlan withLimit(LogicalPlan input, 
Optional<LimitClauseContext> limitCtx) {
         return input.optionalMap(limitCtx, () -> {
             long limit = Long.parseLong(limitCtx.get().limit.getText());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromCommand.java
index 04e155b29fc..be067378310 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromCommand.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromCommand.java
@@ -181,7 +181,7 @@ public class DeleteFromCommand extends Command implements 
ForwardWithSync, Expla
         } catch (Exception e) {
             try {
                 new DeleteFromUsingCommand(nameParts, tableAlias, isTempPart, 
partitions,
-                        logicalQuery, Optional.empty()).run(ctx, executor);
+                        logicalQuery, Optional.empty(), false).run(ctx, 
executor);
                 return;
             } catch (Exception e2) {
                 LOG.warn("delete from command failed", e2);
@@ -193,7 +193,7 @@ public class DeleteFromCommand extends Command implements 
ForwardWithSync, Expla
         if (olapTable.getKeysType() == KeysType.UNIQUE_KEYS && 
olapTable.getEnableUniqueKeyMergeOnWrite()
                 && !olapTable.getEnableMowLightDelete()) {
             new DeleteFromUsingCommand(nameParts, tableAlias, isTempPart, 
partitions, logicalQuery,
-                    Optional.empty()).run(ctx, executor);
+                    Optional.empty(), false).run(ctx, executor);
             return;
         }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromUsingCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromUsingCommand.java
index 764ff05c00f..2364c690712 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromUsingCommand.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromUsingCommand.java
@@ -35,14 +35,17 @@ import java.util.Optional;
  */
 public class DeleteFromUsingCommand extends DeleteFromCommand {
     private final Optional<LogicalPlan> cte;
+    private final boolean hasOrderByLimit;
 
     /**
      * constructor
      */
     public DeleteFromUsingCommand(List<String> nameParts, String tableAlias,
-            boolean isTempPart, List<String> partitions, LogicalPlan 
logicalQuery, Optional<LogicalPlan> cte) {
+            boolean isTempPart, List<String> partitions, LogicalPlan 
logicalQuery,
+            Optional<LogicalPlan> cte, boolean hasOrderByLimit) {
         super(nameParts, tableAlias, isTempPart, partitions, logicalQuery);
         this.cte = cte;
+        this.hasOrderByLimit = hasOrderByLimit;
     }
 
     @Override
@@ -80,7 +83,12 @@ public class DeleteFromUsingCommand extends 
DeleteFromCommand {
     @Override
     protected void checkTargetTable(OlapTable targetTable) {
         if (targetTable.getKeysType() != KeysType.UNIQUE_KEYS) {
-            throw new AnalysisException("delete command on with using clause 
only supports unique key model");
+            if (hasOrderByLimit) {
+                throw new AnalysisException(
+                        "delete command with ORDER BY/LIMIT only supports 
unique key model");
+            }
+            throw new AnalysisException(
+                    "delete command on with using clause only supports unique 
key model");
         }
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/LogicalPlanBuilderTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/LogicalPlanBuilderTest.java
new file mode 100644
index 00000000000..9690e24ea85
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/LogicalPlanBuilderTest.java
@@ -0,0 +1,251 @@
+// 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.parser;
+
+import org.apache.doris.nereids.analyzer.UnboundSlot;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLikeLiteral;
+import org.apache.doris.nereids.trees.plans.commands.DeleteFromCommand;
+import org.apache.doris.nereids.trees.plans.commands.DeleteFromUsingCommand;
+import org.apache.doris.nereids.trees.plans.commands.UpdateCommand;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalLimit;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for LogicalPlanBuilder to verify DELETE/UPDATE with ORDER BY and LIMIT
+ * produce the correct logical plan tree structure.
+ */
+public class LogicalPlanBuilderTest {
+
+    private final NereidsParser parser = new NereidsParser();
+
+    @Test
+    public void testDeleteWithOrderByLimitProducesCorrectPlanTree() {
+        String sql = "DELETE FROM t ORDER BY c1 LIMIT 10";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+        LogicalPlan query = ((DeleteFromUsingCommand) plan).getLogicalQuery();
+        // plan tree: LogicalLimit -> LogicalSort -> 
CheckPolicy(UnboundRelation)
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalLimit<?> limit = (LogicalLimit<?>) query;
+        Assertions.assertEquals(10, limit.getLimit());
+        Assertions.assertEquals(0, limit.getOffset());
+        Assertions.assertInstanceOf(LogicalSort.class, limit.child());
+        LogicalSort<?> sort = (LogicalSort<?>) limit.child();
+        Assertions.assertEquals(1, sort.getOrderKeys().size());
+    }
+
+    @Test
+    public void testDeleteWithOrderByLimitOffset() {
+        String sql = "DELETE FROM t ORDER BY c1 ASC NULLS FIRST LIMIT 10, 3";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+        LogicalPlan query = ((DeleteFromUsingCommand) plan).getLogicalQuery();
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalLimit<?> limit = (LogicalLimit<?>) query;
+        // LIMIT offset, count: offset=10, limit=3
+        Assertions.assertEquals(3, limit.getLimit());
+        Assertions.assertEquals(10, limit.getOffset());
+        Assertions.assertInstanceOf(LogicalSort.class, limit.child());
+    }
+
+    @Test
+    public void testDeleteWithWhereOrderByLimit() {
+        String sql = "DELETE FROM t WHERE c1 > 0 ORDER BY c1 DESC NULLS LAST 
LIMIT 5";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+        LogicalPlan query = ((DeleteFromUsingCommand) plan).getLogicalQuery();
+        // plan tree: LogicalLimit -> LogicalSort -> LogicalFilter -> 
CheckPolicy(UnboundRelation)
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalLimit<?> limit = (LogicalLimit<?>) query;
+        Assertions.assertEquals(5, limit.getLimit());
+        Assertions.assertInstanceOf(LogicalSort.class, limit.child());
+        LogicalSort<?> sort = (LogicalSort<?>) limit.child();
+        Assertions.assertInstanceOf(LogicalFilter.class, sort.child());
+    }
+
+    @Test
+    public void testDeleteWithOrderByOnlyProducesDeleteFromUsingCommand() {
+        String sql = "DELETE FROM t ORDER BY c1";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+        LogicalPlan query = ((DeleteFromUsingCommand) plan).getLogicalQuery();
+        Assertions.assertInstanceOf(LogicalSort.class, query);
+    }
+
+    @Test
+    public void testDeleteWithLimitOnlyProducesDeleteFromUsingCommand() {
+        String sql = "DELETE FROM t LIMIT 5";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+        LogicalPlan query = ((DeleteFromUsingCommand) plan).getLogicalQuery();
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalLimit<?> limit = (LogicalLimit<?>) query;
+        Assertions.assertEquals(5, limit.getLimit());
+    }
+
+    @Test
+    public void testDeleteWithoutOrderByLimitProducesDeleteFromCommand() {
+        String sql = "DELETE FROM t WHERE c1 = 1";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromCommand.class, plan);
+        Assertions.assertFalse(plan instanceof DeleteFromUsingCommand);
+    }
+
+    @Test
+    public void testUpdateWithOrderByLimitProducesCorrectPlanTree() {
+        String sql = "UPDATE t SET c1 = 10 ORDER BY c2 LIMIT 100";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+        LogicalPlan query = ((UpdateCommand) plan).getLogicalQuery();
+        // plan tree: LogicalLimit -> LogicalSort -> 
CheckPolicy(UnboundRelation)
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalLimit<?> limit = (LogicalLimit<?>) query;
+        Assertions.assertEquals(100, limit.getLimit());
+        Assertions.assertEquals(0, limit.getOffset());
+        Assertions.assertInstanceOf(LogicalSort.class, limit.child());
+        LogicalSort<?> sort = (LogicalSort<?>) limit.child();
+        Assertions.assertEquals(1, sort.getOrderKeys().size());
+    }
+
+    @Test
+    public void testUpdateWithOrderByLimitOffset() {
+        String sql = "UPDATE t SET c1 = 10 ORDER BY c2 LIMIT 100, 20";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+        LogicalPlan query = ((UpdateCommand) plan).getLogicalQuery();
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalLimit<?> limit = (LogicalLimit<?>) query;
+        // LIMIT offset, count: offset=100, limit=20
+        Assertions.assertEquals(20, limit.getLimit());
+        Assertions.assertEquals(100, limit.getOffset());
+        Assertions.assertInstanceOf(LogicalSort.class, limit.child());
+    }
+
+    @Test
+    public void testUpdateWithWhereOrderByLimit() {
+        String sql = "UPDATE t SET c1 = 10 WHERE c2 > 5 ORDER BY c2 DESC LIMIT 
50";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+        LogicalPlan query = ((UpdateCommand) plan).getLogicalQuery();
+        // plan tree: LogicalLimit -> LogicalSort -> LogicalFilter -> 
CheckPolicy(UnboundRelation)
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalLimit<?> limit = (LogicalLimit<?>) query;
+        Assertions.assertInstanceOf(LogicalSort.class, limit.child());
+        LogicalSort<?> sort = (LogicalSort<?>) limit.child();
+        Assertions.assertInstanceOf(LogicalFilter.class, sort.child());
+    }
+
+    @Test
+    public void testUpdateWithOrderByOnlyProducesCorrectPlanTree() {
+        String sql = "UPDATE t SET c1 = 10 ORDER BY c2";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+        LogicalPlan query = ((UpdateCommand) plan).getLogicalQuery();
+        Assertions.assertInstanceOf(LogicalSort.class, query);
+    }
+
+    @Test
+    public void testUpdateWithLimitOnlyProducesCorrectPlanTree() {
+        String sql = "UPDATE t SET c1 = 10 LIMIT 50";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+        LogicalPlan query = ((UpdateCommand) plan).getLogicalQuery();
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalLimit<?> limit = (LogicalLimit<?>) query;
+        Assertions.assertEquals(50, limit.getLimit());
+    }
+
+    @Test
+    public void testUpdateWithoutOrderByLimitProducesUpdateCommand() {
+        String sql = "UPDATE t SET c1 = 10 WHERE c2 = 1";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+        LogicalPlan query = ((UpdateCommand) plan).getLogicalQuery();
+        // No sort or limit in query
+        Assertions.assertInstanceOf(LogicalFilter.class, query);
+    }
+
+    @Test
+    public void testDeleteWithMultipleOrderByColumns() {
+        String sql = "DELETE FROM t ORDER BY c1 ASC, c2 DESC NULLS LAST LIMIT 
10";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+        LogicalPlan query = ((DeleteFromUsingCommand) plan).getLogicalQuery();
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalSort<?> sort = (LogicalSort<?>) ((LogicalLimit<?>) 
query).child();
+        Assertions.assertEquals(2, sort.getOrderKeys().size());
+    }
+
+    @Test
+    public void testUpdateWithMultipleOrderByColumns() {
+        String sql = "UPDATE t SET c1 = 10 ORDER BY c2 ASC, c3 DESC NULLS 
FIRST LIMIT 5";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+        LogicalPlan query = ((UpdateCommand) plan).getLogicalQuery();
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalSort<?> sort = (LogicalSort<?>) ((LogicalLimit<?>) 
query).child();
+        Assertions.assertEquals(2, sort.getOrderKeys().size());
+    }
+
+    @Test
+    public void testDeleteOrderByIntegerOrdinalConvertedToUnboundSlot() {
+        String sql = "DELETE FROM t ORDER BY 1 LIMIT 10";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+        LogicalPlan query = ((DeleteFromUsingCommand) plan).getLogicalQuery();
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalSort<?> sort = (LogicalSort<?>) ((LogicalLimit<?>) 
query).child();
+        Assertions.assertEquals(1, sort.getOrderKeys().size());
+        // Integer ordinal should be converted to UnboundSlot, not remain as 
IntegerLikeLiteral
+        Assertions.assertInstanceOf(UnboundSlot.class, 
sort.getOrderKeys().get(0).getExpr());
+        Assertions.assertFalse(sort.getOrderKeys().get(0).getExpr() instanceof 
IntegerLikeLiteral);
+    }
+
+    @Test
+    public void testUpdateOrderByIntegerOrdinalConvertedToUnboundSlot() {
+        String sql = "UPDATE t SET c1 = 10 ORDER BY 1 LIMIT 100";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+        LogicalPlan query = ((UpdateCommand) plan).getLogicalQuery();
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalSort<?> sort = (LogicalSort<?>) ((LogicalLimit<?>) 
query).child();
+        Assertions.assertEquals(1, sort.getOrderKeys().size());
+        Assertions.assertInstanceOf(UnboundSlot.class, 
sort.getOrderKeys().get(0).getExpr());
+        Assertions.assertFalse(sort.getOrderKeys().get(0).getExpr() instanceof 
IntegerLikeLiteral);
+    }
+
+    @Test
+    public void testDeleteOrderByMixedOrdinalAndColumn() {
+        String sql = "DELETE FROM t ORDER BY 1, c2 DESC LIMIT 5";
+        LogicalPlan plan = parser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+        LogicalPlan query = ((DeleteFromUsingCommand) plan).getLogicalQuery();
+        Assertions.assertInstanceOf(LogicalLimit.class, query);
+        LogicalSort<?> sort = (LogicalSort<?>) ((LogicalLimit<?>) 
query).child();
+        Assertions.assertEquals(2, sort.getOrderKeys().size());
+        // First key: integer ordinal converted to UnboundSlot
+        Assertions.assertInstanceOf(UnboundSlot.class, 
sort.getOrderKeys().get(0).getExpr());
+        // Second key: column name remains as UnboundSlot
+        Assertions.assertInstanceOf(UnboundSlot.class, 
sort.getOrderKeys().get(1).getExpr());
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
index 48320525a9d..977177161fd 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
@@ -42,11 +42,13 @@ import org.apache.doris.nereids.trees.plans.PlanType;
 import 
org.apache.doris.nereids.trees.plans.commands.CreateMaterializedViewCommand;
 import org.apache.doris.nereids.trees.plans.commands.CreateTableCommand;
 import org.apache.doris.nereids.trees.plans.commands.CreateViewCommand;
+import org.apache.doris.nereids.trees.plans.commands.DeleteFromUsingCommand;
 import org.apache.doris.nereids.trees.plans.commands.DropTableCommand;
 import org.apache.doris.nereids.trees.plans.commands.ExecuteActionCommand;
 import org.apache.doris.nereids.trees.plans.commands.ExplainCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel;
 import org.apache.doris.nereids.trees.plans.commands.ReplayCommand;
+import org.apache.doris.nereids.trees.plans.commands.UpdateCommand;
 import org.apache.doris.nereids.trees.plans.commands.merge.MergeIntoCommand;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
 import org.apache.doris.nereids.trees.plans.logical.LogicalCTE;
@@ -1524,4 +1526,81 @@ public class NereidsParserTest extends ParserTestBase {
         String sql = "SELECT t.* FROM LATERAL unnest([1,2], ['hi','hello']) 
WITH ORDINALITY AS t(c1,c2);";
         parsePlan(sql).matches(logicalGenerate().when(plan -> 
plan.getGenerators().get(0) instanceof Unnest));
     }
+
+    @Test
+    public void testDeleteWithOrderByAndLimit() {
+        NereidsParser nereidsParser = new NereidsParser();
+
+        // DELETE with ORDER BY and LIMIT
+        String sql = "DELETE FROM t ORDER BY c1 LIMIT 10";
+        LogicalPlan plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+        Assertions.assertEquals(StmtType.DELETE, plan.stmtType());
+
+        // DELETE with WHERE, ORDER BY DESC NULLS LAST, and LIMIT with offset
+        sql = "DELETE FROM t WHERE c1 > 0 ORDER BY c1 DESC NULLS LAST LIMIT 5, 
10";
+        plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+
+        // DELETE with ORDER BY ASC NULLS FIRST and LIMIT with offset
+        sql = "DELETE FROM t ORDER BY c1 ASC NULLS FIRST LIMIT 10, 3";
+        plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+
+        // DELETE with LIMIT only
+        sql = "DELETE FROM t LIMIT 5";
+        plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+
+        // DELETE with ORDER BY only
+        sql = "DELETE FROM t ORDER BY c1";
+        plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+
+        // DELETE with LIMIT OFFSET syntax
+        sql = "DELETE FROM t ORDER BY c1 LIMIT 10 OFFSET 5";
+        plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+
+        // DELETE with multiple ORDER BY columns
+        sql = "DELETE FROM t ORDER BY c1 ASC, c2 DESC LIMIT 10";
+        plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, plan);
+    }
+
+    @Test
+    public void testUpdateWithOrderByAndLimit() {
+        NereidsParser nereidsParser = new NereidsParser();
+
+        // UPDATE with ORDER BY and LIMIT
+        String sql = "UPDATE t SET c1 = 10 ORDER BY c2 LIMIT 100";
+        LogicalPlan plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+        Assertions.assertEquals(StmtType.UPDATE, plan.stmtType());
+
+        // UPDATE with WHERE, ORDER BY DESC, and LIMIT with offset
+        sql = "UPDATE t SET c1 = 10 WHERE c2 > 5 ORDER BY c2 DESC LIMIT 100, 
20";
+        plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+
+        // UPDATE with LIMIT only
+        sql = "UPDATE t SET c1 = 10 LIMIT 50";
+        plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+
+        // UPDATE with ORDER BY only
+        sql = "UPDATE t SET c1 = 10 ORDER BY c2 ASC NULLS FIRST";
+        plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+
+        // UPDATE with LIMIT OFFSET syntax
+        sql = "UPDATE t SET c1 = 10 ORDER BY c2 LIMIT 20 OFFSET 10";
+        plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+
+        // UPDATE with multiple ORDER BY columns
+        sql = "UPDATE t SET c1 = 10 ORDER BY c2 ASC, c3 DESC NULLS LAST LIMIT 
5";
+        plan = nereidsParser.parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, plan);
+    }
 }
diff --git a/regression-test/data/delete_p0/test_delete_order_by_limit.out 
b/regression-test/data/delete_p0/test_delete_order_by_limit.out
new file mode 100644
index 00000000000..d8474eb877f
--- /dev/null
+++ b/regression-test/data/delete_p0/test_delete_order_by_limit.out
@@ -0,0 +1,61 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !before_delete --
+1      100     a
+10     10      j
+2      90      b
+3      80      c
+4      70      d
+5      60      e
+6      50      f
+7      40      g
+8      30      h
+9      20      i
+
+-- !delete_order_limit --
+1      100     a
+2      90      b
+3      80      c
+4      70      d
+5      60      e
+6      50      f
+7      40      g
+
+-- !delete_order_limit_offset --
+1      100     a
+10     10      j
+2      90      b
+3      80      c
+4      70      d
+5      60      e
+9      20      i
+
+-- !delete_where_order_limit --
+1      100     a
+10     10      j
+2      90      b
+3      80      c
+4      70      d
+5      60      e
+8      30      h
+9      20      i
+
+-- !delete_order_desc_limit --
+10     10      j
+3      80      c
+4      70      d
+5      60      e
+6      50      f
+7      40      g
+8      30      h
+9      20      i
+
+-- !delete_limit_offset_syntax --
+1      100     a
+10     10      j
+2      90      b
+3      80      c
+4      70      d
+5      60      e
+6      50      f
+7      40      g
+
diff --git a/regression-test/data/update/test_update_order_by_limit.out 
b/regression-test/data/update/test_update_order_by_limit.out
new file mode 100644
index 00000000000..7eb546d8a23
--- /dev/null
+++ b/regression-test/data/update/test_update_order_by_limit.out
@@ -0,0 +1,85 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !before_update --
+1      100     a
+10     10      j
+2      90      b
+3      80      c
+4      70      d
+5      60      e
+6      50      f
+7      40      g
+8      30      h
+9      20      i
+
+-- !update_order_limit --
+1      100     a
+10     10      updated
+2      90      b
+3      80      c
+4      70      d
+5      60      e
+6      50      f
+7      40      g
+8      30      updated
+9      20      updated
+
+-- !update_order_limit_offset --
+1      100     a
+10     10      j
+2      90      b
+3      80      c
+4      70      d
+5      60      e
+6      50      updated
+7      40      updated
+8      30      updated
+9      20      i
+
+-- !update_where_order_limit --
+1      100     a
+10     10      j
+2      90      b
+3      80      c
+4      70      d
+5      60      e
+6      50      updated
+7      40      updated
+8      30      h
+9      20      i
+
+-- !update_order_desc_limit --
+1      100     updated
+10     10      j
+2      90      updated
+3      80      c
+4      70      d
+5      60      e
+6      50      f
+7      40      g
+8      30      h
+9      20      i
+
+-- !update_limit_offset_syntax --
+1      100     a
+10     10      j
+2      90      b
+3      80      c
+4      70      d
+5      60      e
+6      50      f
+7      40      g
+8      30      updated
+9      20      updated
+
+-- !update_multi_set --
+1      999     top3
+10     10      j
+2      999     top3
+3      999     top3
+4      70      d
+5      60      e
+6      50      f
+7      40      g
+8      30      h
+9      20      i
+
diff --git a/regression-test/suites/delete_p0/test_delete_order_by_limit.groovy 
b/regression-test/suites/delete_p0/test_delete_order_by_limit.groovy
new file mode 100644
index 00000000000..b81e9b6cbb8
--- /dev/null
+++ b/regression-test/suites/delete_p0/test_delete_order_by_limit.groovy
@@ -0,0 +1,178 @@
+// 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.
+
+suite("test_delete_order_by_limit") {
+    sql "DROP TABLE IF EXISTS test_delete_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_delete_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+
+    // insert test data: 10 rows with id 1..10
+    sql """
+        INSERT INTO test_delete_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+    order_qt_before_delete """SELECT * FROM test_delete_obl ORDER BY id;"""
+
+    // test DELETE with ORDER BY and LIMIT: delete 3 rows with smallest c1 
values
+    // c1 ascending: 10(id=10), 20(id=9), 30(id=8), 40(id=7), 50(id=6), ...
+    // LIMIT 3 means delete the first 3 rows: id=10, id=9, id=8
+    sql "DELETE FROM test_delete_obl ORDER BY c1 ASC LIMIT 3;"
+    order_qt_delete_order_limit """SELECT * FROM test_delete_obl ORDER BY 
id;"""
+
+    // reset data
+    sql "DROP TABLE IF EXISTS test_delete_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_delete_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+    sql """
+        INSERT INTO test_delete_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+
+    // test DELETE with ORDER BY, LIMIT and OFFSET
+    // c1 ascending: 10(id=10), 20(id=9), 30(id=8), 40(id=7), 50(id=6), ...
+    // LIMIT 2, 3 means offset=2, limit=3: skip 2 rows (id=10, id=9), delete 
next 3: id=8, id=7, id=6
+    sql "DELETE FROM test_delete_obl ORDER BY c1 ASC LIMIT 2, 3;"
+    order_qt_delete_order_limit_offset """SELECT * FROM test_delete_obl ORDER 
BY id;"""
+
+    // reset data
+    sql "DROP TABLE IF EXISTS test_delete_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_delete_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+    sql """
+        INSERT INTO test_delete_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+
+    // test DELETE with WHERE, ORDER BY and LIMIT
+    // filter: c1 > 30 leaves: 100(id=1), 90(id=2), 80(id=3), 70(id=4), 
60(id=5), 50(id=6), 40(id=7)
+    // order by c1 ASC: 40(id=7), 50(id=6), 60(id=5), 70(id=4), 80(id=3), 
90(id=2), 100(id=1)
+    // LIMIT 2: delete id=7, id=6
+    sql "DELETE FROM test_delete_obl WHERE c1 > 30 ORDER BY c1 ASC LIMIT 2;"
+    order_qt_delete_where_order_limit """SELECT * FROM test_delete_obl ORDER 
BY id;"""
+
+    // reset data
+    sql "DROP TABLE IF EXISTS test_delete_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_delete_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+    sql """
+        INSERT INTO test_delete_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+
+    // test DELETE with LIMIT only (no ORDER BY)
+    // delete any 3 rows (order is non-deterministic without ORDER BY)
+    sql "DELETE FROM test_delete_obl LIMIT 3;"
+    // just check the count
+    def result = sql "SELECT count(*) FROM test_delete_obl;"
+    assertEquals(7, result[0][0] as int)
+
+    // reset data
+    sql "DROP TABLE IF EXISTS test_delete_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_delete_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+    sql """
+        INSERT INTO test_delete_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+
+    // test DELETE with ORDER BY DESC and LIMIT
+    // c1 descending: 100(id=1), 90(id=2), 80(id=3), ...
+    // LIMIT 2: delete id=1, id=2
+    sql "DELETE FROM test_delete_obl ORDER BY c1 DESC LIMIT 2;"
+    order_qt_delete_order_desc_limit """SELECT * FROM test_delete_obl ORDER BY 
id;"""
+
+    // test DELETE with LIMIT OFFSET syntax
+    sql "DROP TABLE IF EXISTS test_delete_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_delete_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+    sql """
+        INSERT INTO test_delete_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+
+    // LIMIT 2 OFFSET 1 means skip 1 row then delete next 2 rows
+    // c1 ascending: 10(id=10), 20(id=9), 30(id=8), ...
+    // skip 1 (id=10), delete 2 (id=9, id=8)
+    sql "DELETE FROM test_delete_obl ORDER BY c1 ASC LIMIT 2 OFFSET 1;"
+    order_qt_delete_limit_offset_syntax """SELECT * FROM test_delete_obl ORDER 
BY id;"""
+}
diff --git a/regression-test/suites/update/test_update_order_by_limit.groovy 
b/regression-test/suites/update/test_update_order_by_limit.groovy
new file mode 100644
index 00000000000..442b1857659
--- /dev/null
+++ b/regression-test/suites/update/test_update_order_by_limit.groovy
@@ -0,0 +1,175 @@
+// 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.
+
+suite("test_update_order_by_limit") {
+    sql "DROP TABLE IF EXISTS test_update_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_update_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+
+    // insert test data: 10 rows with id 1..10
+    sql """
+        INSERT INTO test_update_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+    order_qt_before_update """SELECT * FROM test_update_obl ORDER BY id;"""
+
+    // test UPDATE with ORDER BY and LIMIT
+    // c1 ascending: 10(id=10), 20(id=9), 30(id=8)
+    // LIMIT 3 means update the first 3 rows: set c2='updated' for id=10, 
id=9, id=8
+    sql "UPDATE test_update_obl SET c2 = 'updated' ORDER BY c1 ASC LIMIT 3;"
+    order_qt_update_order_limit """SELECT * FROM test_update_obl ORDER BY 
id;"""
+
+    // reset data
+    sql "DROP TABLE IF EXISTS test_update_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_update_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+    sql """
+        INSERT INTO test_update_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+
+    // test UPDATE with ORDER BY, LIMIT and OFFSET
+    // c1 ascending: 10(id=10), 20(id=9), 30(id=8), 40(id=7), 50(id=6)
+    // LIMIT 2, 3 means offset=2, limit=3: skip 2 rows (id=10, id=9), update 
next 3: id=8, id=7, id=6
+    sql "UPDATE test_update_obl SET c2 = 'updated' ORDER BY c1 ASC LIMIT 2, 3;"
+    order_qt_update_order_limit_offset """SELECT * FROM test_update_obl ORDER 
BY id;"""
+
+    // reset data
+    sql "DROP TABLE IF EXISTS test_update_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_update_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+    sql """
+        INSERT INTO test_update_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+
+    // test UPDATE with WHERE, ORDER BY and LIMIT
+    // filter: c1 > 30 leaves: 100(id=1), 90(id=2), 80(id=3), 70(id=4), 
60(id=5), 50(id=6), 40(id=7)
+    // order by c1 ASC: 40(id=7), 50(id=6), 60(id=5), 70(id=4), 80(id=3), 
90(id=2), 100(id=1)
+    // LIMIT 2: update id=7, id=6
+    sql "UPDATE test_update_obl SET c2 = 'updated' WHERE c1 > 30 ORDER BY c1 
ASC LIMIT 2;"
+    order_qt_update_where_order_limit """SELECT * FROM test_update_obl ORDER 
BY id;"""
+
+    // reset data
+    sql "DROP TABLE IF EXISTS test_update_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_update_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+    sql """
+        INSERT INTO test_update_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+
+    // test UPDATE with ORDER BY DESC and LIMIT
+    // c1 descending: 100(id=1), 90(id=2), 80(id=3)
+    // LIMIT 2: update id=1, id=2
+    sql "UPDATE test_update_obl SET c2 = 'updated' ORDER BY c1 DESC LIMIT 2;"
+    order_qt_update_order_desc_limit """SELECT * FROM test_update_obl ORDER BY 
id;"""
+
+    // test UPDATE with LIMIT OFFSET syntax
+    sql "DROP TABLE IF EXISTS test_update_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_update_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+    sql """
+        INSERT INTO test_update_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+
+    // LIMIT 2 OFFSET 1 means skip 1 then update next 2
+    // c1 ascending: 10(id=10), 20(id=9), 30(id=8)
+    // skip 1 (id=10), update 2 (id=9, id=8)
+    sql "UPDATE test_update_obl SET c2 = 'updated' ORDER BY c1 ASC LIMIT 2 
OFFSET 1;"
+    order_qt_update_limit_offset_syntax """SELECT * FROM test_update_obl ORDER 
BY id;"""
+
+    // test UPDATE with multiple SET assignments and ORDER BY LIMIT
+    sql "DROP TABLE IF EXISTS test_update_obl"
+    sql """
+        CREATE TABLE IF NOT EXISTS test_update_obl (
+            id int,
+            c1 int,
+            c2 varchar(32)
+        )
+        UNIQUE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES (
+            'replication_num' = '1'
+        );
+    """
+    sql """
+        INSERT INTO test_update_obl VALUES
+            (1, 100, 'a'), (2, 90, 'b'), (3, 80, 'c'), (4, 70, 'd'), (5, 60, 
'e'),
+            (6, 50, 'f'), (7, 40, 'g'), (8, 30, 'h'), (9, 20, 'i'), (10, 10, 
'j');
+    """
+
+    // update both c1 and c2 for the 3 rows with largest c1
+    sql "UPDATE test_update_obl SET c1 = 999, c2 = 'top3' ORDER BY c1 DESC 
LIMIT 3;"
+    order_qt_update_multi_set """SELECT * FROM test_update_obl ORDER BY id;"""
+}


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


Reply via email to