Commit c8b6d105 authored by unknown's avatar unknown

Bug #29136 erred multi-delete on trans table does not rollback the statement

similar to bug_27716, but it was stressed on in the synopsis on that there is another
side of the artifact affecting behaviour in transaction.

Fixed with deploying multi_delete::send_error() - otherwise never called - and refining its logic
to perform binlogging job if needed.

The changeset includes the following side effects:
- added tests to check bug_23333's scenarios on the mixture of tables for multi_update;
- fixes bug@30763 with two-liner patch and a test coinciding to one added for bug_23333.


mysql-test/r/innodb.result:
  results changed
mysql-test/r/mix_innodb_myisam_binlog.result:
  results changed
mysql-test/r/multi_update.result:
  results changed
mysql-test/t/innodb.test:
  trans table specific test added
mysql-test/t/mix_innodb_myisam_binlog.test:
  multi-update  and multi-delete of mixure of ta and not-ta tables tests added (relates to bug_23333).
mysql-test/t/multi_update.test:
  testing another branch of mult-delete: send_eof() (binloggin there), send_error (early return)
sql/sql_class.h:
  a new flag to designate the fact the statement's error has been handled.
  The flag is checked by ::send_error() methods (multi_update and _delete classes)
sql/sql_delete.cc:
  expanding multi_delete::send_error to 
  1. early return if error_handled == t
  2. binlogging locally if there was a non-trans table modified side effect
sql/sql_parse.cc:
  adding multi_update::send_error which can perform binlogging and rollback job in needed
sql/sql_update.cc:
  issues relating to
   
  1. bug_27716 with zeroing of `updated' to serve as the flag of early return from send_error().
     The flag is changed to be a new member error_handled; also moved outside binlogging branch.
     The reason for this change is that bug_23333 fixes were pushed after the bug_27716's and they
     left this flaw (also no test coverage).
  2. bug_30763 with assertion on trans_safe. I decide to make 2 liner fix for that bug here instead of to remove
     those two assertions. This new bug test case is the same as for multi-update on the mixure of tables.
     The rational for this fix:
     presumption for mutli_update::trans_safe to be set to zero at
     multi_update::multi_update or multi_update::initialize_tables() is incorrect.
  
     trans_safe := false should happen only when a non-transactional table gets modified. 
     Therefore, at initialization the member must be be set to true.
parent 5aba177b
...@@ -1119,6 +1119,19 @@ show master status /* there must be no UPDATE query event */; ...@@ -1119,6 +1119,19 @@ show master status /* there must be no UPDATE query event */;
File Position Binlog_Do_DB Binlog_Ignore_DB File Position Binlog_Do_DB Binlog_Ignore_DB
master-bin.000001 98 master-bin.000001 98
drop table t1, t2; drop table t1, t2;
drop table if exists t1, t2;
CREATE TABLE t1 (a int, PRIMARY KEY (a));
CREATE TABLE t2 (a int, PRIMARY KEY (a)) ENGINE=InnoDB;
create trigger trg_del_t2 after delete on t2 for each row
insert into t1 values (1);
insert into t1 values (1);
insert into t2 values (1),(2);
delete t2 from t2;
ERROR 23000: Duplicate entry '1' for key 1
select count(*) from t2 /* must be 2 as restored after rollback caused by the error */;
count(*)
2
drop table t1, t2;
create table t1 (a int, b int) engine=innodb; create table t1 (a int, b int) engine=innodb;
insert into t1 values(20,null); insert into t1 values(20,null);
select t2.b, ifnull(t2.b,"this is null") from t1 as t2 left join t1 as t3 on select t2.b, ifnull(t2.b,"this is null") from t1 as t2 left join t1 as t3 on
...@@ -1792,10 +1805,10 @@ Variable_name Value ...@@ -1792,10 +1805,10 @@ Variable_name Value
Innodb_page_size 16384 Innodb_page_size 16384
show status like "Innodb_rows_deleted"; show status like "Innodb_rows_deleted";
Variable_name Value Variable_name Value
Innodb_rows_deleted 72 Innodb_rows_deleted 73
show status like "Innodb_rows_inserted"; show status like "Innodb_rows_inserted";
Variable_name Value Variable_name Value
Innodb_rows_inserted 29732 Innodb_rows_inserted 29734
show status like "Innodb_rows_updated"; show status like "Innodb_rows_updated";
Variable_name Value Variable_name Value
Innodb_rows_updated 29532 Innodb_rows_updated 29532
......
...@@ -351,7 +351,7 @@ drop function if exists bug27417; ...@@ -351,7 +351,7 @@ drop function if exists bug27417;
drop table if exists t1,t2; drop table if exists t1,t2;
CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM; CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM;
CREATE TABLE t2 (a int NOT NULL auto_increment, PRIMARY KEY (a)); CREATE TABLE t2 (a int NOT NULL auto_increment, PRIMARY KEY (a));
create function bug27417(n int) create function bug27417(n int)
RETURNS int(11) RETURNS int(11)
begin begin
insert into t1 values (null); insert into t1 values (null);
...@@ -393,7 +393,9 @@ count(*) ...@@ -393,7 +393,9 @@ count(*)
drop table t1,t2; drop table t1,t2;
CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM; CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM;
CREATE TABLE t2 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; CREATE TABLE t2 (a int, PRIMARY KEY (a)) ENGINE=InnoDB;
CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique); CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique) ENGINE=MyISAM;
CREATE TABLE t4 (a int, PRIMARY KEY (a), b int unique) ENGINE=Innodb;
CREATE TABLE t5 (a int, PRIMARY KEY (a)) ENGINE=InnoDB;
insert into t2 values (1); insert into t2 values (1);
reset master; reset master;
insert into t2 values (bug27417(1)); insert into t2 values (bug27417(1));
...@@ -427,6 +429,31 @@ master-bin.000001 190 ...@@ -427,6 +429,31 @@ master-bin.000001 190
select count(*) from t1 /* must be 2 */; select count(*) from t1 /* must be 2 */;
count(*) count(*)
2 2
delete from t3;
delete from t4;
insert into t3 values (1,1);
insert into t4 values (1,1),(2,2);
reset master;
UPDATE t4,t3 SET t4.a=t3.a + bug27417(1) /* top level non-ta table */;
ERROR 23000: Duplicate entry '2' for key 1
show master status /* the offset must denote there is the query */;
File Position Binlog_Do_DB Binlog_Ignore_DB
master-bin.000001 301
select count(*) from t1 /* must be 4 */;
count(*)
4
delete from t1;
delete from t3;
delete from t4;
insert into t3 values (1,1),(2,2);
insert into t4 values (1,1),(2,2);
reset master;
UPDATE t3,t4 SET t3.a=t4.a + bug27417(1);
ERROR 23000: Duplicate entry '2' for key 1
select count(*) from t1 /* must be 1 */;
count(*)
1
drop table t4;
delete from t1; delete from t1;
delete from t2; delete from t2;
delete from t3; delete from t3;
...@@ -443,6 +470,23 @@ master-bin.000001 246 ...@@ -443,6 +470,23 @@ master-bin.000001 246
select count(*) from t1 /* must be 1 */; select count(*) from t1 /* must be 1 */;
count(*) count(*)
1 1
drop trigger trg_del;
delete from t1;
delete from t2;
delete from t5;
create trigger trg_del_t2 after delete on t2 for each row
insert into t1 values (1);
insert into t2 values (2),(3);
insert into t5 values (1),(2);
reset master;
delete t2.* from t2,t5 where t2.a=t5.a + 1;
ERROR 23000: Duplicate entry '1' for key 1
show master status /* the offset must denote there is the query */;
File Position Binlog_Do_DB Binlog_Ignore_DB
master-bin.000001 274
select count(*) from t1 /* must be 1 */;
count(*)
1
delete from t1; delete from t1;
create table t4 (a int default 0, b int primary key) engine=innodb; create table t4 (a int default 0, b int primary key) engine=innodb;
insert into t4 values (0, 17); insert into t4 values (0, 17);
...@@ -458,7 +502,7 @@ count(*) ...@@ -458,7 +502,7 @@ count(*)
show master status /* the offset must denote there is the query */; show master status /* the offset must denote there is the query */;
File Position Binlog_Do_DB Binlog_Ignore_DB File Position Binlog_Do_DB Binlog_Ignore_DB
master-bin.000001 376 master-bin.000001 376
drop trigger trg_del; drop trigger trg_del_t2;
drop table t1,t2,t3,t4; drop table t1,t2,t3,t4,t5;
drop function bug27417; drop function bug27417;
end of tests end of tests
...@@ -545,7 +545,7 @@ a b ...@@ -545,7 +545,7 @@ a b
4 4 4 4
show master status /* there must be the UPDATE query event */; show master status /* there must be the UPDATE query event */;
File Position Binlog_Do_DB Binlog_Ignore_DB File Position Binlog_Do_DB Binlog_Ignore_DB
master-bin.000001 189 master-bin.000001 260
delete from t1; delete from t1;
delete from t2; delete from t2;
insert into t1 values (1,2),(3,4),(4,4); insert into t1 values (1,2),(3,4),(4,4);
...@@ -555,6 +555,26 @@ UPDATE t2,t1 SET t2.a=t2.b where t2.a=t1.a; ...@@ -555,6 +555,26 @@ UPDATE t2,t1 SET t2.a=t2.b where t2.a=t1.a;
ERROR 23000: Duplicate entry '4' for key 1 ERROR 23000: Duplicate entry '4' for key 1
show master status /* there must be the UPDATE query event */; show master status /* there must be the UPDATE query event */;
File Position Binlog_Do_DB Binlog_Ignore_DB File Position Binlog_Do_DB Binlog_Ignore_DB
master-bin.000001 204 master-bin.000001 275
drop table t1, t2; drop table t1, t2;
drop table if exists t1, t2, t3;
CREATE TABLE t1 (a int, PRIMARY KEY (a));
CREATE TABLE t2 (a int, PRIMARY KEY (a));
CREATE TABLE t3 (a int, PRIMARY KEY (a)) ENGINE=MyISAM;
create trigger trg_del_t3 before delete on t3 for each row insert into t1 values (1);
insert into t2 values (1),(2);
insert into t3 values (1),(2);
reset master;
delete t3.* from t2,t3 where t2.a=t3.a;
ERROR 23000: Duplicate entry '1' for key 1
select count(*) from t1 /* must be 1 */;
count(*)
1
select count(*) from t3 /* must be 1 */;
count(*)
1
show binlog events from 98;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 98 Query 1 # use `test`; delete t3.* from t2,t3 where t2.a=t3.a
drop table t1, t2, t3;
end of tests end of tests
...@@ -792,6 +792,38 @@ show master status /* there must be no UPDATE query event */; ...@@ -792,6 +792,38 @@ show master status /* there must be no UPDATE query event */;
# cleanup bug#27716 # cleanup bug#27716
drop table t1, t2; drop table t1, t2;
#
# Bug #29136 erred multi-delete on trans table does not rollback
#
# prepare
--disable_warnings
drop table if exists t1, t2;
--enable_warnings
CREATE TABLE t1 (a int, PRIMARY KEY (a));
CREATE TABLE t2 (a int, PRIMARY KEY (a)) ENGINE=InnoDB;
create trigger trg_del_t2 after delete on t2 for each row
insert into t1 values (1);
insert into t1 values (1);
insert into t2 values (1),(2);
# exec cases A, B - see multi_update.test
# A. send_error() w/o send_eof() branch
--error ER_DUP_ENTRY
delete t2 from t2;
# check
select count(*) from t2 /* must be 2 as restored after rollback caused by the error */;
# cleanup bug#29136
drop table t1, t2;
# #
# Testing of IFNULL # Testing of IFNULL
# #
......
...@@ -347,7 +347,7 @@ CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM; ...@@ -347,7 +347,7 @@ CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM;
CREATE TABLE t2 (a int NOT NULL auto_increment, PRIMARY KEY (a)); CREATE TABLE t2 (a int NOT NULL auto_increment, PRIMARY KEY (a));
delimiter |; delimiter |;
create function bug27417(n int) create function bug27417(n int)
RETURNS int(11) RETURNS int(11)
begin begin
insert into t1 values (null); insert into t1 values (null);
...@@ -385,13 +385,17 @@ drop table t1,t2; ...@@ -385,13 +385,17 @@ drop table t1,t2;
# #
# Bug#23333 using the patch (and the test) for bug#27471 # Bug#23333 using the patch (and the test) for bug#27471
#
# throughout the bug tests # throughout the bug tests
# t1 - non-trans side effects gatherer; # t1 - non-trans side effects gatherer;
# t2 - transactional table; # t2 - transactional table;
# #
CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM; CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM;
CREATE TABLE t2 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; CREATE TABLE t2 (a int, PRIMARY KEY (a)) ENGINE=InnoDB;
CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique); CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique) ENGINE=MyISAM;
CREATE TABLE t4 (a int, PRIMARY KEY (a), b int unique) ENGINE=Innodb;
CREATE TABLE t5 (a int, PRIMARY KEY (a)) ENGINE=InnoDB;
# #
...@@ -434,7 +438,7 @@ CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique); ...@@ -434,7 +438,7 @@ CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique);
select count(*) from t1 /* must be 2 */; select count(*) from t1 /* must be 2 */;
# #
# UPDATE (multi-update see bug#27716) # UPDATE inc multi-update
# #
# prepare # prepare
...@@ -450,9 +454,48 @@ CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique); ...@@ -450,9 +454,48 @@ CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique);
show master status /* the offset must denote there is the query */; show master status /* the offset must denote there is the query */;
select count(*) from t1 /* must be 2 */; select count(*) from t1 /* must be 2 */;
## multi_update::send_eof() branch
# prepare
delete from t3;
delete from t4;
insert into t3 values (1,1);
insert into t4 values (1,1),(2,2);
reset master;
# execute
--error ER_DUP_ENTRY
UPDATE t4,t3 SET t4.a=t3.a + bug27417(1) /* top level non-ta table */;
# check
show master status /* the offset must denote there is the query */;
select count(*) from t1 /* must be 4 */;
## send_error() branch of multi_update
# prepare
delete from t1;
delete from t3;
delete from t4;
insert into t3 values (1,1),(2,2);
insert into t4 values (1,1),(2,2);
reset master;
# execute
--error ER_DUP_ENTRY
UPDATE t3,t4 SET t3.a=t4.a + bug27417(1);
# check
select count(*) from t1 /* must be 1 */;
# cleanup
drop table t4;
# #
# DELETE (for multi-delete see Bug #29136) # DELETE incl multi-delete
# #
# prepare # prepare
...@@ -472,6 +515,27 @@ CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique); ...@@ -472,6 +515,27 @@ CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique);
show master status /* the offset must denote there is the query */; show master status /* the offset must denote there is the query */;
select count(*) from t1 /* must be 1 */; select count(*) from t1 /* must be 1 */;
# cleanup
drop trigger trg_del;
# prepare
delete from t1;
delete from t2;
delete from t5;
create trigger trg_del_t2 after delete on t2 for each row
insert into t1 values (1);
insert into t2 values (2),(3);
insert into t5 values (1),(2);
reset master;
# execute
--error ER_DUP_ENTRY
delete t2.* from t2,t5 where t2.a=t5.a + 1;
# check
show master status /* the offset must denote there is the query */;
select count(*) from t1 /* must be 1 */;
# #
# LOAD DATA # LOAD DATA
...@@ -496,8 +560,8 @@ CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique); ...@@ -496,8 +560,8 @@ CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique);
# #
drop trigger trg_del; drop trigger trg_del_t2;
drop table t1,t2,t3,t4; drop table t1,t2,t3,t4,t5;
drop function bug27417; drop function bug27417;
......
...@@ -574,4 +574,38 @@ show master status /* there must be the UPDATE query event */; ...@@ -574,4 +574,38 @@ show master status /* there must be the UPDATE query event */;
# cleanup bug#27716 # cleanup bug#27716
drop table t1, t2; drop table t1, t2;
#
# Bug #29136 erred multi-delete on trans table does not rollback
#
# prepare
--disable_warnings
drop table if exists t1, t2, t3;
--enable_warnings
CREATE TABLE t1 (a int, PRIMARY KEY (a));
CREATE TABLE t2 (a int, PRIMARY KEY (a));
CREATE TABLE t3 (a int, PRIMARY KEY (a)) ENGINE=MyISAM;
create trigger trg_del_t3 before delete on t3 for each row insert into t1 values (1);
insert into t2 values (1),(2);
insert into t3 values (1),(2);
reset master;
# exec cases B, A - see innodb.test
# B. send_eof() and send_error() afterward
--error ER_DUP_ENTRY
delete t3.* from t2,t3 where t2.a=t3.a;
# check
select count(*) from t1 /* must be 1 */;
select count(*) from t3 /* must be 1 */;
# the query must be in binlog (no surprise though)
source include/show_binlog_events.inc;
# cleanup bug#29136
drop table t1, t2, t3;
--echo end of tests --echo end of tests
...@@ -2356,6 +2356,11 @@ class multi_delete :public select_result_interceptor ...@@ -2356,6 +2356,11 @@ class multi_delete :public select_result_interceptor
/* True if at least one table we delete from is not transactional */ /* True if at least one table we delete from is not transactional */
bool normal_tables; bool normal_tables;
bool delete_while_scanning; bool delete_while_scanning;
/*
error handling (rollback and binlogging) can happen in send_eof()
so that afterward send_error() needs to find out that.
*/
bool error_handled;
public: public:
multi_delete(TABLE_LIST *dt, uint num_of_tables); multi_delete(TABLE_LIST *dt, uint num_of_tables);
...@@ -2391,6 +2396,11 @@ class multi_update :public select_result_interceptor ...@@ -2391,6 +2396,11 @@ class multi_update :public select_result_interceptor
/* True if the update operation has made a change in a transactional table */ /* True if the update operation has made a change in a transactional table */
bool transactional_tables; bool transactional_tables;
bool ignore; bool ignore;
/*
error handling (rollback and binlogging) can happen in send_eof()
so that afterward send_error() needs to find out that.
*/
bool error_handled;
public: public:
multi_update(TABLE_LIST *ut, TABLE_LIST *leaves_list, multi_update(TABLE_LIST *ut, TABLE_LIST *leaves_list,
......
...@@ -504,7 +504,7 @@ bool mysql_multi_delete_prepare(THD *thd) ...@@ -504,7 +504,7 @@ bool mysql_multi_delete_prepare(THD *thd)
multi_delete::multi_delete(TABLE_LIST *dt, uint num_of_tables_arg) multi_delete::multi_delete(TABLE_LIST *dt, uint num_of_tables_arg)
: delete_tables(dt), deleted(0), found(0), : delete_tables(dt), deleted(0), found(0),
num_of_tables(num_of_tables_arg), error(0), num_of_tables(num_of_tables_arg), error(0),
do_delete(0), transactional_tables(0), normal_tables(0) do_delete(0), transactional_tables(0), normal_tables(0), error_handled(0)
{ {
tempfiles= (Unique **) sql_calloc(sizeof(Unique *) * num_of_tables); tempfiles= (Unique **) sql_calloc(sizeof(Unique *) * num_of_tables);
} }
...@@ -685,12 +685,14 @@ void multi_delete::send_error(uint errcode,const char *err) ...@@ -685,12 +685,14 @@ void multi_delete::send_error(uint errcode,const char *err)
/* First send error what ever it is ... */ /* First send error what ever it is ... */
my_message(errcode, err, MYF(0)); my_message(errcode, err, MYF(0));
/* If nothing deleted return */ /* the error was handled or nothing deleted and no side effects return */
if (!deleted) if (error_handled ||
!thd->transaction.stmt.modified_non_trans_table && !deleted)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
/* Something already deleted so we have to invalidate cache */ /* Something already deleted so we have to invalidate cache */
query_cache_invalidate3(thd, delete_tables, 1); if (deleted)
query_cache_invalidate3(thd, delete_tables, 1);
/* /*
If rows from the first table only has been deleted and it is If rows from the first table only has been deleted and it is
...@@ -710,12 +712,29 @@ void multi_delete::send_error(uint errcode,const char *err) ...@@ -710,12 +712,29 @@ void multi_delete::send_error(uint errcode,const char *err)
*/ */
error= 1; error= 1;
send_eof(); send_eof();
DBUG_ASSERT(error_handled);
DBUG_VOID_RETURN;
}
if (thd->transaction.stmt.modified_non_trans_table)
{
/*
there is only side effects; to binlog with the error
*/
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
transactional_tables, FALSE);
mysql_bin_log.write(&qinfo);
}
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; DBUG_VOID_RETURN;
} }
/* /*
Do delete from other tables. Do delete from other tables.
Returns values: Returns values:
...@@ -832,6 +851,8 @@ bool multi_delete::send_eof() ...@@ -832,6 +851,8 @@ bool multi_delete::send_eof()
if (thd->transaction.stmt.modified_non_trans_table) if (thd->transaction.stmt.modified_non_trans_table)
thd->transaction.all.modified_non_trans_table= TRUE; thd->transaction.all.modified_non_trans_table= TRUE;
} }
if (local_error != 0)
error_handled= TRUE; // to force early leave from ::send_error()
/* Commit or rollback the current SQL statement */ /* Commit or rollback the current SQL statement */
if (transactional_tables) if (transactional_tables)
......
...@@ -3701,6 +3701,13 @@ end_with_restore_list: ...@@ -3701,6 +3701,13 @@ end_with_restore_list:
SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
OPTION_SETUP_TABLES_DONE, OPTION_SETUP_TABLES_DONE,
del_result, unit, select_lex); del_result, unit, select_lex);
res|= thd->net.report_error;
if (unlikely(res))
{
/* If we had a another error reported earlier then this will be ignored */
del_result->send_error(ER_UNKNOWN_ERROR, "Execution of the query failed");
del_result->abort();
}
delete del_result; delete del_result;
} }
else else
......
...@@ -994,8 +994,8 @@ multi_update::multi_update(TABLE_LIST *table_list, ...@@ -994,8 +994,8 @@ multi_update::multi_update(TABLE_LIST *table_list,
:all_tables(table_list), leaves(leaves_list), update_tables(0), :all_tables(table_list), leaves(leaves_list), update_tables(0),
tmp_tables(0), updated(0), found(0), fields(field_list), tmp_tables(0), updated(0), found(0), fields(field_list),
values(value_list), table_count(0), copy_field(0), values(value_list), table_count(0), copy_field(0),
handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(0), handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(1),
transactional_tables(1), ignore(ignore_arg) transactional_tables(1), ignore(ignore_arg), error_handled(0)
{} {}
...@@ -1202,7 +1202,6 @@ multi_update::initialize_tables(JOIN *join) ...@@ -1202,7 +1202,6 @@ multi_update::initialize_tables(JOIN *join)
if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join)) if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join))
DBUG_RETURN(1); DBUG_RETURN(1);
main_table=join->join_tab->table; main_table=join->join_tab->table;
trans_safe= transactional_tables= main_table->file->has_transactions();
table_to_update= 0; table_to_update= 0;
/* Any update has at least one pair (field, value) */ /* Any update has at least one pair (field, value) */
...@@ -1484,12 +1483,14 @@ void multi_update::send_error(uint errcode,const char *err) ...@@ -1484,12 +1483,14 @@ void multi_update::send_error(uint errcode,const char *err)
/* First send error what ever it is ... */ /* First send error what ever it is ... */
my_error(errcode, MYF(0), err); my_error(errcode, MYF(0), err);
/* If nothing updated return */ /* the error was handled or nothing deleted and no side effects return */
if (updated == 0) /* the counter might be reset in send_eof */ if (error_handled ||
return; /* and then the query has been binlogged */ !thd->transaction.stmt.modified_non_trans_table && !updated)
return;
/* Something already updated so we have to invalidate cache */ /* Something already updated so we have to invalidate cache */
query_cache_invalidate3(thd, update_tables, 1); if (updated)
query_cache_invalidate3(thd, update_tables, 1);
/* /*
If all tables that has been updated are trans safe then just do rollback. If all tables that has been updated are trans safe then just do rollback.
If not attempt to do remaining updates. If not attempt to do remaining updates.
...@@ -1525,8 +1526,7 @@ void multi_update::send_error(uint errcode,const char *err) ...@@ -1525,8 +1526,7 @@ void multi_update::send_error(uint errcode,const char *err)
transactional_tables, FALSE); transactional_tables, FALSE);
mysql_bin_log.write(&qinfo); mysql_bin_log.write(&qinfo);
} }
if (!trans_safe) thd->transaction.all.modified_non_trans_table= TRUE;
thd->transaction.all.modified_non_trans_table= TRUE;
} }
DBUG_ASSERT(trans_safe || !updated || thd->transaction.stmt.modified_non_trans_table); DBUG_ASSERT(trans_safe || !updated || thd->transaction.stmt.modified_non_trans_table);
...@@ -1739,8 +1739,6 @@ bool multi_update::send_eof() ...@@ -1739,8 +1739,6 @@ bool multi_update::send_eof()
{ {
if (local_error == 0) if (local_error == 0)
thd->clear_error(); thd->clear_error();
else
updated= 0; /* if there's an error binlog it here not in ::send_error */
Query_log_event qinfo(thd, thd->query, thd->query_length, Query_log_event qinfo(thd, thd->query, thd->query_length,
transactional_tables, FALSE); transactional_tables, FALSE);
if (mysql_bin_log.write(&qinfo) && trans_safe) if (mysql_bin_log.write(&qinfo) && trans_safe)
...@@ -1749,6 +1747,8 @@ bool multi_update::send_eof() ...@@ -1749,6 +1747,8 @@ bool multi_update::send_eof()
if (thd->transaction.stmt.modified_non_trans_table) if (thd->transaction.stmt.modified_non_trans_table)
thd->transaction.all.modified_non_trans_table= TRUE; thd->transaction.all.modified_non_trans_table= TRUE;
} }
if (local_error != 0)
error_handled= TRUE; // to force early leave from ::send_error()
if (transactional_tables) if (transactional_tables)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment