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 914c20d4b0a branch-4.1: [fix](fe) Handle generated columns in delete 
partial update #64884 (#65027)
914c20d4b0a is described below

commit 914c20d4b0aafb41d721abdeb11d1760aa54d133
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Jul 1 10:52:25 2026 +0800

    branch-4.1: [fix](fe) Handle generated columns in delete partial update 
#64884 (#65027)
    
    Cherry-picked from #64884
    
    Co-authored-by: bobhan1 <[email protected]>
---
 .../doris/nereids/rules/analysis/BindSink.java     | 30 +++++--
 .../trees/plans/DeleteFromUsingCommandTest.java    | 89 ++++++++++++++++++++
 .../test_delete_generated_column.out               | 12 +++
 .../test_delete_generated_column.groovy            | 96 ++++++++++++++++++++++
 4 files changed, 219 insertions(+), 8 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSink.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSink.java
index 9757cbcba2a..3ac7d8f05fa 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSink.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSink.java
@@ -178,6 +178,7 @@ public class BindSink implements AnalysisRuleFactory {
         Database database = pair.first;
         OlapTable table = pair.second;
         boolean isPartialUpdate = sink.isPartialUpdate() && 
table.getKeysType() == KeysType.UNIQUE_KEYS;
+        boolean isDeletePartialUpdate = isPartialUpdate && 
sink.getDMLCommandType() == DMLCommandType.DELETE;
         TPartialUpdateNewRowPolicy partialUpdateNewKeyPolicy = 
sink.getPartialUpdateNewRowPolicy();
 
         LogicalPlan child = ((LogicalPlan) sink.child());
@@ -189,7 +190,7 @@ public class BindSink implements AnalysisRuleFactory {
         // 1. bind target columns: from sink's column names to target tables' 
Columns
         Pair<List<Column>, Integer> bindColumnsResult =
                 bindTargetColumns(table, sink.getColNames(), childHasSeqCol, 
needExtraSeqCol,
-                        sink.getDMLCommandType() == 
DMLCommandType.GROUP_COMMIT);
+                        sink.getDMLCommandType() == 
DMLCommandType.GROUP_COMMIT, isDeletePartialUpdate);
         List<Column> bindColumns = bindColumnsResult.first;
         int extraColumnsNum = bindColumnsResult.second;
 
@@ -274,7 +275,7 @@ public class BindSink implements AnalysisRuleFactory {
         }
 
         Map<String, NamedExpression> columnToOutput = getColumnToOutput(
-                ctx, table, isPartialUpdate, boundSink, child);
+                ctx, table, isPartialUpdate, isDeletePartialUpdate, boundSink, 
child);
         LogicalProject<?> fullOutputProject = getOutputProjectByCoercion(
                 table.getFullSchema(), child, columnToOutput);
         List<Column> columns = new ArrayList<>(table.getFullSchema().size());
@@ -366,7 +367,8 @@ public class BindSink implements AnalysisRuleFactory {
 
     private static Map<String, NamedExpression> getColumnToOutput(
             MatchingContext<? extends UnboundLogicalSink<Plan>> ctx,
-            TableIf table, boolean isPartialUpdate, LogicalTableSink<?> 
boundSink, LogicalPlan child) {
+            TableIf table, boolean isPartialUpdate, boolean 
isDeletePartialUpdate,
+            LogicalTableSink<?> boundSink, LogicalPlan child) {
         // we need to insert all the columns of the target table
         // although some columns are not mentions.
         // so we add a projects to supply the default value.
@@ -489,6 +491,18 @@ public class BindSink implements AnalysisRuleFactory {
         // if processed in upper for loop, will lead to not found slot error
         // It's the same reason for moving the processing of materialized 
columns down.
         for (Column column : generatedColumns) {
+            if (isDeletePartialUpdate) {
+                NamedExpression childOutput = columnToChildOutput.get(column);
+                if (childOutput == null) {
+                    continue;
+                }
+                Alias output = new Alias(TypeCoercionUtils.castIfNotSameType(
+                        childOutput, 
DataType.fromCatalogType(column.getType())), column.getName());
+                columnToOutput.put(column.getName(), output);
+                columnToReplaced.put(column.getName(), output.toSlot());
+                replaceMap.put(output.toSlot(), output.child());
+                continue;
+            }
             Map<String, String> currentSessionVars =
                     
ctx.connectContext.getSessionVariable().getAffectQueryResultInPlanVariables();
             try (AutoCloseSessionVariable autoClose = new 
AutoCloseSessionVariable(ctx.connectContext,
@@ -685,7 +699,7 @@ public class BindSink implements AnalysisRuleFactory {
         if (boundSink.getCols().size() != child.getOutput().size()) {
             throw new AnalysisException("insert into cols should be 
corresponding to the query output");
         }
-        Map<String, NamedExpression> columnToOutput = getColumnToOutput(ctx, 
table, false,
+        Map<String, NamedExpression> columnToOutput = getColumnToOutput(ctx, 
table, false, false,
                 boundSink, child);
         LogicalProject<?> fullOutputProject = 
getOutputProjectByCoercion(table.getFullSchema(), child, columnToOutput);
         return boundSink.withChildAndUpdateOutput(fullOutputProject);
@@ -761,7 +775,7 @@ public class BindSink implements AnalysisRuleFactory {
                     + "Expected " + boundSink.getCols().size() + " columns but 
got " + child.getOutput().size());
         }
 
-        Map<String, NamedExpression> columnToOutput = getColumnToOutput(ctx, 
table, false,
+        Map<String, NamedExpression> columnToOutput = getColumnToOutput(ctx, 
table, false, false,
                 boundSink, child);
 
         // For static partition columns, add constant expressions from 
PARTITION clause
@@ -886,7 +900,7 @@ public class BindSink implements AnalysisRuleFactory {
         if (boundSink.getCols().size() != child.getOutput().size()) {
             throw new AnalysisException("insert into cols should be 
corresponding to the query output");
         }
-        Map<String, NamedExpression> columnToOutput = getColumnToOutput(ctx, 
table, false,
+        Map<String, NamedExpression> columnToOutput = getColumnToOutput(ctx, 
table, false, false,
                 boundSink, child);
         LogicalProject<?> fullOutputProject = 
getOutputProjectByCoercion(table.getFullSchema(), child, columnToOutput);
         return boundSink.withChildAndUpdateOutput(fullOutputProject);
@@ -1106,7 +1120,7 @@ public class BindSink implements AnalysisRuleFactory {
 
     // bindTargetColumns means bind sink node's target columns' names to 
target table's columns
     private Pair<List<Column>, Integer> bindTargetColumns(OlapTable table, 
List<String> colsName,
-            boolean childHasSeqCol, boolean needExtraSeqCol, boolean 
isGroupCommit) {
+            boolean childHasSeqCol, boolean needExtraSeqCol, boolean 
isGroupCommit, boolean isDeletePartialUpdate) {
         // if the table set sequence column in stream load phase, the sequence 
map column is null, we query it.
         if (colsName.isEmpty()) {
             // ATTN: group commit without column list should return all base 
index column
@@ -1124,7 +1138,7 @@ public class BindSink implements AnalysisRuleFactory {
                         ++extraColumnsNum;
                         processedColsName.add(col.getName());
                     }
-                } else if (col.isGeneratedColumn()) {
+                } else if (col.isGeneratedColumn() && !isDeletePartialUpdate) {
                     ++extraColumnsNum;
                     processedColsName.add(col.getName());
                 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/DeleteFromUsingCommandTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/DeleteFromUsingCommandTest.java
index bf5f660e837..04531e50999 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/DeleteFromUsingCommandTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/DeleteFromUsingCommandTest.java
@@ -68,6 +68,60 @@ public class DeleteFromUsingCommandTest extends 
TestWithFeService implements Pla
                 + "properties(\n"
                 + "    \"replication_num\"=\"1\"\n"
                 + ")");
+        createTable("create table gen_value (\n"
+                + "    a int,\n"
+                + "    b int,\n"
+                + "    c int as (b + 1),\n"
+                + "    d int\n"
+                + ")\n"
+                + "unique key(a)\n"
+                + "distributed by hash(a) buckets 4\n"
+                + "properties(\n"
+                + "    \"replication_num\"=\"1\",\n"
+                + "    \"enable_unique_key_merge_on_write\" = \"true\",\n"
+                + "    \"enable_mow_light_delete\" = \"false\"\n"
+                + ")");
+        createTable("create table gen_key (\n"
+                + "    a int,\n"
+                + "    c int as (b + 1),\n"
+                + "    b int,\n"
+                + "    d int\n"
+                + ")\n"
+                + "unique key(a, c)\n"
+                + "distributed by hash(a) buckets 4\n"
+                + "properties(\n"
+                + "    \"replication_num\"=\"1\",\n"
+                + "    \"enable_unique_key_merge_on_write\" = \"true\",\n"
+                + "    \"enable_mow_light_delete\" = \"false\"\n"
+                + ")");
+        createTable("create table gen_value_required (\n"
+                + "    a int,\n"
+                + "    b int,\n"
+                + "    c int as (b + 1),\n"
+                + "    d int not null\n"
+                + ")\n"
+                + "unique key(a)\n"
+                + "distributed by hash(a) buckets 4\n"
+                + "properties(\n"
+                + "    \"replication_num\"=\"1\",\n"
+                + "    \"enable_unique_key_merge_on_write\" = \"true\",\n"
+                + "    \"enable_mow_light_delete\" = \"false\"\n"
+                + ")");
+        createTable("create table gen_variant (\n"
+                + "    id int not null,\n"
+                + "    create_time datetime not null,\n"
+                + "    order_no varchar(128) not null,\n"
+                + "    receive_address_detail varchar(1024) not null default 
\"{}\",\n"
+                + "    d int not null,\n"
+                + "    new_col variant as (receive_address_detail) null\n"
+                + ")\n"
+                + "unique key(id, create_time, order_no)\n"
+                + "distributed by hash(order_no) buckets 4\n"
+                + "properties(\n"
+                + "    \"replication_num\"=\"1\",\n"
+                + "    \"enable_unique_key_merge_on_write\" = \"true\",\n"
+                + "    \"enable_mow_light_delete\" = \"false\"\n"
+                + ")");
     }
 
     @Test
@@ -106,4 +160,39 @@ public class DeleteFromUsingCommandTest extends 
TestWithFeService implements Pla
                         )
                 );
     }
+
+    @Test
+    public void testDeletePartialUpdateWithGeneratedValueColumn() {
+        assertDeletePartialUpdateAnalyze("delete from gen_value t using src 
where t.a = src.k1");
+    }
+
+    @Test
+    public void testDeletePartialUpdateWithGeneratedKeyColumn() {
+        assertDeletePartialUpdateAnalyze("delete from gen_key t using src 
where t.a = src.k1");
+    }
+
+    @Test
+    public void testDeletePartialUpdateWithNotNullValueColumn() {
+        assertDeletePartialUpdateAnalyze("delete from gen_value_required t 
using src where t.a = src.k1");
+    }
+
+    @Test
+    public void testDeletePartialUpdateWithVariantGeneratedColumn() {
+        assertDeletePartialUpdateAnalyze("delete from gen_variant t using src 
where t.id = src.k1");
+    }
+
+    private void assertDeletePartialUpdateAnalyze(String sql) {
+        LogicalPlan parsed = new NereidsParser().parseSingle(sql);
+        Assertions.assertInstanceOf(DeleteFromUsingCommand.class, parsed);
+        DeleteFromUsingCommand command = ((DeleteFromUsingCommand) parsed);
+        LogicalPlan plan = command.completeQueryPlan(connectContext, 
command.getLogicalQuery());
+        PlanChecker.from(connectContext, plan)
+                .analyze(plan)
+                .rewrite()
+                .matches(
+                        logicalOlapTableSink(
+                                logicalProject()
+                        )
+                );
+    }
 }
diff --git 
a/regression-test/data/ddl_p0/test_create_table_generated_column/test_delete_generated_column.out
 
b/regression-test/data/ddl_p0/test_create_table_generated_column/test_delete_generated_column.out
index 2441607cf64..efaa9bc707a 100644
--- 
a/regression-test/data/ddl_p0/test_create_table_generated_column/test_delete_generated_column.out
+++ 
b/regression-test/data/ddl_p0/test_create_table_generated_column/test_delete_generated_column.out
@@ -27,3 +27,15 @@
 1      2       3
 10     2       12
 
+-- !delete_mow_without_light_delete_generated_column --
+2      20      21      200
+
+-- !delete_mow_without_light_delete_generated_key_column --
+2      21      20      200
+
+-- !delete_mow_without_light_delete_generated_column_with_not_null_value --
+2      20      21      200
+
+-- !delete_mow_without_light_delete_variant_generated_column --
+2      2026-06-08T11:00        order-2 {"city":"bj"}   200
+
diff --git 
a/regression-test/suites/ddl_p0/test_create_table_generated_column/test_delete_generated_column.groovy
 
b/regression-test/suites/ddl_p0/test_create_table_generated_column/test_delete_generated_column.groovy
index 8cbe723d929..c38250d6b32 100644
--- 
a/regression-test/suites/ddl_p0/test_create_table_generated_column/test_delete_generated_column.groovy
+++ 
b/regression-test/suites/ddl_p0/test_create_table_generated_column/test_delete_generated_column.groovy
@@ -70,5 +70,101 @@ suite("test_generated_column_delete") {
     ) delete from test_par_gen_col_unique t1 using cte where t1.c=cte.c and 
t1.b=cte.b"""
     qt_delete_query_cte_select "select * from test_par_gen_col_unique order by 
a,b,c"
 
+    multi_sql """
+        drop table if exists test_gen_col_mow_delete_partial_update;
+        create table test_gen_col_mow_delete_partial_update (
+            a int,
+            b int,
+            c int as (b + 1),
+            d int
+        )
+        unique key(a)
+        distributed by hash(a) buckets 1
+        properties (
+            "enable_unique_key_merge_on_write" = "true",
+            "enable_mow_light_delete" = "false",
+            "replication_num" = "1"
+        );
+        insert into test_gen_col_mow_delete_partial_update(a, b, d) values (1, 
10, 100), (2, 20, 200);
+    """
+    sql "delete from test_gen_col_mow_delete_partial_update where a = 1;"
+    qt_delete_mow_without_light_delete_generated_column """
+        select * from test_gen_col_mow_delete_partial_update order by a;
+    """
+
+    multi_sql """
+        drop table if exists test_gen_key_mow_delete_partial_update;
+        create table test_gen_key_mow_delete_partial_update (
+            a int,
+            c int as (b + 1),
+            b int,
+            d int
+        )
+        unique key(a, c)
+        distributed by hash(a) buckets 1
+        properties (
+            "enable_unique_key_merge_on_write" = "true",
+            "enable_mow_light_delete" = "false",
+            "replication_num" = "1"
+        );
+        insert into test_gen_key_mow_delete_partial_update(a, b, d) values (1, 
10, 100), (2, 20, 200);
+    """
+    sql "delete from test_gen_key_mow_delete_partial_update where a = 1;"
+    qt_delete_mow_without_light_delete_generated_key_column """
+        select * from test_gen_key_mow_delete_partial_update order by a;
+    """
+
+    multi_sql """
+        drop table if exists 
test_gen_col_mow_delete_partial_update_not_null_value;
+        create table test_gen_col_mow_delete_partial_update_not_null_value (
+            a int,
+            b int,
+            c int as (b + 1),
+            d int not null
+        )
+        unique key(a)
+        distributed by hash(a) buckets 1
+        properties (
+            "enable_unique_key_merge_on_write" = "true",
+            "enable_mow_light_delete" = "false",
+            "replication_num" = "1"
+        );
+        insert into test_gen_col_mow_delete_partial_update_not_null_value(a, 
b, d)
+            values (1, 10, 100), (2, 20, 200);
+    """
+    sql "delete from test_gen_col_mow_delete_partial_update_not_null_value 
where a = 1;"
+    qt_delete_mow_without_light_delete_generated_column_with_not_null_value """
+        select * from test_gen_col_mow_delete_partial_update_not_null_value 
order by a;
+    """
+
+    multi_sql """
+        drop table if exists test_gen_variant_mow_delete_partial_update;
+        create table test_gen_variant_mow_delete_partial_update (
+            id int not null,
+            create_time datetime not null,
+            order_no varchar(128) not null,
+            receive_address_detail varchar(1024) not null default "{}",
+            d int not null,
+            new_col variant as (receive_address_detail) null
+        )
+        unique key(id, create_time, order_no)
+        distributed by hash(order_no) buckets 1
+        properties (
+            "enable_unique_key_merge_on_write" = "true",
+            "enable_mow_light_delete" = "false",
+            "replication_num" = "1"
+        );
+        insert into test_gen_variant_mow_delete_partial_update(
+            id, create_time, order_no, receive_address_detail, d
+        ) values
+            (1, '2026-06-08 10:00:00', 'order-1', '{"city":"sh"}', 100),
+            (2, '2026-06-08 11:00:00', 'order-2', '{"city":"bj"}', 200);
+    """
+    sql "delete from test_gen_variant_mow_delete_partial_update where id = 1;"
+    qt_delete_mow_without_light_delete_variant_generated_column """
+        select id, create_time, order_no, receive_address_detail, d
+        from test_gen_variant_mow_delete_partial_update order by id;
+    """
+
 
 }


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

Reply via email to