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.
This commit is contained in:
parent
141ab971d8
commit
a5dc12eefd
27
mysql-test/suite/rpl/r/rpl_iodku,stmt.rdiff
Normal file
27
mysql-test/suite/rpl/r/rpl_iodku,stmt.rdiff
Normal file
@ -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;
|
32
mysql-test/suite/rpl/r/rpl_iodku.result
Normal file
32
mysql-test/suite/rpl/r/rpl_iodku.result
Normal file
@ -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
|
@ -1,5 +1,6 @@
|
|||||||
include/master-slave.inc
|
include/master-slave.inc
|
||||||
[connection master]
|
[connection master]
|
||||||
|
# Case 1: UNSAFE
|
||||||
call mtr.add_suppression("Unsafe statement written to the binary log using statement format");
|
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,
|
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY , b INT,
|
||||||
UNIQUE(b), c int) engine=innodb;
|
UNIQUE(b), c int) engine=innodb;
|
||||||
@ -37,6 +38,7 @@ drop table t1;
|
|||||||
connection slave;
|
connection slave;
|
||||||
start slave;
|
start slave;
|
||||||
include/wait_for_slave_to_start.inc
|
include/wait_for_slave_to_start.inc
|
||||||
|
# Case 2: UNSAFE
|
||||||
connection master;
|
connection master;
|
||||||
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
|
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
|
||||||
UNIQUE(b), c int) engine=innodb;
|
UNIQUE(b), c int) engine=innodb;
|
||||||
@ -45,8 +47,12 @@ connection master;
|
|||||||
INSERT INTO t1 VALUES (default, 1, 1);
|
INSERT INTO t1 VALUES (default, 1, 1);
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO t1 VALUES (default, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
|
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;
|
connection master1;
|
||||||
INSERT INTO t1 VALUES(default, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
|
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;
|
connection master;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
SELECT * FROM t1;
|
SELECT * FROM t1;
|
||||||
@ -62,6 +68,7 @@ a b c
|
|||||||
connection master;
|
connection master;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
connection slave;
|
connection slave;
|
||||||
|
# Case 3A: UNSAFE
|
||||||
connection master;
|
connection master;
|
||||||
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
|
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
|
||||||
UNIQUE(b), c int, d int ) engine=innodb;
|
UNIQUE(b), c int, d int ) engine=innodb;
|
||||||
@ -93,6 +100,67 @@ a b c d
|
|||||||
connection master;
|
connection master;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
connection slave;
|
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;
|
connection master;
|
||||||
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
|
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
|
||||||
UNIQUE(b), c int) engine=innodb;
|
UNIQUE(b), c int) engine=innodb;
|
||||||
@ -101,8 +169,12 @@ connection master;
|
|||||||
INSERT INTO t1 VALUES (1, 1, 1);
|
INSERT INTO t1 VALUES (1, 1, 1);
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
|
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;
|
connection master1;
|
||||||
INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c);
|
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;
|
connection master;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
SELECT * FROM t1;
|
SELECT * FROM t1;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
include/master-slave.inc
|
include/master-slave.inc
|
||||||
[connection master]
|
[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 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 TABLE t2(id INT AUTO_INCREMENT, i INT, PRIMARY KEY (id)) ENGINE=INNODB;
|
||||||
CREATE TRIGGER trig1 AFTER INSERT ON t1
|
CREATE TRIGGER trig1 AFTER INSERT ON t1
|
||||||
@ -50,13 +49,9 @@ connection master;
|
|||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
CREATE TABLE t1(i INT, j INT, UNIQUE KEY(i), UNIQUE KEY(j)) ENGINE=INNODB;
|
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;
|
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;
|
START TRANSACTION;
|
||||||
LOCK TABLES t1 WRITE;
|
LOCK TABLES t1 WRITE;
|
||||||
INSERT INTO t1 (i,j) VALUES (1,2) ON DUPLICATE KEY UPDATE j=j+1;
|
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;
|
UNLOCK TABLES;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
connection slave;
|
connection slave;
|
||||||
|
50
mysql-test/suite/rpl/t/rpl_iodku.test
Normal file
50
mysql-test/suite/rpl/t/rpl_iodku.test
Normal file
@ -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
|
@ -2,15 +2,22 @@ source include/have_debug.inc;
|
|||||||
source include/have_innodb.inc;
|
source include/have_innodb.inc;
|
||||||
-- source include/have_binlog_format_statement.inc
|
-- source include/have_binlog_format_statement.inc
|
||||||
source include/master-slave.inc;
|
source include/master-slave.inc;
|
||||||
# MDEV-17614
|
# MDEV-17614 INSERT on dup key update is replication unsafe
|
||||||
# INSERT on dup key update is replication unsafe
|
#
|
||||||
# There can be three case
|
# The following cases are tested below:
|
||||||
# 1. 2 unique key, Replication is unsafe.
|
# 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
|
# 2. 2 unique key, with one auto increment key and implicit value to it.
|
||||||
# 3. n no of unique keys (n>1) but insert is only in 1 unique key
|
# It is UNSAFE because autoinc column values of being inserted records
|
||||||
# 4. 2 unique key , with one auto increment key(but user gives auto inc value), unsafe to replicate
|
# 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");
|
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,
|
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY , b INT,
|
||||||
UNIQUE(b), c int) engine=innodb;
|
UNIQUE(b), c int) engine=innodb;
|
||||||
@ -42,7 +49,8 @@ drop table t1;
|
|||||||
connection slave;
|
connection slave;
|
||||||
start slave;
|
start slave;
|
||||||
--source include/wait_for_slave_to_start.inc
|
--source include/wait_for_slave_to_start.inc
|
||||||
# Case 2
|
|
||||||
|
--echo # Case 2: UNSAFE
|
||||||
--connection master
|
--connection master
|
||||||
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
|
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
|
||||||
UNIQUE(b), c int) engine=innodb;
|
UNIQUE(b), c int) engine=innodb;
|
||||||
@ -64,7 +72,7 @@ connection master;
|
|||||||
drop table t1;
|
drop table t1;
|
||||||
--sync_slave_with_master
|
--sync_slave_with_master
|
||||||
|
|
||||||
# Case 3
|
--echo # Case 3A: UNSAFE
|
||||||
--connection master
|
--connection master
|
||||||
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
|
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT,
|
||||||
UNIQUE(b), c int, d int ) engine=innodb;
|
UNIQUE(b), c int, d int ) engine=innodb;
|
||||||
@ -85,7 +93,50 @@ connection master;
|
|||||||
drop table t1;
|
drop table t1;
|
||||||
--sync_slave_with_master
|
--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
|
--connection master
|
||||||
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
|
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY auto_increment, b INT,
|
||||||
UNIQUE(b), c int) engine=innodb;
|
UNIQUE(b), c int) engine=innodb;
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
--source include/have_innodb.inc
|
--source include/have_innodb.inc
|
||||||
--source include/have_binlog_format_mixed.inc
|
--source include/have_binlog_format_mixed.inc
|
||||||
--source include/master-slave.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
|
# Case-1: BINLOG_STMT_UNSAFE_AUTOINC_COLUMNS
|
||||||
# Statement is unsafe because it invokes a trigger or a
|
# Statement is unsafe because it invokes a trigger or a
|
||||||
# stored function that inserts into an AUTO_INCREMENT column.
|
# stored function that inserts into an AUTO_INCREMENT column.
|
||||||
|
@ -6285,47 +6285,84 @@ int THD::decide_logging_format(TABLE_LIST *tables)
|
|||||||
DBUG_RETURN(0);
|
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)
|
||||||
{
|
{
|
||||||
/*
|
DBUG_ENTER("reconsider_logging_format_for_iodup");
|
||||||
INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys
|
enum_binlog_format bf= (enum_binlog_format) wsrep_binlog_format();
|
||||||
can be unsafe.
|
|
||||||
*/
|
DBUG_ASSERT(lex->duplicates == DUP_UPDATE);
|
||||||
if(wsrep_binlog_format() <= BINLOG_FORMAT_STMT &&
|
|
||||||
!is_current_stmt_binlog_format_row() &&
|
if (bf <= BINLOG_FORMAT_STMT &&
|
||||||
!lex->is_stmt_unsafe() &&
|
!is_current_stmt_binlog_format_row())
|
||||||
lex->sql_command == SQLCOM_INSERT &&
|
|
||||||
lex->duplicates == DUP_UPDATE)
|
|
||||||
{
|
{
|
||||||
|
KEY *end= table->s->key_info + table->s->keys;
|
||||||
uint unique_keys= 0;
|
uint unique_keys= 0;
|
||||||
uint keys= table->s->keys, i= 0;
|
|
||||||
Field *field;
|
for (KEY *keyinfo= table->s->key_info; keyinfo < end ; keyinfo++)
|
||||||
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()))
|
|
||||||
{
|
{
|
||||||
|
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++)
|
for (uint j= 0; j < keyinfo->user_defined_key_parts; j++)
|
||||||
{
|
{
|
||||||
field= keyinfo->key_part[j].field;
|
Field *field= keyinfo->key_part[j].field;
|
||||||
if(!bitmap_is_set(table->write_set,field->field_index))
|
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;
|
goto exit;
|
||||||
}
|
}
|
||||||
unique_keys++;
|
}
|
||||||
|
if (unique_keys++)
|
||||||
|
break;
|
||||||
exit:;
|
exit:;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (unique_keys > 1)
|
if (unique_keys > 1)
|
||||||
|
{
|
||||||
|
if (bf == BINLOG_FORMAT_STMT && !lex->is_stmt_unsafe())
|
||||||
{
|
{
|
||||||
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
|
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
|
||||||
binlog_unsafe_warning_flags|= lex->get_stmt_unsafe_flags();
|
binlog_unsafe_warning_flags|= lex->get_stmt_unsafe_flags();
|
||||||
|
}
|
||||||
set_current_stmt_binlog_format_row_if_mixed();
|
set_current_stmt_binlog_format_row_if_mixed();
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -4294,18 +4294,18 @@ public:
|
|||||||
mdl_context.release_transactional_locks();
|
mdl_context.release_transactional_locks();
|
||||||
}
|
}
|
||||||
int decide_logging_format(TABLE_LIST *tables);
|
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
|
|
||||||
|
|
||||||
@returns 0 if no format is changed
|
/*
|
||||||
1 if there is change in binlog format
|
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 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};
|
enum need_invoker { INVOKER_NONE=0, INVOKER_USER, INVOKER_ROLE};
|
||||||
void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; }
|
void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; }
|
||||||
|
@ -943,6 +943,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
|||||||
goto values_loop_end;
|
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
|
do
|
||||||
{
|
{
|
||||||
DBUG_PRINT("info", ("iteration %llu", iteration));
|
DBUG_PRINT("info", ("iteration %llu", iteration));
|
||||||
@ -1051,7 +1056,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
thd->decide_logging_format_low(table);
|
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
if (lock_type == TL_WRITE_DELAYED)
|
if (lock_type == TL_WRITE_DELAYED)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user