Commit 1408095a authored by unknown's avatar unknown

Merge koti.dsl.inet.fi:/home/elkin/MySQL/TEAM/FIXES/5.0/bug29136-mdelete

into  koti.dsl.inet.fi:/home/elkin/MySQL/merge-5.1


mysql-test/t/multi_update.test:
  Auto merged
sql/slave.cc:
  Auto merged
sql/sql_class.h:
  Auto merged
sql/sql_delete.cc:
  Auto merged
sql/sql_parse.cc:
  Auto merged
sql/sql_update.cc:
  Auto merged
mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test:
  manual merge use local; another file has to be changed in 5_1.
mysql-test/r/innodb.result:
  manual merge use local to re-record the results
mysql-test/r/multi_update.result:
  manual merge use local to re-record the results
mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result:
  manual merge use local to re-record the results
mysql-test/t/innodb.test:
  restoring bug#27716 snippet
sql/log_event.cc:
  trasfering simulation to another file: rpl_rli.cc
parents b3b01cf4 c8b6d105
stop slave;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
reset master;
reset slave;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
start slave;
show slave status /* Second_behind reports 0 */;;
Slave_IO_State #
Master_Host 127.0.0.1
Master_User root
Master_Port 9306
Connect_Retry 1
Master_Log_File master-bin.000001
Read_Master_Log_Pos 98
Relay_Log_File #
Relay_Log_Pos #
Relay_Master_Log_File master-bin.000001
Slave_IO_Running Yes
Slave_SQL_Running Yes
Replicate_Do_DB
Replicate_Ignore_DB
Replicate_Do_Table
Replicate_Ignore_Table
Replicate_Wild_Do_Table
Replicate_Wild_Ignore_Table
Last_Errno 0
Last_Error
Skip_Counter 0
Exec_Master_Log_Pos 98
Relay_Log_Space #
Until_Condition None
Until_Log_File
Until_Log_Pos 0
Master_SSL_Allowed No
Master_SSL_CA_File
Master_SSL_CA_Path
Master_SSL_Cert
Master_SSL_Cipher
Master_SSL_Key
Seconds_Behind_Master 0
drop table if exists t1;
Warnings:
Note 1051 Unknown table 't1'
create table t1 (f1 int);
flush logs /* contaminate rli->last_master_timestamp */;
lock table t1 write;
insert into t1 values (1);
show slave status /* bug emulated: reports slave threads starting time about 3*3 not 3 secs */;;
Slave_IO_State #
Master_Host 127.0.0.1
Master_User root
Master_Port 9306
Connect_Retry 1
Master_Log_File master-bin.000001
Read_Master_Log_Pos 359
Relay_Log_File #
Relay_Log_Pos #
Relay_Master_Log_File master-bin.000001
Slave_IO_Running Yes
Slave_SQL_Running Yes
Replicate_Do_DB
Replicate_Ignore_DB
Replicate_Do_Table
Replicate_Ignore_Table
Replicate_Wild_Do_Table
Replicate_Wild_Ignore_Table
Last_Errno 0
Last_Error
Skip_Counter 0
Exec_Master_Log_Pos 271
Relay_Log_Space #
Until_Condition None
Until_Log_File
Until_Log_Pos 0
Master_SSL_Allowed No
Master_SSL_CA_File
Master_SSL_CA_Path
Master_SSL_Cert
Master_SSL_Cipher
Master_SSL_Key
Seconds_Behind_Master 10
unlock tables;
flush logs /* this time rli->last_master_timestamp is not affected */;
lock table t1 write;
insert into t1 values (2);
show slave status /* reports the correct diff with master query time about 3+3 secs */;;
Slave_IO_State #
Master_Host 127.0.0.1
Master_User root
Master_Port 9306
Connect_Retry 1
Master_Log_File master-bin.000001
Read_Master_Log_Pos 447
Relay_Log_File #
Relay_Log_Pos #
Relay_Master_Log_File master-bin.000001
Slave_IO_Running Yes
Slave_SQL_Running Yes
Replicate_Do_DB
Replicate_Ignore_DB
Replicate_Do_Table
Replicate_Ignore_Table
Replicate_Wild_Do_Table
Replicate_Wild_Ignore_Table
Last_Errno 0
Last_Error
Skip_Counter 0
Exec_Master_Log_Pos 359
Relay_Log_Space #
Until_Condition None
Until_Log_File
Until_Log_Pos 0
Master_SSL_Allowed No
Master_SSL_CA_File
Master_SSL_CA_Path
Master_SSL_Cert
Master_SSL_Cipher
Master_SSL_Key
Seconds_Behind_Master 6
unlock tables;
drop table t1;
--loose-debug=d,let_first_flush_log_change_timestamp
#
# Testing replication delay reporting (bug#29309)
# there is an unavoidable non-determinism in the test
# please compare the results with the comments
#
source include/master-slave.inc;
connection master;
#connection slave;
sync_slave_with_master;
--replace_result $DEFAULT_MASTER_PORT DEFAULT_MASTER_PORT
--replace_column 1 # 8 # 9 # 23 #
--query_vertical show slave status /* Second_behind reports 0 */;
sleep 3;
### bug emulation
connection master;
drop table if exists t1;
create table t1 (f1 int);
sleep 3;
#connection slave;
sync_slave_with_master;
flush logs /* contaminate rli->last_master_timestamp */;
connection slave;
lock table t1 write;
connection master;
insert into t1 values (1);
sleep 3;
connection slave;
--replace_result $DEFAULT_MASTER_PORT DEFAULT_MASTER_PORT
--replace_column 1 # 8 # 9 # 23 #
--query_vertical show slave status /* bug emulated: reports slave threads starting time about 3*3 not 3 secs */;
unlock tables;
connection master;
sync_slave_with_master;
### bugfix
connection slave;
flush logs /* this time rli->last_master_timestamp is not affected */;
lock table t1 write;
connection master;
insert into t1 values (2);
sleep 3;
connection slave;
--replace_result $DEFAULT_MASTER_PORT DEFAULT_MASTER_PORT
--replace_column 1 # 8 # 9 # 23 #
--query_vertical show slave status /* reports the correct diff with master query time about 3+3 secs */;
unlock tables;
connection master;
drop table t1;
#connection slave;
sync_slave_with_master;
# End of tests
...@@ -753,6 +753,77 @@ select * from t1; ...@@ -753,6 +753,77 @@ select * from t1;
select * from t2; select * from t2;
drop table t1,t2; drop table t1,t2;
#
# Bug#27716 multi-update did partially and has not binlogged
#
CREATE TABLE `t1` (
`a` int(11) NOT NULL auto_increment,
`b` int(11) default NULL,
PRIMARY KEY (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;
CREATE TABLE `t2` (
`a` int(11) NOT NULL auto_increment,
`b` int(11) default NULL,
PRIMARY KEY (`a`)
) ENGINE=INNODB DEFAULT CHARSET=latin1 ;
# A. testing multi_update::send_eof() execution branch
insert into t1 values (1,1),(2,2);
insert into t2 values (1,1),(4,4);
reset master;
--error ER_DUP_ENTRY
UPDATE t2,t1 SET t2.a=t1.a+2;
# check
select * from t2 /* must be (3,1), (4,4) */;
show master status /* there must no UPDATE in binlog */;
# B. testing multi_update::send_error() execution branch
delete from t1;
delete from t2;
insert into t1 values (1,2),(3,4),(4,4);
insert into t2 values (1,2),(3,4),(4,4);
reset master;
--error ER_DUP_ENTRY
UPDATE t2,t1 SET t2.a=t2.b where t2.a=t1.a;
show master status /* there must be no UPDATE query event */;
# cleanup bug#27716
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
# #
......
...@@ -615,4 +615,38 @@ show master status /* there must be the UPDATE query event */; ...@@ -615,4 +615,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
...@@ -114,6 +114,9 @@ class Write_on_release_cache ...@@ -114,6 +114,9 @@ class Write_on_release_cache
flag_set m_flags; flag_set m_flags;
}; };
#ifndef DBUG_OFF
uint debug_not_change_ts_if_art_event= 1; // bug#29309 simulation
#endif
/* /*
pretty_print_str() pretty_print_str()
......
...@@ -3581,7 +3581,16 @@ static Log_event* next_event(Relay_log_info* rli) ...@@ -3581,7 +3581,16 @@ static Log_event* next_event(Relay_log_info* rli)
a new event and is queuing it; the false "0" will exist until SQL a new event and is queuing it; the false "0" will exist until SQL
finishes executing the new event; it will be look abnormal only if finishes executing the new event; it will be look abnormal only if
the events have old timestamps (then you get "many", 0, "many"). the events have old timestamps (then you get "many", 0, "many").
Transient phases like this can't really be fixed.
Transient phases like this can be fixed with implemeting
Heartbeat event which provides the slave the status of the
master at time the master does not have any new update to send.
Seconds_Behind_Master would be zero only when master has no
more updates in binlog for slave. The heartbeat can be sent
in a (small) fraction of slave_net_timeout. Until it's done
rli->last_master_timestamp is temporarely (for time of
waiting for the following event) reset whenever EOF is
reached.
*/ */
time_t save_timestamp= rli->last_master_timestamp; time_t save_timestamp= rli->last_master_timestamp;
rli->last_master_timestamp= 0; rli->last_master_timestamp= 0;
......
...@@ -2436,6 +2436,11 @@ class multi_delete :public select_result_interceptor ...@@ -2436,6 +2436,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);
...@@ -2471,6 +2476,11 @@ class multi_update :public select_result_interceptor ...@@ -2471,6 +2476,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,
......
...@@ -541,7 +541,7 @@ bool mysql_multi_delete_prepare(THD *thd) ...@@ -541,7 +541,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);
} }
...@@ -720,12 +720,14 @@ void multi_delete::send_error(uint errcode,const char *err) ...@@ -720,12 +720,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
...@@ -745,12 +747,29 @@ void multi_delete::send_error(uint errcode,const char *err) ...@@ -745,12 +747,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:
...@@ -880,6 +899,8 @@ bool multi_delete::send_eof() ...@@ -880,6 +899,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)
......
...@@ -2919,6 +2919,13 @@ mysql_execute_command(THD *thd) ...@@ -2919,6 +2919,13 @@ mysql_execute_command(THD *thd)
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
......
...@@ -1215,8 +1215,8 @@ multi_update::multi_update(TABLE_LIST *table_list, ...@@ -1215,8 +1215,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)
{} {}
...@@ -1418,7 +1418,6 @@ multi_update::initialize_tables(JOIN *join) ...@@ -1418,7 +1418,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) */
...@@ -1713,12 +1712,14 @@ void multi_update::send_error(uint errcode,const char *err) ...@@ -1713,12 +1712,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.
...@@ -1754,8 +1755,7 @@ void multi_update::send_error(uint errcode,const char *err) ...@@ -1754,8 +1755,7 @@ void multi_update::send_error(uint errcode,const char *err)
thd->query, thd->query_length, thd->query, thd->query_length,
transactional_tables, FALSE); transactional_tables, FALSE);
} }
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);
...@@ -1978,8 +1978,6 @@ bool multi_update::send_eof() ...@@ -1978,8 +1978,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 */
if (thd->binlog_query(THD::ROW_QUERY_TYPE, if (thd->binlog_query(THD::ROW_QUERY_TYPE,
thd->query, thd->query_length, thd->query, thd->query_length,
transactional_tables, FALSE) && transactional_tables, FALSE) &&
...@@ -1991,6 +1989,8 @@ bool multi_update::send_eof() ...@@ -1991,6 +1989,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