mirror of
https://github.com/MariaDB/server.git
synced 2025-12-18 10:22:14 +03:00
Problem:-
When mysql executes INSERT ON DUPLICATE KEY INSERT, the storage engine checks
if the inserted row would generate a duplicate key error. If yes, it returns
the existing row to mysql, mysql updates it and sends it back to the storage
engine.When the table has more than one unique or primary key, this statement
is sensitive to the order in which the storage engines checks the keys.
Depending on this order, the storage engine may determine different rows
to mysql, and hence mysql can update different rows.The order that the
storage engine checks keys is not deterministic. For example, InnoDB checks
keys in an order that depends on the order in which indexes were added to
the table. The first added index is checked first. So if master and slave
have added indexes in different orders, then slave may go out of sync.
Solution:-
Make INSERT...ON DUPLICATE KEY UPDATE unsafe while using stmt or mixed format
When there is more then one unique key.
Although there is two exception.
1. Auto Increment key is not counted because Innodb will get gap lock for
failed Insert and concurrent insert will get a next increment value. But if
user supplies auto inc value it can be unsafe.
2. Count only unique keys for which insertion is performed.
So this patch also addresses the bug id #72921
59 lines
2.2 KiB
Plaintext
59 lines
2.2 KiB
Plaintext
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
|
|
FOR EACH ROW
|
|
INSERT INTO t2(i) VALUES(new.i);
|
|
START TRANSACTION;
|
|
INSERT INTO t2(i) VALUES (1);
|
|
ROLLBACK;
|
|
INSERT INTO t1(i) VALUES(2);
|
|
START TRANSACTION;
|
|
LOCK TABLES t1 WRITE, t2 WRITE;
|
|
INSERT INTO t1(i) VALUES(3);
|
|
UNLOCK TABLES;
|
|
COMMIT;
|
|
include/diff_tables.inc [master:t1, slave:t1]
|
|
include/diff_tables.inc [master:t2, slave:t2]
|
|
DROP TABLE t1,t2;
|
|
CREATE TABLE t1(i INT) ENGINE=INNODB;
|
|
CREATE TABLE t2(id INT AUTO_INCREMENT, i INT, PRIMARY KEY (id)) ENGINE=INNODB;
|
|
INSERT INTO t1 values (1), (2), (3);
|
|
START TRANSACTION;
|
|
INSERT INTO t2(i) VALUES (1);
|
|
ROLLBACK;
|
|
INSERT INTO t2(i) SELECT i FROM t1;
|
|
START TRANSACTION;
|
|
LOCK TABLES t2 WRITE, t1 READ;
|
|
INSERT INTO t2(i) SELECT i FROM t1;
|
|
UNLOCK TABLES;
|
|
COMMIT;
|
|
include/diff_tables.inc [master:t1, slave:t1]
|
|
include/diff_tables.inc [master:t2, slave:t2]
|
|
DROP TABLE t1,t2;
|
|
CREATE TABLE t1(i int, id INT AUTO_INCREMENT, PRIMARY KEY (i, id)) ENGINE=MYISAM;
|
|
INSERT INTO t1 (i) values (1);
|
|
START TRANSACTION;
|
|
LOCK TABLES t1 WRITE;
|
|
INSERT INTO t1 (i) values (2);
|
|
UNLOCK TABLES;
|
|
COMMIT;
|
|
include/diff_tables.inc [master:t1, slave:t1]
|
|
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;
|
|
include/diff_tables.inc [master:t1, slave:t1]
|
|
DROP TABLE t1;
|
|
include/rpl_end.inc
|