Commit 388cf622 authored by guilhem@mysql.com's avatar guilhem@mysql.com

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.
parent ad53c58e
...@@ -19,4 +19,13 @@ a ...@@ -19,4 +19,13 @@ a
select * from t2; select * from t2;
a a
1 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; drop table t1,t2;
...@@ -24,3 +24,16 @@ a b ...@@ -24,3 +24,16 @@ a b
1 0 1 0
2 1 2 1
UPDATE t1, t2 SET t1.b = t2.b WHERE t1.a = t2.a; 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; ...@@ -16,10 +16,26 @@ sync_with_master;
select * from t1; select * from t1;
select * from t2; select * from t2;
# End of 4.1 tests
# Check if deleting 0 rows is binlogged (BUG#13348)
connection master; connection master;
drop table t1,t2; delete from t1;
save_master_pos; delete from t2;
connection slave; 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; ...@@ -24,3 +24,26 @@ connection slave;
sync_with_master; sync_with_master;
# End of 4.1 tests # 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 @@ cleanup: ...@@ -258,19 +258,12 @@ cleanup:
delete select; delete select;
transactional_table= table->file->has_transactions(); transactional_table= table->file->has_transactions();
/* /* See similar binlogging code in sql_update.cc, for comments */
We write to the binary log even if we deleted no row, because maybe the if ((error < 0) || (deleted && !transactional_table))
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))
{ {
if (mysql_bin_log.is_open()) if (mysql_bin_log.is_open())
{ {
if (error <= 0) if (error < 0)
thd->clear_error(); thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length, Query_log_event qinfo(thd, thd->query, thd->query_length,
transactional_table, FALSE); transactional_table, FALSE);
...@@ -718,6 +711,9 @@ bool multi_delete::send_eof() ...@@ -718,6 +711,9 @@ bool multi_delete::send_eof()
/* Does deletes for the last n - 1 tables, returns 0 if ok */ /* Does deletes for the last n - 1 tables, returns 0 if ok */
int local_error= do_deletes(); // returns 0 if success 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 */ /* reset used flags */
thd->proc_info="end"; thd->proc_info="end";
...@@ -730,19 +726,11 @@ bool multi_delete::send_eof() ...@@ -730,19 +726,11 @@ bool multi_delete::send_eof()
query_cache_invalidate3(thd, delete_tables, 1); query_cache_invalidate3(thd, delete_tables, 1);
} }
/* if ((local_error == 0) || (deleted && normal_tables))
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 (mysql_bin_log.is_open()) if (mysql_bin_log.is_open())
{ {
if (error <= 0 && !local_error) if (local_error == 0)
thd->clear_error(); thd->clear_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);
......
...@@ -475,11 +475,20 @@ int mysql_update(THD *thd, ...@@ -475,11 +475,20 @@ int mysql_update(THD *thd,
query_cache_invalidate3(thd, table_list, 1); 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 (mysql_bin_log.is_open())
{ {
if (error <= 0) if (error < 0)
thd->clear_error(); thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length, Query_log_event qinfo(thd, thd->query, thd->query_length,
transactional_table, FALSE); transactional_table, FALSE);
...@@ -1439,16 +1448,14 @@ bool multi_update::send_eof() ...@@ -1439,16 +1448,14 @@ bool multi_update::send_eof()
/* /*
Write the SQL statement to the binlog if we updated Write the SQL statement to the binlog if we updated
rows and we succeeded or if we updated some non rows and we succeeded or if we updated some non
transacational tables. transactional tables.
Note that if we updated nothing we don't write to the binlog (TODO:
fix this).
*/ */
if (updated && (local_error <= 0 || !trans_safe)) if ((local_error == 0) || (updated && !trans_safe))
{ {
if (mysql_bin_log.is_open()) if (mysql_bin_log.is_open())
{ {
if (local_error <= 0) if (local_error == 0)
thd->clear_error(); thd->clear_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);
......
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