diff --git a/mysql-test/extra/rpl_tests/rpl_conflicts.test b/mysql-test/extra/rpl_tests/rpl_conflicts.test new file mode 100644 index 00000000000..8a98059b0ad --- /dev/null +++ b/mysql-test/extra/rpl_tests/rpl_conflicts.test @@ -0,0 +1,168 @@ +# ==== Purpose ==== +# +# Test that slave behaves well in some conflict situations. The +# following are tested: +# +# - The slave SQL thread sees an 'INSERT' of a row with a key that +# already exists in the table; +# +# - The slave SQL thread sees a 'DELETE' of a row that does not +# exist in the table. +# +# In statement-logging mode, the first conflict type causes the slave +# to stop with an error and the second conflict is ignored. +# +# In row-logging mode, the slave behavior depends the value of +# @@slave_exec_mode on the slave: if @@slave_exec_mode is IDEMPOTENT, +# the slave should ignore the conflicting statement and continue +# normally. If @@slave_exec_mode is STRICT, the slave should stop +# with an error. +# +# This test was previously named rpl_stm_mystery22/rpl_row_mystery22. +# +# +# ==== Method ==== +# +# Create a table on master and slave, insert a row on slave, and +# insert the same row on master. +# +# Create a table on master and slave, insert a row on master with +# binlogging turned off, and remove the row on master with binlogging +# turned on. +# +# +# ==== Related bugs ==== +# +# BUG#31552: Replication breaks when deleting rows from out-of-sync table without PK +# BUG#31609: Not all RBR slave errors reported as errors +# +# Bug in this test case: +# BUG#37718: rpl.rpl_stm_mystery22 fails sporadically on pushbuild +# +# +# ==== Usage ==== +# +# This file assumes the following: +# +# - The test language variable $slave_is_idempotent is set to 1 if the +# slave is expected to stop on duplicate key errors (i.e., if the +# binlog is in statement mode or +# @@global.slave_exec_mode=STRICT). It is set to 0 otherwise. +# +# - Replication has been initialized by include/master-slave.inc +# +# - The test adds a suppression for the following warning: +# Slave: Can't find record in 't1' Error_code: 1032 + + +--echo ==== Initialize ==== + +--echo [on master] +connection master; +CREATE TABLE t1(a INT PRIMARY KEY); +--echo [on slave] +sync_slave_with_master; + + +--echo ==== Test: SQL thread sees 'INSERT' of existing key ==== + +--echo ---- Prepare slave so that it will get duplicate key error ---- +# This row will be in the way of the row inserted by master. +INSERT INTO t1 VALUES (1); + +--echo ---- Insert rows on master ---- +--echo [on master] +connection master; +# Insert the same row on master +INSERT INTO t1 VALUES (1); +save_master_pos; +SELECT * FROM t1; + +--echo [on slave] +connection slave; + +# If we are statement-logging or if slave_exec_mode=STRICT, we now +# expect to see an error on the slave. Otherwise (i.e., we are +# row-logging and slave_exec_mode=IDEMPOTENT), we expect that the +# duplicate row is ignored by the slave and replication continues. +if (`SELECT @@global.binlog_format != 'ROW' OR @@global.slave_exec_mode = 'STRICT'`) { + --echo ---- Wait until slave stops with an error ---- + # Wait until the slave tries to run the query, fails with duplicate + # key error, and stops the SQL thread. + let $slave_sql_errno= 1062; # ER_DUP_ENTRY + source include/wait_for_slave_sql_error.inc; + let $err= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1); + --echo Last_SQL_Error = $err (expected "duplicate key" error) + SELECT * FROM t1; + + --echo ---- Resolve the conflict on the slave and restart SQL thread ---- + DELETE FROM t1 WHERE a = 1; + START SLAVE SQL_THREAD; + source include/wait_for_slave_sql_to_start.inc; +} + +--echo ---- Sync slave and verify that there is no error ---- +sync_with_master; +let $err= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1); +--echo Last_SQL_Error = '$err' (expected no error) +SELECT * FROM t1; + + +--echo ==== Test: SQL thread sees 'DELETE' of non-existing row ==== + +--echo ---- On master, insert two rows, the second with binlogging off ---- +--echo [on master] +connection master; +DELETE FROM t1; +INSERT INTO t1 VALUES (1); + +--echo [on slave] +sync_slave_with_master; +DELETE FROM t1 WHERE a = 1; + +--echo ---- On master, remove the row that does not exist on slave ---- +--echo [on master] +connection master; +DELETE FROM t1 WHERE a = 1; +SELECT * FROM t1; +save_master_pos; + +--echo [on slave] +connection slave; + +# If we are row-logging and slave_exec_mode is STRICT, we now expect +# an error since the row to delete does not exist on slave. Otherwise +# (i.e., either we are statement-logging or slave_exec_mode is +# IDEMPOTENT), the absence of the row to delete is ignored and +# replication continues. +if (`SELECT @@global.binlog_format = 'ROW' AND @@global.slave_exec_mode = 'STRICT'`) { + --echo ---- Wait until slave stops with an error ---- + let $slave_sql_errno= 1032; # ER_KEY_NOT_FOUND + source include/wait_for_slave_sql_error.inc; + let $err= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1); + --echo Last_SQL_Error = $err (expected "can't find record" error) + SELECT * FROM t1; + + --echo ---- Resolve the conflict on the slave and restart SQL thread ---- + INSERT INTO t1 VALUES (1); + START SLAVE SQL_THREAD; + source include/wait_for_slave_sql_to_start.inc; +} + +--echo ---- Sync slave and verify that there is no error ---- +# The slave should sync ok, and SHOW SLAVE STATUS should give no +# error. +sync_with_master; +let $err= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1); +--echo Last_SQL_Error = $err (expected no error) +SELECT * FROM t1; + + +--echo ==== Clean up ==== + +--echo [on master] +connection master; +DROP TABLE t1; + +--echo [on slave] +sync_slave_with_master; diff --git a/mysql-test/include/show_rpl_debug_info.inc b/mysql-test/include/show_rpl_debug_info.inc new file mode 100644 index 00000000000..252d63f1bf4 --- /dev/null +++ b/mysql-test/include/show_rpl_debug_info.inc @@ -0,0 +1,87 @@ +# ==== Purpose ==== +# +# Print status information for replication, typically used to debug +# test failures. +# +# First, the following is printed on slave: +# +# SHOW SLAVE STATUS +# SHOW PROCESSLIST +# SHOW BINLOG EVENTS IN +# +# Where is the currently active binlog. +# +# Then, the following is printed on master: +# +# SHOW MASTER STATUS +# SHOW PROCESSLIST +# SHOW BINLOG EVENTS IN +# SHOW BINLOG EVENTS IN +# +# Where is the binlog name that the slave sql thread +# is currently reading from and is the binlog that +# the slave IO thread is currently reading from. +# +# ==== Usage ==== +# +# [let $master_connection= ;] +# source include/show_rpl_debug_info.inc; +# +# If $master_connection is set, debug info will be retrieved from the +# connection named $master_connection. Otherwise, it will be +# retrieved from the 'master' connection if the current connection is +# 'slave'. + +let $_con= $CURRENT_CONNECTION; +--echo +--echo [on $_con] +--echo +--echo **** SHOW SLAVE STATUS on $_con **** +query_vertical SHOW SLAVE STATUS; +--echo +--echo **** SHOW PROCESSLIST on $_con **** +SHOW PROCESSLIST; +--echo +--echo **** SHOW BINLOG EVENTS on $_con **** +let $binlog_name= query_get_value("SHOW MASTER STATUS", File, 1); +eval SHOW BINLOG EVENTS IN '$binlog_name'; + +let $_master_con= $master_connection; +if (`SELECT '$_master_con' = ''`) +{ + if (`SELECT '$_con' = 'slave'`) + { + let $_master_con= master; + } + if (`SELECT '$_master_con' = ''`) + { + --echo Unable to determine master connection. No debug info printed for master. + --echo Please fix the test case by setting $master_connection before sourcing + --echo show_rpl_debug_info.inc. + } +} + +if (`SELECT '$_master_con' != ''`) +{ + + let $master_binlog_name_io= query_get_value("SHOW SLAVE STATUS", Master_Log_File, 1); + let $master_binlog_name_sql= query_get_value("SHOW SLAVE STATUS", Relay_Master_Log_File, 1); + --echo + --echo [on $_master_con] + connection $_master_con; + --echo + --echo **** SHOW MASTER STATUS on $_master_con **** + query_vertical SHOW MASTER STATUS; + --echo + --echo **** SHOW PROCESSLIST on $_master_con **** + SHOW PROCESSLIST; + --echo + --echo **** SHOW BINLOG EVENTS on $_master_con **** + eval SHOW BINLOG EVENTS IN '$master_binlog_name_sql'; + if (`SELECT '$master_binlog_name_io' != '$master_binlog_name_sql'`) + { + eval SHOW BINLOG EVENTS IN '$master_binlog_name_io'; + } + + connection $_con; +} diff --git a/mysql-test/include/start_slave.inc b/mysql-test/include/start_slave.inc index 0a78c5d8026..78a02736de8 100644 --- a/mysql-test/include/start_slave.inc +++ b/mysql-test/include/start_slave.inc @@ -11,7 +11,7 @@ # source include/wait_for_slave_to_start.inc; # # Parameters to this macro are $slave_timeout and -# $slave_keep_connection. See wait_for_slave_param.inc for +# $master_connection. See wait_for_slave_param.inc for # descriptions. --disable_query_log diff --git a/mysql-test/include/stop_slave.inc b/mysql-test/include/stop_slave.inc index e9c56034fb3..7161e6fe739 100644 --- a/mysql-test/include/stop_slave.inc +++ b/mysql-test/include/stop_slave.inc @@ -11,7 +11,7 @@ # source include/wait_for_slave_to_start.inc; # # Parameters to this macro are $slave_timeout and -# $slave_keep_connection. See wait_for_slave_param.inc for +# $master_connection. See wait_for_slave_param.inc for # descriptions. --disable_query_log diff --git a/mysql-test/include/sync_slave_io_with_master.inc b/mysql-test/include/sync_slave_io_with_master.inc index 882724bc986..f7dd563039c 100644 --- a/mysql-test/include/sync_slave_io_with_master.inc +++ b/mysql-test/include/sync_slave_io_with_master.inc @@ -15,7 +15,7 @@ # Must be called on the master. Will change connection to the slave. # # Parameters to this macro are $slave_timeout and -# $slave_keep_connection. See wait_for_slave_param.inc for +# $master_connection. See wait_for_slave_param.inc for # descriptions. let $_master_file= query_get_value("SHOW MASTER STATUS", File, 1); diff --git a/mysql-test/include/wait_for_slave_io_to_start.inc b/mysql-test/include/wait_for_slave_io_to_start.inc index 3ada2a0e927..abdc8339290 100644 --- a/mysql-test/include/wait_for_slave_io_to_start.inc +++ b/mysql-test/include/wait_for_slave_io_to_start.inc @@ -9,7 +9,7 @@ # source include/wait_for_slave_io_to_start.inc; # # Parameters to this macro are $slave_timeout and -# $slave_keep_connection. See wait_for_slave_param.inc for +# $master_connection. See wait_for_slave_param.inc for # descriptions. let $slave_param= Slave_IO_Running; diff --git a/mysql-test/include/wait_for_slave_io_to_stop.inc b/mysql-test/include/wait_for_slave_io_to_stop.inc index 16a03d94511..f61b0db1ed7 100644 --- a/mysql-test/include/wait_for_slave_io_to_stop.inc +++ b/mysql-test/include/wait_for_slave_io_to_stop.inc @@ -8,7 +8,7 @@ # source include/wait_for_slave_io_to_stop.inc; # # Parameters to this macro are $slave_timeout and -# $slave_keep_connection. See wait_for_slave_param.inc for +# $master_connection. See wait_for_slave_param.inc for # descriptions. # if server has not used CHANGE MASTER to initiate slave, SHOW SLAVE diff --git a/mysql-test/include/wait_for_slave_param.inc b/mysql-test/include/wait_for_slave_param.inc index 6d61f2f8caa..82e57922913 100644 --- a/mysql-test/include/wait_for_slave_param.inc +++ b/mysql-test/include/wait_for_slave_param.inc @@ -7,7 +7,7 @@ # # let $slave_param= Slave_SQL_Running; # let $slave_param_value= No; -# --source include/slave_wait_param.inc +# source include/slave_wait_param.inc; # # Parameters: # @@ -27,14 +27,14 @@ # The default timeout is 5 minutes. You can change the timeout by # setting $slave_timeout. The unit is tenths of seconds. # -# $slave_keep_connection +# $master_connection # If the timeout is reached, debug info is given by calling SHOW -# SLAVE STATUS, SHOW PROCESSLIST, and SHOW BINLOG EVENTS. By -# default (assuming the current connection is slave), a 'connection -# master' is then issued, and the same information is printed again -# on the master host. You can avoid switching to master (and thus -# suppress debug info on master too) by setting -# $slave_keep_connection to 1. +# SLAVE STATUS, SHOW PROCESSLIST, and SHOW BINLOG EVENTS. Then, a +# 'connection master' is then issued, and more debug info is given +# by calling SHOW MASTER STATUS, SHOW PROCESSLIST, and SHOW BINLOG +# EVENTS. If $master_connection is set, the latter three commands +# will be issued on $master_connection instead of on the host named +# 'master'. See also show_rpl_debug_info.inc # # $slave_error_message # If set, this is printed when a timeout occurs. This is primarily @@ -57,38 +57,26 @@ if (`SELECT '$_slave_param_comparison' = ''`) } let $_show_slave_status_value= query_get_value("SHOW SLAVE STATUS", $slave_param, 1); -while (`SELECT NOT('$_show_slave_status_value' $_slave_param_comparison '$slave_param_value')`) +while (`SELECT NOT('$_show_slave_status_value' $_slave_param_comparison '$slave_param_value') AND $_slave_timeout_counter > 0`) { - if (!$_slave_timeout_counter) - { - --echo **** ERROR: failed while waiting for slave parameter $slave_param $_slave_param_comparison $slave_param_value **** - if (`SELECT '$slave_error_message' != ''`) - { - --echo Message: $slave_error_message - } - --echo Note: the following output may have changed since the failure was detected - --echo **** Showing SLAVE STATUS, PROCESSLIST, and BINLOG EVENTS on slave **** - query_vertical SHOW SLAVE STATUS; - SHOW PROCESSLIST; - let $binlog_name= query_get_value("SHOW MASTER STATUS", File, 1); - eval SHOW BINLOG EVENTS IN '$binlog_name'; - if (!$slave_keep_connection) { - let $master_binlog_name_io= query_get_value("SHOW SLAVE STATUS", Master_Log_File, 1); - let $master_binlog_name_sql= query_get_value("SHOW SLAVE STATUS", Relay_Master_Log_File, 1); - --echo **** Showing MASTER STATUS, PROCESSLIST, and BINLOG EVENTS on master **** - --echo [on master] - connection master; - query_vertical SHOW MASTER STATUS; - SHOW PROCESSLIST; - eval SHOW BINLOG EVENTS IN '$master_binlog_name_sql'; - if (`SELECT '$master_binlog_name_io' != '$master_binlog_name_sql'`) - { - eval SHOW BINLOG EVENTS IN '$master_binlog_name_io'; - } - } - exit; - } dec $_slave_timeout_counter; - sleep 0.1; - let $_show_slave_status_value= query_get_value("SHOW SLAVE STATUS", $slave_param, 1); + if ($_slave_timeout_counter) + { + sleep 0.1; + let $_show_slave_status_value= query_get_value("SHOW SLAVE STATUS", $slave_param, 1); + } +} + +# This has to be outside the loop until BUG#41913 has been fixed +if (!$_slave_timeout_counter) +{ + --echo **** ERROR: timeout after $slave_timeout seconds while waiting for slave parameter $slave_param $_slave_param_comparison $slave_param_value **** + if (`SELECT '$slave_error_message' != ''`) + { + --echo Message: $slave_error_message + } + --echo Current connection is '$CURRENT_CONNECTION' + echo Note: the following output may have changed since the failure was detected; + source include/show_rpl_debug_info.inc; + exit; } diff --git a/mysql-test/include/wait_for_slave_sql_error.inc b/mysql-test/include/wait_for_slave_sql_error.inc index 2d392f0e761..ad1d7a9e639 100644 --- a/mysql-test/include/wait_for_slave_sql_error.inc +++ b/mysql-test/include/wait_for_slave_sql_error.inc @@ -1,7 +1,8 @@ # ==== Purpose ==== # # Waits until the SQL thread of the current connection has got an -# error, or until a timeout is reached. +# error, or until a timeout is reached. Also waits until the SQL +# thread has completely stopped. # # ==== Usage ==== # @@ -10,28 +11,29 @@ # Parameters: # # $slave_sql_errno -# Number of expected SQL error. If it skipped then any error -# will pass. +# The expected SQL error number. This is required. +# (After BUG#41956 has been fixed, this will be required to be a +# symbolic name instead of a number.) # -# $slave_timeout and -# See wait_for_slave_param.inc for descriptions. +# $slave_timeout +# See wait_for_slave_param.inc for description. # -# $slave_keep_connection. -# See wait_for_slave_param.inc for descriptions. +# $master_connection +# See wait_for_slave_param.inc for description. -let $old_slave_param_comparison= $slave_param_comparison; - -let $slave_param= Last_SQL_Errno; -let $slave_param_comparison= !=; -let $slave_param_value= 0; - -if ($slave_sql_errno) { - let $slave_param_comparison= =; - let $slave_param_value= $slave_sql_errno; +if (`SELECT '$slave_sql_errno' = ''`) { + --echo !!!ERROR IN TEST: you must set \$slave_sql_errno before sourcing wait_fro_slave_sql_error.inc + exit; } -let $slave_error_message= Failed while waiting for slave to produce an error in its sql thread; +let $slave_param= Slave_SQL_Running; +let $slave_param_value= No; +let $slave_error_message= Failed while waiting for slave to stop the SQL thread (expecting error in the SQL thread); source include/wait_for_slave_param.inc; -let $slave_error_message= ; -let $slave_param_comparison= $old_slave_param_comparison; +let $_error= query_get_value(SHOW SLAVE STATUS, Last_SQL_Errno, 1); +if (`SELECT '$_error' != '$slave_sql_errno'`) { + --echo Slave stopped with wrong error code: $_error (expected $slave_sql_errno) + source include/show_rpl_debug_info.inc; + exit; +} diff --git a/mysql-test/include/wait_for_slave_sql_error_and_skip.inc b/mysql-test/include/wait_for_slave_sql_error_and_skip.inc index 9b5641bfa34..247de3a41a1 100644 --- a/mysql-test/include/wait_for_slave_sql_error_and_skip.inc +++ b/mysql-test/include/wait_for_slave_sql_error_and_skip.inc @@ -5,8 +5,23 @@ # # ==== Usage ==== # -# let show_sql_error=0|1; +# let $slave_sql_error= ; # source include/wait_for_slave_sql_error_and_skip.inc; +# +# Parameters: +# +# $slave_sql_errno +# The error number to wait for. This is required. (See +# wait_for_slave_sql_error.inc) +# +# $show_sql_error +# If set, will print the error to the query log. +# +# $slave_timeout +# See wait_for_slave_param.inc for description. +# +# $master_connection +# See wait_for_slave_param.inc for description. echo --source include/wait_for_slave_sql_error_and_skip.inc; connection slave; @@ -17,9 +32,6 @@ if ($show_sql_error) echo Last_SQL_Error = $error; } -# wait for SQL thread to stop after the error -source include/wait_for_slave_sql_to_stop.inc; - # skip the erroneous statement set global sql_slave_skip_counter=1; source include/start_slave.inc; diff --git a/mysql-test/include/wait_for_slave_sql_to_start.inc b/mysql-test/include/wait_for_slave_sql_to_start.inc index 45f32f97553..48744f5dd13 100644 --- a/mysql-test/include/wait_for_slave_sql_to_start.inc +++ b/mysql-test/include/wait_for_slave_sql_to_start.inc @@ -8,7 +8,7 @@ # source include/wait_for_slave_sql_to_start.inc; # # Parameters to this macro are $slave_timeout and -# $slave_keep_connection. See wait_for_slave_param.inc for +# $master_connection. See wait_for_slave_param.inc for # descriptions. let $slave_param= Slave_SQL_Running; diff --git a/mysql-test/include/wait_for_slave_sql_to_stop.inc b/mysql-test/include/wait_for_slave_sql_to_stop.inc index 565203a9f59..6992613b646 100644 --- a/mysql-test/include/wait_for_slave_sql_to_stop.inc +++ b/mysql-test/include/wait_for_slave_sql_to_stop.inc @@ -8,7 +8,7 @@ # source include/wait_for_slave_sql_to_stop.inc; # # Parameters to this macro are $slave_timeout and -# $slave_keep_connection. See wait_for_slave_param.inc for +# $master_connection. See wait_for_slave_param.inc for # descriptions. # if server has not used CHANGE MASTER to initiate slave, SHOW SLAVE diff --git a/mysql-test/include/wait_for_slave_to_start.inc b/mysql-test/include/wait_for_slave_to_start.inc index 3e23ceac4cc..567950cc6d7 100644 --- a/mysql-test/include/wait_for_slave_to_start.inc +++ b/mysql-test/include/wait_for_slave_to_start.inc @@ -8,7 +8,7 @@ # source include/wait_for_slave_to_start.inc; # # Parameters to this macro are $slave_timeout and -# $slave_keep_connection. See wait_for_slave_param.inc for +# $master_connection. See wait_for_slave_param.inc for # descriptions. let $slave_error_message= Failed while waiting for slave to start; diff --git a/mysql-test/include/wait_for_slave_to_stop.inc b/mysql-test/include/wait_for_slave_to_stop.inc index 86604a15b95..56d0e7b0c91 100644 --- a/mysql-test/include/wait_for_slave_to_stop.inc +++ b/mysql-test/include/wait_for_slave_to_stop.inc @@ -8,7 +8,7 @@ # source include/wait_for_slave_to_stop.inc; # # Parameters to this macro are $slave_timeout and -# $slave_keep_connection. See wait_for_slave_param.inc for +# $master_connection. See wait_for_slave_param.inc for # descriptions. # if server has not used CHANGE MASTER to initiate slave, SHOW SLAVE diff --git a/mysql-test/suite/rpl/r/rpl_row_conflicts.result b/mysql-test/suite/rpl/r/rpl_row_conflicts.result new file mode 100644 index 00000000000..0f15bfc7156 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_row_conflicts.result @@ -0,0 +1,109 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +call mtr.add_suppression("Slave: Can\'t find record in \'t1\' Error_code: .*"); +[on slave] +SET @old_slave_exec_mode= @@global.slave_exec_mode; +######## Run with slave_exec_mode=STRICT ######## +SET @@global.slave_exec_mode = 'STRICT'; +==== Initialize ==== +[on master] +CREATE TABLE t1(a INT PRIMARY KEY); +[on slave] +==== Test: SQL thread sees 'INSERT' of existing key ==== +---- Prepare slave so that it will get duplicate key error ---- +INSERT INTO t1 VALUES (1); +---- Insert rows on master ---- +[on master] +INSERT INTO t1 VALUES (1); +SELECT * FROM t1; +a +1 +[on slave] +---- Wait until slave stops with an error ---- +Last_SQL_Error = Could not execute Write_rows event on table test.t1; Duplicate entry '1' for key 'PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log master-bin.000001, end_log_pos 346 (expected "duplicate key" error) +SELECT * FROM t1; +a +1 +---- Resolve the conflict on the slave and restart SQL thread ---- +DELETE FROM t1 WHERE a = 1; +START SLAVE SQL_THREAD; +---- Sync slave and verify that there is no error ---- +Last_SQL_Error = '' (expected no error) +SELECT * FROM t1; +a +1 +==== Test: SQL thread sees 'DELETE' of non-existing row ==== +---- On master, insert two rows, the second with binlogging off ---- +[on master] +DELETE FROM t1; +INSERT INTO t1 VALUES (1); +[on slave] +DELETE FROM t1 WHERE a = 1; +---- On master, remove the row that does not exist on slave ---- +[on master] +DELETE FROM t1 WHERE a = 1; +SELECT * FROM t1; +a +[on slave] +---- Wait until slave stops with an error ---- +Last_SQL_Error = Could not execute Delete_rows event on table test.t1; Can't find record in 't1', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log master-bin.000001, end_log_pos 982 (expected "can't find record" error) +SELECT * FROM t1; +a +---- Resolve the conflict on the slave and restart SQL thread ---- +INSERT INTO t1 VALUES (1); +START SLAVE SQL_THREAD; +---- Sync slave and verify that there is no error ---- +Last_SQL_Error = (expected no error) +SELECT * FROM t1; +a +==== Clean up ==== +[on master] +DROP TABLE t1; +[on slave] +######## Run with slave_exec_mode=IDEMPOTENT ######## +set @@global.slave_exec_mode= 'IDEMPOTENT'; +==== Initialize ==== +[on master] +CREATE TABLE t1(a INT PRIMARY KEY); +[on slave] +==== Test: SQL thread sees 'INSERT' of existing key ==== +---- Prepare slave so that it will get duplicate key error ---- +INSERT INTO t1 VALUES (1); +---- Insert rows on master ---- +[on master] +INSERT INTO t1 VALUES (1); +SELECT * FROM t1; +a +1 +[on slave] +---- Sync slave and verify that there is no error ---- +Last_SQL_Error = '' (expected no error) +SELECT * FROM t1; +a +1 +==== Test: SQL thread sees 'DELETE' of non-existing row ==== +---- On master, insert two rows, the second with binlogging off ---- +[on master] +DELETE FROM t1; +INSERT INTO t1 VALUES (1); +[on slave] +DELETE FROM t1 WHERE a = 1; +---- On master, remove the row that does not exist on slave ---- +[on master] +DELETE FROM t1 WHERE a = 1; +SELECT * FROM t1; +a +[on slave] +---- Sync slave and verify that there is no error ---- +Last_SQL_Error = (expected no error) +SELECT * FROM t1; +a +==== Clean up ==== +[on master] +DROP TABLE t1; +[on slave] +SET @@global.slave_exec_mode= @old_slave_exec_mode; diff --git a/mysql-test/suite/rpl/r/rpl_row_mystery22.result b/mysql-test/suite/rpl/r/rpl_row_mystery22.result deleted file mode 100644 index 5e42a89d741..00000000000 --- a/mysql-test/suite/rpl/r/rpl_row_mystery22.result +++ /dev/null @@ -1,32 +0,0 @@ -stop slave; -drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; -reset master; -reset slave; -drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; -start slave; -create table t1(n int auto_increment primary key, s char(10)); -set @@global.slave_exec_mode= 'IDEMPOTENT'; -insert into t1 values (2,'old'); -insert into t1 values(NULL,'new'); -insert into t1 values(NULL,'new'); -select * from t1 order by n; -n s -1 new -2 new -delete from t1 where n = 2; -start slave; -stop slave; -create table t2(n int); -drop table t2; -insert into t1 values(NULL,'new'); -set sql_log_bin=0; -insert into t1 values(NULL,'new'); -set sql_log_bin=1; -delete from t1 where n=4; -start slave; -select * from t1 order by n; -n s -1 new -3 new -drop table t1; -set @@global.slave_exec_mode= default; diff --git a/mysql-test/suite/rpl/r/rpl_stm_conflicts.result b/mysql-test/suite/rpl/r/rpl_stm_conflicts.result new file mode 100644 index 00000000000..b0df9516b7c --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_stm_conflicts.result @@ -0,0 +1,54 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +==== Initialize ==== +[on master] +CREATE TABLE t1(a INT PRIMARY KEY); +[on slave] +==== Test: SQL thread sees 'INSERT' of existing key ==== +---- Prepare slave so that it will get duplicate key error ---- +INSERT INTO t1 VALUES (1); +---- Insert rows on master ---- +[on master] +INSERT INTO t1 VALUES (1); +SELECT * FROM t1; +a +1 +[on slave] +---- Wait until slave stops with an error ---- +Last_SQL_Error = Error 'Duplicate entry '1' for key 'PRIMARY'' on query. Default database: 'test'. Query: 'INSERT INTO t1 VALUES (1)' (expected "duplicate key" error) +SELECT * FROM t1; +a +1 +---- Resolve the conflict on the slave and restart SQL thread ---- +DELETE FROM t1 WHERE a = 1; +START SLAVE SQL_THREAD; +---- Sync slave and verify that there is no error ---- +Last_SQL_Error = '' (expected no error) +SELECT * FROM t1; +a +1 +==== Test: SQL thread sees 'DELETE' of non-existing row ==== +---- On master, insert two rows, the second with binlogging off ---- +[on master] +DELETE FROM t1; +INSERT INTO t1 VALUES (1); +[on slave] +DELETE FROM t1 WHERE a = 1; +---- On master, remove the row that does not exist on slave ---- +[on master] +DELETE FROM t1 WHERE a = 1; +SELECT * FROM t1; +a +[on slave] +---- Sync slave and verify that there is no error ---- +Last_SQL_Error = (expected no error) +SELECT * FROM t1; +a +==== Clean up ==== +[on master] +DROP TABLE t1; +[on slave] diff --git a/mysql-test/suite/rpl/r/rpl_stm_mystery22.result b/mysql-test/suite/rpl/r/rpl_stm_mystery22.result deleted file mode 100644 index ea34b308ec2..00000000000 --- a/mysql-test/suite/rpl/r/rpl_stm_mystery22.result +++ /dev/null @@ -1,31 +0,0 @@ -stop slave; -drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; -reset master; -reset slave; -drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; -start slave; -create table t1(n int auto_increment primary key, s char(10)); -insert into t1 values (2,'old'); -insert into t1 values(NULL,'new'); -insert into t1 values(NULL,'new'); -select * from t1 order by n; -n s -1 new -2 old -delete from t1 where n = 2; -start slave; -stop slave; -create table t2(n int); -drop table t2; -insert into t1 values(NULL,'new'); -set sql_log_bin=0; -insert into t1 values(NULL,'new'); -set sql_log_bin=1; -delete from t1 where n=4; -start slave; -select * from t1 order by n; -n s -1 new -2 new -3 new -drop table t1; diff --git a/mysql-test/suite/rpl/t/rpl_dual_pos_advance.test b/mysql-test/suite/rpl/t/rpl_dual_pos_advance.test index 11dd46727dc..9efb3d16d2b 100644 --- a/mysql-test/suite/rpl/t/rpl_dual_pos_advance.test +++ b/mysql-test/suite/rpl/t/rpl_dual_pos_advance.test @@ -12,7 +12,8 @@ source include/have_innodb.inc; # set up "dual head" -let $slave_keep_connection= 1; +# Needed for debug info in wait_for_slave_sql_to_stop. +let $master_connection= slave; connection slave; reset master; diff --git a/mysql-test/suite/rpl/t/rpl_filter_tables_not_exist.test b/mysql-test/suite/rpl/t/rpl_filter_tables_not_exist.test index 274599857be..bf71ffbfd1e 100644 --- a/mysql-test/suite/rpl/t/rpl_filter_tables_not_exist.test +++ b/mysql-test/suite/rpl/t/rpl_filter_tables_not_exist.test @@ -124,9 +124,11 @@ UPDATE t7 LEFT JOIN (t8, t4, t1) ON (t7.id=t8.id and t7.id=t4.id and t7.id=t1.id sync_slave_with_master; connection master; -# Parameter for include/wait_for_slave_sql_error_and_skip.inc, ask it -# to show SQL error message -let show_sql_error=1; +# Parameters for include/wait_for_slave_sql_error_and_skip.inc: +# Ask it to show SQL error message. +let $show_sql_error= 1; +# The expected error will always be 1146 (ER_NO_SUCH_TABLE). +let $slave_sql_errno= 1146; # # Only do tables are referenced for update, these statements should diff --git a/mysql-test/suite/rpl/t/rpl_row_conflicts.test b/mysql-test/suite/rpl/t/rpl_row_conflicts.test new file mode 100644 index 00000000000..59757e2e802 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_row_conflicts.test @@ -0,0 +1,31 @@ +# See the top of mysql-test/extra/rpl_tests/rpl_conflicts.test for +# explanation of what this test does. +# +# This test file is for row-logging mode. It runs the test twice, with +# slave_exec_mode=STRICT and slave_exec_mode=IDEMPOTENT, respectively. + +source include/have_binlog_format_row.inc; +source include/master-slave.inc; + +connection slave; +call mtr.add_suppression("Slave: Can\'t find record in \'t1\' Error_code: .*"); + +--echo [on slave] +connection slave; +SET @old_slave_exec_mode= @@global.slave_exec_mode; + + +--echo ######## Run with slave_exec_mode=STRICT ######## + +SET @@global.slave_exec_mode = 'STRICT'; +source extra/rpl_tests/rpl_conflicts.test; + + +--echo ######## Run with slave_exec_mode=IDEMPOTENT ######## + +set @@global.slave_exec_mode= 'IDEMPOTENT'; +source extra/rpl_tests/rpl_conflicts.test; + + +SET @@global.slave_exec_mode= @old_slave_exec_mode; +source include/master-slave-end.inc; diff --git a/mysql-test/suite/rpl/t/rpl_row_mystery22.test b/mysql-test/suite/rpl/t/rpl_row_mystery22.test deleted file mode 100644 index a3ba8648b22..00000000000 --- a/mysql-test/suite/rpl/t/rpl_row_mystery22.test +++ /dev/null @@ -1,52 +0,0 @@ -# Originally taken from rpl_mystery22.test, -# but this row-based-replication test has a totally different spirit: -# slave will not stop because of dup key, -# instead we test if it does overwrite the dup key -# as expected. --- source include/have_binlog_format_row.inc --- source include/master-slave.inc - -# first, cause a duplicate key problem on the slave -create table t1(n int auto_increment primary key, s char(10)); -sync_slave_with_master; - -# bug#31552/31609 idempotency is not default any longer -# so that the declared in heading comments aim of the test -# should be backed up with explicit setting of the slave mode -set @@global.slave_exec_mode= 'IDEMPOTENT'; - -insert into t1 values (2,'old'); -connection master; -insert into t1 values(NULL,'new'); -insert into t1 values(NULL,'new'); -save_master_pos; -connection slave; -sync_with_master; -select * from t1 order by n; -delete from t1 where n = 2; ---disable_warnings -start slave; ---enable_warnings -sync_with_master; -stop slave; -connection master; -create table t2(n int); -drop table t2; -insert into t1 values(NULL,'new'); -# what happens when we delete a row which does not exist on slave? -set sql_log_bin=0; -insert into t1 values(NULL,'new'); -set sql_log_bin=1; -delete from t1 where n=4; -save_master_pos; -connection slave; ---disable_warnings -start slave; ---enable_warnings -sync_with_master; -select * from t1 order by n; -#clean up -connection master; -drop table t1; -sync_slave_with_master; -set @@global.slave_exec_mode= default; diff --git a/mysql-test/suite/rpl/t/rpl_stm_conflicts.test b/mysql-test/suite/rpl/t/rpl_stm_conflicts.test new file mode 100644 index 00000000000..07b7a0bf8f7 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_stm_conflicts.test @@ -0,0 +1,11 @@ +# See the top of mysql-test/extra/rpl_tests/rpl_conflicts.test for an +# explanation of what this test does. +# +# This test file is for statement-logging mode. + +source include/have_binlog_format_mixed_or_statement.inc; +source include/master-slave.inc; + +source extra/rpl_tests/rpl_conflicts.test; + +source include/master-slave-end.inc; diff --git a/mysql-test/suite/rpl/t/rpl_stm_mystery22.test b/mysql-test/suite/rpl/t/rpl_stm_mystery22.test deleted file mode 100644 index 171bd9d5531..00000000000 --- a/mysql-test/suite/rpl/t/rpl_stm_mystery22.test +++ /dev/null @@ -1,67 +0,0 @@ -################################ -# Change Author: JBM -# Change Date: 2006-01-12 -# Change: Added back have stm binlog -# and added requirments comments -################################ -# test case to make slave thread get ahead by 22 bytes -################################ -#REQUIREMENT: If there is a faked slave duplicate key insert -#error and the slave is restarted, the replication should -#proceed in a correct way. -################################ -#REQUIREMENT: If there is a faked slave non-existing record -#delete error and the slave is restarted, then the replication -#should proceed in a correct way. -################################# - --- source include/have_binlog_format_mixed_or_statement.inc --- source include/master-slave.inc - -# first, cause a duplicate key problem on the slave -create table t1(n int auto_increment primary key, s char(10)); -sync_slave_with_master; -insert into t1 values (2,'old'); -connection master; -insert into t1 values(NULL,'new'); -insert into t1 values(NULL,'new'); -save_master_pos; -connection slave; -# wait until the slave tries to run the query, fails and aborts slave thread -let $slave_sql_errno= 1062; -source include/wait_for_slave_sql_error.inc; -select * from t1 order by n; -delete from t1 where n = 2; ---disable_warnings -start slave; ---enable_warnings -sync_with_master; -#now the buggy slave would be confused on the offset but it can replicate -#in order to make it break, we need to stop/start the slave one more time -stop slave; -connection master; -# to be able to really confuse the slave, we need some non-auto-increment -# events in the log -create table t2(n int); -drop table t2; -insert into t1 values(NULL,'new'); -# what happens when we delete a row which does not exist on slave? -set sql_log_bin=0; -insert into t1 values(NULL,'new'); -set sql_log_bin=1; -delete from t1 where n=4; -save_master_pos; -connection slave; ---disable_warnings -start slave; ---enable_warnings -#now the truth comes out - if the slave is buggy, it will never sync because -#the slave thread is not able to read events -sync_with_master; -select * from t1 order by n; -#clean up -connection master; -drop table t1; -sync_slave_with_master; - -# End of 4.1 tests