|
|||||||||||
|
bk commit into 5.0 tree (aelkin:1.2439) BUG#27417
From: Andrei Elkin <aelkin(at)mysql.com>
Date: Tue Jun 26 2007 - 11:22:03 EDT
ChangeSet@1.2439, 2007-06-26 18:21:52+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +18 -0 Bug #27417 thd->no_trans_update.stmt lost value inside of SF-exec-stack Once had been set the flag might later got reset inside of a stored routine execution stack. The reason was in that there was no check if a new statement started at time of resetting. The artifact affects most of binlogable DML queries. Notice, that multi-update is wrapped up within bug@27716 fix, multi-delete bug@29136. Fixed with saving parent's statement flag of whether the statement modified non-transactional table, and unioning (merging) the value with that was gained in mysql_execute_command.
Resettling thd->no_trans_update members into thd->transaction.`member`;
Asserting code;
Computing of thd->transaction.all.modified_non_trans_table is refined to base to the stmt's
value for all the case including insert .. select statement which before the patch had an
extra issue bug#28960.
The supplied test verifies limitely, mostly asserts. The ultimate testing is defered for bug_13270, bug_23333. mysql-test/r/mix_innodb_myisam_binlog.result@1.31, 2007-06-26 18:21:46+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +68 -0 results changed mysql-test/r/stored_routines_side_effect.result@1.1, 2007-06-26 18:21:49+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +46 -0 new result file mysql-test/r/stored_routines_side_effect.result@1.0, 2007-06-26 18:21:49+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +0 -0 mysql-test/t/mix_innodb_myisam_binlog.test@1.26, 2007-06-26 18:21:47+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +63 -2 regression test for a related bug#28960. mysql-test/t/stored_routines_side_effect.test@1.1, 2007-06-26 18:21:49+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +56 -0 the test basicly is to check asserts deployed within the patch. mysql-test/t/stored_routines_side_effect.test@1.0, 2007-06-26 18:21:49+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +0 -0 sql/ha_ndbcluster.cc@1.309, 2007-06-26 18:21:47+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +1 -1 thd->transaction.{all,stmt}.modified_non_trans_table instead of thd->no_trans_update.{all,stmt} sql/handler.cc@1.233, 2007-06-26 18:21:47+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +1 -1 thd->transaction.{all,stmt}.modified_non_trans_table instead of thd->no_trans_update.{all,stmt} sql/handler.h@1.184, 2007-06-26 18:21:47+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +1 -0 new member is added. sql/log.cc@1.210, 2007-06-26 18:21:47+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +2 -2 thd->transaction.{all,stmt}.modified_non_trans_table instead of thd->no_trans_update.{all,stmt} sql/set_var.cc@1.184, 2007-06-26 18:21:47+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +2 -2 thd->transaction.{all,stmt}.modified_non_trans_table instead of thd->no_trans_update.{all,stmt} sql/sp_head.cc@1.243, 2007-06-26 18:21:48+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +14 -4 thd->transaction.{all,stmt}.modified_non_trans_table instead of thd->no_trans_update.{all,stmt} and saving and merging stmt's flag at the end of a substatement. sql/sql_class.cc@1.270, 2007-06-26 18:21:48+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +1 -1 thd->transaction.{all,stmt}.modified_non_trans_table instead of thd->no_trans_update.{all,stmt} sql/sql_class.h@1.329, 2007-06-26 18:21:48+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +1 -5 moving thd->no_trans_update members into thd->transaction.`member`; thd->transaction.{all,stmt}.modified_non_trans_table instead of thd->no_trans_update.{all,stmt}
sql/sql_delete.cc@1.198, 2007-06-26 18:21:48+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +25 -12
correcting basic delete incl truncate branch and multi-delete queries to set
stmt.modified_non_trans_table;
sql/sql_insert.cc@1.231, 2007-06-26 18:21:48+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +22 -19 insert and insert..select (send_eof and send_error both) cases. thd->transaction.{all,stmt}.modified_non_trans_table instead of thd->no_trans_update.{all,stmt} sql/sql_load.cc@1.114, 2007-06-26 18:21:48+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +6 -10 eliminating a separate issue where the stmt flag was saved and re-stored after write_record that actually could change it and the change would be lost but should remain permanent; thd->transaction.{all,stmt}.modified_non_trans_table instead of thd->no_trans_update.{all,stmt} sql/sql_parse.cc@1.618, 2007-06-26 18:21:48+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +12 -10 initialization to transaction.stmt.modified_non_trans_table at the common part of all types of statements processing - mysql_execute_command(). sql/sql_table.cc@1.341, 2007-06-26 18:21:49+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +0 -1 moving the reset up to the mysql_execute_command() caller sql/sql_update.cc@1.216, 2007-06-26 18:21:49+03:00, aelkin@dsl-hkibras1-ff5dc300-70.dhcp.inet.fi +10 -12 correcting update query case (multi-update part of the issues covered by other bug#27716 fix) thd->transaction.{all,stmt}.modified_non_trans_table instead of thd->no_trans_update.{all,stmt} # This is a BitKeeper patch. What follows are the unified diffs for the # set of deltas contained in the patch. The rest of the patch, the part # that BitKeeper cares about, is below these diffs. # User: aelkin # Host: dsl-hkibras1-ff5dc300-70.dhcp.inet.fi # Root: /home/elkin/MySQL/TEAM/FIXES/5.0/bug27417-no_trans_update__stmt --- 1.232/sql/handler.cc 2007-04-12 12:46:06 +03:00 +++ 1.233/sql/handler.cc 2007-06-26 18:21:47 +03:00*/ - if (is_real_trans && thd->no_trans_update.all && + if (is_real_trans && thd->transaction.all.modified_non_trans_table &&
!thd->slave_thread)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
--- 1.183/sql/handler.h 2007-03-30 11:00:20 +03:00
+++ 1.184/sql/handler.h 2007-06-26 18:21:47 +03:00
/* storage engines that registered themselves for this transaction */ handlerton *ht[MAX_HA]; + bool modified_non_trans_table; } THD_TRANS; enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED,
select_lex->no_error= thd->lex->ignore;
-
/*
Test if the user wants to delete all rows and deletion doesn't have
any side-effects (because of triggers), so we can use optimized
}
+ if (!transactional_table && deleted > 0)
+ thd->transaction.stmt.modified_non_trans_table= TRUE;
+
if (thd->killed && !error)
error= 1; // Aborted
thd->proc_info="end"; @@ -314,7 +320,6 @@ cleanup: }
delete select;
/* See similar binlogging code in sql_update.cc, for comments */
if ((error < 0) || (deleted && !transactional_table))
{
if (mysql_bin_log.write(&qinfo) && transactional_table)
error=1;
}
- if (!transactional_table)
- thd->no_trans_update.all= TRUE;
+ if (thd->transaction.stmt.modified_non_trans_table)
+ thd->transaction.all.modified_non_trans_table= TRUE;
}
free_underlaid_joins(thd, select_lex);
if (transactional_table)
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, FALSE))
- DBUG_RETURN(1);
+ DBUG_RETURN(1);
table->status|= STATUS_DELETED;
if (!(error=table->file->delete_row(table->record[0])))
{
- deleted++;
+ deleted++;
+ if (!table->file->has_transactions())
+ thd->transaction.stmt.modified_non_trans_table= TRUE;
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_AFTER, FALSE))
- DBUG_RETURN(1);
+ DBUG_RETURN(1);
}
else
{
- table->file->print_error(error,MYF(0));
- DBUG_RETURN(1);
+ table->file->print_error(error,MYF(0));
+ DBUG_RETURN(1);
}
}
else
} + DBUG_ASSERT(!normal_tables || !deleted || thd->transaction.stmt.modified_non_trans_table); DBUG_VOID_RETURN;
@@ -734,6 +743,7 @@ int multi_delete::do_deletes() for (; table_being_deleted;
table_being_deleted= table_being_deleted->next_local, counter++)
{
TABLE *table = table_being_deleted->table;
if (tempfiles[counter]->get(table))
{
@@ -809,7 +821,6 @@ bool multi_delete::send_eof()
{
if ((local_error == 0) || (deleted && normal_tables))
{
if (mysql_bin_log.write(&qinfo) && !normal_tables)
local_error=1; // Log write failed: roll back the SQL statement
}
- if (!transactional_tables)
- thd->no_trans_update.all= TRUE;
+ if (thd->transaction.stmt.modified_non_trans_table)
+ thd->transaction.all.modified_non_trans_table= TRUE;
}
/* Commit or rollback the current SQL statement */
if (transactional_tables)
@@ -1175,7 +1175,7 @@ static int last_uniq_key(TABLE *table,ui
then both on update triggers will work instead. Similarly both on
delete triggers will be invoked if we will delete conflicting records.
RETURN VALUE
goto err;
info->deleted++;
if (!table->file->has_transactions())
- thd->no_trans_update.stmt= TRUE;
+ thd->transaction.stmt.modified_non_trans_table= TRUE;
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_AFTER, TRUE))
@@ -1373,7 +1373,7 @@ ok_or_after_trg_err:
if (key)
DBUG_RETURN(trg_error);
err:
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
}
- thd->no_trans_update.stmt= FALSE; thd->abort_on_warning= (!info.ignore &&
(thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES |
@@ -2691,6 +2690,7 @@ void select_insert::store_values(List<It
void select_insert::send_error(uint errcode,const char *err)
{
DBUG_ENTER("select_insert::send_error");
my_message(errcode, err, MYF(0));
*/
DBUG_VOID_RETURN;
} + transactional_table= table->file->has_transactions();
if (!thd->prelocked_mode)
error while inserting into a MyISAM table) we must write to the binlog (and
the error code will make the slave stop).
*/
- if ((info.copied || info.deleted || info.updated) && - !table->file->has_transactions()) + if ((changed= info.copied || info.deleted || info.updated) && + !transactional_table) {
if (last_insert_id)
thd->insert_id(last_insert_id); // For binary log
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
- table->file->has_transactions(), FALSE);
+ transactional_table, FALSE);
mysql_bin_log.write(&qinfo);
}
- if (!table->s->tmp_table)
- thd->no_trans_update.all= TRUE;
+ if (thd->transaction.stmt.modified_non_trans_table)
+ thd->transaction.all.modified_non_trans_table= TRUE;
}
{
bool select_insert::send_eof()
DBUG_ENTER("select_insert::send_eof");
error= (!thd->prelocked_mode) ? table->file->end_bulk_insert():0;
and ha_autocommit_or_rollback
if (last_insert_id)
if (!error)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
- table->file->has_transactions(), FALSE);
+ transactional_table, FALSE);
mysql_bin_log.write(&qinfo);
} if ((error2=ha_autocommit_or_rollback(thd,error)) && ! error) @@ -2978,7 +2982,6 @@ select_create::prepare(List<Item> &value
}
thd->abort_on_warning= (!info.ignore &&
(thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES |
--- 1.113/sql/sql_load.cc 2007-05-08 03:43:16 +03:00
+++ 1.114/sql/sql_load.cc 2007-06-26 18:21:48 +03:00
*/ query_cache_invalidate3(thd, table_list, 0); -
if (error)
sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted, (ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
id= 0;
read_info.row_end[0]=0;
restore_record(table, s->default_values);
/*
- thd->no_trans_update.stmt= no_trans_update_stmt;
/*
If auto_increment values are used, save the first one for
@@ -671,12 +669,11 @@ read_sep_field(THD *thd, COPY_INFO &info
TABLE *table= table_list->table;
DBUG_ENTER("read_sep_field");
enclosed_length=enclosed.length();
for (;;it.rewind())
We don't need to reset auto-increment field since we are restoring
its default value at the beginning of each loop iteration.
*/
- thd->no_trans_update.stmt= no_trans_update_stmt;
if (read_info.next_line()) // Skip to next line
break;
if (read_info.line_cuted)
--- 1.617/sql/sql_parse.cc 2007-04-03 14:11:32 +03:00
+++ 1.618/sql/sql_parse.cc 2007-06-26 18:21:48 +03:00
- thd->no_trans_update.all= FALSE;
+ thd->transaction.all.modified_non_trans_table= FALSE;
}
else
- thd->no_trans_update.all= FALSE;
+ thd->transaction.all.modified_non_trans_table= FALSE;
thd->options|= (ulong) OPTION_BEGIN;
thd->server_status|= SERVER_STATUS_IN_TRANS;
if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
- thd->no_trans_update.all= FALSE;
+ thd->transaction.all.modified_non_trans_table= FALSE;
break;
if (ha_rollback(thd))
res= -1;
thd->options&= ~(ulong) OPTION_BEGIN;
- thd->no_trans_update.all= FALSE;
+ thd->transaction.all.modified_non_trans_table= FALSE;
if (!res && (completion == ROLLBACK_AND_CHAIN))
res= begin_trans(thd);
break;
+ thd->transaction.stmt.modified_non_trans_table= FALSE; +
switch (lex->sql_command) {
else
{
/* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
- thd->no_trans_update.all= TRUE;
+ thd->transaction.all.modified_non_trans_table= TRUE;
}
DBUG_ASSERT(first_table == all_tables && first_table != 0);
bool link_to_local;
@@ -4004,7 +4004,6 @@ copy_data_between_tables(TABLE *from,TAB alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff); /* We can abort alter table for any table type */ - thd->no_trans_update.stmt= FALSE; thd->abort_on_warning= !ignore && test(thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES |
MODE_STRICT_ALL_TABLES));
--- 1.215/sql/sql_update.cc 2007-04-12 12:46:07 +03:00
+++ 1.216/sql/sql_update.cc 2007-06-26 18:21:49 +03:00
@@ -429,7 +429,6 @@ int mysql_update(THD *thd, query_id=thd->query_id;
transactional_table= table->file->has_transactions();
- thd->no_trans_update.stmt= FALSE;
thd->abort_on_warning= test(!ignore &&
(thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES |
}
+
+ if (!transactional_table && updated > 0)
+ thd->transaction.stmt.modified_non_trans_table= TRUE;
+
if (thd->killed && !error)
error= 1; // Aborted
end_read_record(&info); @@ -557,9 +559,10 @@ int mysql_update(THD *thd,
if (mysql_bin_log.write(&qinfo) && transactional_table)
error=1; // Rollback update
}
- if (!transactional_table)
- thd->no_trans_update.all= TRUE;
+ if (thd->transaction.stmt.modified_non_trans_table)
+ thd->transaction.all.modified_non_trans_table= TRUE;
}
free_underlaid_joins(thd, select_lex);
if (transactional_table)
handle_duplicates, ignore)))
DBUG_RETURN(TRUE);
@@ -1337,7 +1337,7 @@ bool multi_update::send_data(List<Item>
else
{
if (!table->file->has_transactions())
- thd->no_trans_update.stmt= TRUE;
+ thd->transaction.stmt.modified_non_trans_table= TRUE;
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
TRG_ACTION_AFTER, TRUE))
@@ -1384,7 +1384,6 @@ void multi_update::send_error(uint errco /* Something already updated so we have to invalidate cache */ query_cache_invalidate3(thd, update_tables, 1); - /*
If all tables that has been updated are trans safe then just do rollback.
If not attempt to do remaining updates.
@@ -1551,7 +1550,6 @@ 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
}
if (transactional_tables)
File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 98 /* only (!) with fixes for #23333 will show there is the query */; select count(*) from t1 /* must be 3 */; count(*) 3 reset master; select count(*) from t2; count(*) 2 delete from t2 where a=bug27417(3); select count(*) from t2 /* nothing got deleted */; count(*) 2 show master status; File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 195 /* the query must be in regardless of #23333 */; select count(*) from t1 /* must be 5 */; count(*) 5 delete t2 from t2 where t2.a=bug27417(100) /* must not affect t2 */; affected rows: 0 select count(*) from t1 /* must be 7 */; count(*) 7 drop table t1,t2; end of tests
--disable_warnings
delimiter |;
reset master; # execute
insert into t2 values (bug27417(1));
--error ER_DUP_ENTRY
reset master;
--enable_info
drop table t1,t2; --echo end of tests
-# End of 4.1 tests
# Test for BUG#16559 (ROLLBACK should always have a zero error code in # binlog). Has to be here and not earlier, as the SELECTs influence # XIDs differently between normal and ps-protocol (and SHOW BINLOG @@ -293,3 +291,66 @@ eval select @a like "%#%error_code=0%ROLLBACK/*!*/;%ROLLBACK /* added by mysqlbinlog */;%", @a not like "%#%error_code=%error_code=%"; drop table t1, t2; + +# +# bug#27417,bug#28960 +# +# testing appearence of insert into temp_table +# + +## send_eof() branch + +# prepare + +create temporary table tt (a int unique); +create table ti (a int) engine=innodb; +reset master; +show master status; + +# action + +begin; +insert into ti values (1); +insert into ti values (2) ; +insert into tt select * from ti; +rollback; + +# check + +select count(*) from tt /* 2 */; +show master status; +--replace_column 2 # 5 # +show binlog events from 98; +select count(*) from ti /* zero */; +insert into ti select * from tt; +select * from ti /* that is what slave would miss - a bug */; + + +## send_error() branch +delete from ti; +delete from tt where a=1; +reset master; +show master status; + +# action + +begin; +insert into ti values (1); +insert into ti values (2) /* to make the dup error in the following */; +--error ER_DUP_ENTRY +insert into tt select * from ti /* one affected and error */; +rollback; + +# check + +show master status; +--replace_column 2 # 5 # +show binlog events from 98; +select count(*) from ti /* zero */; +insert into ti select * from tt; +select * from tt /* that is what slave would miss - the bug */; + +drop table ti; + +--echo end of tests + --- 1.308/sql/ha_ndbcluster.cc 2007-04-17 16:51:56 +03:00 +++ 1.309/sql/ha_ndbcluster.cc 2007-06-26 18:21:47 +03:00 }
enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; bool save_abort_on_warning= thd->abort_on_warning; - bool save_no_trans_update_stmt= thd->no_trans_update.stmt; + bool save_stmt_modified_non_trans_table= thd->transaction.stmt.modified_non_trans_table; thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL; thd->abort_on_warning=
thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES);
- thd->no_trans_update.stmt= FALSE;
+ thd->transaction.stmt.modified_non_trans_table= FALSE; /* Save the value in the field. Convert the value if needed. */ @@ -351,7 +351,7 @@ sp_eval_expr(THD *thd, Field *result_fie thd->count_cuted_fields= save_count_cuted_fields; thd->abort_on_warning= save_abort_on_warning; - thd->no_trans_update.stmt= save_no_trans_update_stmt; + thd->transaction.stmt.modified_non_trans_table= save_stmt_modified_non_trans_table;
if (thd->net.report_error)
bool open_tables, sp_instr* instr)
{
int res= 0; + /* + the flag is saved at the entry to the following substatement. + It's reset further in the common code part. + It's merged with the saved parent's value at the exit of this func. + */ + bool parent_modified_non_trans_table= thd->transaction.stmt.modified_non_trans_table;
DBUG_ASSERT(!thd->derived_tables);
/* Update the state of the active arena. */ thd->stmt_arena->state= Query_arena::EXECUTED; -
+ /*
+ merge here with the saved parent's values
+ what is needed from the substatement gained
+ */
+ thd->transaction.stmt.modified_non_trans_table |= parent_modified_non_trans_table;
/*
Unlike for PS we should not call Item's destructors for newly created
items after execution of each instruction in stored routine. This is
-- MySQL Code Commits Mailing List For list archives: http://lists.mysql.com/commits To unsubscribe: http://lists.mysql.com/commits?unsub=lists@pantek.comReceived on Tue Jun 26 11:22:20 2007 This archive was generated by hypermail 2.1.8 : Tue Jun 26 2007 - 11:30:03 EDT |
||||||||||
|
|||||||||||