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]