summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <aelkin/elkin@koti.dsl.inet.fi>2007-10-29 15:20:59 +0200
committerunknown <aelkin/elkin@koti.dsl.inet.fi>2007-10-29 15:20:59 +0200
commit95f3db7be1b3425b47f34090f33c7eeb52f97703 (patch)
tree7ee3c865779ddc72072daa205d4b5d3c3c0280fa
parentc8b6d1050933f0b9b18367c421702c9f16907de5 (diff)
downloadmariadb-git-95f3db7be1b3425b47f34090f33c7eeb52f97703.tar.gz
Bug #27571 asynchronousity in setting mysql_`query`::error and
Query_log_event::error_code A query can perform completely having the local var error of mysql_$query zero, where $query in insert, update, delete, load, and be binlogged with error_code e.g KILLED_QUERY while there is no reason do to so. That can happen because Query_log_event consults thd->killed flag to evaluate error_code. Fixed with implementing a scheme suggested and partly implemented at time of bug@22725 work-on. error_status is cached immediatly after the control leaves the main rows-loop and that instance always corresponds to `error' the local of mysql_$query functions. The cached value is passed to Query_log_event constructor, not the default thd->killed which can be changed in between of the caching and the constructing. mysql-test/r/binlog_killed.result: results changed mysql-test/t/binlog_killed.test: Demonstrating that effective killing during rows-loop execution leads to the speficied actions: binlogging with the error for a query modified a not-transactional table or rolling back effects for transactional table; fixing possible non-determinism with ID when query_log_enabled; leave commented out tests for multi-update,delete due to another bug; removing an obsolete tests template; changing system rm to --remove_file. sql/log_event.cc: adding killed status arg sql/log_event.h: added killed status arg sql/sql_delete.cc: deploying the update part patch for delete, multi-delete sql/sql_insert.cc: deploying the update-part patch for insert..select sql/sql_load.cc: deploying the update-part patch for load data. simulation added. sql/sql_update.cc: Impementing the fix as described in the comments left by bug@22725. Also simulation of killing after the loop that would affect binlogging in the old code. mysql-test/t/binlog_killed_bug27571-master.opt: post rows-loop killing simulation's options mysql-test/t/binlog_killed_bug27571.test: 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. mysql-test/t/binlog_killed_simulate-master.opt: simulation options mysql-test/t/binlog_killed_simulate.test: tests for a query (update is choosen) being killed after the row-loop; load data killed within the loop - effective killed error in the event is gained.
-rw-r--r--mysql-test/r/binlog_killed.result106
-rw-r--r--mysql-test/t/binlog_killed.test297
-rw-r--r--mysql-test/t/binlog_killed_bug27571-master.opt1
-rw-r--r--mysql-test/t/binlog_killed_bug27571.test68
-rw-r--r--mysql-test/t/binlog_killed_simulate-master.opt1
-rw-r--r--mysql-test/t/binlog_killed_simulate.test65
-rw-r--r--sql/log_event.cc11
-rw-r--r--sql/log_event.h10
-rw-r--r--sql/sql_delete.cc18
-rw-r--r--sql/sql_insert.cc3
-rw-r--r--sql/sql_load.cc26
-rw-r--r--sql/sql_update.cc70
12 files changed, 491 insertions, 185 deletions
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
-#
-# TODO: use if's block as regression test for the bugs or remove
-#
-if (0)
-{
-delimiter |;
-create function bug27563()
-RETURNS int(11)
-DETERMINISTIC
-begin
- select get_lock("a", 10) into @a;
- return 1;
-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 ;|
-
-reset master;
+--remove_file $MYSQLTEST_VARDIR/tmp/kill_query_calling_sp.binlog
-### ta table case: killing causes rollback
+#
+# bug#27571 asynchronous setting mysql_`query`::error and Query_log_e::error_code
+#
-# A. autocommit ON
-connection con1;
-select get_lock("a", 20);
+# checking that killing inside of select loops is safe as before
+# killing after the loop can be only simulated - another test
-connection con2;
+delete from t1;
+delete from t2;
+insert into t1 values (1,1),(2,2);
let $ID= `select connection_id()`;
-send insert into t1 values (bug27563(),1);
+#
+# simple update
+#
connection con1;
-eval kill query $ID;
+begin; update t1 set b=11 where a=2;
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);
+send update t1 set b=b+10;
connection con1;
+--replace_result $ID ID
eval kill query $ID;
+rollback;
+
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 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;
-### non-ta table case: killing must be recorded in binlog
+# connection con1;
+# --replace_result $ID ID
+# eval kill query $ID;
+# rollback;
-reset master;
+# 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;
-let $ID= `select connection_id()`;
-send insert into t2 values (bug27563(),1);
+send delete from t1 where a=2;
connection con1;
+--replace_result $ID ID
eval kill query $ID;
+rollback;
connection con2;
-# todo: remove 0 return after fixing Bug#27563
---error 0,ER_QUERY_INTERRUPTED
+--error 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
+select * from t1 /* must be the same as before (1,1),(2,2) */;
-connection con1;
-select RELEASE_LOCK("a");
+#
+# multi delete
+# the same as for multi-update
+#
+# connection con1;
+# begin; delete from t1 where a=2;
-### test with effective killing of SF()
+# connection con2;
+# send delete t1 from t1 where t1.a=2;
-delete from t1;
-delete from t2;
-insert into t1 values (1,1);
-insert into t2 values (1,1);
+# connection con1;
+# --replace_result $ID ID
+# eval kill query $ID;
+# rollback;
-#
-# 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;
+# --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;
-let $ID= `select connection_id()`;
-send update t2 set b=bug27565()-1 where a=1;
+begin;
+send insert into t1 select * from t4 for update;
connection con1;
+--replace_result $ID ID
eval kill query $ID;
-commit;
+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 * from t1 /* must be: (1,0) */;
-select * from t2 /* must be as before: (1,1) */;
+--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(n int)
+RETURNS int(11)
+DETERMINISTIC
+begin
+ if n > 1 then
+ select get_lock("a", 10) into @a;
+ end if;
+ return n;
+end|
+delimiter ;|
-## bug#22725 with effective and propagating killing
#
-# top-level ta-table
-connection con1;
-delete from t3;
+# update
+#
+
+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 interrupted
-send insert into t3 values (0,0),(1,bug27565());
+send update t2 set b=b + bug27563(b) 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
-select count(*) from t3 /* must be zero */;
-show master status /* nothing in binlog */;
-
-# top-level non-ta-table
+--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 */;
+
+# 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
+
+#
+# delete
+#
+
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<Item> 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
}