From 95f3db7be1b3425b47f34090f33c7eeb52f97703 Mon Sep 17 00:00:00 2001
From: unknown <aelkin/elkin@koti.dsl.inet.fi>
Date: Mon, 29 Oct 2007 15:20:59 +0200
Subject: [PATCH] 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.
---
 mysql-test/r/binlog_killed.result             | 106 +++++++
 mysql-test/t/binlog_killed.test               | 297 ++++++++++--------
 .../t/binlog_killed_bug27571-master.opt       |   1 +
 mysql-test/t/binlog_killed_bug27571.test      |  68 ++++
 .../t/binlog_killed_simulate-master.opt       |   1 +
 mysql-test/t/binlog_killed_simulate.test      |  65 ++++
 sql/log_event.cc                              |  11 +-
 sql/log_event.h                               |  10 +-
 sql/sql_delete.cc                             |  18 +-
 sql/sql_insert.cc                             |   3 +-
 sql/sql_load.cc                               |  26 +-
 sql/sql_update.cc                             |  70 ++---
 12 files changed, 491 insertions(+), 185 deletions(-)
 create mode 100644 mysql-test/t/binlog_killed_bug27571-master.opt
 create mode 100644 mysql-test/t/binlog_killed_bug27571.test
 create mode 100644 mysql-test/t/binlog_killed_simulate-master.opt
 create mode 100644 mysql-test/t/binlog_killed_simulate.test

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 @@ class Execute_load_query_log_event: public Query_log_event
 
 #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 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
       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 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
 /* 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 @@ int multi_update::do_updates(bool from_send_error)
 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
     }
-- 
2.30.9