summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrei <andrei.elkin@mariadb.com>2022-05-03 22:38:20 +0300
committerAndrei <andrei.elkin@mariadb.com>2022-05-06 22:16:42 +0300
commita5dc12eefd4bea1c3f77d02c55d0d459b4ae0566 (patch)
treed4ed5d65587dd66731b2858048db29fa2949d15e
parent141ab971d8d31968ac7104e71801c6ec75638af3 (diff)
downloadmariadb-git-a5dc12eefd4bea1c3f77d02c55d0d459b4ae0566.tar.gz
MDEV-28310 Missing binlog data for INSERT .. ON DUPLICATE KEY UPDATE
MDEV-21810 MBR: Unexpected "Unsafe statement" warning for unsafe IODKU MDEV-17614 fixes to replication unsafety for INSERT ON DUP KEY UPDATE on two or more unique key table left a flaw. The fixes checked the safety condition per each inserted record with the idea to catch a user-created value to an autoincrement column and when that succeeds the autoincrement column would become the source of unsafety too. It was not expected that after a duplicate error the next record's write_set may become different and the unsafe decision for that specific record will be computed to screw the Query's binlogging state and when @@binlog_format is MIXED nothing gets bin-logged. This case has been already fixed in 10.5.2 by 91ab42a823 that relocated/optimized THD::decide_logging_format_low() out of the record insert loop. The safety decision is computed once and at the right time. Pertinent parts of the commit are cherry-picked. Also a spurious warning about unsafety is removed when MIXED @@binlog_format; original MDEV-17614 test result corrected. The original test of MDEV-17614 is extended and made more readable.
-rw-r--r--mysql-test/suite/rpl/r/rpl_iodku,stmt.rdiff27
-rw-r--r--mysql-test/suite/rpl/r/rpl_iodku.result32
-rw-r--r--mysql-test/suite/rpl/r/rpl_mdev_17614.result72
-rw-r--r--mysql-test/suite/rpl/r/rpl_unsafe_statements.result5
-rw-r--r--mysql-test/suite/rpl/t/rpl_iodku.test50
-rw-r--r--mysql-test/suite/rpl/t/rpl_mdev_17614.test73
-rw-r--r--mysql-test/suite/rpl/t/rpl_unsafe_statements.test2
-rw-r--r--sql/sql_class.cc91
-rw-r--r--sql/sql_class.h18
-rw-r--r--sql/sql_insert.cc6
10 files changed, 322 insertions, 54 deletions
diff --git a/mysql-test/suite/rpl/r/rpl_iodku,stmt.rdiff b/mysql-test/suite/rpl/r/rpl_iodku,stmt.rdiff
new file mode 100644
index 00000000000..e31f1e5d991
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_iodku,stmt.rdiff
@@ -0,0 +1,27 @@
+--- r/rpl_iodku.result 2022-05-04 18:51:24.956414404 +0300
++++ r/rpl_iodku,stmt.reject 2022-05-04 18:51:49.520106231 +0300
+@@ -1,10 +1,15 @@
+ include/master-slave.inc
+ [connection master]
++call mtr.add_suppression("Unsafe statement written to the binary log using statement");
+ CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, a INT, b INT, c INT,
+ UNIQUE (a), UNIQUE (b)) ENGINE=innodb;
+ INSERT INTO t1 (`a`,`c`) VALUES (1,1), (2,1) ON DUPLICATE KEY UPDATE c = 1;
++Warnings:
++Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+ # UNSAFE
+ INSERT INTO t1 (`a`,`c`) VALUES (3, 1),(2,1), (1,1) ON DUPLICATE KEY UPDATE c = a * 10 + VALUES(c);
++Warnings:
++Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+ SELECT * from t1;
+ id a b c
+ 1 1 NULL 11
+@@ -17,6 +22,8 @@
+ INSERT INTO t1 VALUES (1,10,1);
+ # eligable for the statement format run unsafe warning
+ INSERT INTO t1 VALUES (2,20,2) ON DUPLICATE KEY UPDATE c = 100;
++Warnings:
++Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+ # not eligable: no warning in the statement format run
+ INSERT INTO t1 (`a`,`c`) VALUES (3, 1) ON DUPLICATE KEY UPDATE c = 99;
+ SELECT * from t1;
diff --git a/mysql-test/suite/rpl/r/rpl_iodku.result b/mysql-test/suite/rpl/r/rpl_iodku.result
new file mode 100644
index 00000000000..55348da1439
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_iodku.result
@@ -0,0 +1,32 @@
+include/master-slave.inc
+[connection master]
+CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, a INT, b INT, c INT,
+UNIQUE (a), UNIQUE (b)) ENGINE=innodb;
+INSERT INTO t1 (`a`,`c`) VALUES (1,1), (2,1) ON DUPLICATE KEY UPDATE c = 1;
+# UNSAFE
+INSERT INTO t1 (`a`,`c`) VALUES (3, 1),(2,1), (1,1) ON DUPLICATE KEY UPDATE c = a * 10 + VALUES(c);
+SELECT * from t1;
+id a b c
+1 1 NULL 11
+2 2 NULL 21
+3 3 NULL 1
+connection slave;
+include/diff_tables.inc [master:t1,slave:t1]
+connection master;
+CREATE OR REPLACE TABLE t1 (a INT, b INT, c INT, UNIQUE (a), UNIQUE (b)) ENGINE=innodb;
+INSERT INTO t1 VALUES (1,10,1);
+# eligable for the statement format run unsafe warning
+INSERT INTO t1 VALUES (2,20,2) ON DUPLICATE KEY UPDATE c = 100;
+# not eligable: no warning in the statement format run
+INSERT INTO t1 (`a`,`c`) VALUES (3, 1) ON DUPLICATE KEY UPDATE c = 99;
+SELECT * from t1;
+a b c
+1 10 1
+2 20 2
+3 NULL 1
+connection slave;
+include/diff_tables.inc [master:t1,slave:t1]
+connection master;
+DROP TABLE t1;
+connection slave;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_mdev_17614.result b/mysql-test/suite/rpl/r/rpl_mdev_17614.result
index 39057334926..ba077111522 100644
--- a/mysql-test/suite/rpl/r/rpl_mdev_17614.result
+++ b/mysql-test/suite/rpl/r/rpl_mdev_17614.result
@@ -1,5 +1,6 @@
include/master-slave.inc
[connection master]
+# Case 1: UNSAFE
call mtr.add_suppression("Unsafe statement written to the binary log using statement format");
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY , b INT,
UNIQUE(b), c int) engine=innodb;
@@ -37,6 +38,7 @@ drop table t1;
connection slave;
start slave;
include/wait_for_slave_to_start.inc
+# Case 2: UNSAFE
connection master;
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
UNIQUE(b), c int) engine=innodb;
@@ -45,8 +47,12 @@ connection master;
INSERT INTO t1 VALUES (default, 1, 1);
BEGIN;
INSERT INTO t1 VALUES (default, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
connection master1;
INSERT INTO t1 VALUES(default, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
connection master;
COMMIT;
SELECT * FROM t1;
@@ -62,6 +68,7 @@ a b c
connection master;
drop table t1;
connection slave;
+# Case 3A: UNSAFE
connection master;
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
UNIQUE(b), c int, d int ) engine=innodb;
@@ -93,6 +100,67 @@ a b c d
connection master;
drop table t1;
connection slave;
+# Case 3B: UNSAFE - all column specified.
+connection master;
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
+UNIQUE(b), c int, d int ) engine=innodb;
+connection slave;
+connection master;
+INSERT INTO t1 VALUES (1, 1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, NULL, 2, 2) ON DUPLICATE KEY UPDATE c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+connection master1;
+INSERT INTO t1 VALUES(3, NULL, 2, 3) ON DUPLICATE KEY UPDATE c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
+connection master;
+COMMIT;
+SELECT * FROM t1;
+a b c d
+1 1 1 1
+2 NULL 2 2
+3 NULL 2 3
+connection slave;
+#same data as master
+SELECT * FROM t1;
+a b c d
+1 1 1 1
+2 NULL 2 2
+3 NULL 2 3
+connection master;
+drop table t1;
+connection slave;
+# Case 3C: SAFE - only one unique key (PK) specified.
+connection master;
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
+UNIQUE(b), c int, d int ) engine=innodb;
+connection slave;
+connection master;
+INSERT INTO t1 VALUES (1, 1, 1, 1);
+BEGIN;
+INSERT INTO t1 (`a`, `c`, `d`) VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE c=99;
+connection master1;
+INSERT INTO t1 (`a`, `c`, `d`) VALUES(3, 2, 3) ON DUPLICATE KEY UPDATE c=100;
+connection master;
+COMMIT;
+SELECT * FROM t1;
+a b c d
+1 1 1 1
+2 NULL 2 2
+3 NULL 2 3
+connection slave;
+#same data as master
+SELECT * FROM t1;
+a b c d
+1 1 1 1
+2 NULL 2 2
+3 NULL 2 3
+connection master;
+drop table t1;
+connection slave;
+# Case 4: UNSAFE
connection master;
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
UNIQUE(b), c int) engine=innodb;
@@ -101,8 +169,12 @@ connection master;
INSERT INTO t1 VALUES (1, 1, 1);
BEGIN;
INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
connection master1;
INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
+Warnings:
+Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
connection master;
COMMIT;
SELECT * FROM t1;
diff --git a/mysql-test/suite/rpl/r/rpl_unsafe_statements.result b/mysql-test/suite/rpl/r/rpl_unsafe_statements.result
index 0ce94ca63d0..ca790f5d148 100644
--- a/mysql-test/suite/rpl/r/rpl_unsafe_statements.result
+++ b/mysql-test/suite/rpl/r/rpl_unsafe_statements.result
@@ -1,6 +1,5 @@
include/master-slave.inc
[connection master]
-call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
CREATE TABLE t1(id INT AUTO_INCREMENT, i INT, PRIMARY KEY (id)) ENGINE=INNODB;
CREATE TABLE t2(id INT AUTO_INCREMENT, i INT, PRIMARY KEY (id)) ENGINE=INNODB;
CREATE TRIGGER trig1 AFTER INSERT ON t1
@@ -50,13 +49,9 @@ connection master;
DROP TABLE t1;
CREATE TABLE t1(i INT, j INT, UNIQUE KEY(i), UNIQUE KEY(j)) ENGINE=INNODB;
INSERT INTO t1 (i,j) VALUES (1,2) ON DUPLICATE KEY UPDATE j=j+1;
-Warnings:
-Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
START TRANSACTION;
LOCK TABLES t1 WRITE;
INSERT INTO t1 (i,j) VALUES (1,2) ON DUPLICATE KEY UPDATE j=j+1;
-Warnings:
-Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe
UNLOCK TABLES;
COMMIT;
connection slave;
diff --git a/mysql-test/suite/rpl/t/rpl_iodku.test b/mysql-test/suite/rpl/t/rpl_iodku.test
new file mode 100644
index 00000000000..815b927c350
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_iodku.test
@@ -0,0 +1,50 @@
+--source include/have_innodb.inc
+--source include/master-slave.inc
+
+if (`select @@binlog_format = "statement"`)
+{
+ call mtr.add_suppression("Unsafe statement written to the binary log using statement");
+}
+
+## MDEV-28310 loss of binlog event for multi-record IODKU
+# Check that the duplicate key error does not cause
+# loss of replication event for IODKU that specifies values
+# for at least two unique columns per record.
+# "Implicit" NULL value of the auto-increment column also counts.
+
+CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, a INT, b INT, c INT,
+ UNIQUE (a), UNIQUE (b)) ENGINE=innodb;
+INSERT INTO t1 (`a`,`c`) VALUES (1,1), (2,1) ON DUPLICATE KEY UPDATE c = 1;
+--echo # UNSAFE
+# because of two keys involved: a UK and PK even though implicitly via auto-inc
+INSERT INTO t1 (`a`,`c`) VALUES (3, 1),(2,1), (1,1) ON DUPLICATE KEY UPDATE c = a * 10 + VALUES(c);
+SELECT * from t1;
+
+--sync_slave_with_master
+--let $diff_tables = master:t1,slave:t1
+--source include/diff_tables.inc
+
+## MDEV-21810 MBR: Unexpected "Unsafe statement" warning for unsafe IODKU
+# Unnecessary unsafe statement warning is not error-logged anymore.
+
+
+--connection master
+CREATE OR REPLACE TABLE t1 (a INT, b INT, c INT, UNIQUE (a), UNIQUE (b)) ENGINE=innodb;
+INSERT INTO t1 VALUES (1,10,1);
+--echo # eligable for the statement format run unsafe warning
+INSERT INTO t1 VALUES (2,20,2) ON DUPLICATE KEY UPDATE c = 100;
+--echo # not eligable: no warning in the statement format run
+INSERT INTO t1 (`a`,`c`) VALUES (3, 1) ON DUPLICATE KEY UPDATE c = 99;
+SELECT * from t1;
+
+--sync_slave_with_master
+--let $diff_tables = master:t1,slave:t1
+--source include/diff_tables.inc
+
+# Cleanup
+--connection master
+DROP TABLE t1;
+--sync_slave_with_master
+
+
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_mdev_17614.test b/mysql-test/suite/rpl/t/rpl_mdev_17614.test
index 9b86c8c15b5..c11aad3305e 100644
--- a/mysql-test/suite/rpl/t/rpl_mdev_17614.test
+++ b/mysql-test/suite/rpl/t/rpl_mdev_17614.test
@@ -2,15 +2,22 @@ source include/have_debug.inc;
source include/have_innodb.inc;
-- source include/have_binlog_format_statement.inc
source include/master-slave.inc;
-# MDEV-17614
-# INSERT on dup key update is replication unsafe
-# There can be three case
-# 1. 2 unique key, Replication is unsafe.
-# 2. 2 unique key , with one auto increment key, Safe to replicate because Innodb will acquire gap lock
-# 3. n no of unique keys (n>1) but insert is only in 1 unique key
-# 4. 2 unique key , with one auto increment key(but user gives auto inc value), unsafe to replicate
+# MDEV-17614 INSERT on dup key update is replication unsafe
+#
+# The following cases are tested below:
+# 1. 2 unique key, replication is UNSAFE
+# 2. 2 unique key, with one auto increment key and implicit value to it.
+# It is UNSAFE because autoinc column values of being inserted records
+# are revealed dynamically, so unknown at the binlog-format decision time
+# and hence this pessimistic expectation
+# 3. 2 unique keys
+# A. insert is only in 1 unique key, still all colums are specified => UNSAFE
+# B. both unique keys are specified => UNSAFE
+# C. only one unique key is specified => SAFE (motivated by MDEV-28310)
+# 4. 2 unique key, with one auto increment key(but user gives auto inc value) =>
+# UNSAFE to replicate
-# Case 1
+--echo # Case 1: UNSAFE
call mtr.add_suppression("Unsafe statement written to the binary log using statement format");
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY , b INT,
UNIQUE(b), c int) engine=innodb;
@@ -42,7 +49,8 @@ drop table t1;
connection slave;
start slave;
--source include/wait_for_slave_to_start.inc
-# Case 2
+
+--echo # Case 2: UNSAFE
--connection master
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
UNIQUE(b), c int) engine=innodb;
@@ -64,7 +72,7 @@ connection master;
drop table t1;
--sync_slave_with_master
-# Case 3
+--echo # Case 3A: UNSAFE
--connection master
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
UNIQUE(b), c int, d int ) engine=innodb;
@@ -85,7 +93,50 @@ connection master;
drop table t1;
--sync_slave_with_master
-# Case 4
+--echo # Case 3B: UNSAFE - all column specified.
+--connection master
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
+UNIQUE(b), c int, d int ) engine=innodb;
+sync_slave_with_master;
+connection master;
+INSERT INTO t1 VALUES (1, 1, 1, 1);
+BEGIN;
+INSERT INTO t1 VALUES (2, NULL, 2, 2) ON DUPLICATE KEY UPDATE c=VALUES(c);
+ --connection master1
+ INSERT INTO t1 VALUES(3, NULL, 2, 3) ON DUPLICATE KEY UPDATE c=VALUES(c);
+--connection master
+COMMIT;
+SELECT * FROM t1;
+--sync_slave_with_master
+--echo #same data as master
+SELECT * FROM t1;
+connection master;
+drop table t1;
+--sync_slave_with_master
+
+
+--echo # Case 3C: SAFE - only one unique key (PK) specified.
+--connection master
+CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
+UNIQUE(b), c int, d int ) engine=innodb;
+sync_slave_with_master;
+connection master;
+INSERT INTO t1 VALUES (1, 1, 1, 1);
+BEGIN;
+INSERT INTO t1 (`a`, `c`, `d`) VALUES (2, 2, 2) ON DUPLICATE KEY UPDATE c=99;
+ --connection master1
+ INSERT INTO t1 (`a`, `c`, `d`) VALUES(3, 2, 3) ON DUPLICATE KEY UPDATE c=100;
+--connection master
+COMMIT;
+SELECT * FROM t1;
+--sync_slave_with_master
+--echo #same data as master
+SELECT * FROM t1;
+connection master;
+drop table t1;
+--sync_slave_with_master
+
+--echo # Case 4: UNSAFE
--connection master
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
UNIQUE(b), c int) engine=innodb;
diff --git a/mysql-test/suite/rpl/t/rpl_unsafe_statements.test b/mysql-test/suite/rpl/t/rpl_unsafe_statements.test
index aa0bd076398..cbb4b54a220 100644
--- a/mysql-test/suite/rpl/t/rpl_unsafe_statements.test
+++ b/mysql-test/suite/rpl/t/rpl_unsafe_statements.test
@@ -24,7 +24,7 @@
--source include/have_innodb.inc
--source include/have_binlog_format_mixed.inc
--source include/master-slave.inc
-call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
+
# Case-1: BINLOG_STMT_UNSAFE_AUTOINC_COLUMNS
# Statement is unsafe because it invokes a trigger or a
# stored function that inserts into an AUTO_INCREMENT column.
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index aa64e42e144..67373c99966 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -6285,47 +6285,84 @@ int THD::decide_logging_format(TABLE_LIST *tables)
DBUG_RETURN(0);
}
-int THD::decide_logging_format_low(TABLE *table)
+
+/*
+ Reconsider logging format in case of INSERT...ON DUPLICATE KEY UPDATE
+ for tables with more than one unique keys in case of MIXED binlog format.
+
+ Unsafe means that a master could execute the statement differently than
+ the slave.
+ This could can happen in the following cases:
+ - The unique check are done in different order on master or slave
+ (different engine or different key order).
+ - There is a conflict on another key than the first and before the
+ statement is committed, another connection commits a row that conflicts
+ on an earlier unique key. Example follows:
+
+ Below a and b are unique keys, the table has a row (1,1,0)
+ connection 1:
+ INSERT INTO t1 set a=2,b=1,c=0 ON DUPLICATE KEY UPDATE c=1;
+ connection 2:
+ INSERT INTO t1 set a=2,b=2,c=0;
+
+ If 2 commits after 1 has been executed but before 1 has committed
+ (and are thus put before the other in the binary log), one will
+ get different data on the slave:
+ (1,1,1),(2,2,1) instead of (1,1,1),(2,2,0)
+*/
+
+void THD::reconsider_logging_format_for_iodup(TABLE *table)
{
- /*
- INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys
- can be unsafe.
- */
- if(wsrep_binlog_format() <= BINLOG_FORMAT_STMT &&
- !is_current_stmt_binlog_format_row() &&
- !lex->is_stmt_unsafe() &&
- lex->sql_command == SQLCOM_INSERT &&
- lex->duplicates == DUP_UPDATE)
+ DBUG_ENTER("reconsider_logging_format_for_iodup");
+ enum_binlog_format bf= (enum_binlog_format) wsrep_binlog_format();
+
+ DBUG_ASSERT(lex->duplicates == DUP_UPDATE);
+
+ if (bf <= BINLOG_FORMAT_STMT &&
+ !is_current_stmt_binlog_format_row())
{
+ KEY *end= table->s->key_info + table->s->keys;
uint unique_keys= 0;
- uint keys= table->s->keys, i= 0;
- Field *field;
- for (KEY* keyinfo= table->s->key_info;
- i < keys && unique_keys <= 1; i++, keyinfo++)
- if (keyinfo->flags & HA_NOSAME &&
- !(keyinfo->key_part->field->flags & AUTO_INCREMENT_FLAG &&
- //User given auto inc can be unsafe
- !keyinfo->key_part->field->val_int()))
+
+ for (KEY *keyinfo= table->s->key_info; keyinfo < end ; keyinfo++)
+ {
+ if (keyinfo->flags & HA_NOSAME)
{
+ /*
+ We assume that the following cases will guarantee that the
+ key is unique if a key part is not set:
+ - The key part is an autoincrement (autogenerated)
+ - The key part has a default value that is null and it not
+ a virtual field that will be calculated later.
+ */
for (uint j= 0; j < keyinfo->user_defined_key_parts; j++)
{
- field= keyinfo->key_part[j].field;
- if(!bitmap_is_set(table->write_set,field->field_index))
- goto exit;
+ Field *field= keyinfo->key_part[j].field;
+ if (!bitmap_is_set(table->write_set, field->field_index))
+ {
+ /* Check auto_increment */
+ if (field == table->next_number_field)
+ goto exit;
+ if (field->is_real_null() && !field->default_value)
+ goto exit;
+ }
}
- unique_keys++;
+ if (unique_keys++)
+ break;
exit:;
}
-
+ }
if (unique_keys > 1)
{
- lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
- binlog_unsafe_warning_flags|= lex->get_stmt_unsafe_flags();
+ if (bf == BINLOG_FORMAT_STMT && !lex->is_stmt_unsafe())
+ {
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
+ binlog_unsafe_warning_flags|= lex->get_stmt_unsafe_flags();
+ }
set_current_stmt_binlog_format_row_if_mixed();
- return 1;
}
}
- return 0;
+ DBUG_VOID_RETURN;
}
/*
diff --git a/sql/sql_class.h b/sql/sql_class.h
index cceefb1793c..e3d9bc9ded0 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -4294,18 +4294,18 @@ public:
mdl_context.release_transactional_locks();
}
int decide_logging_format(TABLE_LIST *tables);
+
/*
- In Some cases when decide_logging_format is called it does not have all
- information to decide the logging format. So that cases we call decide_logging_format_2
- at later stages in execution.
- One example would be binlog format for IODKU but column with unique key is not inserted.
- We dont have inserted columns info when we call decide_logging_format so on later stage we call
- decide_logging_format_low
+ In Some cases when decide_logging_format is called it does not have
+ all information to decide the logging format. So that cases we call
+ decide_logging_format_2 at later stages in execution.
- @returns 0 if no format is changed
- 1 if there is change in binlog format
+ One example would be binlog format for insert on duplicate key
+ (IODKU) but column with unique key is not inserted. We do not have
+ inserted columns info when we call decide_logging_format so on
+ later stage we call reconsider_logging_format_for_iodup()
*/
- int decide_logging_format_low(TABLE *table);
+ void reconsider_logging_format_for_iodup(TABLE *table);
enum need_invoker { INVOKER_NONE=0, INVOKER_USER, INVOKER_ROLE};
void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; }
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 13dbbaed539..5137e7230d4 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -943,6 +943,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
goto values_loop_end;
}
}
+ if (duplic == DUP_UPDATE)
+ {
+ restore_record(table,s->default_values); // Get empty record
+ thd->reconsider_logging_format_for_iodup(table);
+ }
do
{
DBUG_PRINT("info", ("iteration %llu", iteration));
@@ -1051,7 +1056,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
break;
}
- thd->decide_logging_format_low(table);
#ifndef EMBEDDED_LIBRARY
if (lock_type == TL_WRITE_DELAYED)
{