# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2007/07/20 19:00:54+03:00 aelkin@koti.dsl.inet.fi 
#   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;
#   Effectively the following properties are held.
#   
#   1. At the end of a substatement thd->transaction.stmt.modified_non_trans_table
#      reflects the fact if such a table got modified by the substatement.
#      That also respects THD::really_abort_on_warnin() requirements.
#   2. Eventually thd->transaction.stmt.modified_non_trans_table will be computed as
#      the union of the values of all invoked sub-statements.
#      That fixes this bug#27417;
#   
#   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.
#   Minor issues are covered with mysql_load, mysql_delete, and binloggin of insert into temp_table select. 
#   
#   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
#   2007/07/20 19:00:46+03:00 aelkin@koti.dsl.inet.fi +114 -0
#   results changed
# 
# mysql-test/t/mix_innodb_myisam_binlog.test
#   2007/07/20 19:00:46+03:00 aelkin@koti.dsl.inet.fi +121 -2
#   regression test incl the related bug#28960.
# 
# sql/ha_ndbcluster.cc
#   2007/07/20 19:00:46+03:00 aelkin@koti.dsl.inet.fi +1 -1
#   thd->transaction.{all,stmt}.modified_non_trans_table
#   
#   instead of
#   
#   thd->no_trans_update.{all,stmt}
# 
# sql/handler.cc
#   2007/07/20 19:00:47+03:00 aelkin@koti.dsl.inet.fi +1 -1
#   thd->transaction.{all,stmt}.modified_non_trans_table
#   
#   instead of
#   
#   thd->no_trans_update.{all,stmt}
# 
# sql/handler.h
#   2007/07/20 19:00:47+03:00 aelkin@koti.dsl.inet.fi +5 -0
#   new member is added.
# 
# sql/log.cc
#   2007/07/20 19:00:47+03:00 aelkin@koti.dsl.inet.fi +2 -2
#   thd->transaction.{all,stmt}.modified_non_trans_table
#   
#   instead of
#   
#   thd->no_trans_update.{all,stmt}
# 
# sql/set_var.cc
#   2007/07/20 19:00:47+03:00 aelkin@koti.dsl.inet.fi +2 -2
#   thd->transaction.{all,stmt}.modified_non_trans_table
#   
#   instead of
#   
#   thd->no_trans_update.{all,stmt}
# 
# sql/sp_head.cc
#   2007/07/20 19:00:48+03:00 aelkin@koti.dsl.inet.fi +15 -5
#   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
#   2007/07/20 19:00:48+03:00 aelkin@koti.dsl.inet.fi +1 -1
#   thd->transaction.{all,stmt}.modified_non_trans_table
#   
#   instead of
#   
#   thd->no_trans_update.{all,stmt}
# 
# sql/sql_class.h
#   2007/07/20 19:00:48+03:00 aelkin@koti.dsl.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
#   2007/07/20 19:00:49+03:00 aelkin@koti.dsl.inet.fi +21 -10
#   correcting basic delete incl truncate branch and multi-delete queries to set
#   stmt.modified_non_trans_table;
#   optimization to set the flag at the end of per-row loop;
#   multi-delete still has an extra issue similar to bug#27716 of multi-update - to be address with
#   bug_29136 fix.
# 
# sql/sql_insert.cc
#   2007/07/20 19:00:49+03:00 aelkin@koti.dsl.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
#   2007/07/20 19:00:49+03:00 aelkin@koti.dsl.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
#   2007/07/20 19:00:49+03:00 aelkin@koti.dsl.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
#   2007/07/20 19:00:50+03:00 aelkin@koti.dsl.inet.fi +0 -1
#   moving the reset up to the mysql_execute_command() caller
# 
# sql/sql_update.cc
#   2007/07/20 19:00:51+03:00 aelkin@koti.dsl.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}
# 
diff -Nru a/mysql-test/r/mix_innodb_myisam_binlog.result b/mysql-test/r/mix_innodb_myisam_binlog.result
--- a/mysql-test/r/mix_innodb_myisam_binlog.result	2007-07-20 19:01:46 +03:00
+++ b/mysql-test/r/mix_innodb_myisam_binlog.result	2007-07-20 19:01:46 +03:00
@@ -280,3 +280,117 @@
 @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)
+DETERMINISTIC
+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
diff -Nru a/mysql-test/t/mix_innodb_myisam_binlog.test b/mysql-test/t/mix_innodb_myisam_binlog.test
--- a/mysql-test/t/mix_innodb_myisam_binlog.test	2007-07-20 19:01:46 +03:00
+++ b/mysql-test/t/mix_innodb_myisam_binlog.test	2007-07-20 19:01:46 +03:00
@@ -259,8 +259,6 @@
 do release_lock("lock1");
 drop table t0,t2;
 
-# 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,124 @@
 @a like "%#%error_code=0%ROLLBACK/*!*/;%ROLLBACK /* added by mysqlbinlog */;%",
 @a not like "%#%error_code=%error_code=%";
 drop table t1, t2;
+
+#
+# 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)
+DETERMINISTIC
+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
+
diff -Nru a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
--- a/sql/ha_ndbcluster.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/ha_ndbcluster.cc	2007-07-20 19:01:46 +03:00
@@ -3697,7 +3697,7 @@
     {
       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;
diff -Nru a/sql/handler.cc b/sql/handler.cc
--- a/sql/handler.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/handler.cc	2007-07-20 19:01:46 +03:00
@@ -830,7 +830,7 @@
     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)
     push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                  ER_WARNING_NOT_COMPLETE_ROLLBACK,
diff -Nru a/sql/handler.h b/sql/handler.h
--- a/sql/handler.h	2007-07-20 19:01:46 +03:00
+++ b/sql/handler.h	2007-07-20 19:01:46 +03:00
@@ -418,6 +418,11 @@
   bool        no_2pc;
   /* storage engines that registered themselves for this transaction */
   handlerton *ht[MAX_HA];
+  /* 
+     The flag is ON if a non-transactional table has been modified 
+     within the scope of the current transaction 
+  */
+  bool modified_non_trans_table;
 } THD_TRANS;
 
 enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED,
diff -Nru a/sql/log.cc b/sql/log.cc
--- a/sql/log.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/log.cc	2007-07-20 19:01:46 +03:00
@@ -162,7 +162,7 @@
     table. Such cases should be rare (updating a
     non-transactional table inside a transaction...)
   */
-  if (unlikely(thd->no_trans_update.all))
+  if (unlikely(thd->transaction.all.modified_non_trans_table))
   {
     Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE);
     qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
@@ -217,7 +217,7 @@
     non-transactional table. Otherwise, truncate the binlog cache starting
     from the SAVEPOINT command.
   */
-  if (unlikely(thd->no_trans_update.all))
+  if (unlikely(thd->transaction.all.modified_non_trans_table))
   {
     Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE, FALSE);
     DBUG_RETURN(mysql_bin_log.write(&qinfo));
diff -Nru a/sql/set_var.cc b/sql/set_var.cc
--- a/sql/set_var.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/set_var.cc	2007-07-20 19:01:46 +03:00
@@ -2882,14 +2882,14 @@
     {
       /* We changed to auto_commit mode */
       thd->options&= ~(ulong) OPTION_BEGIN;
-      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;
     }
   }
diff -Nru a/sql/sp_head.cc b/sql/sp_head.cc
--- a/sql/sp_head.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/sp_head.cc	2007-07-20 19:01:46 +03:00
@@ -337,13 +337,13 @@
   
   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 @@
 
   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)
   {
@@ -2365,7 +2365,13 @@
                                        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;
+  thd->transaction.stmt.modified_non_trans_table= FALSE;
   DBUG_ASSERT(!thd->derived_tables);
   DBUG_ASSERT(thd->change_list.is_empty());
   /*
@@ -2432,7 +2438,11 @@
   /* 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
diff -Nru a/sql/sql_class.cc b/sql/sql_class.cc
--- a/sql/sql_class.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/sql_class.cc	2007-07-20 19:01:46 +03:00
@@ -333,7 +333,7 @@
   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 :
diff -Nru a/sql/sql_class.h b/sql/sql_class.h
--- a/sql/sql_class.h	2007-07-20 19:01:46 +03:00
+++ b/sql/sql_class.h	2007-07-20 19:01:46 +03:00
@@ -1440,10 +1440,6 @@
   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() */
@@ -1668,7 +1664,7 @@
   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();
diff -Nru a/sql/sql_delete.cc b/sql/sql_delete.cc
--- a/sql/sql_delete.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/sql_delete.cc	2007-07-20 19:01:46 +03:00
@@ -315,6 +315,9 @@
 
   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))
   {
@@ -327,9 +330,10 @@
       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;
   }
+  DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
   free_underlaid_joins(thd, select_lex);
   if (transactional_table)
   {
@@ -644,20 +648,22 @@
       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
@@ -707,6 +713,7 @@
     error= 1;
     send_eof();
   }
+  DBUG_ASSERT(!normal_tables || !deleted || thd->transaction.stmt.modified_non_trans_table);
   DBUG_VOID_RETURN;
 }
 
@@ -734,6 +741,7 @@
   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))
     {
@@ -771,6 +779,8 @@
         break;
       }
     }
+    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;
@@ -809,7 +819,6 @@
   {
     query_cache_invalidate3(thd, delete_tables, 1);
   }
-
   if ((local_error == 0) || (deleted && normal_tables))
   {
     if (mysql_bin_log.is_open())
@@ -821,9 +830,11 @@
       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;
   }
+  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))
diff -Nru a/sql/sql_insert.cc b/sql/sql_insert.cc
--- a/sql/sql_insert.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/sql_insert.cc	2007-07-20 19:01:46 +03:00
@@ -629,7 +629,6 @@
   if (lock_type != TL_WRITE_DELAYED && !thd->prelocked_mode)
     table->file->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)));
@@ -775,10 +774,11 @@
           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;
       }
     }
+    DBUG_ASSERT(transactional_table || !changed || thd->transaction.stmt.modified_non_trans_table);
     if (transactional_table)
       error=ha_autocommit_or_rollback(thd,error);
 
@@ -1175,7 +1175,7 @@
     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
@@ -1341,7 +1341,7 @@
             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 @@
   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:
@@ -2564,7 +2564,6 @@
       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::send_error(uint errcode,const char *err)
 {
+  bool changed, transactional_table;
   DBUG_ENTER("select_insert::send_error");
 
   my_message(errcode, err, MYF(0));
@@ -2703,6 +2703,7 @@
     */
     DBUG_VOID_RETURN;
   }
+  transactional_table= table->file->has_transactions();
   if (!thd->prelocked_mode)
     table->file->end_bulk_insert();
   /*
@@ -2711,21 +2712,22 @@
     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;
   }
-  if (info.copied || info.deleted || info.updated)
+  DBUG_ASSERT(transactional_table || !changed || thd->transaction.stmt.modified_non_trans_table);
+  if (changed)
   {
     query_cache_invalidate3(thd, table, 1);
   }
@@ -2736,7 +2738,8 @@
 
 bool select_insert::send_eof()
 {
-  int error,error2;
+  int error, error2;
+  bool changed, transactional_table= table->file->has_transactions();
   DBUG_ENTER("select_insert::send_eof");
 
   error= (!thd->prelocked_mode) ? table->file->end_bulk_insert():0;
@@ -2748,12 +2751,13 @@
     and ha_autocommit_or_rollback
   */
 
-  if (info.copied || info.deleted || info.updated)
+  if (changed= (info.copied || info.deleted || info.updated))
   {
     query_cache_invalidate3(thd, table, 1);
-    if (!(table->file->has_transactions() || 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;
   }
+  DBUG_ASSERT(transactional_table || !changed || thd->transaction.stmt.modified_non_trans_table);
 
   if (last_insert_id)
     thd->insert_id(info.copied ? last_insert_id : 0);		// For binary log
@@ -2763,7 +2767,7 @@
     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 @@
   }
   if (!thd->prelocked_mode)
     table->file->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 |
diff -Nru a/sql/sql_load.cc b/sql/sql_load.cc
--- a/sql/sql_load.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/sql_load.cc	2007-07-20 19:01:46 +03:00
@@ -377,7 +377,6 @@
       table->file->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 |
@@ -411,7 +410,6 @@
     ha_autocommit_...
   */
   query_cache_invalidate3(thd, table_list, 0);
-
   if (error)
   {
     if (read_file_from_client)
@@ -466,8 +464,8 @@
   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())
   {
@@ -488,6 +486,8 @@
   /* 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);
   if (thd->lock)
   {
     mysql_unlock_tables(thd, thd->lock);
@@ -532,7 +532,7 @@
   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;
@@ -560,7 +560,6 @@
 #ifdef HAVE_purify
     read_info.row_end[0]=0;
 #endif
-    no_trans_update_stmt= !table->file->has_transactions();
 
     restore_record(table, s->default_values);
     /*
@@ -628,7 +627,6 @@
     table->auto_increment_field_not_null= FALSE;
     if (err)
       DBUG_RETURN(1);
-    thd->no_trans_update.stmt= no_trans_update_stmt;
    
     /*
       If auto_increment values are used, save the first one for
@@ -671,12 +669,11 @@
   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())
   {
@@ -817,7 +814,6 @@
       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)
diff -Nru a/sql/sql_parse.cc b/sql/sql_parse.cc
--- a/sql/sql_parse.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/sql_parse.cc	2007-07-20 19:01:46 +03:00
@@ -148,7 +148,7 @@
     if (ha_commit(thd))
       error=1;
     thd->options&= ~(ulong) OPTION_BEGIN;
-    thd->no_trans_update.all= FALSE;
+    thd->transaction.all.modified_non_trans_table= FALSE;
   }
   DBUG_RETURN(error);
 }
@@ -172,7 +172,7 @@
   else
   {
     LEX *lex= thd->lex;
-    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)
@@ -1466,7 +1466,7 @@
     thd->server_status&= ~SERVER_STATUS_IN_TRANS;
     res= ha_commit(thd);
     thd->options&= ~(ulong) OPTION_BEGIN;
-    thd->no_trans_update.all= FALSE;
+    thd->transaction.all.modified_non_trans_table= FALSE;
     break;
   case COMMIT_RELEASE:
     do_release= 1; /* fall through */
@@ -1484,7 +1484,7 @@
     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;
@@ -2587,6 +2587,8 @@
     statistic_increment(thd->status_var.com_stat[lex->sql_command],
                         &LOCK_status);
 
+  DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
+  
   switch (lex->sql_command) {
   case SQLCOM_SELECT:
   {
@@ -2924,7 +2926,7 @@
     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;
@@ -3701,7 +3703,7 @@
         lex->drop_if_exists= 1;
 
       /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
-      thd->no_trans_update.all= TRUE;
+      thd->transaction.all.modified_non_trans_table= TRUE;
     }
     /* DDL and binlog write order protected by LOCK_open */
     res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
@@ -4292,7 +4294,7 @@
         res= TRUE; // cannot happen
       else
       {
-        if (thd->no_trans_update.all &&
+        if (thd->transaction.all.modified_non_trans_table &&
             !thd->slave_thread)
           push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                        ER_WARNING_NOT_COMPLETE_ROLLBACK,
@@ -4935,7 +4937,7 @@
     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->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;
     send_ok(thd);
@@ -5030,7 +5032,7 @@
       break;
     }
     thd->options&= ~(ulong) OPTION_BEGIN;
-    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;
@@ -5061,7 +5063,7 @@
     else
       send_ok(thd);
     thd->options&= ~(ulong) OPTION_BEGIN;
-    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;
diff -Nru a/sql/sql_table.cc b/sql/sql_table.cc
--- a/sql/sql_table.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/sql_table.cc	2007-07-20 19:01:46 +03:00
@@ -4004,7 +4004,6 @@
   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));
diff -Nru a/sql/sql_update.cc b/sql/sql_update.cc
--- a/sql/sql_update.cc	2007-07-20 19:01:46 +03:00
+++ b/sql/sql_update.cc	2007-07-20 19:01:46 +03:00
@@ -429,7 +429,6 @@
   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 |
@@ -486,7 +485,6 @@
                                             (byte*) table->record[0])))
         {
           updated++;
-          thd->no_trans_update.stmt= !transactional_table;
           
           if (table->triggers &&
               table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
@@ -520,6 +518,10 @@
       table->file->unlock_row();
     thd->row_count++;
   }
+
+  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 @@
       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;
   }
+  DBUG_ASSERT(transactional_table || !updated || thd->transaction.stmt.modified_non_trans_table);
   free_underlaid_joins(thd, select_lex);
   if (transactional_table)
   {
@@ -922,7 +925,6 @@
 				 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));
@@ -1249,8 +1251,6 @@
   if (copy_field)
     delete [] copy_field;
   thd->count_cuted_fields= CHECK_FIELD_IGNORE;		// Restore this setting
-  if (!trans_safe)
-    thd->no_trans_update.all= TRUE;
 }
 
 
@@ -1337,7 +1337,7 @@
         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 @@
 
   /* 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 @@
   {
     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
@@ -1569,8 +1567,8 @@
       if (mysql_bin_log.write(&qinfo) && trans_safe)
         local_error= 1;				// Rollback update
     }
-    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;
   }
 
   if (transactional_tables)


