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

bobhan1 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 895cbec74a6 [fix](update) Include generated column dependencies in 
updates (#67348)
895cbec74a6 is described below

commit 895cbec74a68d050d791a117695969b743ee7552
Author: bobhan1 <[email protected]>
AuthorDate: Wed Oct 14 15:13:41 2026 +0800

    [fix](update) Include generated column dependencies in updates (#67348)
    
    ### What problem does this PR solve?
    
    Issue Number: N/A
    
    Related PR: N/A
    
    Problem Summary: UPDATE statements on merge-on-write unique tables can
    be planned as partial updates when only a small number of columns are
    assigned. The partial-update projection previously included only key and
    assigned columns, while BindSink recomputes generated columns and
    requires their ordinary source columns. Updating an unrelated column
    therefore failed during analysis when a generated-column dependency was
    omitted.
    
    This change includes unassigned ordinary columns referenced by generated
    columns in the UPDATE partial-update projection. An unassigned
    dependency with an ON UPDATE value is deliberately left out: BindSink
    already supplies its ON UPDATE expression and then uses that new value
    to recompute the generated column. Explicit assignments remain in the
    projection.
    
    ### Original reproduction
    
    ```sql
    CREATE TABLE t (
        a INT,
        b INT,
        c INT AS (b + 1),
        d INT
    )
    UNIQUE KEY(a)
    DISTRIBUTED BY HASH(a) BUCKETS 1
    PROPERTIES (
        "replication_num" = "1",
        "enable_unique_key_merge_on_write" = "true"
    );
    
    INSERT INTO t(a, b, d) VALUES (1, 10, 100);
    UPDATE t SET d = 999 WHERE a = 1;
    ```
    
    Before this change, the UPDATE failed during FE analysis:
    
    ```text
    cannot find column from target table [b]
    ```
    
    The row remained `1 | 10 | 11 | 100`. After this change, the UPDATE
    succeeds and the row becomes `1 | 10 | 11 | 999`.
    
    ### ON UPDATE dependency found during review
    
    ```sql
    CREATE TABLE t_on_update (
        id INT,
        ts DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        g INT AS (YEAR(ts)),
        v INT
    )
    UNIQUE KEY(id)
    DISTRIBUTED BY HASH(id) BUCKETS 1
    PROPERTIES (
        "replication_num" = "1",
        "enable_unique_key_merge_on_write" = "true"
    );
    
    INSERT INTO t_on_update(id, ts, v) VALUES (1, '2000-01-01 00:00:00', 100);
    UPDATE t_on_update SET v = 999 WHERE id = 1;
    ```
    
    Projecting the stored value of `ts` would make BindSink treat it as
    explicitly supplied and bypass `ON UPDATE CURRENT_TIMESTAMP`. The final
    implementation omits that unassigned dependency, so `ts` advances, `g =
    YEAR(ts)` remains consistent with the new timestamp, and `v` becomes
    `999`.
    
    Generated columns can reference generated columns defined before them.
    This PR keeps those generated values out of the partial projection and
    supplies their ordinary leaf dependencies; the existing BindSink path
    recomputes the generated-column chain in schema order.
    
    ### Release note
    
    Fix UPDATE failures on merge-on-write unique tables with generated
    columns while preserving automatic ON UPDATE values used by generated
    columns.
    
    ### Check List (For Author)
    
    - Test
        - [x] Regression test
        - [x] Unit Test
        - [ ] 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
    
        Commands run:
    
        - `./build.sh --fe -j100`
        - `./build.sh --be -j100`
    - `./run-fe-ut.sh --run
    org.apache.doris.nereids.trees.plans.UpdateCommandTest`
    - `./run-regression-test.sh --run -d
    ddl_p0/test_create_table_generated_column -s
    test_update_generated_column_dependency`
    - `./run-regression-test.sh --run -d
    ddl_p0/test_create_table_generated_column -s
    test_partial_update_generated_column`
    
    - Behavior changed:
        - [ ] No.
    - [x] Yes. UPDATE partial projections include ordinary generated-column
    dependencies while unassigned ON UPDATE dependencies continue through
    BindSink's automatic-value path.
    
    - Does this need documentation?
        - [x] No.
        - [ ] Yes.
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label
---
 .../trees/plans/commands/UpdateCommand.java        |  7 ++-
 .../nereids/trees/plans/UpdateCommandTest.java     | 33 ++++++++++++
 .../test_update_generated_column_dependency.out    |  7 +++
 .../test_update_generated_column_dependency.groovy | 58 ++++++++++++++++++++++
 4 files changed, 104 insertions(+), 1 deletion(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/UpdateCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/UpdateCommand.java
index aa6853cea9f..e030d010234 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/UpdateCommand.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/UpdateCommand.java
@@ -52,6 +52,7 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import org.apache.commons.collections4.CollectionUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -197,7 +198,11 @@ public class UpdateCommand extends Command implements 
ForwardWithSync, Explainab
                         break;
                     }
                 }
-                if (column.isKey() || existInExpr) {
+                boolean isGeneratedColumnDependency = 
column.getGeneratedColumnInfo() == null
+                        && 
CollectionUtils.isNotEmpty(column.getGeneratedColumnsThatReferToThis());
+                boolean shouldReadGeneratedColumnDependency = 
isGeneratedColumnDependency
+                        && !column.hasOnUpdateDefaultValue();
+                if (column.isKey() || existInExpr || 
shouldReadGeneratedColumnDependency) {
                     partialUpdateSelectItems.add(expr instanceof UnboundSlot
                             ? ((NamedExpression) expr)
                             : new UnboundAlias(expr));
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/UpdateCommandTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/UpdateCommandTest.java
index 4151115a2c8..2ed79848fa7 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/UpdateCommandTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/UpdateCommandTest.java
@@ -66,6 +66,18 @@ public class UpdateCommandTest extends TestWithFeService 
implements PlanPatternM
                 + "properties(\n"
                 + "    \"replication_num\"=\"1\"\n"
                 + ")");
+        createTable("create table generated_column_table (\n"
+                + "    k1 int,\n"
+                + "    v1 int,\n"
+                + "    v2 int as (v1 + 1),\n"
+                + "    v3 int\n"
+                + ")\n"
+                + "unique key(k1)\n"
+                + "distributed by hash(k1) buckets 4\n"
+                + "properties(\n"
+                + "    \"replication_num\"=\"1\",\n"
+                + "    \"enable_unique_key_merge_on_write\"=\"true\"\n"
+                + ")");
     }
 
     @Test
@@ -117,4 +129,25 @@ public class UpdateCommandTest extends TestWithFeService 
implements PlanPatternM
                         )
                 );
     }
+
+    @Test
+    public void testPartialUpdateWithGeneratedColumnDependency() {
+        String sql = "update generated_column_table set v3 = 999 where k1 = 1";
+        LogicalPlan parsed = new NereidsParser().parseSingle(sql);
+        Assertions.assertInstanceOf(UpdateCommand.class, parsed);
+        UpdateCommand command = ((UpdateCommand) parsed);
+        LogicalPlan plan = command.completeQueryPlan(connectContext, 
command.getLogicalQuery());
+        PlanChecker.from(connectContext, plan)
+                .analyze(plan)
+                .rewrite()
+                .matches(
+                        logicalOlapTableSink(
+                                logicalProject(
+                                        logicalFilter(
+                                                logicalOlapScan()
+                                        )
+                                )
+                        )
+                );
+    }
 }
diff --git 
a/regression-test/data/ddl_p0/test_create_table_generated_column/test_update_generated_column_dependency.out
 
b/regression-test/data/ddl_p0/test_create_table_generated_column/test_update_generated_column_dependency.out
new file mode 100644
index 00000000000..4fa0df261f2
--- /dev/null
+++ 
b/regression-test/data/ddl_p0/test_create_table_generated_column/test_update_generated_column_dependency.out
@@ -0,0 +1,7 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !update_unrelated_column --
+1      10      11      999
+
+-- !update_on_update_dependency --
+1      updated matched 999
+
diff --git 
a/regression-test/suites/ddl_p0/test_create_table_generated_column/test_update_generated_column_dependency.groovy
 
b/regression-test/suites/ddl_p0/test_create_table_generated_column/test_update_generated_column_dependency.groovy
new file mode 100644
index 00000000000..b99df3a5b15
--- /dev/null
+++ 
b/regression-test/suites/ddl_p0/test_create_table_generated_column/test_update_generated_column_dependency.groovy
@@ -0,0 +1,58 @@
+// 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_generated_column_dependency") {
+    sql "drop table if exists test_update_generated_column_dependency"
+    sql """create table test_update_generated_column_dependency (
+        a int,
+        b int,
+        c int generated always as (b + 1),
+        d int
+    )
+    unique key(a)
+    distributed by hash(a) buckets 1
+    properties(
+        "replication_num" = "1",
+        "enable_unique_key_merge_on_write" = "true"
+    );"""
+
+    sql "insert into test_update_generated_column_dependency(a, b, d) 
values(1, 10, 100)"
+    sql "update test_update_generated_column_dependency set d = 999 where a = 
1"
+    order_qt_update_unrelated_column "select a, b, c, d from 
test_update_generated_column_dependency"
+
+    sql "drop table if exists 
test_update_generated_column_on_update_dependency"
+    sql """create table test_update_generated_column_on_update_dependency (
+        id int,
+        ts datetime default current_timestamp on update current_timestamp,
+        g int generated always as (year(ts)),
+        v int
+    )
+    unique key(id)
+    distributed by hash(id) buckets 1
+    properties(
+        "replication_num" = "1",
+        "enable_unique_key_merge_on_write" = "true"
+    );"""
+
+    sql """insert into test_update_generated_column_on_update_dependency(id, 
ts, v)
+        values(1, '2000-01-01 00:00:00', 100)"""
+    sql "update test_update_generated_column_on_update_dependency set v = 999 
where id = 1"
+    order_qt_update_on_update_dependency """select id,
+        if(ts > '2000-01-01 00:00:00', 'updated', 'stale'),
+        if(g = year(ts), 'matched', 'mismatched'), v
+        from test_update_generated_column_on_update_dependency"""
+}


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

Reply via email to