This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.0 by this push:
new 8989e23cb9c branch-4.0: [Fix](nereids) Fix session variable not take
effect for partial update in multi-statement batch #60803 (#60862)
8989e23cb9c is described below
commit 8989e23cb9c373db1711d2bab269f51423d5267e
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sat Feb 28 11:19:55 2026 +0800
branch-4.0: [Fix](nereids) Fix session variable not take effect for partial
update in multi-statement batch #60803 (#60862)
Cherry-picked from #60803
Co-authored-by: bobhan1 <[email protected]>
---
.../doris/nereids/analyzer/UnboundTableSink.java | 6 +-
.../trees/plans/commands/insert/InsertUtils.java | 13 ++
.../test_partial_update_multi_stmt.out | 25 +++
.../test_partial_update_multi_stmt.groovy | 168 +++++++++++++++++++++
4 files changed, 211 insertions(+), 1 deletion(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundTableSink.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundTableSink.java
index b9290c796c2..ed04ad57372 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundTableSink.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundTableSink.java
@@ -50,7 +50,7 @@ public class UnboundTableSink<CHILD_TYPE extends Plan>
extends UnboundLogicalSin
private final boolean temporaryPartition;
private final List<String> partitions;
private boolean isPartialUpdate;
- private final TPartialUpdateNewRowPolicy partialUpdateNewKeyPolicy;
+ private TPartialUpdateNewRowPolicy partialUpdateNewKeyPolicy;
private final DMLCommandType dmlCommandType;
private final boolean autoDetectPartition;
@@ -126,6 +126,10 @@ public class UnboundTableSink<CHILD_TYPE extends Plan>
extends UnboundLogicalSin
this.isPartialUpdate = isPartialUpdate;
}
+ public void setPartialUpdateNewKeyPolicy(TPartialUpdateNewRowPolicy
policy) {
+ this.partialUpdateNewKeyPolicy = policy;
+ }
+
@Override
public Plan withChildren(List<Plan> children) {
Preconditions.checkArgument(children.size() == 1,
"UnboundOlapTableSink only accepts one child");
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
index 1cfef22a10e..b03b248b651 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
@@ -293,6 +293,19 @@ public class InsertUtils {
// For JDBC External Table, we always allow certain columns to be
missing during insertion
// Specific check for non-nullable columns only if insertion is
direct VALUES or SELECT constants
}
+ // Re-read partial update settings from session variable to handle
multi-statement
+ // batches where SET and INSERT are parsed together before execution.
+ // Only apply to original INSERT statements, not DELETE/UPDATE
converted to INSERT.
+ if (unboundLogicalSink instanceof UnboundTableSink
+ && unboundLogicalSink.getDMLCommandType() ==
DMLCommandType.INSERT) {
+ ConnectContext ctx = ConnectContext.get();
+ if (ctx != null) {
+ ((UnboundTableSink<? extends Plan>) unboundLogicalSink)
+
.setPartialUpdate(ctx.getSessionVariable().isEnableUniqueKeyPartialUpdate());
+ ((UnboundTableSink<? extends Plan>) unboundLogicalSink)
+
.setPartialUpdateNewKeyPolicy(ctx.getSessionVariable().getPartialUpdateNewRowPolicy());
+ }
+ }
if (table instanceof OlapTable && ((OlapTable) table).getKeysType() ==
KeysType.UNIQUE_KEYS) {
if (unboundLogicalSink instanceof UnboundTableSink
&& ((UnboundTableSink<? extends Plan>)
unboundLogicalSink).isPartialUpdate()) {
diff --git
a/regression-test/data/unique_with_mow_p0/partial_update/test_partial_update_multi_stmt.out
b/regression-test/data/unique_with_mow_p0/partial_update/test_partial_update_multi_stmt.out
new file mode 100644
index 00000000000..546b0cf2c4a
--- /dev/null
+++
b/regression-test/data/unique_with_mow_p0/partial_update/test_partial_update_multi_stmt.out
@@ -0,0 +1,25 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !base1 --
+1 1 1 1
+2 2 2 2
+
+-- !base2 --
+1 1 1 1
+2 2 2 2
+
+-- !multi_stmt_values --
+1 100 1 1
+2 200 2 2
+
+-- !multi_stmt_select --
+1 501 1 1
+2 502 2 2
+
+-- !separate_stmt_values --
+1 100 1 1
+2 200 2 2
+
+-- !separate_stmt_select --
+1 501 1 1
+2 502 2 2
+
diff --git
a/regression-test/suites/unique_with_mow_p0/partial_update/test_partial_update_multi_stmt.groovy
b/regression-test/suites/unique_with_mow_p0/partial_update/test_partial_update_multi_stmt.groovy
new file mode 100644
index 00000000000..7dbc33b5c43
--- /dev/null
+++
b/regression-test/suites/unique_with_mow_p0/partial_update/test_partial_update_multi_stmt.groovy
@@ -0,0 +1,168 @@
+// 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.
+
+import java.sql.Connection
+import java.sql.DriverManager
+import java.sql.Statement
+
+// Test that `set enable_unique_key_partial_update=true; insert into ...`
works correctly
+// when sent as a single multi-statement query (single COM_QUERY).
+// This is a regression test for the issue where session variables set via SET
in a
+// multi-statement batch might not be visible during the parse phase of
subsequent
+// statements in the same batch, because all statements are parsed together
before
+// any of them are executed.
+suite("test_partial_update_multi_stmt", "p0") {
+
+ String db = context.config.getDbNameByFile(context.file)
+ sql "select 1;" // to create database
+
+ String jdbcUrl = context.config.jdbcUrl
+ String urlWithoutSchema = jdbcUrl.substring(jdbcUrl.indexOf("://") + 3)
+ def sql_ip = urlWithoutSchema.substring(0, urlWithoutSchema.indexOf(":"))
+ def sql_port
+ if (urlWithoutSchema.indexOf("/") >= 0) {
+ sql_port = urlWithoutSchema.substring(urlWithoutSchema.indexOf(":") +
1, urlWithoutSchema.indexOf("/"))
+ } else {
+ sql_port = urlWithoutSchema.substring(urlWithoutSchema.indexOf(":") +
1)
+ }
+
+ def tableName1 = "test_partial_update_multi_stmt1"
+ def tableName2 = "test_partial_update_multi_stmt2"
+ def tableName3 = "test_partial_update_multi_stmt3"
+ def tableName4 = "test_partial_update_multi_stmt4"
+
+ def createTable = { tblName ->
+ sql """ DROP TABLE IF EXISTS ${tblName} """
+ sql """
+ CREATE TABLE ${tblName} (
+ `k` int(11) NOT NULL COMMENT "key",
+ `v1` int(11) NULL DEFAULT "10" COMMENT "value1",
+ `v2` int(11) NULL DEFAULT "20" COMMENT "value2",
+ `v3` int(11) NULL DEFAULT "30" COMMENT "value3"
+ ) UNIQUE KEY(`k`)
+ DISTRIBUTED BY HASH(`k`) BUCKETS 1
+ PROPERTIES(
+ "replication_num" = "1",
+ "enable_unique_key_merge_on_write" = "true"
+ ); """
+ sql "insert into ${tblName} values(1, 1, 1, 1), (2, 2, 2, 2);"
+ sql "sync;"
+ }
+
+ // ============================================================
+ // Setup: create all tables with initial data (1,1,1,1),(2,2,2,2)
+ // ============================================================
+ connect(context.config.jdbcUser, context.config.jdbcPassword,
context.config.jdbcUrl) {
+ sql "use ${db};"
+ sql "sync;"
+ createTable(tableName1)
+ createTable(tableName2)
+ createTable(tableName3)
+ createTable(tableName4)
+ qt_base1 "select * from ${tableName1} order by k;"
+ qt_base2 "select * from ${tableName2} order by k;"
+ }
+
+ // ============================================================
+ // Test 1 & 2: multi-statement with allowMultiQueries=true
+ // SET + INSERT sent as a single COM_QUERY
+ // ============================================================
+ def multiStmtUrl =
"jdbc:mysql://${sql_ip}:${sql_port}/${db}?useLocalSessionState=false&allowMultiQueries=true"
+ logger.info("multi-statement JDBC URL: ${multiStmtUrl}")
+
+ try (Connection conn = DriverManager.getConnection(multiStmtUrl,
+ context.config.jdbcUser, context.config.jdbcPassword)) {
+ Statement stmt = conn.createStatement()
+
+ // Test 1: SET + INSERT VALUES as single COM_QUERY
+ logger.info("Test 1: allowMultiQueries=true, SET + INSERT VALUES")
+ stmt.execute(
+ "set enable_unique_key_partial_update=true;" +
+ "set enable_insert_strict=false;" +
+ "insert into ${tableName1}(k, v1) values(1, 100), (2, 200);"
+ )
+ stmt.execute("set enable_unique_key_partial_update=false;")
+
+ // Test 2: SET + INSERT SELECT as single COM_QUERY
+ logger.info("Test 2: allowMultiQueries=true, SET + INSERT SELECT")
+ stmt.execute(
+ "set enable_unique_key_partial_update=true;" +
+ "set enable_insert_strict=false;" +
+ "insert into ${tableName2}(k, v1) select k, v1 + 500 from
${tableName2};"
+ )
+ stmt.execute("set enable_unique_key_partial_update=false;")
+
+ stmt.close()
+ }
+
+ // ============================================================
+ // Test 3 & 4: without allowMultiQueries (statements sent separately)
+ // SET and INSERT are separate COM_QUERY packets
+ // ============================================================
+ def singleStmtUrl =
"jdbc:mysql://${sql_ip}:${sql_port}/${db}?useLocalSessionState=false"
+ logger.info("single-statement JDBC URL: ${singleStmtUrl}")
+
+ try (Connection conn = DriverManager.getConnection(singleStmtUrl,
+ context.config.jdbcUser, context.config.jdbcPassword)) {
+ Statement stmt = conn.createStatement()
+
+ // Test 3: SET then INSERT VALUES as separate COM_QUERY
+ logger.info("Test 3: separate statements, SET + INSERT VALUES")
+ stmt.execute("set enable_unique_key_partial_update=true;")
+ stmt.execute("set enable_insert_strict=false;")
+ stmt.execute("insert into ${tableName3}(k, v1) values(1, 100), (2,
200);")
+ stmt.execute("set enable_unique_key_partial_update=false;")
+
+ // Test 4: SET then INSERT SELECT as separate COM_QUERY
+ logger.info("Test 4: separate statements, SET + INSERT SELECT")
+ stmt.execute("set enable_unique_key_partial_update=true;")
+ stmt.execute("set enable_insert_strict=false;")
+ stmt.execute("insert into ${tableName4}(k, v1) select k, v1 + 500 from
${tableName4};")
+ stmt.execute("set enable_unique_key_partial_update=false;")
+
+ stmt.close()
+ }
+
+ // ============================================================
+ // Verify all results
+ // ============================================================
+ connect(context.config.jdbcUser, context.config.jdbcPassword,
context.config.jdbcUrl) {
+ sql "use ${db};"
+ sql "sync;"
+
+ // Test 1: allowMultiQueries + INSERT VALUES
+ // Expected (partial update): v2,v3 keep old values (1,1) and (2,2)
+ qt_multi_stmt_values "select * from ${tableName1} order by k;"
+
+ // Test 2: allowMultiQueries + INSERT SELECT
+ // Expected (partial update): v2,v3 keep old values
+ qt_multi_stmt_select "select * from ${tableName2} order by k;"
+
+ // Test 3: separate statements + INSERT VALUES
+ // Expected (partial update): v2,v3 keep old values (1,1) and (2,2)
+ qt_separate_stmt_values "select * from ${tableName3} order by k;"
+
+ // Test 4: separate statements + INSERT SELECT
+ // Expected (partial update): v2,v3 keep old values
+ qt_separate_stmt_select "select * from ${tableName4} order by k;"
+
+ sql """ DROP TABLE IF EXISTS ${tableName1} """
+ sql """ DROP TABLE IF EXISTS ${tableName2} """
+ sql """ DROP TABLE IF EXISTS ${tableName3} """
+ sql """ DROP TABLE IF EXISTS ${tableName4} """
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]