diff --git a/mysql-test/r/binlog_killed.result b/mysql-test/r/binlog_killed.result index ba4f38fb4c1..ddd80283eca 100644 --- a/mysql-test/r/binlog_killed.result +++ b/mysql-test/r/binlog_killed.result @@ -9,4 +9,110 @@ insert into t2 values (null, null), (null, get_lock("a", 10)); select @result /* must be zero either way */; @result 0 +delete from t1; +delete from t2; +insert into t1 values (1,1),(2,2); +begin; +update t1 set b=11 where a=2; +update t1 set b=b+10; +kill query ID; +rollback; +ERROR 70100: Query execution was interrupted +select * from t1 /* must be the same as before (1,1),(2,2) */; +a b +1 1 +2 2 +begin; +delete from t1 where a=2; +delete from t1 where a=2; +kill query ID; +rollback; +ERROR 70100: Query execution was interrupted +select * from t1 /* must be the same as before (1,1),(2,2) */; +a b +1 1 +2 2 +drop table if exists t4; +create table t4 (a int, b int) engine=innodb; +insert into t4 values (3, 3); +begin; +insert into t1 values (3, 3); +begin; +insert into t1 select * from t4 for update; +kill query ID; +rollback; +ERROR 70100: Query execution was interrupted +rollback; +select * from t1 /* must be the same as before (1,1),(2,2) */; +a b +1 1 +2 2 +drop table t4; +create function bug27563(n int) +RETURNS int(11) +DETERMINISTIC +begin +if n > 1 then +select get_lock("a", 10) into @a; +end if; +return n; +end| +delete from t2; +insert into t2 values (1,1), (2,2); +reset master; +select get_lock("a", 20); +get_lock("a", 20) +1 +update t2 set b=b + bug27563(b) order by a; +kill query ID; +ERROR 70100: Query execution was interrupted +select * from t2 /* must be (1,2), (2,2) */; +a b +1 2 +2 2 +show master status /* must have the update event more to FD */; +File Position Binlog_Do_DB Binlog_Ignore_DB +master-bin.000001 211 +select +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null +1 +select 0 /* must return 0 to mean the killed query is in */; +0 +0 +select RELEASE_LOCK("a"); +RELEASE_LOCK("a") +1 +delete from t2; +insert into t2 values (1,1), (2,2); +reset master; +select get_lock("a", 20); +get_lock("a", 20) +1 +delete from t2 where a=1 or a=bug27563(2) order by a; +kill query ID; +ERROR 70100: Query execution was interrupted +select * from t2 /* must be (1,2), (2,2) */; +a b +1 1 +2 2 +show master status /* must have the update event more to FD */; +File Position Binlog_Do_DB Binlog_Ignore_DB +master-bin.000001 98 +select +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null +1 +select 0 /* must return 0 to mean the killed query is in */; +0 +0 +select RELEASE_LOCK("a"); +RELEASE_LOCK("a") +1 +drop function bug27563; drop table t1,t2,t3; +end of the tests diff --git a/mysql-test/t/binlog_killed.test b/mysql-test/t/binlog_killed.test index 034895f17cb..0e35e46c845 100644 --- a/mysql-test/t/binlog_killed.test +++ b/mysql-test/t/binlog_killed.test @@ -55,194 +55,239 @@ enable_result_log; select @result /* must be zero either way */; -# the functions are either *insensitive* to killing or killing can cause -# strange problmes with the error propagation out of SF's stack -# Bug#27563, Bug#27565, BUG#24971 + +--remove_file $MYSQLTEST_VARDIR/tmp/kill_query_calling_sp.binlog + # -# TODO: use if's block as regression test for the bugs or remove +# bug#27571 asynchronous setting mysql_`query`::error and Query_log_e::error_code # -if (0) -{ + +# checking that killing inside of select loops is safe as before +# killing after the loop can be only simulated - another test + +delete from t1; +delete from t2; +insert into t1 values (1,1),(2,2); +let $ID= `select connection_id()`; + +# +# simple update +# +connection con1; +begin; update t1 set b=11 where a=2; + +connection con2; +send update t1 set b=b+10; + +connection con1; +--replace_result $ID ID +eval kill query $ID; +rollback; + +connection con2; +--error ER_QUERY_INTERRUPTED +reap; +select * from t1 /* must be the same as before (1,1),(2,2) */; + +# +# multi update +# commented out as Bug #31807 multi-update,delete killing does not report with ER_QUERY_INTERRUPTED +# in the way +# +# connection con1; +# begin; update t1 set b=b+10; + +# connection con2; +# send update t1 as t_1,t1 as t_2 set t_1.b=11 where t_2.a=2; + +# connection con1; +# --replace_result $ID ID +# eval kill query $ID; +# rollback; + +# disable_abort_on_error; + +# connection con2; +# --error HY000,ER_QUERY_INTERRUPTED +# reap; +# select * from t1 /* must be the same as before (1,1),(2,2) */; + +# enable_abort_on_error; +# +# simple delete +# +connection con1; +begin; delete from t1 where a=2; + +connection con2; +send delete from t1 where a=2; + +connection con1; +--replace_result $ID ID +eval kill query $ID; +rollback; + +connection con2; +--error ER_QUERY_INTERRUPTED +reap; +select * from t1 /* must be the same as before (1,1),(2,2) */; + +# +# multi delete +# the same as for multi-update +# +# connection con1; +# begin; delete from t1 where a=2; + +# connection con2; +# send delete t1 from t1 where t1.a=2; + +# connection con1; +# --replace_result $ID ID +# eval kill query $ID; +# rollback; + +# connection con2; +# --error 0,ER_QUERY_INTERRUPTED +# reap; +# select * from t1 /* must be the same as before (1,1),(2,2) */; +# +# insert select +# +connection con1; +--disable_warnings +drop table if exists t4; +--enable_warnings +create table t4 (a int, b int) engine=innodb; +insert into t4 values (3, 3); +begin; insert into t1 values (3, 3); + +connection con2; +begin; +send insert into t1 select * from t4 for update; + +connection con1; +--replace_result $ID ID +eval kill query $ID; +rollback; + +connection con2; +--error ER_QUERY_INTERRUPTED +reap; +rollback; +select * from t1 /* must be the same as before (1,1),(2,2) */; + +drop table t4; # cleanup for the sub-case + +### +## non-ta table case: killing must be recorded in binlog +### delimiter |; -create function bug27563() +create function bug27563(n int) RETURNS int(11) DETERMINISTIC begin - select get_lock("a", 10) into @a; - return 1; + if n > 1 then + select get_lock("a", 10) into @a; + end if; + return n; end| delimiter ;| -# the function is sensitive to killing requiring innodb though with wrong client error -# TO FIX in BUG#27565; TODO: remove --error 1105 afterwards -delimiter |; -create function bug27565() -RETURNS int(11) -DETERMINISTIC -begin - select a from t1 where a=1 into @a for update; - return 1; -end| -delimiter ;| +# +# update +# +delete from t2; +insert into t2 values (1,1), (2,2); reset master; - - -### ta table case: killing causes rollback - -# A. autocommit ON connection con1; select get_lock("a", 20); connection con2; let $ID= `select connection_id()`; -send insert into t1 values (bug27563(),1); +send update t2 set b=b + bug27563(b) order by a; connection con1; +--replace_result $ID ID eval kill query $ID; connection con2; -# todo (re-record test): after bugs 27563,27565 got fixed affected rows will report zero ---enable_info -# todo: remove 0 return after fixing Bug#27563 ---error 0,ER_QUERY_INTERRUPTED -reap; ### pb: wrong error ---disable_info -###--replace_column 2 # 5 # -### show binlog events from 98 /* nothing in binlog unless Bug#27563 */; -show master status /* must be only FD event unless Bug#27563 */; -select count(*) from t1 /* must be zero unless Bug#27563 */; - -# M. multi-statement-ta -connection con2; -let $ID= `select connection_id()`; -begin; -send insert into t1 values (bug27563(),1); - -connection con1; -eval kill query $ID; -connection con2; -# todo (re-record test): after bugs 27563,27565 got fixed affected rows will report zero ---enable_info -# todo: remove 0 return after fixing Bug#27563 ---error 0,ER_QUERY_INTERRUPTED +--error ER_QUERY_INTERRUPTED reap; ---disable_info -select count(*) from t1 /* must be zero unless Bug#27563 */; -commit; +select * from t2 /* must be (1,2), (2,2) */; +show master status /* must have the update event more to FD */; +# a proof the query is binlogged with an error -### non-ta table case: killing must be recorded in binlog - -reset master; - -connection con2; -let $ID= `select connection_id()`; -send insert into t2 values (bug27563(),1); - -connection con1; -eval kill query $ID; - -connection con2; -# todo: remove 0 return after fixing Bug#27563 ---error 0,ER_QUERY_INTERRUPTED -reap; -select count(*) from t2 /* must be one */; -#show binlog events from 98 /* must have the insert on non-ta table */; -show master status /* must have the insert event more to FD */; -# the value of the error flag of KILLED_QUERY is tested further +--exec $MYSQL_BINLOG --start-position=98 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval select +(@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +let $error_code= `select @a like "%#%error_code=0%" /* must return 0*/`; +eval select $error_code /* must return 0 to mean the killed query is in */; +# cleanup for the sub-case connection con1; select RELEASE_LOCK("a"); +--remove_file $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog -### test with effective killing of SF() - -delete from t1; -delete from t2; -insert into t1 values (1,1); -insert into t2 values (1,1); - -# -# Bug#27565 -# test where KILL is propagated as error to the top level -# still another bug with the error message to the user -# todo: fix reexecute the result file after fixing -# -begin; update t1 set b=0 where a=1; - -connection con2; -let $ID= `select connection_id()`; -send update t2 set b=bug27565()-1 where a=1; - -connection con1; -eval kill query $ID; -commit; - -connection con2; -# todo: fix Bug #27565 killed query of SF() is not reported correctly and -# remove 1105 (wrong) -#--error ER_QUERY_INTERRUPTED ---error 1105,ER_QUERY_INTERRUPTED -reap; ### pb: wrong error -select * from t1 /* must be: (1,0) */; -select * from t2 /* must be as before: (1,1) */; - -## bug#22725 with effective and propagating killing # -# top-level ta-table -connection con1; -delete from t3; -reset master; -begin; update t1 set b=0 where a=1; +# delete +# -connection con2; -let $ID= `select connection_id()`; -# the query won't perform completely since the function gets interrupted -send insert into t3 values (0,0),(1,bug27565()); - -connection con1; -eval kill query $ID; -rollback; - -connection con2; -# todo: fix Bug #27565 killed query of SF() is not reported correctly and -# remove 1105 (wrong) -#--error ER_QUERY_INTERRUPTED ---error 1105,ER_QUERY_INTERRUPTED -reap; ### pb: wrong error -select count(*) from t3 /* must be zero */; -show master status /* nothing in binlog */; - -# top-level non-ta-table -connection con1; delete from t2; +insert into t2 values (1,1), (2,2); reset master; -begin; update t1 set b=0 where a=1; +connection con1; +select get_lock("a", 20); connection con2; let $ID= `select connection_id()`; -# the query won't perform completely since the function gets intrurrupted -send insert into t2 values (0,0),(1,bug27565()) /* non-ta t2 */; +send delete from t2 where a=1 or a=bug27563(2) order by a; connection con1; +--replace_result $ID ID eval kill query $ID; -rollback; connection con2; -# todo: fix Bug #27565 killed query of SF() is not reported correctly and -# remove 1105 (wrong) -#--error ER_QUERY_INTERRUPTED ---error 1105,ER_QUERY_INTERRUPTED -reap; ### pb: wrong error +--error ER_QUERY_INTERRUPTED +reap; +select * from t2 /* must be (1,2), (2,2) */; +show master status /* must have the update event more to FD */; -select count(*) from t2 /* count must be one */; -show master status /* insert into non-ta must be in binlog */; +# a proof the query is binlogged with an error + +--exec $MYSQL_BINLOG --start-position=98 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval select +(@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +let $error_code= `select @a like "%#%error_code=0%" /* must return 0*/`; +eval select $error_code /* must return 0 to mean the killed query is in */; + +# cleanup for the sub-case +connection con1; +select RELEASE_LOCK("a"); +--remove_file $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog + +# +# load data - see simulation tests +# + + +# bug#27571 cleanup drop function bug27563; -drop function bug27565; -} -system rm $MYSQLTEST_VARDIR/tmp/kill_query_calling_sp.binlog ; + +# +# common cleanup +# drop table t1,t2,t3; +--echo end of the tests diff --git a/mysql-test/t/binlog_killed_bug27571-master.opt b/mysql-test/t/binlog_killed_bug27571-master.opt new file mode 100644 index 00000000000..d269cf246d5 --- /dev/null +++ b/mysql-test/t/binlog_killed_bug27571-master.opt @@ -0,0 +1 @@ +--loose-debug=d,stop_after_row_loop_done diff --git a/mysql-test/t/binlog_killed_bug27571.test b/mysql-test/t/binlog_killed_bug27571.test new file mode 100644 index 00000000000..6fa3c6d256f --- /dev/null +++ b/mysql-test/t/binlog_killed_bug27571.test @@ -0,0 +1,68 @@ +--source include/have_innodb.inc +--source include/not_embedded.inc +--source include/have_log_bin.inc + +# +# bug#27571 asynchronous setting mysql_`query`::error and Query_log_e::error_code +# +# Checking that if killing happens inbetween of the end of rows loop and +# recording into binlog that will not lead to recording any error incl +# the killed error. +# + +connect (looser, localhost, root,,); +connect (killer, localhost, root,,); + +create table t1 (a int auto_increment, b int, PRIMARY KEY (a)) ENGINE=InnoDB; + +delete from t1; +insert into t1 values (1,1),(2,2); +reset master; + +connection looser; +let $ID= `select connection_id()`; +send update t1 set b=11 where a=2; + +connection killer; +sleep 1; # let 1 second for the update to get to the sleeping point +--replace_result $ID ID +eval kill query $ID; + +connection looser; +--error 0 # zero even though the query must be got killed while it was sleepin for 5 secs +reap; + +# +# this is another possible artifact. The killed error was not caught +# as that is logical as killing was not effective: +# data are ok and well as binlog event is without killed error (further). +# The reason of the following `show error' is to prove that +# killing simulation was effective +# +show errors; + +connection killer; + +# nothing is rolled back + +select * from t1 where a=2 /* must be 11 */; + +# a proof the query is binlogged with an error + +--exec $MYSQL_BINLOG --start-position=98 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval select +(@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +let $error_code= `select @a like "%#%error_code=0%"`; + +eval select $error_code /* must return 1*/; + +# +# cleanup +# + +drop table t1; + +--echo end of the tests diff --git a/mysql-test/t/binlog_killed_simulate-master.opt b/mysql-test/t/binlog_killed_simulate-master.opt new file mode 100644 index 00000000000..90c70ecee29 --- /dev/null +++ b/mysql-test/t/binlog_killed_simulate-master.opt @@ -0,0 +1 @@ +--loose-debug=d,simulate_kill_bug27571 diff --git a/mysql-test/t/binlog_killed_simulate.test b/mysql-test/t/binlog_killed_simulate.test new file mode 100644 index 00000000000..d6234d1bfd7 --- /dev/null +++ b/mysql-test/t/binlog_killed_simulate.test @@ -0,0 +1,65 @@ +# +# bug#27571 asynchronous setting mysql_$query()'s local error and +# Query_log_event::error_code +# + +--disable_warnings +drop table if exists t1,t2; +--enable_warnings + +# +# Checking that killing upon successful row-loop does not affect binlogging +# + +create table t1 (a int) engine=MyISAM; +insert into t1 set a=1; +reset master; + +update t1 set a=2 /* will be "killed" after work has been done */; + +# a proof the query is binlogged with no error + +--exec $MYSQL_BINLOG --start-position=98 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval select +(@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +let $error_code= `select @a like "%#%error_code=0%" /* must return 1 */`; +eval select $error_code /* must return 1 as query completed before got killed*/; + +# cleanup for the sub-case +system rm $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog; + + +# +# Checking that killing inside of row-loop for LOAD DATA into +# non-transactional table affects binlogging +# + +create table t2 (a int, b int) ENGINE=MyISAM; +reset master; +--error ER_QUERY_INTERRUPTED +load data infile '../std_data_ln/rpl_loaddata.dat' into table t2 /* will be "killed" in the middle */; + + +# a proof the query is binlogged with an error + +source include/show_binlog_events.inc; + +--exec $MYSQL_BINLOG --start-position=98 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval select +(@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +let $error_code= `select @a like "%#%error_code=0%" /* must return 0*/`; +eval select $error_code /* must return 0 to mean the killed query is in */; + +# cleanup for the sub-case +system rm $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog; + + +drop table t1,t2; + +--echo end of the tests diff --git a/sql/log_event.cc b/sql/log_event.cc index 3899e772bf8..da616adbb09 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -4994,12 +4994,13 @@ int Begin_load_query_log_event::get_create_or_append() const #ifndef MYSQL_CLIENT Execute_load_query_log_event:: Execute_load_query_log_event(THD* thd_arg, const char* query_arg, - ulong query_length_arg, uint fn_pos_start_arg, - uint fn_pos_end_arg, - enum_load_dup_handling dup_handling_arg, - bool using_trans, bool suppress_use): + ulong query_length_arg, uint fn_pos_start_arg, + uint fn_pos_end_arg, + enum_load_dup_handling dup_handling_arg, + bool using_trans, bool suppress_use, + THD::killed_state killed_err_arg): Query_log_event(thd_arg, query_arg, query_length_arg, using_trans, - suppress_use), + suppress_use, killed_err_arg), file_id(thd_arg->file_id), fn_pos_start(fn_pos_start_arg), fn_pos_end(fn_pos_end_arg), dup_handling(dup_handling_arg) { diff --git a/sql/log_event.h b/sql/log_event.h index 04aac5d08fc..5b065a33dd1 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1619,10 +1619,12 @@ public: #ifndef MYSQL_CLIENT Execute_load_query_log_event(THD* thd, const char* query_arg, - ulong query_length, uint fn_pos_start_arg, - uint fn_pos_end_arg, - enum_load_dup_handling dup_handling_arg, - bool using_trans, bool suppress_use); + ulong query_length, uint fn_pos_start_arg, + uint fn_pos_end_arg, + enum_load_dup_handling dup_handling_arg, + bool using_trans, bool suppress_use, + THD::killed_state + killed_err_arg= THD::KILLED_NO_VALUE); #ifdef HAVE_REPLICATION void pack_info(Protocol* protocol); int exec_event(struct st_relay_log_info* rli); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index add37c8c552..a28a39a769d 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -38,6 +38,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ha_rows deleted; uint usable_index= MAX_KEY; SELECT_LEX *select_lex= &thd->lex->select_lex; + THD::killed_state killed_status= THD::NOT_KILLED; DBUG_ENTER("mysql_delete"); if (open_and_lock_tables(thd, table_list)) @@ -280,8 +281,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, else table->file->unlock_row(); // Row failed selection, release lock on it } - if (thd->killed && !error) - error= 1; // Aborted + killed_status= thd->killed; + error= (killed_status == THD::NOT_KILLED)? error : 1; thd->proc_info="end"; end_read_record(&info); free_io_cache(table); // Will not do any harm @@ -326,7 +327,7 @@ cleanup: if (error < 0) thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, - transactional_table, FALSE); + transactional_table, FALSE, killed_status); if (mysql_bin_log.write(&qinfo) && transactional_table) error=1; } @@ -729,7 +730,8 @@ void multi_delete::send_error(uint errcode,const char *err) } thd->transaction.all.modified_non_trans_table= true; } - DBUG_ASSERT(!normal_tables || !deleted || thd->transaction.stmt.modified_non_trans_table); + DBUG_ASSERT(!normal_tables || !deleted || + thd->transaction.stmt.modified_non_trans_table); DBUG_VOID_RETURN; } @@ -817,6 +819,7 @@ int multi_delete::do_deletes() bool multi_delete::send_eof() { + THD::killed_state killed_status= THD::NOT_KILLED; thd->proc_info="deleting from reference tables"; /* Does deletes for the last n - 1 tables, returns 0 if ok */ @@ -824,7 +827,7 @@ bool multi_delete::send_eof() /* compute a total error to know if something failed */ local_error= local_error || error; - + killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed; /* reset used flags */ thd->proc_info="end"; @@ -836,7 +839,8 @@ bool multi_delete::send_eof() { query_cache_invalidate3(thd, delete_tables, 1); } - DBUG_ASSERT(!normal_tables || !deleted || thd->transaction.stmt.modified_non_trans_table); + DBUG_ASSERT(!normal_tables || !deleted || + thd->transaction.stmt.modified_non_trans_table); if ((local_error == 0) || thd->transaction.stmt.modified_non_trans_table) { if (mysql_bin_log.is_open()) @@ -844,7 +848,7 @@ bool multi_delete::send_eof() if (local_error == 0) thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, - transactional_tables, FALSE); + transactional_tables, FALSE, killed_status); if (mysql_bin_log.write(&qinfo) && !normal_tables) local_error=1; // Log write failed: roll back the SQL statement } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index d33e8509eaf..c5f6575b756 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2936,6 +2936,7 @@ bool select_insert::send_eof() { int error, error2; bool changed, transactional_table= table->file->has_transactions(); + THD::killed_state killed_status= thd->killed; DBUG_ENTER("select_insert::send_eof"); error= (!thd->prelocked_mode) ? table->file->end_bulk_insert():0; @@ -2964,7 +2965,7 @@ bool select_insert::send_eof() if (!error) thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, - transactional_table, FALSE); + transactional_table, FALSE, killed_status); mysql_bin_log.write(&qinfo); } if ((error2=ha_autocommit_or_rollback(thd,error)) && ! error) diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 0dc02ac4a68..d687ceff393 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -85,7 +85,8 @@ static int read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, #ifndef EMBEDDED_LIBRARY static bool write_execute_load_query_log_event(THD *thd, bool duplicates, bool ignore, - bool transactional_table); + bool transactional_table, + THD::killed_state killed_status); #endif /* EMBEDDED_LIBRARY */ /* @@ -135,6 +136,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, char *tdb= thd->db ? thd->db : db; // Result is never null ulong skip_lines= ex->skip_lines; bool transactional_table; + THD::killed_state killed_status= THD::NOT_KILLED; DBUG_ENTER("mysql_load"); #ifdef EMBEDDED_LIBRARY @@ -404,7 +406,16 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, free_blobs(table); /* if pack_blob was used */ table->copy_blobs=0; thd->count_cuted_fields= CHECK_FIELD_IGNORE; - + /* + simulated killing in the middle of per-row loop + must be effective for binlogging + */ + DBUG_EXECUTE_IF("simulate_kill_bug27571", + { + error=1; + thd->killed= THD::KILL_QUERY; + };); + killed_status= (error == 0)? THD::NOT_KILLED : thd->killed; /* We must invalidate the table in query cache before binlog writing and ha_autocommit_... @@ -446,7 +457,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, { if (thd->transaction.stmt.modified_non_trans_table) write_execute_load_query_log_event(thd, handle_duplicates, - ignore, transactional_table); + ignore, transactional_table, + killed_status); else { Delete_file_log_event d(thd, db, transactional_table); @@ -477,7 +489,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, read_info.end_io_cache(); if (lf_info.wrote_create_file) write_execute_load_query_log_event(thd, handle_duplicates, - ignore, transactional_table); + ignore, transactional_table, + killed_status); } #endif /*!EMBEDDED_LIBRARY*/ if (transactional_table) @@ -504,7 +517,8 @@ err: /* Not a very useful function; just to avoid duplication of code */ static bool write_execute_load_query_log_event(THD *thd, bool duplicates, bool ignore, - bool transactional_table) + bool transactional_table, + THD::killed_state killed_err_arg) { Execute_load_query_log_event e(thd, thd->query, thd->query_length, @@ -512,7 +526,7 @@ static bool write_execute_load_query_log_event(THD *thd, (char*)thd->lex->fname_end - (char*)thd->query, (duplicates == DUP_REPLACE) ? LOAD_DUP_REPLACE : (ignore ? LOAD_DUP_IGNORE : LOAD_DUP_ERROR), - transactional_table, FALSE); + transactional_table, FALSE, killed_err_arg); return mysql_bin_log.write(&e); } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index f3695976508..14c34b6e0f1 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -134,6 +134,7 @@ int mysql_update(THD *thd, SELECT_LEX *select_lex= &thd->lex->select_lex; bool need_reopen; List all_fields; + THD::killed_state killed_status= THD::NOT_KILLED; DBUG_ENTER("mysql_update"); LINT_INIT(timestamp_query_id); @@ -519,43 +520,26 @@ int mysql_update(THD *thd, table->file->unlock_row(); thd->row_count++; } + /* + Caching the killed status to pass as the arg to query event constuctor; + The cached value can not change whereas the killed status can + (externally) since this point and change of the latter won't affect + binlogging. + It's assumed that if an error was set in combination with an effective + killed status then the error is due to killing. + */ + killed_status= thd->killed; // get the status of the volatile + // simulated killing after the loop must be ineffective for binlogging + DBUG_EXECUTE_IF("simulate_kill_bug27571", + { + thd->killed= THD::KILL_QUERY; + };); + error= (killed_status == THD::NOT_KILLED)? error : 1; + if (!transactional_table && updated > 0) thd->transaction.stmt.modified_non_trans_table= TRUE; - - /* - todo bug#27571: to avoid asynchronization of `error' and - `error_code' of binlog event constructor - - The concept, which is a bit different for insert(!), is to - replace `error' assignment with the following lines - - killed_status= thd->killed; // get the status of the volatile - - Notice: thd->killed is type of "state" whereas the lhs has - "status" the suffix which translates according to WordNet: a state - at a particular time - at the time of the end of per-row loop in - our case. Binlogging ops are conducted with the status. - - error= (killed_status == THD::NOT_KILLED)? error : 1; - - which applies to most mysql_$query functions. - Event's constructor will accept `killed_status' as an argument: - - Query_log_event qinfo(..., killed_status); - - thd->killed might be changed after killed_status had got cached and this - won't affect binlogging event but other effects remain. - - Open issue: In a case the error happened not because of KILLED - - and then KILLED was caught later still within the loop - we shall - do something to avoid binlogging of incorrect ER_SERVER_SHUTDOWN - error_code. - */ - - if (thd->killed && !error) - error= 1; // Aborted end_read_record(&info); free_io_cache(table); // If ORDER BY delete select; @@ -587,7 +571,7 @@ int mysql_update(THD *thd, if (error < 0) thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, - transactional_table, FALSE); + transactional_table, FALSE, killed_status); if (mysql_bin_log.write(&qinfo) && transactional_table) error=1; // Rollback update } @@ -1522,6 +1506,11 @@ void multi_update::send_error(uint errcode,const char *err) */ if (mysql_bin_log.is_open()) { + /* + THD::killed status might not have been set ON at time of an error + got caught and if happens later the killed error is written + into repl event. + */ Query_log_event qinfo(thd, thd->query, thd->query_length, transactional_tables, FALSE); mysql_bin_log.write(&qinfo); @@ -1709,10 +1698,19 @@ err2: bool multi_update::send_eof() { char buff[STRING_BUFFER_USUAL_SIZE]; + THD::killed_state killed_status= THD::NOT_KILLED; thd->proc_info="updating reference tables"; - /* Does updates for the last n - 1 tables, returns 0 if ok */ + /* + Does updates for the last n - 1 tables, returns 0 if ok; + error takes into account killed status gained in do_updates() + */ int local_error = (table_count) ? do_updates(0) : 0; + /* + if local_error is not set ON until after do_updates() then + later carried out killing should not affect binlogging. + */ + killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed; thd->proc_info= "end"; /* We must invalidate the query cache before binlog writing and @@ -1740,7 +1738,7 @@ bool multi_update::send_eof() if (local_error == 0) thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, - transactional_tables, FALSE); + transactional_tables, FALSE, killed_status); if (mysql_bin_log.write(&qinfo) && trans_safe) local_error= 1; // Rollback update }