(Pushing for Andrei)
Merge magare.gmz:/home/kgeorge/mysql/work/B27417-5.0-opt into magare.gmz:/home/kgeorge/mysql/work/B27417-5.1-opt mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result: Auto merged sql/ha_ndbcluster.cc: Auto merged sql/handler.cc: Auto merged sql/handler.h: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_table.cc: Auto merged mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test: merge of bug 27471 from 5.0-opt to 5.1-opt sql/log.cc: merge of bug 27471 from 5.0-opt to 5.1-opt sql/set_var.cc: merge of bug 27471 from 5.0-opt to 5.1-opt sql/sp_head.cc: merge of bug 27471 from 5.0-opt to 5.1-opt sql/sql_delete.cc: merge of bug 27471 from 5.0-opt to 5.1-opt sql/sql_insert.cc: merge of bug 27471 from 5.0-opt to 5.1-opt sql/sql_load.cc: merge of bug 27471 from 5.0-opt to 5.1-opt sql/sql_parse.cc: merge of bug 27471 from 5.0-opt to 5.1-opt sql/sql_update.cc: merge of bug 27471 from 5.0-opt to 5.1-opt
This commit is contained in:
commit
e5fd6b3c71
@ -316,3 +316,121 @@ disconnect con3;
|
||||
connection con4;
|
||||
select get_lock("a",10); # wait for rollback to finish
|
||||
|
||||
#
|
||||
# Bug #27417 thd->no_trans_update.stmt lost value inside of SF-exec-stack
|
||||
# bug #28960 non-trans temp table changes with insert .. select
|
||||
# not binlogged after rollback
|
||||
#
|
||||
# testing appearence of insert into temp_table in binlog.
|
||||
# There are two branches of execution that require different setup.
|
||||
|
||||
## 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 otherwise slave missed - the bug */;
|
||||
|
||||
drop table ti;
|
||||
|
||||
|
||||
#
|
||||
# Bug #27417 thd->no_trans_update.stmt lost value inside of SF-exec-stack
|
||||
#
|
||||
# Testing asserts: if there is a side effect of modifying non-transactional
|
||||
# table thd->no_trans_update.stmt must be TRUE;
|
||||
# the assert is active with debug build
|
||||
#
|
||||
|
||||
--disable_warnings
|
||||
drop function if exists bug27417;
|
||||
drop table if exists t1,t2;
|
||||
--enable_warnings
|
||||
# side effect table
|
||||
CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM;
|
||||
# target tables
|
||||
CREATE TABLE t2 (a int NOT NULL auto_increment, PRIMARY KEY (a));
|
||||
|
||||
delimiter |;
|
||||
create function bug27417(n int)
|
||||
RETURNS int(11)
|
||||
begin
|
||||
insert into t1 values (null);
|
||||
return n;
|
||||
end|
|
||||
delimiter ;|
|
||||
|
||||
reset master;
|
||||
|
||||
# execute
|
||||
|
||||
insert into t2 values (bug27417(1));
|
||||
insert into t2 select bug27417(2);
|
||||
reset master;
|
||||
|
||||
--error ER_DUP_ENTRY
|
||||
insert into t2 values (bug27417(2));
|
||||
show master status; /* only (!) with fixes for #23333 will show there is the query */;
|
||||
select count(*) from t1 /* must be 3 */;
|
||||
|
||||
reset master;
|
||||
select count(*) from t2;
|
||||
delete from t2 where a=bug27417(3);
|
||||
select count(*) from t2 /* nothing got deleted */;
|
||||
show master status; /* the query must be in regardless of #23333 */;
|
||||
select count(*) from t1 /* must be 5 */;
|
||||
|
||||
--enable_info
|
||||
delete t2 from t2 where t2.a=bug27417(100) /* must not affect t2 */;
|
||||
--disable_info
|
||||
select count(*) from t1 /* must be 7 */;
|
||||
|
||||
drop function bug27417;
|
||||
drop table t1,t2;
|
||||
|
||||
--echo end of tests
|
||||
|
@ -380,3 +380,116 @@ select
|
||||
@a like "%#%error_code=0%ROLLBACK/*!*/;%ROLLBACK /* added by mysqlbinlog */;%" @a not like "%#%error_code=%error_code=%"
|
||||
1 1
|
||||
drop table t1, t2;
|
||||
create temporary table tt (a int unique);
|
||||
create table ti (a int) engine=innodb;
|
||||
reset master;
|
||||
show master status;
|
||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||
master-bin.000001 98
|
||||
begin;
|
||||
insert into ti values (1);
|
||||
insert into ti values (2) ;
|
||||
insert into tt select * from ti;
|
||||
rollback;
|
||||
Warnings:
|
||||
Warning 1196 Some non-transactional changed tables couldn't be rolled back
|
||||
select count(*) from tt /* 2 */;
|
||||
count(*)
|
||||
2
|
||||
show master status;
|
||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||
master-bin.000001 507
|
||||
show binlog events from 98;
|
||||
Log_name Pos Event_type Server_id End_log_pos Info
|
||||
master-bin.000001 # Query 1 # use `test`; BEGIN
|
||||
master-bin.000001 # Query 1 # use `test`; insert into ti values (1)
|
||||
master-bin.000001 # Query 1 # use `test`; insert into ti values (2)
|
||||
master-bin.000001 # Query 1 # use `test`; insert into tt select * from ti
|
||||
master-bin.000001 # Query 1 # use `test`; ROLLBACK
|
||||
select count(*) from ti /* zero */;
|
||||
count(*)
|
||||
0
|
||||
insert into ti select * from tt;
|
||||
select * from ti /* that is what slave would miss - a bug */;
|
||||
a
|
||||
1
|
||||
2
|
||||
delete from ti;
|
||||
delete from tt where a=1;
|
||||
reset master;
|
||||
show master status;
|
||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||
master-bin.000001 98
|
||||
begin;
|
||||
insert into ti values (1);
|
||||
insert into ti values (2) /* to make the dup error in the following */;
|
||||
insert into tt select * from ti /* one affected and error */;
|
||||
ERROR 23000: Duplicate entry '2' for key 1
|
||||
rollback;
|
||||
Warnings:
|
||||
Warning 1196 Some non-transactional changed tables couldn't be rolled back
|
||||
show master status;
|
||||
File Position Binlog_Do_DB Binlog_Ignore_DB
|
||||
master-bin.000001 581
|
||||
show binlog events from 98;
|
||||
Log_name Pos Event_type Server_id End_log_pos Info
|
||||
master-bin.000001 # Query 1 # use `test`; BEGIN
|
||||
master-bin.000001 # Query 1 # use `test`; insert into ti values (1)
|
||||
master-bin.000001 # Query 1 # use `test`; insert into ti values (2) /* to make the dup error in the following */
|
||||
master-bin.000001 # Query 1 # use `test`; insert into tt select * from ti /* one affected and error */
|
||||
master-bin.000001 # Query 1 # use `test`; ROLLBACK
|
||||
select count(*) from ti /* zero */;
|
||||
count(*)
|
||||
0
|
||||
insert into ti select * from tt;
|
||||
select * from tt /* that is what otherwise slave missed - the bug */;
|
||||
a
|
||||
1
|
||||
2
|
||||
drop table ti;
|
||||
drop function if exists bug27417;
|
||||
drop table if exists t1,t2;
|
||||
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 function bug27417(n int)
|
||||
RETURNS int(11)
|
||||
begin
|
||||
insert into t1 values (null);
|
||||
return n;
|
||||
end|
|
||||
reset master;
|
||||
insert into t2 values (bug27417(1));
|
||||
insert into t2 select bug27417(2);
|
||||
reset master;
|
||||
insert into t2 values (bug27417(2));
|
||||
ERROR 23000: Duplicate entry '2' for key 1
|
||||
show master status;
|
||||
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 function bug27417;
|
||||
drop table t1,t2;
|
||||
end of tests
|
||||
|
@ -4366,7 +4366,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
|
||||
{
|
||||
m_transaction_on= FALSE;
|
||||
/* Would be simpler if has_transactions() didn't always say "yes" */
|
||||
thd->no_trans_update.all= thd->no_trans_update.stmt= TRUE;
|
||||
thd->transaction.all.modified_non_trans_table= thd->transaction.stmt.modified_non_trans_table= TRUE;
|
||||
}
|
||||
else if (!thd->transaction.on)
|
||||
m_transaction_on= FALSE;
|
||||
|
@ -860,7 +860,7 @@ int ha_rollback_trans(THD *thd, bool all)
|
||||
the error log; but we don't want users to wonder why they have this
|
||||
message in the error log, so we don't send it.
|
||||
*/
|
||||
if (is_real_trans && thd->no_trans_update.all &&
|
||||
if (is_real_trans && thd->transaction.all.modified_non_trans_table &&
|
||||
!thd->slave_thread && thd->killed != THD::KILL_CONNECTION)
|
||||
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
||||
ER_WARNING_NOT_COMPLETE_ROLLBACK,
|
||||
|
@ -728,6 +728,35 @@ typedef struct st_thd_trans
|
||||
bool no_2pc;
|
||||
/* storage engines that registered themselves for this transaction */
|
||||
handlerton *ht[MAX_HA];
|
||||
/*
|
||||
The purpose of this flag is to keep track of non-transactional
|
||||
tables that were modified in scope of:
|
||||
- transaction, when the variable is a member of
|
||||
THD::transaction.all
|
||||
- top-level statement or sub-statement, when the variable is a
|
||||
member of THD::transaction.stmt
|
||||
This member has the following life cycle:
|
||||
* stmt.modified_non_trans_table is used to keep track of
|
||||
modified non-transactional tables of top-level statements. At
|
||||
the end of the previous statement and at the beginning of the session,
|
||||
it is reset to FALSE. If such functions
|
||||
as mysql_insert, mysql_update, mysql_delete etc modify a
|
||||
non-transactional table, they set this flag to TRUE. At the
|
||||
end of the statement, the value of stmt.modified_non_trans_table
|
||||
is merged with all.modified_non_trans_table and gets reset.
|
||||
* all.modified_non_trans_table is reset at the end of transaction
|
||||
|
||||
* Since we do not have a dedicated context for execution of a
|
||||
sub-statement, to keep track of non-transactional changes in a
|
||||
sub-statement, we re-use stmt.modified_non_trans_table.
|
||||
At entrance into a sub-statement, a copy of the value of
|
||||
stmt.modified_non_trans_table (containing the changes of the
|
||||
outer statement) is saved on stack. Then
|
||||
stmt.modified_non_trans_table is reset to FALSE and the
|
||||
substatement is executed. Then the new value is merged with the
|
||||
saved value.
|
||||
*/
|
||||
bool modified_non_trans_table;
|
||||
} THD_TRANS;
|
||||
|
||||
enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED,
|
||||
|
@ -1609,7 +1609,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
|
||||
table. Such cases should be rare (updating a
|
||||
non-transactional table inside a transaction...)
|
||||
*/
|
||||
if (unlikely(thd->no_trans_update.all || (thd->options & OPTION_KEEP_LOG)))
|
||||
if (unlikely(thd->transaction.all.modified_non_trans_table ||
|
||||
(thd->options & OPTION_KEEP_LOG)))
|
||||
{
|
||||
Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE);
|
||||
qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
|
||||
@ -1663,7 +1664,8 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
|
||||
non-transactional table. Otherwise, truncate the binlog cache starting
|
||||
from the SAVEPOINT command.
|
||||
*/
|
||||
if (unlikely(thd->no_trans_update.all || (thd->options & OPTION_KEEP_LOG)))
|
||||
if (unlikely(thd->transaction.all.modified_non_trans_table ||
|
||||
(thd->options & OPTION_KEEP_LOG)))
|
||||
{
|
||||
int error=
|
||||
thd->binlog_query(THD::STMT_QUERY_TYPE,
|
||||
@ -4094,6 +4096,7 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
|
||||
/* NULL would represent nothing to replicate after ROLLBACK */
|
||||
DBUG_ASSERT(commit_event != NULL);
|
||||
|
||||
DBUG_ASSERT(is_open());
|
||||
if (likely(is_open())) // Should always be true
|
||||
{
|
||||
/*
|
||||
|
@ -2564,14 +2564,14 @@ static bool set_option_autocommit(THD *thd, set_var *var)
|
||||
{
|
||||
/* We changed to auto_commit mode */
|
||||
thd->options&= ~(ulonglong) (OPTION_BEGIN | OPTION_KEEP_LOG);
|
||||
thd->no_trans_update.all= FALSE;
|
||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||
thd->server_status|= SERVER_STATUS_AUTOCOMMIT;
|
||||
if (ha_commit(thd))
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
thd->no_trans_update.all= FALSE;
|
||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||
thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT;
|
||||
}
|
||||
}
|
||||
|
@ -349,13 +349,13 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
|
||||
|
||||
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. */
|
||||
|
||||
@ -363,7 +363,7 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
|
||||
|
||||
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)
|
||||
{
|
||||
@ -2547,6 +2547,13 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
|
||||
int res= 0;
|
||||
DBUG_ENTER("reset_lex_and_exec_core");
|
||||
|
||||
/*
|
||||
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;
|
||||
thd->transaction.stmt.modified_non_trans_table= FALSE;
|
||||
DBUG_ASSERT(!thd->derived_tables);
|
||||
DBUG_ASSERT(thd->change_list.is_empty());
|
||||
/*
|
||||
@ -2616,7 +2623,11 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
|
||||
/* 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
|
||||
|
@ -582,7 +582,7 @@ void THD::init(void)
|
||||
if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)
|
||||
server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES;
|
||||
options= thd_startup_options;
|
||||
no_trans_update.stmt= no_trans_update.all= FALSE;
|
||||
transaction.all.modified_non_trans_table= transaction.stmt.modified_non_trans_table= FALSE;
|
||||
open_options=ha_open_options;
|
||||
update_lock_default= (variables.low_priority_updates ?
|
||||
TL_WRITE_LOW_PRIORITY :
|
||||
|
@ -1409,10 +1409,6 @@ public:
|
||||
bool charset_is_system_charset, charset_is_collation_connection;
|
||||
bool charset_is_character_set_filesystem;
|
||||
bool enable_slow_log; /* enable slow log for current statement */
|
||||
struct {
|
||||
bool all:1;
|
||||
bool stmt:1;
|
||||
} no_trans_update;
|
||||
bool abort_on_warning;
|
||||
bool got_warning; /* Set on call to push_warning() */
|
||||
bool no_warnings_for_error; /* no warnings on call to my_error() */
|
||||
@ -1699,7 +1695,7 @@ public:
|
||||
inline bool really_abort_on_warning()
|
||||
{
|
||||
return (abort_on_warning &&
|
||||
(!no_trans_update.stmt ||
|
||||
(!transaction.stmt.modified_non_trans_table ||
|
||||
(variables.sql_mode & MODE_STRICT_ALL_TABLES)));
|
||||
}
|
||||
void set_status_var_init();
|
||||
|
@ -341,6 +341,9 @@ cleanup:
|
||||
delete select;
|
||||
transactional_table= table->file->has_transactions();
|
||||
|
||||
if (!transactional_table && deleted > 0)
|
||||
thd->transaction.stmt.modified_non_trans_table= TRUE;
|
||||
|
||||
/* See similar binlogging code in sql_update.cc, for comments */
|
||||
if ((error < 0) || (deleted && !transactional_table))
|
||||
{
|
||||
@ -364,9 +367,10 @@ cleanup:
|
||||
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;
|
||||
}
|
||||
DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
|
||||
free_underlaid_joins(thd, select_lex);
|
||||
if (transactional_table)
|
||||
{
|
||||
@ -677,20 +681,22 @@ bool multi_delete::send_data(List<Item> &values)
|
||||
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->ha_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
|
||||
@ -740,6 +746,7 @@ void multi_delete::send_error(uint errcode,const char *err)
|
||||
error= 1;
|
||||
send_eof();
|
||||
}
|
||||
DBUG_ASSERT(!normal_tables || !deleted || thd->transaction.stmt.modified_non_trans_table);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
@ -768,6 +775,7 @@ int multi_delete::do_deletes()
|
||||
for (; table_being_deleted;
|
||||
table_being_deleted= table_being_deleted->next_local, counter++)
|
||||
{
|
||||
ha_rows last_deleted= deleted;
|
||||
TABLE *table = table_being_deleted->table;
|
||||
if (tempfiles[counter]->get(table))
|
||||
{
|
||||
@ -814,6 +822,8 @@ int multi_delete::do_deletes()
|
||||
table->file->print_error(local_error,MYF(0));
|
||||
}
|
||||
}
|
||||
if (last_deleted != deleted && !table->file->has_transactions())
|
||||
thd->transaction.stmt.modified_non_trans_table= TRUE;
|
||||
end_read_record(&info);
|
||||
if (thd->killed && !local_error)
|
||||
local_error= 1;
|
||||
@ -852,7 +862,6 @@ bool multi_delete::send_eof()
|
||||
{
|
||||
query_cache_invalidate3(thd, delete_tables, 1);
|
||||
}
|
||||
|
||||
if ((local_error == 0) || (deleted && normal_tables))
|
||||
{
|
||||
if (mysql_bin_log.is_open())
|
||||
@ -867,9 +876,11 @@ bool multi_delete::send_eof()
|
||||
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;
|
||||
}
|
||||
DBUG_ASSERT(!normal_tables || !deleted || thd->transaction.stmt.modified_non_trans_table);
|
||||
|
||||
/* Commit or rollback the current SQL statement */
|
||||
if (transactional_tables)
|
||||
if (ha_autocommit_or_rollback(thd,local_error > 0))
|
||||
|
@ -713,7 +713,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
||||
if (lock_type != TL_WRITE_DELAYED && !thd->prelocked_mode)
|
||||
table->file->ha_start_bulk_insert(values_list.elements);
|
||||
|
||||
thd->no_trans_update.stmt= FALSE;
|
||||
thd->abort_on_warning= (!ignore && (thd->variables.sql_mode &
|
||||
(MODE_STRICT_TRANS_TABLES |
|
||||
MODE_STRICT_ALL_TABLES)));
|
||||
@ -883,10 +882,12 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
||||
error=1;
|
||||
}
|
||||
}
|
||||
if (!transactional_table && changed)
|
||||
thd->no_trans_update.all= TRUE;
|
||||
if (thd->transaction.stmt.modified_non_trans_table)
|
||||
thd->transaction.all.modified_non_trans_table= TRUE;
|
||||
}
|
||||
}
|
||||
DBUG_ASSERT(transactional_table || !changed ||
|
||||
thd->transaction.stmt.modified_non_trans_table);
|
||||
if (transactional_table)
|
||||
error=ha_autocommit_or_rollback(thd,error);
|
||||
|
||||
@ -1296,7 +1297,7 @@ static int last_uniq_key(TABLE *table,uint keynr)
|
||||
then both on update triggers will work instead. Similarly both on
|
||||
delete triggers will be invoked if we will delete conflicting records.
|
||||
|
||||
Sets thd->no_trans_update.stmt to TRUE if table which is updated didn't have
|
||||
Sets thd->transaction.stmt.modified_non_trans_table to TRUE if table which is updated didn't have
|
||||
transactions.
|
||||
|
||||
RETURN VALUE
|
||||
@ -1516,7 +1517,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
|
||||
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))
|
||||
@ -1557,7 +1558,7 @@ ok_or_after_trg_err:
|
||||
if (key)
|
||||
my_safe_afree(key,table->s->max_unique_length,MAX_KEY_LENGTH);
|
||||
if (!table->file->has_transactions())
|
||||
thd->no_trans_update.stmt= TRUE;
|
||||
thd->transaction.stmt.modified_non_trans_table= TRUE;
|
||||
DBUG_RETURN(trg_error);
|
||||
|
||||
err:
|
||||
@ -2922,7 +2923,6 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
|
||||
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
|
||||
if (info.handle_duplicates == DUP_UPDATE)
|
||||
table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
|
||||
thd->no_trans_update.stmt= FALSE;
|
||||
thd->abort_on_warning= (!info.ignore &&
|
||||
(thd->variables.sql_mode &
|
||||
(MODE_STRICT_TRANS_TABLES |
|
||||
@ -3071,6 +3071,7 @@ bool select_insert::send_eof()
|
||||
int error;
|
||||
bool const trans_table= table->file->has_transactions();
|
||||
ulonglong id;
|
||||
bool changed;
|
||||
DBUG_ENTER("select_insert::send_eof");
|
||||
DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'",
|
||||
trans_table, table->file->table_type()));
|
||||
@ -3079,24 +3080,18 @@ bool select_insert::send_eof()
|
||||
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
|
||||
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
|
||||
|
||||
if (info.copied || info.deleted || info.updated)
|
||||
if (changed= (info.copied || info.deleted || info.updated))
|
||||
{
|
||||
/*
|
||||
We must invalidate the table in the query cache before binlog writing
|
||||
and ha_autocommit_or_rollback.
|
||||
*/
|
||||
query_cache_invalidate3(thd, table, 1);
|
||||
/*
|
||||
Mark that we have done permanent changes if all of the below is true
|
||||
- Table doesn't support transactions
|
||||
- It's a normal (not temporary) table. (Changes to temporary tables
|
||||
are not logged in RBR)
|
||||
- We are using statement based replication
|
||||
*/
|
||||
if (!trans_table &&
|
||||
(!table->s->tmp_table || !thd->current_stmt_binlog_row_based))
|
||||
thd->no_trans_update.all= TRUE;
|
||||
}
|
||||
if (thd->transaction.stmt.modified_non_trans_table)
|
||||
thd->transaction.all.modified_non_trans_table= TRUE;
|
||||
}
|
||||
DBUG_ASSERT(trans_table || !changed ||
|
||||
thd->transaction.stmt.modified_non_trans_table);
|
||||
|
||||
/*
|
||||
Write to binlog before commiting transaction. No statement will
|
||||
@ -3517,7 +3512,6 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
|
||||
table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
|
||||
if (!thd->prelocked_mode)
|
||||
table->file->ha_start_bulk_insert((ha_rows) 0);
|
||||
thd->no_trans_update.stmt= FALSE;
|
||||
thd->abort_on_warning= (!info.ignore &&
|
||||
(thd->variables.sql_mode &
|
||||
(MODE_STRICT_TRANS_TABLES |
|
||||
|
@ -376,7 +376,6 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
|
||||
table->file->ha_start_bulk_insert((ha_rows) 0);
|
||||
table->copy_blobs=1;
|
||||
|
||||
thd->no_trans_update.stmt= FALSE;
|
||||
thd->abort_on_warning= (!ignore &&
|
||||
(thd->variables.sql_mode &
|
||||
(MODE_STRICT_TRANS_TABLES |
|
||||
@ -410,7 +409,6 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
|
||||
ha_autocommit_...
|
||||
*/
|
||||
query_cache_invalidate3(thd, table_list, 0);
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (read_file_from_client)
|
||||
@ -469,8 +467,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
|
||||
sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted,
|
||||
(ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
|
||||
|
||||
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;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
if (mysql_bin_log.is_open())
|
||||
{
|
||||
@ -506,6 +504,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
|
||||
/* ok to client sent only after binlog write and engine commit */
|
||||
send_ok(thd, info.copied + info.deleted, 0L, name);
|
||||
err:
|
||||
DBUG_ASSERT(transactional_table || !(info.copied || info.deleted) ||
|
||||
thd->transaction.stmt.modified_non_trans_table);
|
||||
table->file->ha_release_auto_increment();
|
||||
if (thd->lock)
|
||||
{
|
||||
@ -552,7 +552,7 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
|
||||
Item_field *sql_field;
|
||||
TABLE *table= table_list->table;
|
||||
ulonglong id;
|
||||
bool no_trans_update_stmt, err;
|
||||
bool err;
|
||||
DBUG_ENTER("read_fixed_length");
|
||||
|
||||
id= 0;
|
||||
@ -580,7 +580,6 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
|
||||
#ifdef HAVE_purify
|
||||
read_info.row_end[0]=0;
|
||||
#endif
|
||||
no_trans_update_stmt= !table->file->has_transactions();
|
||||
|
||||
restore_record(table, s->default_values);
|
||||
/*
|
||||
@ -650,7 +649,6 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
|
||||
table->auto_increment_field_not_null= FALSE;
|
||||
if (err)
|
||||
DBUG_RETURN(1);
|
||||
thd->no_trans_update.stmt= no_trans_update_stmt;
|
||||
|
||||
/*
|
||||
We don't need to reset auto-increment field since we are restoring
|
||||
@ -685,12 +683,11 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
|
||||
TABLE *table= table_list->table;
|
||||
uint enclosed_length;
|
||||
ulonglong id;
|
||||
bool no_trans_update_stmt, err;
|
||||
bool err;
|
||||
DBUG_ENTER("read_sep_field");
|
||||
|
||||
enclosed_length=enclosed.length();
|
||||
id= 0;
|
||||
no_trans_update_stmt= !table->file->has_transactions();
|
||||
|
||||
for (;;it.rewind())
|
||||
{
|
||||
@ -827,7 +824,6 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
|
||||
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)
|
||||
|
@ -120,7 +120,7 @@ bool end_active_trans(THD *thd)
|
||||
error=1;
|
||||
}
|
||||
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
||||
thd->no_trans_update.all= FALSE;
|
||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
@ -578,7 +578,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion)
|
||||
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
||||
res= ha_commit(thd);
|
||||
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_KEEP_LOG);
|
||||
thd->no_trans_update.all= FALSE;
|
||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||
break;
|
||||
case COMMIT_RELEASE:
|
||||
do_release= 1; /* fall through */
|
||||
@ -596,7 +596,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion)
|
||||
if (ha_rollback(thd))
|
||||
res= -1;
|
||||
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_KEEP_LOG);
|
||||
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;
|
||||
@ -1806,6 +1806,8 @@ mysql_execute_command(THD *thd)
|
||||
#endif
|
||||
status_var_increment(thd->status_var.com_stat[lex->sql_command]);
|
||||
|
||||
DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
|
||||
|
||||
switch (lex->sql_command) {
|
||||
case SQLCOM_SHOW_EVENTS:
|
||||
if ((res= check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0,
|
||||
@ -3620,7 +3622,8 @@ end_with_restore_list:
|
||||
res= TRUE; // cannot happen
|
||||
else
|
||||
{
|
||||
if (((thd->options & OPTION_KEEP_LOG) || thd->no_trans_update.all) &&
|
||||
if (((thd->options & OPTION_KEEP_LOG) ||
|
||||
thd->transaction.all.modified_non_trans_table) &&
|
||||
!thd->slave_thread)
|
||||
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
||||
ER_WARNING_NOT_COMPLETE_ROLLBACK,
|
||||
@ -4200,8 +4203,8 @@ create_sp_error:
|
||||
thd->transaction.xid_state.xa_state=XA_ACTIVE;
|
||||
thd->transaction.xid_state.xid.set(thd->lex->xid);
|
||||
xid_cache_insert(&thd->transaction.xid_state);
|
||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||
thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN);
|
||||
thd->no_trans_update.all= FALSE;
|
||||
thd->server_status|= SERVER_STATUS_IN_TRANS;
|
||||
send_ok(thd);
|
||||
break;
|
||||
@ -4295,7 +4298,7 @@ create_sp_error:
|
||||
break;
|
||||
}
|
||||
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
||||
thd->no_trans_update.all= FALSE;
|
||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
||||
xid_cache_delete(&thd->transaction.xid_state);
|
||||
thd->transaction.xid_state.xa_state=XA_NOTR;
|
||||
@ -4326,7 +4329,7 @@ create_sp_error:
|
||||
else
|
||||
send_ok(thd);
|
||||
thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
|
||||
thd->no_trans_update.all= FALSE;
|
||||
thd->transaction.all.modified_non_trans_table= FALSE;
|
||||
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
||||
xid_cache_delete(&thd->transaction.xid_state);
|
||||
thd->transaction.xid_state.xa_state=XA_NOTR;
|
||||
|
@ -6766,7 +6766,6 @@ copy_data_between_tables(TABLE *from,TABLE *to,
|
||||
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));
|
||||
|
@ -530,7 +530,6 @@ int mysql_update(THD *thd,
|
||||
thd->proc_info="Updating";
|
||||
|
||||
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 |
|
||||
@ -641,7 +640,6 @@ int mysql_update(THD *thd,
|
||||
updated++;
|
||||
else
|
||||
error= 0;
|
||||
thd->no_trans_update.stmt= !transactional_table;
|
||||
|
||||
if (table->triggers &&
|
||||
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
|
||||
@ -717,6 +715,10 @@ int mysql_update(THD *thd,
|
||||
}
|
||||
dup_key_found= 0;
|
||||
|
||||
if (!transactional_table && updated > 0)
|
||||
thd->transaction.stmt.modified_non_trans_table= TRUE;
|
||||
|
||||
|
||||
/*
|
||||
todo bug#27571: to avoid asynchronization of `error' and
|
||||
`error_code' of binlog event constructor
|
||||
@ -809,9 +811,10 @@ int mysql_update(THD *thd,
|
||||
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;
|
||||
}
|
||||
DBUG_ASSERT(transactional_table || !updated || thd->transaction.stmt.modified_non_trans_table);
|
||||
free_underlaid_joins(thd, select_lex);
|
||||
if (transactional_table)
|
||||
{
|
||||
@ -1176,7 +1179,6 @@ bool mysql_multi_update(THD *thd,
|
||||
handle_duplicates, ignore)))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
thd->no_trans_update.stmt= FALSE;
|
||||
thd->abort_on_warning= test(thd->variables.sql_mode &
|
||||
(MODE_STRICT_TRANS_TABLES |
|
||||
MODE_STRICT_ALL_TABLES));
|
||||
@ -1549,9 +1551,7 @@ multi_update::~multi_update()
|
||||
if (copy_field)
|
||||
delete [] copy_field;
|
||||
thd->count_cuted_fields= CHECK_FIELD_IGNORE; // Restore this setting
|
||||
if (!trans_safe) // todo: remove since redundant
|
||||
thd->no_trans_update.all= TRUE;
|
||||
DBUG_ASSERT(trans_safe || thd->no_trans_update.all);
|
||||
DBUG_ASSERT(trans_safe || thd->transaction.all.modified_non_trans_table);
|
||||
}
|
||||
|
||||
|
||||
@ -1655,7 +1655,7 @@ bool multi_update::send_data(List<Item> ¬_used_values)
|
||||
else
|
||||
{
|
||||
trans_safe= 0;
|
||||
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,
|
||||
@ -1718,7 +1718,6 @@ void multi_update::send_error(uint errcode,const char *err)
|
||||
|
||||
/* 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.
|
||||
@ -1731,7 +1730,7 @@ void multi_update::send_error(uint errcode,const char *err)
|
||||
}
|
||||
else
|
||||
{
|
||||
DBUG_ASSERT(thd->no_trans_update.stmt);
|
||||
DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table);
|
||||
if (do_update && table_count > 1)
|
||||
{
|
||||
/* Add warning here */
|
||||
@ -1742,7 +1741,7 @@ void multi_update::send_error(uint errcode,const char *err)
|
||||
VOID(do_updates(0));
|
||||
}
|
||||
}
|
||||
if (thd->no_trans_update.stmt)
|
||||
if (thd->transaction.stmt.modified_non_trans_table)
|
||||
{
|
||||
/*
|
||||
The query has to binlog because there's a modified non-transactional table
|
||||
@ -1755,9 +1754,9 @@ void multi_update::send_error(uint errcode,const char *err)
|
||||
transactional_tables, FALSE);
|
||||
}
|
||||
if (!trans_safe)
|
||||
thd->no_trans_update.all= TRUE;
|
||||
thd->transaction.all.modified_non_trans_table= TRUE;
|
||||
}
|
||||
DBUG_ASSERT(trans_safe || !updated || thd->no_trans_update.stmt);
|
||||
DBUG_ASSERT(trans_safe || !updated || thd->transaction.stmt.modified_non_trans_table);
|
||||
|
||||
if (transactional_tables)
|
||||
{
|
||||
@ -1900,7 +1899,7 @@ int multi_update::do_updates(bool from_send_error)
|
||||
else
|
||||
{
|
||||
trans_safe= 0; // Can't do safe rollback
|
||||
thd->no_trans_update.stmt= TRUE;
|
||||
thd->transaction.stmt.modified_non_trans_table= TRUE;
|
||||
}
|
||||
}
|
||||
(void) table->file->ha_rnd_end();
|
||||
@ -1934,7 +1933,7 @@ err2:
|
||||
else
|
||||
{
|
||||
trans_safe= 0;
|
||||
thd->no_trans_update.stmt= TRUE;
|
||||
thd->transaction.stmt.modified_non_trans_table= TRUE;
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(1);
|
||||
@ -1961,7 +1960,6 @@ bool multi_update::send_eof()
|
||||
{
|
||||
query_cache_invalidate3(thd, update_tables, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
Write the SQL statement to the binlog if we updated
|
||||
rows and we succeeded or if we updated some non
|
||||
@ -1971,8 +1969,9 @@ bool multi_update::send_eof()
|
||||
either from the query's list or via a stored routine: bug#13270,23333
|
||||
*/
|
||||
|
||||
DBUG_ASSERT(trans_safe || !updated || thd->no_trans_update.stmt);
|
||||
if (local_error == 0 || thd->no_trans_update.stmt)
|
||||
DBUG_ASSERT(trans_safe || !updated ||
|
||||
thd->transaction.stmt.modified_non_trans_table);
|
||||
if (local_error == 0 || thd->transaction.stmt.modified_non_trans_table)
|
||||
{
|
||||
if (mysql_bin_log.is_open())
|
||||
{
|
||||
@ -1988,8 +1987,8 @@ bool multi_update::send_eof()
|
||||
local_error= 1; // Rollback update
|
||||
}
|
||||
}
|
||||
if (!trans_safe)
|
||||
thd->no_trans_update.all= TRUE;
|
||||
if (thd->transaction.stmt.modified_non_trans_table)
|
||||
thd->transaction.all.modified_non_trans_table= TRUE;
|
||||
}
|
||||
|
||||
if (transactional_tables)
|
||||
|
Loading…
x
Reference in New Issue
Block a user