List:Commits« Previous MessageNext Message »
From:Andrei Elkin Date:June 12 2007 7:32pm
Subject:bk commit into 5.0 tree (aelkin:1.2439) BUG#27417
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of elkin. When elkin does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-06-12 20:32:28+03:00, aelkin@stripped
+12 -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.
  
  Fixed with introducing Sub_statement_state::no_trans_update_stmt flag durable within
substatement life time
  and the new member to thd->no_trans_update.
  At the end of a substatement the gained value contributes to the new
thd->no_trans_update.top
  so that the following properties are held:
  
  1. Eventually thd->no_trans_update.top will be computed as
     the union of values of .stmt gained in all invoked sub-statements.
     That fixes this bug#27417;
  
  2. Inside of any sub-statement thd->no_trans_update.stmt corresponds
     to the value yielded at the sub-statement to satisfy
     THD::really_abort_on_warnin() concern to get the status of the current
     substatement.
  
  The supplied test verifies limitely, mostly asserts. The ultimate testing is defered
  for bug_13270, bug_23333.
  
  Computing of thd->no_trans_update.all is refined to base to .stmt value for all the
case including
  insert .. select statement which before the patch had an extra issue bug#28960.

  mysql-test/r/mix_innodb_myisam_binlog.result@stripped, 2007-06-12 20:32:20+03:00,
aelkin@stripped +68 -0
    results changed

  mysql-test/r/stored_routines_side_effect.result@stripped, 2007-06-12 20:32:22+03:00,
aelkin@stripped +46 -0
    new result file

  mysql-test/r/stored_routines_side_effect.result@stripped, 2007-06-12 20:32:22+03:00,
aelkin@stripped +0 -0

  mysql-test/t/mix_innodb_myisam_binlog.test@stripped, 2007-06-12 20:32:20+03:00,
aelkin@stripped +63 -2
    regression test for a related bug#28960.

  mysql-test/t/stored_routines_side_effect.test@stripped, 2007-06-12 20:32:22+03:00,
aelkin@stripped +56 -0
    the test basicly is to check asserts deployed within the patch.

  mysql-test/t/stored_routines_side_effect.test@stripped, 2007-06-12 20:32:22+03:00,
aelkin@stripped +0 -0

  sql/sql_class.cc@stripped, 2007-06-12 20:32:20+03:00,
aelkin@stripped +4 -1
    backing up and resetting .stmt flag for a new starting substatement group;
    restoring the flag when the subgroup ends.

  sql/sql_class.h@stripped, 2007-06-12 20:32:21+03:00,
aelkin@stripped +10 -2
    adding a member Sub_statement_state;
    adding a member to thd->no_trans_update;

  sql/sql_delete.cc@stripped, 2007-06-12 20:32:21+03:00,
aelkin@stripped +26 -10
    correcting basic delete incl truncate branch and multi-delete queries to set and reset
    thd->no_trans_update.stmt and merge the .top, .all at the end of a substatement;
    multi-delete still has an extra issue similar to bug#27716 of multi-update - to be
address with
    bug_23333 fix.

  sql/sql_insert.cc@stripped, 2007-06-12 20:32:21+03:00,
aelkin@stripped +19 -13
    asserts regarding to the .stmt flag;
    merge .top, .all with .stmt at the end of queries' handling.
    

  sql/sql_load.cc@stripped, 2007-06-12 20:32:21+03:00,
aelkin@stripped +6 -9
    asserts regarding to the flag;
    eliminating a separate issue where the flag was stored and re-stored after
write_record
    that could change it and the change would be lost but should remain permanent;
    merge .stmt to .top (this bug's net issue) and .all fixed.

  sql/sql_parse.cc@stripped, 2007-06-12 20:32:21+03:00,
aelkin@stripped +4 -0
    initialization to .stmt and the new .top is done at the common part of all statements
execution:
    mysql_execute_command().

  sql/sql_table.cc@stripped, 2007-06-12 20:32:22+03:00,
aelkin@stripped +0 -1
    moving the reset up to the mysql_execute_command() caller

  sql/sql_update.cc@stripped, 2007-06-12 20:32:22+03:00,
aelkin@stripped +10 -9
    correcting update (multi-update part of the issues covered by other bug#27716 fix)
    to set and reset the flag;
    assert added;
    merge to .top and .all;

# 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.269/sql/sql_class.cc	2007-04-20 11:35:22 +03:00
+++ 1.270/sql/sql_class.cc	2007-06-12 20:32:20 +03:00
@@ -333,7 +333,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;
+  no_trans_update.all= no_trans_update.top= no_trans_update.stmt= FALSE;
   open_options=ha_open_options;
   update_lock_default= (variables.low_priority_updates ?
 			TL_WRITE_LOW_PRIORITY :
@@ -2098,6 +2098,8 @@ void THD::reset_sub_statement_state(Sub_
   backup->cuted_fields=     cuted_fields;
   backup->client_capabilities= client_capabilities;
   backup->savepoints= transaction.savepoints;
+  backup->no_trans_update_stmt= no_trans_update.stmt;
+  no_trans_update.stmt= 0;
 
   if (!lex->requires_prelocking() || is_update_query(lex->sql_command))
     options&= ~OPTION_BIN_LOG;
@@ -2160,6 +2162,7 @@ void THD::restore_sub_statement_state(Su
   */
   examined_row_count+= backup->examined_row_count;
   cuted_fields+=       backup->cuted_fields;
+  no_trans_update.stmt= backup->no_trans_update_stmt;
 }
 
 

--- 1.328/sql/sql_class.h	2007-04-03 14:55:14 +03:00
+++ 1.329/sql/sql_class.h	2007-06-12 20:32:21 +03:00
@@ -1086,6 +1086,13 @@ public:
   bool last_insert_id_used;
   my_bool no_send_ok;
   SAVEPOINT *savepoints;
+  /* 
+     thd->no_trans_update.stmt flag is stored and dropped at the
+     beginning of substatement and restored at the end of the
+     substatement's level to reflect only changes done in the current
+     (sub)statement.
+  */
+  bool no_trans_update_stmt;
 };
 
 /**
@@ -1441,8 +1448,9 @@ public:
   bool       charset_is_character_set_filesystem;
   bool       enable_slow_log;   /* enable slow log for current statement */
   struct {
-    bool all:1;
-    bool stmt:1;
+    bool all:1;  /* nontrans table modified within transaction   */
+    bool top:1;  /* same but within the current top level stmt   */
+    bool stmt:1; /* same but within the scope of the current stmt*/
   } no_trans_update;
   bool	     abort_on_warning;
   bool 	     got_warning;       /* Set on call to push_warning() */

--- 1.197/sql/sql_delete.cc	2007-04-17 16:51:56 +03:00
+++ 1.198/sql/sql_delete.cc	2007-06-12 20:32:21 +03:00
@@ -91,7 +91,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *
   }
 
   select_lex->no_error= thd->lex->ignore;
-
+  transactional_table= table->file->has_transactions();
   /*
     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
@@ -109,8 +109,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *
     if (!(error=table->file->delete_all_rows()))
     {
       error= -1;				// ok
+      if (!transactional_table && deleted > 0)
+        thd->no_trans_update.stmt= TRUE;
       goto cleanup;
     }
+    deleted= 0;
     if (error != HA_ERR_WRONG_COMMAND)
     {
       table->file->print_error(error,MYF(0));
@@ -280,6 +283,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *
     else
       table->file->unlock_row();  // Row failed selection, release lock on it
   }
+  if (!transactional_table && deleted > 0)
+    thd->no_trans_update.stmt= TRUE;
+
   if (thd->killed && !error)
     error= 1;					// Aborted
   thd->proc_info="end";
@@ -314,7 +320,7 @@ cleanup:
   }
 
   delete select;
-  transactional_table= table->file->has_transactions();
+  thd->no_trans_update.top|= thd->no_trans_update.stmt;
   /* See similar binlogging code in sql_update.cc, for comments */
   if ((error < 0) || (deleted && !transactional_table))
   {
@@ -327,9 +333,10 @@ cleanup:
       if (mysql_bin_log.write(&qinfo) && transactional_table)
 	error=1;
     }
-    if (!transactional_table)
+    if (thd->no_trans_update.stmt)
       thd->no_trans_update.all= TRUE;
   }
+  DBUG_ASSERT(transactional_table || !deleted || thd->no_trans_update.stmt);
   free_underlaid_joins(thd, select_lex);
   if (transactional_table)
   {
@@ -644,20 +651,22 @@ bool multi_delete::send_data(List<Item> 
       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->no_trans_update.stmt= 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
@@ -688,6 +697,7 @@ void multi_delete::send_error(uint errco
   /* Something already deleted so we have to invalidate cache */
   query_cache_invalidate3(thd, delete_tables, 1);
 
+  thd->no_trans_update.top|= thd->no_trans_update.stmt;
   /*
     If rows from the first table only has been deleted and it is
     transactional, just do rollback.
@@ -707,6 +717,7 @@ void multi_delete::send_error(uint errco
     error= 1;
     send_eof();
   }
+  DBUG_ASSERT(!normal_tables || !deleted || thd->no_trans_update.stmt);
   DBUG_VOID_RETURN;
 }
 
@@ -734,6 +745,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))
     {
@@ -771,6 +783,8 @@ int multi_delete::do_deletes()
         break;
       }
     }
+    if (last_deleted != deleted && !table->file->has_transactions())
+      thd->no_trans_update.stmt= TRUE;
     end_read_record(&info);
     if (thd->killed && !local_error)
       local_error= 1;
@@ -809,7 +823,7 @@ bool multi_delete::send_eof()
   {
     query_cache_invalidate3(thd, delete_tables, 1);
   }
-
+  thd->no_trans_update.top|= thd->no_trans_update.stmt;
   if ((local_error == 0) || (deleted && normal_tables))
   {
     if (mysql_bin_log.is_open())
@@ -821,9 +835,11 @@ bool multi_delete::send_eof()
       if (mysql_bin_log.write(&qinfo) && !normal_tables)
 	local_error=1;  // Log write failed: roll back the SQL statement
     }
-    if (!transactional_tables)
+    if (thd->no_trans_update.stmt)
       thd->no_trans_update.all= TRUE;
   }
+  DBUG_ASSERT(!normal_tables || !deleted || thd->no_trans_update.stmt);
+
   /* Commit or rollback the current SQL statement */
   if (transactional_tables)
     if (ha_autocommit_or_rollback(thd,local_error > 0))

--- 1.230/sql/sql_insert.cc	2007-04-12 12:46:07 +03:00
+++ 1.231/sql/sql_insert.cc	2007-06-12 20:32:21 +03:00
@@ -629,7 +629,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
   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)));
@@ -756,6 +755,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
 
     transactional_table= table->file->has_transactions();
 
+    thd->no_trans_update.top|= thd->no_trans_update.stmt;
     if ((changed= (info.copied || info.deleted || info.updated)))
     {
       /*
@@ -775,10 +775,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
           if (mysql_bin_log.write(&qinfo) && transactional_table)
             error=1;
         }
-        if (!transactional_table)
+        if (thd->no_trans_update.stmt)
           thd->no_trans_update.all= TRUE;
       }
     }
+    DBUG_ASSERT(transactional_table || !changed || thd->no_trans_update.stmt);
     if (transactional_table)
       error=ha_autocommit_or_rollback(thd,error);
 
@@ -2564,7 +2565,6 @@ select_insert::prepare(List<Item> &value
       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 +2691,7 @@ void select_insert::store_values(List<It
 
 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,29 +2704,32 @@ void select_insert::send_error(uint errc
     */
     DBUG_VOID_RETURN;
   }
+  transactional_table= table->file->has_transactions();
   if (!thd->prelocked_mode)
     table->file->end_bulk_insert();
+  thd->no_trans_update.top|= thd->no_trans_update.stmt;
   /*
     If at least one row has been inserted/modified and will stay in the table
     (the table doesn't have transactions) (example: we got a duplicate key
     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)
+    if (thd->no_trans_update.stmt)
       thd->no_trans_update.all= TRUE;
   }
-  if (info.copied || info.deleted || info.updated)
+  DBUG_ASSERT(transactional_table || !changed || thd->no_trans_update.stmt);
+  if (changed)
   {
     query_cache_invalidate3(thd, table, 1);
   }
@@ -2736,24 +2740,27 @@ void select_insert::send_error(uint errc
 
 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;
   table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
   table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
 
+  thd->no_trans_update.top|= thd->no_trans_update.stmt;
   /*
     We must invalidate the table in the query cache before binlog writing
     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))
+    if (thd->no_trans_update.stmt)
       thd->no_trans_update.all= TRUE;
   }
+  DBUG_ASSERT(transactional_table || !changed || thd->no_trans_update.stmt);
 
   if (last_insert_id)
     thd->insert_id(info.copied ? last_insert_id : 0);		// For binary log
@@ -2763,7 +2770,7 @@ bool select_insert::send_eof()
     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 +2985,6 @@ select_create::prepare(List<Item> &value
   }
   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 |

--- 1.113/sql/sql_load.cc	2007-05-08 03:43:16 +03:00
+++ 1.114/sql/sql_load.cc	2007-06-12 20:32:21 +03:00
@@ -377,7 +377,6 @@ bool mysql_load(THD *thd,sql_exchange *e
       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,7 @@ bool mysql_load(THD *thd,sql_exchange *e
     ha_autocommit_...
   */
   query_cache_invalidate3(thd, table_list, 0);
-
+  thd->no_trans_update.top|= thd->no_trans_update.stmt;
   if (error)
   {
     if (read_file_from_client)
@@ -466,7 +465,7 @@ bool mysql_load(THD *thd,sql_exchange *e
   sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted,
 	  (ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
 
-  if (!transactional_table)
+  if (thd->no_trans_update.stmt)
     thd->no_trans_update.all= TRUE;
 #ifndef EMBEDDED_LIBRARY
   if (mysql_bin_log.is_open())
@@ -488,6 +487,8 @@ bool mysql_load(THD *thd,sql_exchange *e
   /* 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->no_trans_update.stmt);
   if (thd->lock)
   {
     mysql_unlock_tables(thd, thd->lock);
@@ -532,7 +533,7 @@ read_fixed_length(THD *thd, COPY_INFO &i
   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 +561,6 @@ read_fixed_length(THD *thd, COPY_INFO &i
 #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 +628,6 @@ read_fixed_length(THD *thd, COPY_INFO &i
     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 +670,11 @@ read_sep_field(THD *thd, COPY_INFO &info
   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 +815,6 @@ read_sep_field(THD *thd, COPY_INFO &info
       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-12 20:32:21 +03:00
@@ -2587,6 +2587,10 @@ mysql_execute_command(THD *thd)
     statistic_increment(thd->status_var.com_stat[lex->sql_command],
                         &LOCK_status);
 
+  thd->no_trans_update.stmt= FALSE;
+  if (!thd->in_sub_stmt)
+    thd->no_trans_update.top= FALSE;
+  
   switch (lex->sql_command) {
   case SQLCOM_SELECT:
   {

--- 1.340/sql/sql_table.cc	2007-04-17 16:51:57 +03:00
+++ 1.341/sql/sql_table.cc	2007-06-12 20:32:22 +03:00
@@ -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-12 20:32:22 +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 |
@@ -486,7 +485,6 @@ int mysql_update(THD *thd,
                                             (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 @@ int mysql_update(THD *thd,
       table->file->unlock_row();
     thd->row_count++;
   }
+
+  if (!transactional_table && updated > 0)
+    thd->no_trans_update.stmt= TRUE;
+
   if (thd->killed && !error)
     error= 1;					// Aborted
   end_read_record(&info);
@@ -537,6 +539,7 @@ int mysql_update(THD *thd,
     query_cache_invalidate3(thd, table_list, 1);
   }
 
+  thd->no_trans_update.top|= thd->no_trans_update.stmt;
   /*
     error < 0 means really no error at all: we processed all rows until the
     last one without error. error > 0 means an error (e.g. unique key
@@ -557,9 +560,10 @@ int mysql_update(THD *thd,
       if (mysql_bin_log.write(&qinfo) && transactional_table)
 	error=1;				// Rollback update
     }
-    if (!transactional_table)
+    if (thd->no_trans_update.stmt)
       thd->no_trans_update.all= TRUE;
   }
+  DBUG_ASSERT(transactional_table || !updated || thd->no_trans_update.stmt);
   free_underlaid_joins(thd, select_lex);
   if (transactional_table)
   {
@@ -922,7 +926,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));
@@ -1249,8 +1252,6 @@ multi_update::~multi_update()
   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;
 }
 
 
@@ -1384,7 +1385,7 @@ void multi_update::send_error(uint errco
 
   /* Something already updated so we have to invalidate cache */
   query_cache_invalidate3(thd, update_tables, 1);
-
+  thd->no_trans_update.top|= thd->no_trans_update.stmt;
   /*
     If all tables that has been updated are trans safe then just do rollback.
     If not attempt to do remaining updates.
@@ -1551,7 +1552,7 @@ bool multi_update::send_eof()
   {
     query_cache_invalidate3(thd, update_tables, 1);
   }
-
+  thd->no_trans_update.top|= thd->no_trans_update.stmt;
   /*
     Write the SQL statement to the binlog if we updated
     rows and we succeeded or if we updated some non
@@ -1569,7 +1570,7 @@ bool multi_update::send_eof()
       if (mysql_bin_log.write(&qinfo) && trans_safe)
         local_error= 1;				// Rollback update
     }
-    if (!transactional_tables)
+    if (thd->no_trans_update.stmt)
       thd->no_trans_update.all= TRUE;
   }
 
--- New file ---
+++ mysql-test/r/stored_routines_side_effect.result	07/06/12 20:32:22
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 table t1,t2;
end of tests

--- New file ---
+++ mysql-test/t/stored_routines_side_effect.test	07/06/12 20:32:22
#
# Bug #27417 thd->no_trans_update.stmt lost value inside of SF-exec-stack
#
# Testing the assertion: 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 table t1,t2;

--echo end of tests


--- 1.30/mysql-test/r/mix_innodb_myisam_binlog.result	2006-11-28 14:26:08 +02:00
+++ 1.31/mysql-test/r/mix_innodb_myisam_binlog.result	2007-06-12 20:32:20 +03:00
@@ -280,3 +280,71 @@ 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 slave would miss - the bug */;
+a
+1
+2
+drop table ti;
+end of tests

--- 1.25/mysql-test/t/mix_innodb_myisam_binlog.test	2007-01-18 19:05:03 +02:00
+++ 1.26/mysql-test/t/mix_innodb_myisam_binlog.test	2007-06-12 20:32:20 +03:00
@@ -259,8 +259,6 @@ show binlog events from 98;
 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,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
+
Thread
bk commit into 5.0 tree (aelkin:1.2439) BUG#27417Andrei Elkin12 Jun