# Testing various forms of idempotency for replication that should # work the same way under statement based as under row based. source include/master-slave.inc; connection master; source include/have_innodb.inc; connection slave; source include/have_innodb.inc; # Add suppression for expected warning(s) in slaves error log call mtr.add_suppression("Slave: Can't find record in 't.' Error_code: 1032"); call mtr.add_suppression("Slave: Cannot delete or update a parent row: a foreign key constraint fails .* Error_code: 1451"); call mtr.add_suppression("Slave: Cannot add or update a child row: a foreign key constraint fails .* Error_code: 1452"); SET @old_slave_exec_mode= @@global.slave_exec_mode; connection master; CREATE TABLE t1 (a INT PRIMARY KEY); CREATE TABLE t2 (a INT); INSERT INTO t1 VALUES (-1),(-2),(-3); INSERT INTO t2 VALUES (-1),(-2),(-3); sync_slave_with_master; # A delete for a row that does not exist, the statement is # deliberately written to be idempotent for statement-based # replication as well. We test this towards both a table with a # primary key and without a primary key. connection slave; DELETE FROM t1 WHERE a = -2; DELETE FROM t2 WHERE a = -2; connection master; DELETE FROM t1 WHERE a = -2; DELETE FROM t2 WHERE a = -2; SELECT * FROM t1 ORDER BY a; SELECT * FROM t2 ORDER BY a; sync_slave_with_master; SELECT * FROM t1 ORDER BY a; SELECT * FROM t2 ORDER BY a; --source include/check_slave_no_error.inc # An insert of a row that already exists. Since we are replacing the # row if it already exists, the most apropriate representation is # INSERT IGNORE. We only test this towards a table with a primary key, # since the other case does not make sense. INSERT IGNORE INTO t1 VALUES (-2); connection master; INSERT IGNORE INTO t1 VALUES (-2); SELECT * FROM t1 ORDER BY a; sync_slave_with_master; SELECT * FROM t1 ORDER BY a; --source include/check_slave_no_error.inc # BUG#19958: RBR idempotency issue for UPDATE and DELETE # Statement-based and row-based replication have different behaviour # when updating a row with an explicit WHERE-clause that matches # exactly one row (or no row at all). For statement-based replication, # the statement is idempotent since the first time it is executed, it # will update exactly one row, and the second time it will not update # any row at all. This was not the case for row-based replication, so # we test under both row-based and statement-based replication both # for tables with and without primary keys. connection slave; UPDATE t1 SET a = 1 WHERE a = -1; UPDATE t2 SET a = 1 WHERE a = -1; connection master; UPDATE t1 SET a = 1 WHERE a = -1; UPDATE t2 SET a = 1 WHERE a = -1; SELECT * FROM t1 ORDER BY a; SELECT * FROM t2 ORDER BY a; sync_slave_with_master; SELECT * FROM t1 ORDER BY a; SELECT * FROM t2 ORDER BY a; --source include/check_slave_no_error.inc connection master; DROP TABLE t1, t2; sync_slave_with_master; # bug#31609 Not all RBR slave errors reported as errors # bug#31552 Replication breaks when deleting rows from out-of-sync table # without PK # # Idempotent applying is not default any longer. # The default for slave-exec-mode option and server # variable slave_exec_mode is 'STRICT'. # When 'STRICT' mode is set, the slave SQL thread will stop whenever # the row to change is not found. In 'IDEMPOTENT' mode, the SQL thread # will continue running and apply the row - replace if it's Write_rows event - # or skip to the next event. # the previous part of the tests was with IDEMPOTENT slave's mode. # # Other than above idempotent errors dealing with foreign keys constraint # select @@global.slave_exec_mode /* must be IDEMPOTENT */; connection master; create table ti1 (b int primary key) engine = innodb; create table ti2 (a int primary key, b int, foreign key (b) references ti1(b)) engine = innodb; set foreign_key_checks=1 /* ensure the check */; insert into ti1 values (1),(2),(3); insert into ti2 set a=2, b=2; sync_slave_with_master; #connection slave; select * from ti1 order by b /* must be (1),(2),(3) */; insert into ti2 set a=1, b=1; select * from ti2 order by b /* must be (1,1) (2,2) */; connection master; # from now on checking rbr specific idempotent errors set @save_binlog_format= @@session.binlog_format; set @@session.binlog_format= row; delete from ti1 where b=1; select * from ti1 order by b /* must be (2),(3) */; # slave must catch up (expect some warnings in error.log) sync_slave_with_master; #connection slave; select * from ti1 order by b /* must stays as were on master (1),(2),(3) */; delete from ti1 where b=3; connection master; insert into ti2 set a=3, b=3; # slave must catch up (expect some warnings in error.log) sync_slave_with_master; #connection slave; select * from ti2 order by b /* must be (1,1),(2,2) - not inserted */; # # Checking the new global sys variable # connection slave; set global slave_exec_mode='IDEMPOTENT'; set global slave_exec_mode='STRICT'; # checking mutual exclusion for the options --error ER_SLAVE_AMBIGOUS_EXEC_MODE set global slave_exec_mode='IDEMPOTENT,STRICT'; select @@global.slave_exec_mode /* must be STRICT */; # # Checking stops. # In the following sections strict slave sql thread is going to # stop when faces an idempotent error. In order to proceed # the mode is temporarily switched to indempotent. # # --echo *** foreign keys errors as above now forces to stop # connection master; set foreign_key_checks=0; drop table ti2, ti1; create table ti1 (b int primary key) engine = innodb; create table ti2 (a int primary key, b int, foreign key (b) references ti1(b)) engine = innodb; set foreign_key_checks=1 /* ensure the check */; insert into ti1 values (1),(2),(3); insert into ti2 set a=2, b=2; sync_slave_with_master; #connection slave; select * from ti1 order by b /* must be (1),(2),(3) */; --echo *** conspire future problem insert into ti2 set a=1, b=1; select * from ti2 order by b /* must be (1,1) (2,2) */; connection master; delete from ti1 where b=1 /* offending delete event */; select * from ti1 order by b /* must be (2),(3) */; # foreign key: row is referenced --echo *** slave must stop (Trying to delete a referenced foreing key) connection slave; # 1451 = ER_ROW_ID_REFERENCED_2 --let $slave_sql_errno= 1451 --source include/wait_for_slave_sql_error.inc select * from ti1 order by b /* must be (1),(2),(3) - not deleted */; set foreign_key_checks= 0; delete from ti2 where b=1; set foreign_key_checks= 1; set global slave_exec_mode='IDEMPOTENT'; start slave sql_thread; connection master; sync_slave_with_master; #connection slave; set global slave_exec_mode='STRICT'; connection master; sync_slave_with_master; #connection slave; --echo *** conspire the following insert failure # foreign key: no referenced row --echo *** conspire future problem delete from ti1 where b=3; connection master; insert into ti2 set a=3, b=3 /* offending write event */; --echo *** slave must stop (Trying to insert an invalid foreign key) connection slave; # 1452 = ER_NO_REFERENCED_ROW_2 --let $slave_sql_errno= 1452 --source include/wait_for_slave_sql_error.inc select * from ti2 order by b /* must be (2,2) */; set foreign_key_checks= 0; insert into ti1 set b=3; set foreign_key_checks= 1; set global slave_exec_mode='IDEMPOTENT'; start slave sql_thread; connection master; sync_slave_with_master; #connection slave; set global slave_exec_mode='STRICT'; connection master; sync_slave_with_master; select * from ti2 order by b /* must be (2,2),(3,3) */; # --echo *** other errors # # dup key insert #connection slave; --echo *** conspiring query insert into ti1 set b=1; connection master; insert into ti1 set b=1 /* offending write event */; --echo *** slave must stop (Trying to insert a dupliacte key) connection slave; # 1062 = ER_DUP_ENTRY --let $slave_sql_errno= 1062 --source include/wait_for_slave_sql_error.inc set foreign_key_checks= 0; delete from ti1 where b=1; set foreign_key_checks= 1; set global slave_exec_mode='IDEMPOTENT'; start slave sql_thread; connection master; sync_slave_with_master; #connection slave; set global slave_exec_mode='STRICT'; # key not found connection master; CREATE TABLE t1 (a INT PRIMARY KEY); CREATE TABLE t2 (a INT); INSERT INTO t1 VALUES (-1),(-2),(-3); INSERT INTO t2 VALUES (-1),(-2),(-3); sync_slave_with_master; #connection slave; DELETE FROM t1 WHERE a = -2; DELETE FROM t2 WHERE a = -2; connection master; DELETE FROM t1 WHERE a = -2; --echo *** slave must stop (Key was not found) connection slave; # 1032 = ER_KEY_NOT_FOUND --let $slave_sql_errno= 1032 --source include/wait_for_slave_sql_error.inc set global slave_exec_mode='IDEMPOTENT'; start slave sql_thread; connection master; sync_slave_with_master; #connection slave; set global slave_exec_mode='STRICT'; connection master; DELETE FROM t2 WHERE a = -2; --echo *** slave must stop (Key was not found) connection slave; # 1032 = ER_KEY_NOT_FOUND --let $slave_sql_errno= 1032 source include/wait_for_slave_sql_error.inc; set global slave_exec_mode='IDEMPOTENT'; start slave sql_thread; connection master; sync_slave_with_master; #connection slave; set global slave_exec_mode='STRICT'; UPDATE t1 SET a = 1 WHERE a = -1; UPDATE t2 SET a = 1 WHERE a = -1; connection master; UPDATE t1 SET a = 1 WHERE a = -1; --echo *** slave must stop (Key was not found) connection slave; # 1032 = ER_KEY_NOT_FOUND --let $slave_sql_errno= 1032 source include/wait_for_slave_sql_error.inc; set global slave_exec_mode='IDEMPOTENT'; start slave sql_thread; connection master; sync_slave_with_master; #connection slave; set global slave_exec_mode='STRICT'; connection master; UPDATE t2 SET a = 1 WHERE a = -1; --echo *** slave must stop (Key was not found) connection slave; # 1032 = ER_KEY_NOT_FOUND --let $slave_sql_errno= 1032 source include/wait_for_slave_sql_error.inc; set global slave_exec_mode='IDEMPOTENT'; start slave sql_thread; connection master; sync_slave_with_master; #connection slave; SET @@global.slave_exec_mode= @old_slave_exec_mode; # cleanup for bug#31609 tests connection master; set @@session.binlog_format= @save_binlog_format; drop table t1,t2,ti2,ti1; --source include/rpl_end.inc --echo *** end of tests