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

luwei16 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 f7a0842f968 [fix](binlog) Fix missing DELETE events in row binlog 
(#68034)
f7a0842f968 is described below

commit f7a0842f968b144019955ec25410f77751eefa57
Author: Luwei <[email protected]>
AuthorDate: Thu Sep 17 16:28:33 2026 +0800

    [fix](binlog) Fix missing DELETE events in row binlog (#68034)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: None
    
    Problem Summary: Predicate deletes only persist delete predicates and
    therefore cannot produce row-level DELETE events. A merge-on-write
    unique table with light delete enabled incorrectly used that path even
    when ROW binlog was enabled, leaving downstream consumers with stale
    rows. Route these deletes through row-bearing DELETE FROM USING
    execution, and reject any ROW-binlog predicate delete that still reaches
    the legacy handler.
    
    ### Release note
    
    Ensure DELETE on merge-on-write unique tables with ROW binlog emits
    row-level DELETE events even when light delete is enabled. Predicate
    DELETE on ROW-binlog tables now reports an explicit unsupported error.
    
    ### Check List (For Author)
    
    - Test: Regression test, FE unit test, and manual test
        - Regression test: row_binlog_p0/test_row_binlog_mow_light_delete
    - Unit Test:
    org.apache.doris.nereids.trees.plans.commands.DeleteFromCommandTest
    - Manual test: verified ROW binlog DELETE events with historical values
    disabled and enabled
    - Behavior changed: Yes; MOW ROW-binlog deletes use row-bearing
    execution, while unsupported predicate deletes fail explicitly
    - Does this need documentation: No
---
 .../java/org/apache/doris/load/DeleteHandler.java  |   7 +-
 .../trees/plans/commands/DeleteFromCommand.java    |   5 +-
 .../test_row_binlog_mow_light_delete.out           |  16 ++++
 .../test_row_binlog_mow_light_delete.groovy        | 104 +++++++++++++++++++++
 4 files changed, 126 insertions(+), 6 deletions(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/load/DeleteHandler.java 
b/fe/fe-core/src/main/java/org/apache/doris/load/DeleteHandler.java
index 8f2aee7f9af..aed66e1508d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/load/DeleteHandler.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/load/DeleteHandler.java
@@ -20,7 +20,6 @@ package org.apache.doris.load;
 import org.apache.doris.analysis.Predicate;
 import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.Env;
-import org.apache.doris.catalog.KeysType;
 import org.apache.doris.catalog.OlapTable;
 import org.apache.doris.catalog.Partition;
 import org.apache.doris.common.AnalysisException;
@@ -112,10 +111,10 @@ public class DeleteHandler implements Writable {
         try {
             targetTbl.readLock();
             try {
-                if (targetTbl.needRowBinlog() && targetTbl.getKeysType() == 
KeysType.DUP_KEYS) {
+                if (targetTbl.needRowBinlog()) {
                     throw new AnalysisException(
-                            "DELETE with predicates is not supported on 
DUPLICATE KEY tables when binlog<row>"
-                                    + "is enabled. Please disable binlog<row> 
for this table or avoid DELETE.");
+                            "DELETE with predicates is not supported when 
binlog<row> is enabled because it cannot "
+                                    + "produce row-level DELETE events.");
                 }
                 if (targetTbl.getState() != OlapTable.OlapTableState.NORMAL) {
                     // table under alter operation can also do delete.
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 8b426283e6b..bda236a15bc 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
@@ -210,9 +210,10 @@ public class DeleteFromCommand extends Command implements 
ForwardWithSync, Expla
             }
         }
 
-        // if table's enable_mow_light_delete is false, use 
`DeleteFromUsingCommand`
+        // Row binlog needs row-bearing deletes to emit DELETE events. 
Predicate deletes only write
+        // delete predicates, so use `DeleteFromUsingCommand` even when MOW 
light delete is enabled.
         if (olapTable.getKeysType() == KeysType.UNIQUE_KEYS && 
olapTable.getEnableUniqueKeyMergeOnWrite()
-                && !olapTable.getEnableMowLightDelete()) {
+                && (!olapTable.getEnableMowLightDelete() || 
olapTable.needRowBinlog())) {
             new DeleteFromUsingCommand(nameParts, tableAlias, isTempPart, 
partitions, logicalQuery,
                     Optional.empty(), false).run(ctx, executor);
             return;
diff --git 
a/regression-test/data/row_binlog_p0/test_row_binlog_mow_light_delete.out 
b/regression-test/data/row_binlog_p0/test_row_binlog_mow_light_delete.out
new file mode 100644
index 00000000000..5b12cd39019
--- /dev/null
+++ b/regression-test/data/row_binlog_p0/test_row_binlog_mow_light_delete.out
@@ -0,0 +1,16 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !without_history_base --
+2      two
+
+-- !without_history_binlog --
+0      1       one
+0      2       two
+2      1       \N
+
+-- !with_history_base --
+2      two
+
+-- !with_history_binlog --
+0      1       one     \N
+0      2       two     \N
+2      1       one     one
diff --git 
a/regression-test/suites/row_binlog_p0/test_row_binlog_mow_light_delete.groovy 
b/regression-test/suites/row_binlog_p0/test_row_binlog_mow_light_delete.groovy
new file mode 100644
index 00000000000..096e0fdee65
--- /dev/null
+++ 
b/regression-test/suites/row_binlog_p0/test_row_binlog_mow_light_delete.groovy
@@ -0,0 +1,104 @@
+// 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_row_binlog_mow_light_delete", "nonConcurrent") {
+    sql "DROP TABLE IF EXISTS test_row_binlog_mow_light_delete_without_history 
FORCE"
+    sql "DROP TABLE IF EXISTS test_row_binlog_mow_light_delete_with_history 
FORCE"
+    sql "DROP TABLE IF EXISTS test_row_binlog_dup_delete FORCE"
+
+    sql """
+        CREATE TABLE test_row_binlog_mow_light_delete_without_history (
+            k INT,
+            v STRING
+        )
+        UNIQUE KEY(k)
+        DISTRIBUTED BY HASH(k) BUCKETS 1
+        PROPERTIES (
+            "replication_num" = "1",
+            "enable_unique_key_merge_on_write" = "true",
+            "enable_mow_light_delete" = "true",
+            "binlog.enable" = "true",
+            "binlog.format" = "ROW",
+            "binlog.need_historical_value" = "false"
+        )
+    """
+
+    sql "INSERT INTO test_row_binlog_mow_light_delete_without_history VALUES 
(1, 'one'), (2, 'two')"
+    sql "DELETE FROM test_row_binlog_mow_light_delete_without_history WHERE k 
= 1"
+
+    order_qt_without_history_base """
+        SELECT k, v
+        FROM test_row_binlog_mow_light_delete_without_history
+    """
+
+    qt_without_history_binlog """
+        SELECT __DORIS_BINLOG_OP__ AS op, k, v
+        FROM binlog("table" = 
"test_row_binlog_mow_light_delete_without_history")
+        ORDER BY __DORIS_BINLOG_TSO__, __DORIS_BINLOG_LSN__
+    """
+
+    sql """
+        CREATE TABLE test_row_binlog_mow_light_delete_with_history (
+            k INT,
+            v STRING
+        )
+        UNIQUE KEY(k)
+        DISTRIBUTED BY HASH(k) BUCKETS 1
+        PROPERTIES (
+            "replication_num" = "1",
+            "enable_unique_key_merge_on_write" = "true",
+            "enable_mow_light_delete" = "true",
+            "binlog.enable" = "true",
+            "binlog.format" = "ROW",
+            "binlog.need_historical_value" = "true"
+        )
+    """
+
+    sql "INSERT INTO test_row_binlog_mow_light_delete_with_history VALUES (1, 
'one'), (2, 'two')"
+    sql "DELETE FROM test_row_binlog_mow_light_delete_with_history WHERE k = 1"
+
+    order_qt_with_history_base """
+        SELECT k, v
+        FROM test_row_binlog_mow_light_delete_with_history
+    """
+
+    qt_with_history_binlog """
+        SELECT __DORIS_BINLOG_OP__ AS op, k, v, __BEFORE__v__
+        FROM binlog("table" = "test_row_binlog_mow_light_delete_with_history")
+        ORDER BY __DORIS_BINLOG_TSO__, __DORIS_BINLOG_LSN__
+    """
+
+    sql """
+        CREATE TABLE test_row_binlog_dup_delete (
+            k INT,
+            v STRING
+        )
+        DUPLICATE KEY(k)
+        DISTRIBUTED BY HASH(k) BUCKETS 1
+        PROPERTIES (
+            "replication_num" = "1",
+            "binlog.enable" = "true",
+            "binlog.format" = "ROW"
+        )
+    """
+
+    sql "INSERT INTO test_row_binlog_dup_delete VALUES (1, 'one')"
+    test {
+        sql "DELETE FROM test_row_binlog_dup_delete WHERE k = 1"
+        exception "DELETE with predicates is not supported when binlog<row> is 
enabled"
+    }
+}


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

Reply via email to