Commit 03045ee7 authored by unknown's avatar unknown

Fix fur BUG#13348: "multi-table updates and deletes are not logged if no rows were affected".

Not fixed in 4.1 as not critical. Also I'm correcting error checking of multi-UPDATE/DELETE
when it comes to binlogging, to make it consistent with when we rollback the statement.


mysql-test/r/rpl_multi_delete.result:
  result update
mysql-test/r/rpl_multi_update.result:
  result update
mysql-test/t/rpl_multi_delete.test:
  test for BUG#13348
mysql-test/t/rpl_multi_update.test:
  test of BUG#13348
sql/sql_delete.cc:
  We now binlog multi-DELETE even if no row was updated (like we do for DELETE).
  I'm also correcting some error checking (< instead of <=), basing myself on when we rollback.
sql/sql_update.cc:
  we now binlog multi-UPDATE even if no row was updated (like we do for UPDATE).
  Adding to existing tests to test new behaviour.
  I'm also correcting some error checking (< instead of <=), basing myself on when we rollback.
parent 2b24a2b5
......@@ -19,4 +19,13 @@ a
select * from t2;
a
1
delete from t1;
delete from t2;
insert into t1 values(1);
insert into t2 values(1);
DELETE t1.*, t2.* from t1, t2;
select * from t1;
a
select * from t2;
a
drop table t1,t2;
......@@ -24,3 +24,16 @@ a b
1 0
2 1
UPDATE t1, t2 SET t1.b = t2.b WHERE t1.a = t2.a;
delete from t1;
delete from t2;
insert into t1 values(1,1);
insert into t2 values(1,1);
update t1 set a=2;
UPDATE t1, t2 SET t1.a = t2.a;
select * from t1;
a b
1 1
select * from t2;
a b
1 1
drop table t1, t2;
......@@ -16,10 +16,26 @@ sync_with_master;
select * from t1;
select * from t2;
# End of 4.1 tests
# Check if deleting 0 rows is binlogged (BUG#13348)
connection master;
drop table t1,t2;
save_master_pos;
delete from t1;
delete from t2;
connection slave;
sync_with_master;
# force a difference to see if master's multi-DELETE will correct it
insert into t1 values(1);
insert into t2 values(1);
# End of 4.1 tests
connection master;
DELETE t1.*, t2.* from t1, t2;
sync_slave_with_master;
select * from t1;
select * from t2;
connection master;
drop table t1,t2;
sync_slave_with_master;
......@@ -24,3 +24,26 @@ connection slave;
sync_with_master;
# End of 4.1 tests
# Check if updating 0 rows is binlogged (BUG#13348)
connection master;
delete from t1;
delete from t2;
insert into t1 values(1,1);
insert into t2 values(1,1);
connection slave;
# force a difference to see if master's multi-UPDATE will correct it
update t1 set a=2;
connection master;
UPDATE t1, t2 SET t1.a = t2.a;
sync_slave_with_master;
select * from t1;
select * from t2;
connection master;
drop table t1, t2;
sync_slave_with_master;
......@@ -258,19 +258,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
delete select;
transactional_table= table->file->has_transactions();
/*
We write to the binary log even if we deleted no row, because maybe the
user is using this command to ensure that a table is clean on master *and
on slave*. Think of the case of a user having played separately with the
master's table and slave's table and wanting to take a fresh identical
start now.
error < 0 means "really no error". error <= 0 means "maybe some error".
*/
if ((deleted || (error < 0)) && (error <= 0 || !transactional_table))
/* See similar binlogging code in sql_update.cc, for comments */
if ((error < 0) || (deleted && !transactional_table))
{
if (mysql_bin_log.is_open())
{
if (error <= 0)
if (error < 0)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
transactional_table, FALSE);
......@@ -718,6 +711,9 @@ bool multi_delete::send_eof()
/* Does deletes for the last n - 1 tables, returns 0 if ok */
int local_error= do_deletes(); // returns 0 if success
/* compute a total error to know if something failed */
local_error= local_error || error;
/* reset used flags */
thd->proc_info="end";
......@@ -730,19 +726,11 @@ bool multi_delete::send_eof()
query_cache_invalidate3(thd, delete_tables, 1);
}
/*
Write the SQL statement to the binlog if we deleted
rows and we succeeded, or also in an error case when there
was a non-transaction-safe table involved, since
modifications in it cannot be rolled back.
Note that if we deleted nothing we don't write to the binlog (TODO:
fix this).
*/
if (deleted && ((error <= 0 && !local_error) || normal_tables))
if ((local_error == 0) || (deleted && normal_tables))
{
if (mysql_bin_log.is_open())
{
if (error <= 0 && !local_error)
if (local_error == 0)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
transactional_tables, FALSE);
......
......@@ -475,11 +475,20 @@ int mysql_update(THD *thd,
query_cache_invalidate3(thd, table_list, 1);
}
if ((updated || (error < 0)) && (error <= 0 || !transactional_table))
/*
error < 0 means really no error at all: we processed all rows until the
last one without error. error > 0 means an error (e.g. unique key
violation and no IGNORE or REPLACE). error == 0 is also an error (if
preparing the record or invoking before triggers fails). See
ha_autocommit_or_rollback(error>=0) and DBUG_RETURN(error>=0) below.
Sometimes we want to binlog even if we updated no rows, in case user used
it to be sure master and slave are in same state.
*/
if ((error < 0) || (updated && !transactional_table))
{
if (mysql_bin_log.is_open())
{
if (error <= 0)
if (error < 0)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
transactional_table, FALSE);
......@@ -1439,16 +1448,14 @@ bool multi_update::send_eof()
/*
Write the SQL statement to the binlog if we updated
rows and we succeeded or if we updated some non
transacational tables.
Note that if we updated nothing we don't write to the binlog (TODO:
fix this).
transactional tables.
*/
if (updated && (local_error <= 0 || !trans_safe))
if ((local_error == 0) || (updated && !trans_safe))
{
if (mysql_bin_log.is_open())
{
if (local_error <= 0)
if (local_error == 0)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
transactional_tables, FALSE);
......
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