List:Commits« Previous MessageNext Message »
From:Mats Kindahl Date:March 6 2007 7:52pm
Subject:bk commit into 5.1 tree (mats:1.2472)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of mats. When mats 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-03-06 19:52:34+01:00, mats@romeo.(none) +11 -0
  Merge romeo.(none):/home/bkroot/mysql-5.1-new-rpl
  into  romeo.(none):/home/bk/b23171-mysql-5.1-new-rpl
  MERGE: 1.2303.45.18

  mysql-test/r/rpl_row_tabledefs_2myisam.result@stripped, 2007-03-06 19:08:43+01:00,
mats@romeo.(none) +0 -0
    Auto merged
    MERGE: 1.6.2.1

  mysql-test/r/rpl_row_tabledefs_3innodb.result@stripped, 2007-03-06 19:08:43+01:00,
mats@romeo.(none) +0 -0
    Auto merged
    MERGE: 1.3.2.1

  sql/log.cc@stripped, 2007-03-06 19:52:27+01:00, mats@romeo.(none) +0 -2
    Manual merge
    MERGE: 1.237.6.3

  sql/log_event.cc@stripped, 2007-03-06 19:52:27+01:00, mats@romeo.(none) +4 -13
    Manual merge
    MERGE: 1.246.2.6

  sql/log_event.h@stripped, 2007-03-06 19:52:27+01:00, mats@romeo.(none) +1 -0
    Manual merge
    MERGE: 1.136.1.13

  sql/rpl_rli.cc@stripped, 2007-03-06 19:08:44+01:00, mats@romeo.(none) +0 -0
    Auto merged
    MERGE: 1.2.1.3

  sql/rpl_rli.h@stripped, 2007-03-06 19:08:44+01:00, mats@romeo.(none) +0 -0
    Auto merged
    MERGE: 1.7.1.2

  sql/rpl_utility.cc@stripped, 2007-03-06 19:08:44+01:00, mats@romeo.(none) +0 -0
    Auto merged
    MERGE: 1.3.1.3

  sql/rpl_utility.h@stripped, 2007-03-06 19:08:44+01:00, mats@romeo.(none) +0 -0
    Auto merged
    MERGE: 1.2.1.4

  sql/slave.cc@stripped, 2007-03-06 19:52:27+01:00, mats@romeo.(none) +1 -2
    Manual merge
    MERGE: 1.293.1.21

  sql/slave.h@stripped, 2007-03-06 19:08:44+01:00, mats@romeo.(none) +0 -0
    Auto merged
    MERGE: 1.102.1.5

# 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:	mats
# Host:	romeo.(none)
# Root:	/home/bk/b23171-mysql-5.1-new-rpl/RESYNC

--- 1.263/sql/log.cc	2007-03-06 19:52:44 +01:00
+++ 1.264/sql/log.cc	2007-03-06 19:52:44 +01:00
@@ -1548,7 +1548,13 @@
     (binlog_trx_data*) thd->ha_data[binlog_hton->slot];
   DBUG_ASSERT(mysql_bin_log.is_open());
 
-  if (all && trx_data->empty())
+  /*
+    The condition here has to be identical to the one inside
+    binlog_end_trans(), guarding the write of the transaction cache to
+    the binary log.
+   */
+  if ((all || !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))) &&
+      trx_data->empty())
   {
     // we're here because trans_log was flushed in MYSQL_BIN_LOG::log_xid()
     trx_data->reset();
@@ -2499,7 +2505,7 @@
       /*
         Set 'created' to 0, so that in next relay logs this event does not
         trigger cleaning actions on the slave in
-        Format_description_log_event::exec_event().
+        Format_description_log_event::apply_event_impl().
       */
       description_event_for_queue->created= 0;
       /* Don't set log_pos in event header */
@@ -3206,8 +3212,10 @@
   {
     tc_log_page_waits++;
     pthread_mutex_lock(&LOCK_prep_xids);
-    while (prepared_xids)
+    while (prepared_xids) {
+      DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
       pthread_cond_wait(&COND_prep_xids, &LOCK_prep_xids);
+    }
     pthread_mutex_unlock(&LOCK_prep_xids);
   }
 
@@ -5061,8 +5069,10 @@
 {
   pthread_mutex_lock(&LOCK_prep_xids);
   DBUG_ASSERT(prepared_xids > 0);
-  if (--prepared_xids == 0)
+  if (--prepared_xids == 0) {
+    DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
     pthread_cond_signal(&COND_prep_xids);
+  }
   pthread_mutex_unlock(&LOCK_prep_xids);
   rotate_and_purge(0);     // as ::write() did not rotate
 }

--- 1.273/sql/log_event.cc	2007-03-06 19:52:44 +01:00
+++ 1.274/sql/log_event.cc	2007-03-06 19:52:44 +01:00
@@ -88,9 +88,10 @@
       operator&()
 
     DESCRIPTION
-      Function to return a pointer to the internal, so that the object
-      can be treated as a IO_CACHE and used with the my_b_* IO_CACHE
-      functions
+
+      Function to return a pointer to the internal cache, so that the
+      object can be treated as a IO_CACHE and used with the my_b_*
+      IO_CACHE functions
 
     RETURN VALUE
       A pointer to the internal IO_CACHE.
@@ -531,25 +532,19 @@
 #ifndef MYSQL_CLIENT
 #ifdef HAVE_REPLICATION
 
-/*
-  Log_event::exec_event()
-*/
-
-int Log_event::exec_event(struct st_relay_log_info* rli)
+int Log_event::do_update_pos(RELAY_LOG_INFO *rli)
 {
-  DBUG_ENTER("Log_event::exec_event");
-
   /*
-    rli is null when (as far as I (Guilhem) know)
-    the caller is
-    Load_log_event::exec_event *and* that one is called from
-    Execute_load_log_event::exec_event. 
-    In this case, we don't do anything here ;
-    Execute_load_log_event::exec_event will call Log_event::exec_event
-    again later with the proper rli.
-    Strictly speaking, if we were sure that rli is null
-    only in the case discussed above, 'if (rli)' is useless here.
-    But as we are not 100% sure, keep it for now.
+    rli is null when (as far as I (Guilhem) know) the caller is
+    Load_log_event::do_apply_event *and* that one is called from
+    Execute_load_log_event::do_apply_event.  In this case, we don't
+    do anything here ; Execute_load_log_event::do_apply_event will
+    call Log_event::do_apply_event again later with the proper rli.
+    Strictly speaking, if we were sure that rli is null only in the
+    case discussed above, 'if (rli)' is useless here.  But as we are
+    not 100% sure, keep it for now.
+
+    Matz: I don't think we will need this check with this refactoring.
   */
   if (rli)
   {
@@ -584,18 +579,31 @@
     {
       rli->inc_group_relay_log_pos(log_pos);
       flush_relay_log_info(rli);
-      /* 
-         Note that Rotate_log_event::exec_event() does not call this
-         function, so there is no chance that a fake rotate event resets
-         last_master_timestamp.
-         Note that we update without mutex (probably ok - except in some very
-         rare cases, only consequence is that value may take some time to
-         display in Seconds_Behind_Master - not critical).
+      /*
+         Note that Rotate_log_event::do_apply_event() does not call
+         this function, so there is no chance that a fake rotate event
+         resets last_master_timestamp.  Note that we update without
+         mutex (probably ok - except in some very rare cases, only
+         consequence is that value may take some time to display in
+         Seconds_Behind_Master - not critical).
       */
       rli->last_master_timestamp= when;
     }
   }
-  DBUG_RETURN(0);
+
+  return 0;                                   // Cannot fail currently
+}
+
+
+Log_event::enum_skip_reason
+Log_event::shall_skip(RELAY_LOG_INFO *rli)
+{
+  if (this->server_id == ::server_id && !replicate_same_server_id)
+    return EVENT_SKIP_SAME_SID;
+  else if (rli->slave_skip_counter > 0)
+    return EVENT_SKIP_COUNT;
+  else
+    return EVENT_NOT_SKIPPED;
 }
 
 
@@ -742,7 +750,7 @@
   ulong data_len;
   int result=0;
   char buf[LOG_EVENT_MINIMAL_HEADER_LEN];
-  DBUG_ENTER("read_log_event");
+  DBUG_ENTER("Log_event::read_log_event");
 
   if (log_lock)
     pthread_mutex_lock(log_lock);
@@ -817,7 +825,7 @@
                                      const Format_description_log_event
*description_event)
 #endif
 {
-  DBUG_ENTER("Log_event::read_log_event(IO_CACHE *, Format_description_log_event *");
+  DBUG_ENTER("Log_event::read_log_event");
   DBUG_ASSERT(description_event != 0);
   char head[LOG_EVENT_MINIMAL_HEADER_LEN];
   /*
@@ -1887,27 +1895,28 @@
 
 
 /*
-  Query_log_event::exec_event()
+  Query_log_event::do_apply_event()
 */
 
 #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
 
-int Query_log_event::exec_event(struct st_relay_log_info* rli)
+int Query_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
-  return exec_event(rli, query, q_len);
+  return do_apply_event(rli, query, q_len);
 }
 
 
-int Query_log_event::exec_event(struct st_relay_log_info* rli,
-                                const char *query_arg, uint32 q_len_arg)
+int Query_log_event::do_apply_event(RELAY_LOG_INFO const *rli,
+                                      const char *query_arg, uint32 q_len_arg)
 {
   LEX_STRING new_db;
   int expected_error,actual_error= 0;
   /*
-    Colleagues: please never free(thd->catalog) in MySQL. This would lead to
-    bugs as here thd->catalog is a part of an alloced block, not an entire
-    alloced block (see Query_log_event::exec_event()). Same for thd->db.
-    Thank you.
+    Colleagues: please never free(thd->catalog) in MySQL. This would
+    lead to bugs as here thd->catalog is a part of an alloced block,
+    not an entire alloced block (see
+    Query_log_event::do_apply_event()). Same for thd->db.  Thank
+    you.
   */
   thd->catalog= catalog_len ? (char *) catalog : (char *)"";
   new_db.length= db_len;
@@ -1926,11 +1935,11 @@
     END of the current log event (COMMIT). We save it in rli so that InnoDB can
     access it.
   */
-  rli->future_group_master_log_pos= log_pos;
+  const_cast<RELAY_LOG_INFO*>(rli)->future_group_master_log_pos= log_pos;
   DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
 
-  clear_all_errors(thd, rli);
-  rli->clear_tables_to_lock();
+  clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli));
+  const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
 
   /*
     Note:   We do not need to execute reset_one_shot_variables() if this
@@ -1939,8 +1948,8 @@
             its companion query.  If the SET is ignored because of
             db_ok(), the companion query will also be ignored, and if
             the companion query is ignored in the db_ok() test of
-            ::exec_event(), then the companion SET also have so we
-            don't need to reset_one_shot_variables().
+            ::do_apply_event(), then the companion SET also have so
+            we don't need to reset_one_shot_variables().
   */
   if (rpl_filter->db_ok(thd->db))
   {
@@ -2056,7 +2065,7 @@
         to check/fix it.
       */
       if (mysql_test_parse_for_slave(thd, thd->query, thd->query_length))
-        clear_all_errors(thd, rli);        /* Can ignore query */
+        clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli)); /* Can ignore
query */
       else
       {
         slave_print_msg(ERROR_LEVEL, rli, expected_error, 
@@ -2107,7 +2116,7 @@
  	     ignored_error_code(actual_error))
     {
       DBUG_PRINT("info",("error ignored"));
-      clear_all_errors(thd, rli);
+      clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli));
     }
     /*
       Other cases: mostly we expected no error and get one.
@@ -2174,16 +2183,26 @@
   thd->first_successful_insert_id_in_prev_stmt= 0;
   thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
   free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
+  return thd->query_error;
+}
+
+int Query_log_event::do_update_pos(RELAY_LOG_INFO *rli)
+{
   /*
-    If there was an error we stop. Otherwise we increment positions. Note that
-    we will not increment group* positions if we are just after a SET
-    ONE_SHOT, because SET ONE_SHOT should not be separated from its following
-    updating query.
-  */
-  return (thd->query_error ? thd->query_error : 
-          (thd->one_shot_set ? (rli->inc_event_relay_log_pos(),0) :
-           Log_event::exec_event(rli))); 
+    Note that we will not increment group* positions if we are just
+    after a SET ONE_SHOT, because SET ONE_SHOT should not be separated
+    from its following updating query.
+  */
+  if (thd->one_shot_set)
+  {
+    rli->inc_event_relay_log_pos();
+    return 0;
+  }
+  else
+    return Log_event::do_update_pos(rli);
 }
+
+
 #endif
 
 
@@ -2312,7 +2331,7 @@
 
 
 /*
-  Start_log_event_v3::exec_event()
+  Start_log_event_v3::do_apply_event()
 
   The master started
 
@@ -2331,9 +2350,9 @@
 */
 
 #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Start_log_event_v3::exec_event(struct st_relay_log_info* rli)
+int Start_log_event_v3::do_apply_event(RELAY_LOG_INFO const *rli)
 {
-  DBUG_ENTER("Start_log_event_v3::exec_event");
+  DBUG_ENTER("Start_log_event_v3::do_apply_event");
   switch (binlog_version)
   {
   case 3:
@@ -2375,7 +2394,7 @@
     /* this case is impossible */
     DBUG_RETURN(1);
   }
-  DBUG_RETURN(Log_event::exec_event(rli));
+  DBUG_RETURN(0);
 }
 #endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
 
@@ -2566,24 +2585,10 @@
 }
 #endif
 
-/*
-  SYNOPSIS
-    Format_description_log_event::exec_event()
-
-  IMPLEMENTATION
-    Save the information which describes the binlog's format, to be able to
-    read all coming events.
-    Call Start_log_event_v3::exec_event().
-*/
-
 #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Format_description_log_event::exec_event(struct st_relay_log_info* rli)
+int Format_description_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
-  DBUG_ENTER("Format_description_log_event::exec_event");
-
-  /* save the information describing this binlog */
-  delete rli->relay_log.description_event_for_exec;
-  rli->relay_log.description_event_for_exec= this;
+  DBUG_ENTER("Format_description_log_event::do_apply_event");
 
 #ifdef USING_TRANSACTIONS
   /*
@@ -2605,14 +2610,36 @@
                     "or ROLLBACK in relay log). A probable cause is that "
                     "the master died while writing the transaction to "
                     "its binary log, thus rolled back too."); 
-    rli->cleanup_context(thd, 1);
+    const_cast<RELAY_LOG_INFO*>(rli)->cleanup_context(thd, 1);
   }
 #endif
   /*
-    If this event comes from ourselves, there is no cleaning task to perform,
-    we don't call Start_log_event_v3::exec_event() (this was just to update the
-    log's description event).
+    If this event comes from ourselves, there is no cleaning task to
+    perform, we don't call Start_log_event_v3::do_apply_event()
+    (this was just to update the log's description event).
   */
+  if (server_id != (uint32) ::server_id)
+  {
+    /*
+      If the event was not requested by the slave i.e. the master sent
+      it while the slave asked for a position >4, the event will make
+      rli->group_master_log_pos advance. Say that the slave asked for
+      position 1000, and the Format_desc event's end is 96. Then in
+      the beginning of replication rli->group_master_log_pos will be
+      0, then 96, then jump to first really asked event (which is
+      >96). So this is ok.
+    */
+    DBUG_RETURN(Start_log_event_v3::do_apply_event(rli));
+  }
+  DBUG_RETURN(0);
+}
+
+int Format_description_log_event::do_update_pos(RELAY_LOG_INFO *rli)
+{
+  /* save the information describing this binlog */
+  delete rli->relay_log.description_event_for_exec;
+  rli->relay_log.description_event_for_exec= this;
+
   if (server_id == (uint32) ::server_id)
   {
     /*
@@ -2629,19 +2656,20 @@
       the Intvar_log_event respectively.
      */
     rli->inc_event_relay_log_pos();
-    DBUG_RETURN(0);
+    return 0;
   }
+  else
+  {
+    return Log_event::do_update_pos(rli);
+  }
+}
 
-  /*
-    If the event was not requested by the slave i.e. the master sent it while
-    the slave asked for a position >4, the event will make
-    rli->group_master_log_pos advance. Say that the slave asked for position
-    1000, and the Format_desc event's end is 96. Then in the beginning of
-    replication rli->group_master_log_pos will be 0, then 96, then jump to
-    first really asked event (which is >96). So this is ok.
-  */
-  DBUG_RETURN(Start_log_event_v3::exec_event(rli));
+Log_event::enum_skip_reason
+Format_description_log_event::shall_skip(RELAY_LOG_INFO *rli)
+{
+  return Log_event::EVENT_NOT_SKIPPED;
 }
+
 #endif
 
 
@@ -3155,30 +3183,32 @@
   Does the data loading job when executing a LOAD DATA on the slave
 
   SYNOPSIS
-    Load_log_event::exec_event
-      net  
-      rli                             
-      use_rli_only_for_errors	  - if set to 1, rli is provided to 
-                                  Load_log_event::exec_event only for this 
-				  function to have RPL_LOG_NAME and 
-				  rli->last_slave_error, both being used by 
-				  error reports. rli's position advancing
-				  is skipped (done by the caller which is
-				  Execute_load_log_event::exec_event).
-				  - if set to 0, rli is provided for full use,
-				  i.e. for error reports and position
-				  advancing.
+    Load_log_event::do_apply_event
+      net
+      rli
+      use_rli_only_for_errors	  - if set to 1, rli is provided to
+                                  Load_log_event::do_apply_event
+                                  only for this function to have
+                                  RPL_LOG_NAME and
+                                  rli->last_slave_error, both being
+                                  used by error reports. rli's
+                                  position advancing is skipped (done
+                                  by the caller which is
+                                  Execute_load_log_event::do_apply_event).
+                                  - if set to 0, rli is provided for
+                                  full use, i.e. for error reports and
+                                  position advancing.
 
   DESCRIPTION
     Does the data loading job when executing a LOAD DATA on the slave
- 
+
   RETURN VALUE
-    0           Success                                                 
+    0           Success
     1    	Failure
 */
 
-int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, 
-			       bool use_rli_only_for_errors)
+int Load_log_event::do_apply_event(NET* net, RELAY_LOG_INFO const *rli,
+                                     bool use_rli_only_for_errors)
 {
   LEX_STRING new_db;
   new_db.length= db_len;
@@ -3187,9 +3217,9 @@
   DBUG_ASSERT(thd->query == 0);
   thd->query_length= 0;                         // Should not be needed
   thd->query_error= 0;
-  clear_all_errors(thd, rli);
+  clear_all_errors(thd, const_cast<RELAY_LOG_INFO*>(rli));
 
-  /* see Query_log_event::exec_event() and BUG#13360 */
+  /* see Query_log_event::do_apply_event() and BUG#13360 */
   DBUG_ASSERT(!rli->m_table_map.count());
   /*
     Usually mysql_init_query() is called by mysql_parse(), but we need it here
@@ -3198,22 +3228,26 @@
   mysql_init_query(thd, 0, 0);
   if (!use_rli_only_for_errors)
   {
-    /* Saved for InnoDB, see comment in Query_log_event::exec_event() */
-    rli->future_group_master_log_pos= log_pos;
+    /*
+      Saved for InnoDB, see comment in
+      Query_log_event::do_apply_event()
+    */
+    const_cast<RELAY_LOG_INFO*>(rli)->future_group_master_log_pos= log_pos;
     DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
   }
  
    /*
-    We test replicate_*_db rules. Note that we have already prepared the file
-    to load, even if we are going to ignore and delete it now. So it is
-    possible that we did a lot of disk writes for nothing. In other words, a
-    big LOAD DATA INFILE on the master will still consume a lot of space on
-    the slave (space in the relay log + space of temp files: twice the space
-    of the file to load...) even if it will finally be ignored.
-    TODO: fix this; this can be done by testing rules in
-    Create_file_log_event::exec_event() and then discarding Append_block and
-    al. Another way is do the filtering in the I/O thread (more efficient: no
-    disk writes at all).
+    We test replicate_*_db rules. Note that we have already prepared
+    the file to load, even if we are going to ignore and delete it
+    now. So it is possible that we did a lot of disk writes for
+    nothing. In other words, a big LOAD DATA INFILE on the master will
+    still consume a lot of space on the slave (space in the relay log
+    + space of temp files: twice the space of the file to load...)
+    even if it will finally be ignored.  TODO: fix this; this can be
+    done by testing rules in Create_file_log_event::do_apply_event()
+    and then discarding Append_block and al. Another way is do the
+    filtering in the I/O thread (more efficient: no disk writes at
+    all).
 
 
     Note:   We do not need to execute reset_one_shot_variables() if this
@@ -3222,8 +3256,8 @@
             its companion query.  If the SET is ignored because of
             db_ok(), the companion query will also be ignored, and if
             the companion query is ignored in the db_ok() test of
-            ::exec_event(), then the companion SET also have so we
-            don't need to reset_one_shot_variables().
+            ::do_apply_event(), then the companion SET also have so
+            we don't need to reset_one_shot_variables().
   */
   if (rpl_filter->db_ok(thd->db))
   {
@@ -3419,7 +3453,7 @@
     return 1;
   }
 
-  return ( use_rli_only_for_errors ? 0 : Log_event::exec_event(rli) ); 
+  return ( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rli) ); 
 }
 #endif
 
@@ -3532,8 +3566,18 @@
 }
 #endif
 
+/**
+   Helper function to detect if the event is inside a group.
+ */
+static bool is_in_group(THD *const thd, RELAY_LOG_INFO *const rli)
+{
+  return (thd->options & OPTION_BEGIN) != 0 ||
+         (rli->last_event_start_time > 0);
+}
+
+
 /*
-  Rotate_log_event::exec_event()
+  Rotate_log_event::do_apply_event()
 
   Got a rotate log event from the master
 
@@ -3550,34 +3594,43 @@
 */
 
 #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Rotate_log_event::exec_event(struct st_relay_log_info* rli)
+int Rotate_log_event::do_update_pos(RELAY_LOG_INFO *rli)
 {
-  DBUG_ENTER("Rotate_log_event::exec_event");
+  DBUG_ENTER("Rotate_log_event::do_update_pos");
+#ifndef DBUG_OFF
+  char buf[32];
+#endif
+
+  DBUG_PRINT("info", ("server_id=%lu; ::server_id=%lu", this->server_id,
::server_id));
+  DBUG_PRINT("info", ("new_log_ident: %s", this->new_log_ident));
+  DBUG_PRINT("info", ("pos: %s", llstr(this->pos, buf)));
 
   pthread_mutex_lock(&rli->data_lock);
   rli->event_relay_log_pos= my_b_tell(rli->cur_log);
   /*
-    If we are in a transaction: the only normal case is when the I/O thread was
-    copying a big transaction, then it was stopped and restarted: we have this
-    in the relay log:
+    If we are in a transaction or in a group: the only normal case is
+    when the I/O thread was copying a big transaction, then it was
+    stopped and restarted: we have this in the relay log:
+
     BEGIN
     ...
     ROTATE (a fake one)
     ...
     COMMIT or ROLLBACK
-    In that case, we don't want to touch the coordinates which correspond to
-    the beginning of the transaction.
-    Starting from 5.0.0, there also are some rotates from the slave itself, in
-    the relay log.
+
+    In that case, we don't want to touch the coordinates which
+    correspond to the beginning of the transaction.  Starting from
+    5.0.0, there also are some rotates from the slave itself, in the
+    relay log.
   */
-  if (!(thd->options & OPTION_BEGIN))
+  if (!is_in_group(thd, rli))
   {
     memcpy(rli->group_master_log_name, new_log_ident, ident_len+1);
     rli->notify_group_master_log_name_update();
     rli->group_master_log_pos= pos;
     rli->group_relay_log_pos= rli->event_relay_log_pos;
-    DBUG_PRINT("info", ("group_master_log_name: '%s'  "
-                        "group_master_log_pos: %lu",
+    DBUG_PRINT("info", ("new group_master_log_name: '%s'  "
+                        "new group_master_log_pos: %lu",
                         rli->group_master_log_name,
                         (ulong) rli->group_master_log_pos));
     /*
@@ -3596,8 +3649,28 @@
   pthread_mutex_unlock(&rli->data_lock);
   pthread_cond_broadcast(&rli->data_cond);
   flush_relay_log_info(rli);
+
   DBUG_RETURN(0);
 }
+
+
+Log_event::enum_skip_reason
+Rotate_log_event::shall_skip(RELAY_LOG_INFO *rli)
+{
+  
+  enum_skip_reason reason= Log_event::shall_skip(rli);
+
+  switch (reason) {
+  case Log_event::EVENT_NOT_SKIPPED:
+  case Log_event::EVENT_SKIP_COUNT:
+    return Log_event::EVENT_NOT_SKIPPED;
+
+  case Log_event::EVENT_SKIP_SAME_SID:
+    return Log_event::EVENT_SKIP_SAME_SID;
+  }
+  DBUG_ASSERT(0);
+}
+
 #endif
 
 
@@ -3704,11 +3777,11 @@
 
 
 /*
-  Intvar_log_event::exec_event()
+  Intvar_log_event::do_apply_event()
 */
 
 #if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT)
-int Intvar_log_event::exec_event(struct st_relay_log_info* rli)
+int Intvar_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
   switch (type) {
   case LAST_INSERT_ID_EVENT:
@@ -3719,9 +3792,34 @@
     thd->force_one_auto_inc_interval(val);
     break;
   }
+  return 0;
+}
+
+int Intvar_log_event::do_update_pos(RELAY_LOG_INFO *rli)
+{
   rli->inc_event_relay_log_pos();
   return 0;
 }
+
+
+Log_event::enum_skip_reason
+Intvar_log_event::shall_skip(RELAY_LOG_INFO *rli)
+{
+  /*
+    It is a common error to set the slave skip counter to 1 instead
+    of 2 when recovering from an insert which used a auto increment,
+    rand, or user var.  Therefore, if the slave skip counter is 1,
+    we just say that this event should be skipped because of the
+    slave skip count, but we do not change the value of the slave
+    skip counter since it will be decreased by the following insert
+    event.
+  */
+  if (rli->slave_skip_counter == 1)
+    return Log_event::EVENT_SKIP_COUNT;
+  else
+    return Log_event::shall_skip(rli);
+}
+
 #endif
 
 
@@ -3784,13 +3882,38 @@
 
 
 #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Rand_log_event::exec_event(struct st_relay_log_info* rli)
+int Rand_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
   thd->rand.seed1= (ulong) seed1;
   thd->rand.seed2= (ulong) seed2;
+  return 0;
+}
+
+int Rand_log_event::do_update_pos(RELAY_LOG_INFO *rli)
+{
   rli->inc_event_relay_log_pos();
   return 0;
 }
+
+
+Log_event::enum_skip_reason
+Rand_log_event::shall_skip(RELAY_LOG_INFO *rli)
+{
+  /*
+    It is a common error to set the slave skip counter to 1 instead
+    of 2 when recovering from an insert which used a auto increment,
+    rand, or user var.  Therefore, if the slave skip counter is 1,
+    we just say that this event should be skipped because of the
+    slave skip count, but we do not change the value of the slave
+    skip counter since it will be decreased by the following insert
+    event.
+  */
+  if (rli->slave_skip_counter == 1)
+    return Log_event::EVENT_SKIP_COUNT;
+  else
+    return Log_event::shall_skip(rli);
+}
+
 #endif /* !MYSQL_CLIENT */
 
 
@@ -3857,12 +3980,12 @@
 
 
 #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Xid_log_event::exec_event(struct st_relay_log_info* rli)
+int Xid_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
   /* For a slave Xid_log_event is COMMIT */
   general_log_print(thd, COM_QUERY,
                     "COMMIT /* implicit, from Xid_log_event */");
-  return end_trans(thd, COMMIT) || Log_event::exec_event(rli);
+  return end_trans(thd, COMMIT);
 }
 #endif /* !MYSQL_CLIENT */
 
@@ -4140,11 +4263,11 @@
 
 
 /*
-  User_var_log_event::exec_event()
+  User_var_log_event::do_apply_event()
 */
 
 #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int User_var_log_event::exec_event(struct st_relay_log_info* rli)
+int User_var_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
   Item *it= 0;
   CHARSET_INFO *charset;
@@ -4206,9 +4329,32 @@
   e.update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT, 0);
   free_root(thd->mem_root,0);
 
+  return 0;
+}
+
+int User_var_log_event::do_update_pos(RELAY_LOG_INFO *rli)
+{
   rli->inc_event_relay_log_pos();
   return 0;
 }
+
+Log_event::enum_skip_reason
+User_var_log_event::shall_skip(RELAY_LOG_INFO *rli)
+  {
+    /*
+      It is a common error to set the slave skip counter to 1 instead
+      of 2 when recovering from an insert which used a auto increment,
+      rand, or user var.  Therefore, if the slave skip counter is 1,
+      we just say that this event should be skipped because of the
+      slave skip count, but we do not change the value of the slave
+      skip counter since it will be decreased by the following insert
+      event.
+    */
+    if (rli->slave_skip_counter == 1)
+      return Log_event::EVENT_SKIP_COUNT;
+    else
+      return Log_event::shall_skip(rli);
+  }
 #endif /* !MYSQL_CLIENT */
 
 
@@ -4248,7 +4394,7 @@
 
 #ifndef MYSQL_CLIENT
 Slave_log_event::Slave_log_event(THD* thd_arg,
-				 struct st_relay_log_info* rli)
+				 RELAY_LOG_INFO* rli)
   :Log_event(thd_arg, 0, 0) , mem_pool(0), master_host(0)
 {
   DBUG_ENTER("Slave_log_event");
@@ -4358,11 +4504,11 @@
 
 
 #ifndef MYSQL_CLIENT
-int Slave_log_event::exec_event(struct st_relay_log_info* rli)
+int Slave_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
   if (mysql_bin_log.is_open())
     mysql_bin_log.write(this);
-  return Log_event::exec_event(rli);
+  return 0;
 }
 #endif /* !MYSQL_CLIENT */
 
@@ -4391,21 +4537,21 @@
 
 
 /*
-  Stop_log_event::exec_event()
+  Stop_log_event::do_apply_event()
 
-  The master stopped.
-  We used to clean up all temporary tables but this is useless as, as the
-  master has shut down properly, it has written all DROP TEMPORARY TABLE
-  (prepared statements' deletion is TODO only when we binlog prep stmts).
-  We used to clean up slave_load_tmpdir, but this is useless as it has been
-  cleared at the end of LOAD DATA INFILE.
-  So we have nothing to do here.
-  The place were we must do this cleaning is in Start_log_event_v3::exec_event(),
-  not here. Because if we come here, the master was sane.
+  The master stopped.  We used to clean up all temporary tables but
+  this is useless as, as the master has shut down properly, it has
+  written all DROP TEMPORARY TABLE (prepared statements' deletion is
+  TODO only when we binlog prep stmts).  We used to clean up
+  slave_load_tmpdir, but this is useless as it has been cleared at the
+  end of LOAD DATA INFILE.  So we have nothing to do here.  The place
+  were we must do this cleaning is in
+  Start_log_event_v3::do_apply_event(), not here. Because if we come
+  here, the master was sane.
 */
 
 #ifndef MYSQL_CLIENT
-int Stop_log_event::exec_event(struct st_relay_log_info* rli)
+int Stop_log_event::do_update_pos(RELAY_LOG_INFO *rli)
 {
   /*
     We do not want to update master_log pos because we get a rotate event
@@ -4423,6 +4569,7 @@
   }
   return 0;
 }
+
 #endif /* !MYSQL_CLIENT */
 #endif /* HAVE_REPLICATION */
 
@@ -4613,11 +4760,11 @@
 
 
 /*
-  Create_file_log_event::exec_event()
+  Create_file_log_event::do_apply_event()
 */
 
 #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Create_file_log_event::exec_event(struct st_relay_log_info* rli)
+int Create_file_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
   char proc_info[17+FN_REFLEN+10], *fname_buf;
   char *ext;
@@ -4679,7 +4826,7 @@
   if (fd >= 0)
     my_close(fd, MYF(0));
   thd->proc_info= 0;
-  return error ? 1 : Log_event::exec_event(rli);
+  return error == 0;
 }
 #endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
 
@@ -4787,15 +4934,15 @@
 }
 
 /*
-  Append_block_log_event::exec_event()
+  Append_block_log_event::do_apply_event()
 */
 
-int Append_block_log_event::exec_event(struct st_relay_log_info* rli)
+int Append_block_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
   char proc_info[17+FN_REFLEN+10], *fname= proc_info+17;
   int fd;
   int error = 1;
-  DBUG_ENTER("Append_block_log_event::exec_event");
+  DBUG_ENTER("Append_block_log_event::do_apply_event");
 
   fname= strmov(proc_info, "Making temp file ");
   slave_load_file_stem(fname, file_id, server_id, ".data");
@@ -4834,7 +4981,7 @@
   if (fd >= 0)
     my_close(fd, MYF(0));
   thd->proc_info= 0;
-  DBUG_RETURN(error ? error : Log_event::exec_event(rli));
+  DBUG_RETURN(error);
 }
 #endif
 
@@ -4918,18 +5065,18 @@
 #endif
 
 /*
-  Delete_file_log_event::exec_event()
+  Delete_file_log_event::do_apply_event()
 */
 
 #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Delete_file_log_event::exec_event(struct st_relay_log_info* rli)
+int Delete_file_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
   char fname[FN_REFLEN+10];
   char *ext= slave_load_file_stem(fname, file_id, server_id, ".data");
   (void) my_delete(fname, MYF(MY_WME));
   strmov(ext, ".info");
   (void) my_delete(fname, MYF(MY_WME));
-  return Log_event::exec_event(rli);
+  return 0;
 }
 #endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
 
@@ -5015,10 +5162,10 @@
 
 
 /*
-  Execute_load_log_event::exec_event()
+  Execute_load_log_event::do_apply_event()
 */
 
-int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
+int Execute_load_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
   char fname[FN_REFLEN+10];
   char *ext;
@@ -5049,14 +5196,15 @@
 
   lev->thd = thd;
   /*
-    lev->exec_event should use rli only for errors
-    i.e. should not advance rli's position.
-    lev->exec_event is the place where the table is loaded (it calls
-    mysql_load()).
+    lev->do_apply_event should use rli only for errors i.e. should
+    not advance rli's position.
+
+    lev->do_apply_event is the place where the table is loaded (it
+    calls mysql_load()).
   */
 
-  rli->future_group_master_log_pos= log_pos;
-  if (lev->exec_event(0,rli,1)) 
+  const_cast<RELAY_LOG_INFO*>(rli)->future_group_master_log_pos= log_pos;
+  if (lev->do_apply_event(0,rli,1)) 
   {
     /*
       We want to indicate the name of the file that could not be loaded
@@ -5099,7 +5247,7 @@
     my_close(fd, MYF(0));
     end_io_cache(&file);
   }
-  return error ? error : Log_event::exec_event(rli);
+  return error;
 }
 
 #endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
@@ -5267,7 +5415,7 @@
 
 
 int
-Execute_load_query_log_event::exec_event(struct st_relay_log_info* rli)
+Execute_load_query_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
   char *p;
   char *buf;
@@ -5304,7 +5452,7 @@
   p= strmake(p, STRING_WITH_LEN(" INTO"));
   p= strmake(p, query+fn_pos_end, q_len-fn_pos_end);
 
-  error= Query_log_event::exec_event(rli, buf, p-buf);
+  error= Query_log_event::do_apply_event(rli, buf, p-buf);
 
   /* Forging file name for deletion in same buffer */
   *fname_end= 0;
@@ -5624,7 +5772,7 @@
         the master does not have a default value (and isn't nullable)
  */
 static int
-unpack_row(RELAY_LOG_INFO *rli,
+unpack_row(RELAY_LOG_INFO const *rli,
            TABLE *table, uint const colcnt,
            char const *row, MY_BITMAP const *cols,
            char const **row_end, ulong *master_reclength,
@@ -5730,17 +5878,17 @@
   DBUG_RETURN(error);
 }
 
-int Rows_log_event::exec_event(st_relay_log_info *rli)
+int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
-  DBUG_ENTER("Rows_log_event::exec_event(st_relay_log_info*)");
+  DBUG_ENTER("Rows_log_event::do_apply_event(st_relay_log_info*)");
   int error= 0;
   char const *row_start= (char const *)m_rows_buf;
 
   /*
-    If m_table_id == ~0UL, then we have a dummy event that does
-    not contain any data.  In that case, we just remove all tables in
-    the tables_to_lock list, close the thread tables, step the relay
-    log position, and return with success.
+    If m_table_id == ~0UL, then we have a dummy event that does not
+    contain any data.  In that case, we just remove all tables in the
+    tables_to_lock list, close the thread tables, and return with
+    success.  The relay log position will be stepped in 
    */
   if (m_table_id == ~0UL)
   {
@@ -5750,16 +5898,16 @@
      */
     DBUG_ASSERT(get_flags(STMT_END_F));
 
-    rli->clear_tables_to_lock();
+    const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
     close_thread_tables(thd);
     thd->clear_error();
-    rli->inc_event_relay_log_pos();
     DBUG_RETURN(0);
   }
 
   /*
     'thd' has been set by exec_relay_log_event(), just before calling
-    exec_event(). We still check here to prevent future coding errors.
+    do_apply_event(). We still check here to prevent future coding
+    errors.
   */
   DBUG_ASSERT(rli->sql_thd == thd);
 
@@ -5775,8 +5923,9 @@
 
     /*
       lock_tables() reads the contents of thd->lex, so they must be
-      initialized. Contrary to in Table_map_log_event::exec_event() we don't
-      call mysql_init_query() as that may reset the binlog format.
+      initialized. Contrary to in
+      Table_map_log_event::do_apply_event() we don't call
+      mysql_init_query() as that may reset the binlog format.
     */
     lex_start(thd, NULL, 0);
 
@@ -5805,7 +5954,7 @@
                          "Error in %s event: when locking tables",
                          get_type_str());
         }
-        rli->clear_tables_to_lock();
+        const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
         DBUG_RETURN(error);
       }
 
@@ -5824,9 +5973,10 @@
        */
       thd->binlog_flush_pending_rows_event(false);
       TABLE_LIST *tables= rli->tables_to_lock;
+      uint tables_count= rli->tables_to_lock_count;
       close_tables_for_reopen(thd, &tables);
 
-      if ((error= open_tables(thd, &tables, &rli->tables_to_lock_count, 0)))
+      if ((error= open_tables(thd, &tables, &tables_count, 0)))
       {
         if (thd->query_error || thd->is_fatal_error)
         {
@@ -5841,7 +5991,7 @@
                            "unexpected success or fatal error"));
           thd->query_error= 1;
         }
-        rli->clear_tables_to_lock();
+        const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
         DBUG_RETURN(error);
       }
     }
@@ -5885,24 +6035,24 @@
      */
     for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global)
     {
-      rli->m_table_map.set_table(ptr->table_id, ptr->table);
+      const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.set_table(ptr->table_id,
ptr->table);
     }
 #ifdef HAVE_QUERY_CACHE
     query_cache.invalidate_locked_for_write(rli->tables_to_lock);
 #endif
-    rli->clear_tables_to_lock();
+    const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
   }
 
   DBUG_ASSERT(rli->tables_to_lock == NULL && rli->tables_to_lock_count ==
0);
 
-  TABLE* table= rli->m_table_map.get_table(m_table_id);
+  TABLE* table=
const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.get_table(m_table_id);
 
   if (table)
   {
     /*
       table == NULL means that this table should not be replicated
-      (this was set up by Table_map_log_event::exec_event() which
-      tested replicate-* rules).
+      (this was set up by Table_map_log_event::do_apply_event()
+      which tested replicate-* rules).
     */
 
     /*
@@ -5959,9 +6109,9 @@
 	break;
 
       default:
-	slave_print_msg(ERROR_LEVEL, rli, error,
+	slave_print_msg(ERROR_LEVEL, rli, thd->net.last_errno,
                         "Error in %s event: row application failed",
-                        get_type_str());
+                        get_type_str(), error);
 	thd->query_error= 1;
 	break;
       }
@@ -5969,7 +6119,7 @@
       row_start= row_end;
     }
     DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
-                    rli->abort_slave=1;);
+                    const_cast<RELAY_LOG_INFO*>(rli)->abort_slave= 1;);
     error= do_after_row_operations(table, error);
     if (!cache_stmt)
     {
@@ -5980,11 +6130,12 @@
 
   if (error)
   {                     /* error has occured during the transaction */
-    slave_print_msg(ERROR_LEVEL, rli, error,
+    slave_print_msg(ERROR_LEVEL, rli, thd->net.last_errno,
                     "Error in %s event: error during transaction execution "
                     "on table %s.%s",
                     get_type_str(), table->s->db.str, 
                     table->s->table_name.str);
+
      /*
       If one day we honour --skip-slave-errors in row-based replication, and
       the error should be skipped, then we would clear mappings, rollback,
@@ -5997,7 +6148,8 @@
       rollback at the caller along with sbr.
     */
     thd->reset_current_stmt_binlog_row_based();
-    rli->cleanup_context(thd, 0);  /* rollback at caller in step with sbr */
+    /* rollback at caller in step with sbr */
+    const_cast<RELAY_LOG_INFO*>(rli)->cleanup_context(thd, 0);
     thd->query_error= 1;
     DBUG_RETURN(error);
   }
@@ -6041,8 +6193,7 @@
     */
 
     thd->reset_current_stmt_binlog_row_based();
-    rli->cleanup_context(thd, 0);
-    rli->transaction_end(thd);
+    const_cast<RELAY_LOG_INFO*>(rli)->cleanup_context(thd, 0);
 
     if (error == 0)
     {
@@ -6055,7 +6206,6 @@
         do not become visible. We still prefer to wipe them out.
       */
       thd->clear_error();
-      error= Log_event::exec_event(rli);
     }
     else
       slave_print_msg(ERROR_LEVEL, rli, error,
@@ -6082,17 +6232,17 @@
       wait (reached end of last relay log and nothing gets appended
       there), we timeout after one minute, and notify DBA about the
       problem.  When WL#2975 is implemented, just remove the member
-      st_relay_log_info::unsafe_to_stop_at and all its occurences.
+      st_relay_log_info::last_event_start_time and all its occurences.
     */
-    rli->unsafe_to_stop_at= time(0);
+    const_cast<RELAY_LOG_INFO*>(rli)->last_event_start_time= time(0);
   }
 
   DBUG_ASSERT(error == 0);
   thd->clear_error();
-  rli->inc_event_relay_log_pos();
-  
+
   DBUG_RETURN(0);
 }
+
 #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
 
 #ifndef MYSQL_CLIENT
@@ -6272,15 +6422,15 @@
   const char *const vpart= buf + common_header_len + post_header_len;
 
   /* Extract the length of the various parts from the buffer */
-  byte const* const ptr_dblen= (byte const*)vpart + 0;
+  byte const *const ptr_dblen= (byte const*)vpart + 0;
   m_dblen= *(uchar*) ptr_dblen;
 
   /* Length of database name + counter + terminating null */
-  byte const* const ptr_tbllen= ptr_dblen + m_dblen + 2;
+  byte const *const ptr_tbllen= ptr_dblen + m_dblen + 2;
   m_tbllen= *(uchar*) ptr_tbllen;
 
   /* Length of table name + counter + terminating null */
-  byte const* const ptr_colcnt= ptr_tbllen + m_tbllen + 2;
+  byte const *const ptr_colcnt= ptr_tbllen + m_tbllen + 2;
   uchar *ptr_after_colcnt= (uchar*) ptr_colcnt;
   m_colcnt= net_field_length(&ptr_after_colcnt);
 
@@ -6325,9 +6475,9 @@
  */
 
 #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Table_map_log_event::exec_event(st_relay_log_info *rli)
+int Table_map_log_event::do_apply_event(RELAY_LOG_INFO const *rli)
 {
-  DBUG_ENTER("Table_map_log_event::exec_event(st_relay_log_info*)");
+  DBUG_ENTER("Table_map_log_event::do_apply_event(st_relay_log_info*)");
 
   DBUG_ASSERT(rli->sql_thd == thd);
 
@@ -6450,29 +6600,24 @@
       locked by linking the table into the list of tables to lock.
     */
     table_list->next_global= table_list->next_local= rli->tables_to_lock;
-    rli->tables_to_lock= table_list;
-    rli->tables_to_lock_count++;
+    const_cast<RELAY_LOG_INFO*>(rli)->tables_to_lock= table_list;
+    const_cast<RELAY_LOG_INFO*>(rli)->tables_to_lock_count++;
     /* 'memory' is freed in clear_tables_to_lock */
   }
 
-  /*
-    We explicitly do not call Log_event::exec_event() here since we do not
-    want the relay log position to be flushed to disk. The flushing will be
-    done by the last Rows_log_event that either ends a statement (outside a
-    transaction) or a transaction.
-
-    A table map event can *never* end a transaction or a statement, so we
-    just step the relay log position.
-  */
-
-  if (likely(!error))
-    rli->inc_event_relay_log_pos();
   DBUG_RETURN(error);
 
 err:
   my_free((gptr) memory, MYF(MY_WME));
   DBUG_RETURN(error);
 }
+
+int Table_map_log_event::do_update_pos(RELAY_LOG_INFO *rli)
+{
+  rli->inc_event_relay_log_pos();
+  return 0;
+}
+
 #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
 
 #ifndef MYSQL_CLIENT
@@ -6637,7 +6782,7 @@
   return error;
 }
 
-int Write_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
+int Write_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli,
                                          TABLE *table,
                                          char const *const row_start,
                                          char const **const row_end)
@@ -6778,6 +6923,32 @@
   return 0;                                     // All OK
 }
 
+/**
+   Check if an error is a duplicate key error.
+
+   This function is used to check if an error code is one of the
+   duplicate key error, i.e., and error code for which it is sensible
+   to do a <code>get_dup_key()</code> to retrieve the duplicate key.
+
+   @param errcode The error code to check.
+
+   @return <code>true</code> if the error code is such that
+   <code>get_dup_key()</code> will return true,
<code>false</code>
+   otherwise.
+ */
+bool
+is_duplicate_key_error(int errcode)
+{
+  switch (errcode)
+  {
+  case HA_ERR_FOUND_DUPP_KEY:
+  case HA_ERR_FOUND_DUPP_UNIQUE:
+    return true;
+  }
+  return false;
+}
+
+
 /*
   Replace the provided record in the database.
 
@@ -6820,7 +6991,7 @@
     if ((keynum= table->file->get_dup_key(error)) < 0)
     {
       /* We failed to retrieve the duplicate key */
-      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
+      DBUG_RETURN(error);
     }
 
     /*
@@ -6837,7 +7008,10 @@
     {
       error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
       if (error)
+      {
+        table->file->print_error(error, MYF(0));
         DBUG_RETURN(error);
+      }
     }
     else
     {
@@ -6854,12 +7028,15 @@
       }
 
       key_copy((byte*)key.get(), table->record[0], table->key_info + keynum, 0);
-      error= table->file->index_read_idx(table->record[1], keynum, 
+      error= table->file->index_read_idx(table->record[1], keynum,
                                          (const byte*)key.get(),
                                          table->key_info[keynum].key_length,
                                          HA_READ_KEY_EXACT);
       if (error)
+      {
+        table->file->print_error(error, MYF(0));
         DBUG_RETURN(error);
+      }
     }
 
     /*
@@ -6892,15 +7069,21 @@
     {
       error=table->file->ha_update_row(table->record[1],
                                        table->record[0]);
+      if (error)
+        table->file->print_error(error, MYF(0));
       DBUG_RETURN(error);
     }
     else
     {
       if ((error= table->file->ha_delete_row(table->record[1])))
+      {
+        table->file->print_error(error, MYF(0));
         DBUG_RETURN(error);
+      }
       /* Will retry ha_write_row() with the offending row removed. */
     }
   }
+
   DBUG_RETURN(error);
 }
 
@@ -7239,7 +7422,7 @@
   return error;
 }
 
-int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
+int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli,
                                           TABLE *table,
                                           char const *const row_start,
                                           char const **const row_end)
@@ -7374,7 +7557,7 @@
   return error;
 }
 
-int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
+int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli,
                                           TABLE *table,
                                           char const *const row_start,
                                           char const **const row_end)

--- 1.137/sql/log_event.h	2007-03-06 19:52:44 +01:00
+++ 1.138/sql/log_event.h	2007-03-06 19:52:44 +01:00
@@ -1,9 +1,8 @@
-/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+/* Copyright (C) 2000-2006 MySQL AB
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
+   the Free Software Foundation; version 2 of the License.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -200,8 +199,26 @@
 #define EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN (4 + 4 + 4 + 1)
 #define EXECUTE_LOAD_QUERY_HEADER_LEN  (QUERY_HEADER_LEN +
EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN)
 
-/*
-   Event header offsets;
+/* 
+  Max number of possible extra bytes in a replication event compared to a
+  packet (i.e. a query) sent from client to master;
+  First, an auxiliary log_event status vars estimation:
+*/
+#define MAX_SIZE_LOG_EVENT_STATUS (4 /* flags2 */   + \
+                                   8 /* sql mode */ + \
+                                   1 + 1 + 255 /* catalog */ + \
+                                   4 /* autoinc */ + \
+                                   6 /* charset */ + \
+                                   MAX_TIME_ZONE_NAME_LENGTH)
+#define MAX_LOG_EVENT_HEADER   ( /* in order of Query_log_event::write */ \
+  LOG_EVENT_HEADER_LEN + /* write_header */ \
+  QUERY_HEADER_LEN     + /* write_data */   \
+  EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN + /*write_post_header_for_derived */ \
+  MAX_SIZE_LOG_EVENT_STATUS + /* status */ \
+  NAME_LEN + 1)
+
+/* 
+   Event header offsets; 
    these point to places inside the fixed header.
 */
 
@@ -253,6 +270,9 @@
 */
 #define Q_CATALOG_NZ_CODE       6
 
+#define Q_LC_TIME_NAMES_CODE    7
+
+#define Q_CHARSET_DATABASE_CODE 8
 /* Intvar event post-header */
 
 #define I_TYPE_OFFSET        0
@@ -405,12 +425,18 @@
    either, as the manual says (because a too big in-memory temp table is
    automatically written to disk).
 */
-#define OPTIONS_WRITTEN_TO_BIN_LOG (OPTION_AUTO_IS_NULL | \
-OPTION_NO_FOREIGN_KEY_CHECKS | OPTION_RELAXED_UNIQUE_CHECKS)
+#define OPTIONS_WRITTEN_TO_BIN_LOG \
+  (OPTION_AUTO_IS_NULL | OPTION_NO_FOREIGN_KEY_CHECKS |  \
+   OPTION_RELAXED_UNIQUE_CHECKS | OPTION_NOT_AUTOCOMMIT)
+
+/* Shouldn't be defined before */
+#define EXPECTED_OPTIONS \
+  ((ULL(1) << 14) | (ULL(1) << 26) | (ULL(1) << 27) | (ULL(1) <<
19))
 
-#if OPTIONS_WRITTEN_TO_BIN_LOG != ((1L << 14) | (1L << 26) | (1L <<
27))
+#if OPTIONS_WRITTEN_TO_BIN_LOG != EXPECTED_OPTIONS
 #error OPTIONS_WRITTEN_TO_BIN_LOG must NOT change their values!
 #endif
+#undef EXPECTED_OPTIONS         /* You shouldn't use this one */
 
 enum Log_event_type
 {
@@ -508,9 +534,12 @@
   bool charset_inited;
   char charset[6]; // 3 variables, each of them storable in 2 bytes
   char time_zone_str[MAX_TIME_ZONE_NAME_LENGTH];
+  uint lc_time_names_number;
+  uint charset_database_number;
   st_print_event_info()
     :flags2_inited(0), sql_mode_inited(0),
-     auto_increment_increment(1),auto_increment_offset(1), charset_inited(0)
+     auto_increment_increment(1),auto_increment_offset(1), charset_inited(0),
+     lc_time_names_number(0), charset_database_number(0)
     {
       /*
         Currently we only use static PRINT_EVENT_INFO objects, so zeroed at
@@ -520,15 +549,19 @@
       bzero(db, sizeof(db));
       bzero(charset, sizeof(charset));
       bzero(time_zone_str, sizeof(time_zone_str));
-      uint const flags = MYF(MY_WME | MY_NABP);
-      init_io_cache(&head_cache, -1, 0, WRITE_CACHE, 0L, FALSE, flags);
-      init_io_cache(&body_cache, -1, 0, WRITE_CACHE, 0L, FALSE, flags);
+      delimiter[0]= ';';
+      delimiter[1]= 0;
+      myf const flags = MYF(MY_WME | MY_NABP);
+      open_cached_file(&head_cache, NULL, NULL, 0, flags);
+      open_cached_file(&body_cache, NULL, NULL, 0, flags);
     }
 
   ~st_print_event_info() {
-    end_io_cache(&head_cache);
-    end_io_cache(&body_cache);
+    close_cached_file(&head_cache);
+    close_cached_file(&body_cache);
   }
+  bool init_ok() /* tells if construction was successful */
+    { return my_b_inited(&head_cache) && my_b_inited(&body_cache); }
 
 
   /* Settings on how to print the events */
@@ -536,6 +569,7 @@
   bool base64_output;
   my_off_t hexdump_from;
   uint8 common_header_len;
+  char delimiter[16];
 
   /*
      These two caches are used by the row-based replication events to
@@ -559,6 +593,13 @@
 {
 public:
   /*
+    The following type definition is to be used whenever data is placed 
+    and manipulated in a common buffer. Use this typedef for buffers
+    that contain data containing binary and character data.
+  */
+  typedef unsigned char Byte;
+
+  /*
     The offset in the log where this event originally appeared (it is
     preserved in relay logs, making SHOW SLAVE STATUS able to print
     coordinates of the event in the master's binlog). Note: when a
@@ -803,7 +844,7 @@
 class Query_log_event: public Log_event
 {
 protected:
-  char* data_buf;
+  Log_event::Byte* data_buf;
 public:
   const char* query;
   const char* catalog;
@@ -874,6 +915,8 @@
   char charset[6];
   uint time_zone_len; /* 0 means uninited */
   const char *time_zone_str;
+  uint lc_time_names_number; /* 0 means en_US */
+  uint charset_database_number;
 
 #ifndef MYSQL_CLIENT
 
@@ -936,6 +979,8 @@
 
   bool write(IO_CACHE* file) { return(false); };
   virtual bool write_post_header_for_derived(IO_CACHE* file) { return FALSE; }
+#else
+  Muted_query_log_event() {}
 #endif
 };
 
@@ -1197,6 +1242,7 @@
   uint8 number_of_event_types;
   /* The list of post-headers' lengthes */
   uint8 *post_header_len;
+  uchar server_version_split[3];
 
   Format_description_log_event(uint8 binlog_ver, const char* server_ver=0);
   Format_description_log_event(const char* buf, uint event_len,
@@ -1222,6 +1268,8 @@
     return FORMAT_DESCRIPTION_HEADER_LEN;
   }
 
+  void calc_server_version_split();
+
 private:
 #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
   virtual int apply_event_impl(RELAY_LOG_INFO* rli);
@@ -1796,8 +1844,6 @@
 #endif
 char *str_to_hex(char *to, const char *from, uint len);
 
-#ifdef HAVE_ROW_BASED_REPLICATION
-
 /*****************************************************************************
 
   Table map log event class
@@ -1815,14 +1861,17 @@
     TYPE_CODE = TABLE_MAP_EVENT
   };
 
+  /**
+     Enumeration of the errors that can be returned.
+   */
   enum enum_error
   {
-    ERR_OPEN_FAILURE = -1,                 /* Failure to open table */
-    ERR_OK = 0,                                  /* No error */
-    ERR_TABLE_LIMIT_EXCEEDED = 1,        /* No more room for tables */
-    ERR_OUT_OF_MEM = 2,                         /* Out of memory */
-    ERR_BAD_TABLE_DEF = 3,       /* Table definition does not match */
-    ERR_RBR_TO_SBR = 4    /* daisy-chanining RBR to SBR not allowed */
+    ERR_OPEN_FAILURE = -1,               /**< Failure to open table */
+    ERR_OK = 0,                                 /**< No error */
+    ERR_TABLE_LIMIT_EXCEEDED = 1,      /**< No more room for tables */
+    ERR_OUT_OF_MEM = 2,                         /**< Out of memory */
+    ERR_BAD_TABLE_DEF = 3,     /**< Table definition does not match */
+    ERR_RBR_TO_SBR = 4  /**< daisy-chanining RBR to SBR not allowed */
   };
 
   enum enum_flag
@@ -1905,7 +1954,7 @@
 
  Row level log event class.
 
- Common base class for all row-level log events.
+ Common base class for all row-containing log events.
 
  RESPONSIBILITIES
 
@@ -1919,6 +1968,19 @@
 class Rows_log_event : public Log_event
 {
 public:
+  /**
+     Enumeration of the errors that can be returned.
+   */
+  enum enum_error
+  {
+    ERR_OPEN_FAILURE = -1,               /**< Failure to open table */
+    ERR_OK = 0,                                 /**< No error */
+    ERR_TABLE_LIMIT_EXCEEDED = 1,      /**< No more room for tables */
+    ERR_OUT_OF_MEM = 2,                         /**< Out of memory */
+    ERR_BAD_TABLE_DEF = 3,     /**< Table definition does not match */
+    ERR_RBR_TO_SBR = 4  /**< daisy-chanining RBR to SBR not allowed */
+  };
+
   /*
     These definitions allow you to combine the flags into an
     appropriate flag set using the normal bitwise operators.  The
@@ -1926,7 +1988,6 @@
     accepted by the compiler, which is then used to set the real set
     of flags.
   */
-
   enum enum_flag
   {
     /* Last event of a statement */
@@ -2130,7 +2191,7 @@
   Write_rows_log_event(const char *buf, uint event_len, 
                        const Format_description_log_event *description_event);
 #endif
-#if !defined(MYSQL_CLIENT) && defined(HAVE_ROW_BASED_REPLICATION)
+#if !defined(MYSQL_CLIENT) 
   static bool binlog_row_logging_function(THD *thd, TABLE *table,
                                           bool is_transactional,
                                           MY_BITMAP *cols,
@@ -2195,7 +2256,7 @@
 			const Format_description_log_event *description_event);
 #endif
 
-#if !defined(MYSQL_CLIENT) && defined(HAVE_ROW_BASED_REPLICATION)
+#if !defined(MYSQL_CLIENT) 
   static bool binlog_row_logging_function(THD *thd, TABLE *table,
                                           bool is_transactional,
                                           MY_BITMAP *cols,
@@ -2265,7 +2326,7 @@
   Delete_rows_log_event(const char *buf, uint event_len, 
 			const Format_description_log_event *description_event);
 #endif
-#if !defined(MYSQL_CLIENT) && defined(HAVE_ROW_BASED_REPLICATION)
+#if !defined(MYSQL_CLIENT) 
   static bool binlog_row_logging_function(THD *thd, TABLE *table,
                                           bool is_transactional,
                                           MY_BITMAP *cols,
@@ -2298,7 +2359,5 @@
   virtual int do_exec_row(TABLE *table);
 #endif
 };
-
-#endif /* HAVE_ROW_BASED_REPLICATION */
 
 #endif /* _log_event_h */

--- 1.295/sql/slave.cc	2007-03-06 19:52:44 +01:00
+++ 1.296/sql/slave.cc	2007-03-06 19:52:44 +01:00
@@ -2,8 +2,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
+   the Free Software Foundation; version 2 of the License.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -33,6 +32,7 @@
 
 int queue_event(MASTER_INFO* mi,const char* buf,ulong event_len);
 
+#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
 
 #define MAX_SLAVE_RETRY_PAUSE 5
 bool use_slave_mask = 0;
@@ -53,6 +53,7 @@
 */
 
 int disconnect_slave_event_count = 0, abort_slave_event_count = 0;
+int events_till_abort = -1;
 
 typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE;
 
@@ -73,6 +74,7 @@
 static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
                                   const char* table_name, bool overwrite);
 static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi);
+static Log_event* next_event(RELAY_LOG_INFO* rli);
 
 /*
   Find out which replications threads are running
@@ -913,7 +915,7 @@
   TABLE_LIST tables;
   int error= 1;
   handler *file;
-  ulong save_options;
+  ulonglong save_options;
   NET *net= &mysql->net;
   DBUG_ENTER("create_table_from_dump");
 
@@ -1295,7 +1297,7 @@
     rpl_filter->get_wild_ignore_table(&tmp);
     protocol->store(&tmp);
 
-    protocol->store((uint32) mi->rli.last_slave_errno);
+    protocol->store(mi->rli.last_slave_errno);
     protocol->store(mi->rli.last_slave_error, &my_charset_bin);
     protocol->store((uint32) mi->rli.slave_skip_counter);
     protocol->store((ulonglong) mi->rli.group_master_log_pos);
@@ -1326,12 +1328,12 @@
     if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) &&
         mi->rli.slave_running)
     {
-      long tmp= (long)((time_t)time((time_t*) 0)
-                               - mi->rli.last_master_timestamp)
-        - mi->clock_diff_with_master;
+      long time_diff= ((long)((time_t)time((time_t*) 0)
+                              - mi->rli.last_master_timestamp)
+                       - mi->clock_diff_with_master);
       /*
-        Apparently on some systems tmp can be <0. Here are possible reasons
-        related to MySQL:
+        Apparently on some systems time_diff can be <0. Here are possible
+        reasons related to MySQL:
         - the master is itself a slave of another master whose time is ahead.
         - somebody used an explicit SET TIMESTAMP on the master.
         Possible reason related to granularity-to-second of time functions
@@ -1349,8 +1351,8 @@
         last_master_timestamp == 0 (an "impossible" timestamp 1970) is a
         special marker to say "consider we have caught up".
       */
-      protocol->store((longlong)(mi->rli.last_master_timestamp ? max(0, tmp)
-                                 : 0));
+      protocol->store((longlong)(mi->rli.last_master_timestamp ?
+                                 max(0, time_diff) : 0));
     }
     else
       protocol->store_null();
@@ -1369,9 +1371,21 @@
 void set_slave_thread_options(THD* thd)
 {
   DBUG_ENTER("set_slave_thread_options");
-
-  thd->options = ((opt_log_slave_updates) ? OPTION_BIN_LOG:0) |
-    OPTION_AUTO_IS_NULL;
+  /*
+     It's nonsense to constrain the slave threads with max_join_size; if a
+     query succeeded on master, we HAVE to execute it. So set
+     OPTION_BIG_SELECTS. Setting max_join_size to HA_POS_ERROR is not enough
+     (and it's not needed if we have OPTION_BIG_SELECTS) because an INSERT
+     SELECT examining more than 4 billion rows would still fail (yes, because
+     when max_join_size is 4G, OPTION_BIG_SELECTS is automatically set, but
+     only for client threads.
+  */
+  ulonglong options= thd->options | OPTION_BIG_SELECTS;
+  if (opt_log_slave_updates)
+    options|= OPTION_BIN_LOG;
+  else
+    options&= ~OPTION_BIN_LOG;
+  thd->options= options;
   thd->variables.completion_type= 0;
   DBUG_VOID_RETURN;
 }
@@ -1409,24 +1423,18 @@
     SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO;
   thd->security_ctx->skip_grants();
   my_net_init(&thd->net, 0);
-  thd->net.read_timeout = slave_net_timeout;
+/*
+  Adding MAX_LOG_EVENT_HEADER_LEN to the max_allowed_packet on all
+  slave threads, since a replication event can become this much larger
+  than the corresponding packet (query) sent from client to master.
+*/
+  thd->variables.max_allowed_packet= global_system_variables.max_allowed_packet
+    + MAX_LOG_EVENT_HEADER;  /* note, incr over the global not session var */
   thd->slave_thread = 1;
   set_slave_thread_options(thd);
-  /*
-     It's nonsense to constrain the slave threads with max_join_size; if a
-     query succeeded on master, we HAVE to execute it. So set
-     OPTION_BIG_SELECTS. Setting max_join_size to HA_POS_ERROR is not enough
-     (and it's not needed if we have OPTION_BIG_SELECTS) because an INSERT
-     SELECT examining more than 4 billion rows would still fail (yes, because
-     when max_join_size is 4G, OPTION_BIG_SELECTS is automatically set, but
-     only for client threads.
-  */
-  thd->options = ((opt_log_slave_updates) ? OPTION_BIN_LOG:0) |
-    OPTION_AUTO_IS_NULL | OPTION_BIG_SELECTS;
   thd->client_capabilities = CLIENT_LOCAL_FILES;
-  thd->real_id=pthread_self();
   pthread_mutex_lock(&LOCK_thread_count);
-  thd->thread_id = thread_id++;
+  thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
   pthread_mutex_unlock(&LOCK_thread_count);
 
   if (init_thr_lock() || thd->store_globals())
@@ -1436,12 +1444,6 @@
     DBUG_RETURN(-1);
   }
 
-#if !defined(__WIN__) && !defined(__NETWARE__)
-  sigset_t set;
-  VOID(sigemptyset(&set));                      // Get mask in use
-  VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
-#endif
-
   if (thd_type == SLAVE_THD_SQL)
     thd->proc_info= "Waiting for the next event in relay log";
   else
@@ -1608,7 +1610,7 @@
      DBUG_RETURN(packet_error);
   }
 
-  DBUG_PRINT("info",( "len=%u, net->read_pos[4] = %d\n",
+  DBUG_PRINT("exit", ("len: %lu  net->read_pos[4]: %d",
                       len, mysql->net.read_pos[4]));
   DBUG_RETURN(len - 1);
 }
@@ -1752,9 +1754,12 @@
     if (!ev->when)
       ev->when = time(NULL);
     ev->thd = thd; // because up to this point, ev->thd == 0
+    DBUG_PRINT("info", ("thd->options={ %s%s}",
+                        FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT),
+                        FLAGSTR(thd->options, OPTION_BEGIN)));
 
-    exec_res= ev->exec_event(rli);
-    DBUG_PRINT("info", ("exec_event result = %d", exec_res));
+    exec_res = ev->exec_event(rli);
+    DBUG_PRINT("info", ("exec_event result: %d", exec_res));
     DBUG_ASSERT(rli->sql_thd==thd);
     /*
        Format_description_log_event should not be deleted because it will be
@@ -1904,11 +1909,19 @@
   thd->proc_info = "Connecting to master";
   // we can get killed during safe_connect
   if (!safe_connect(thd, mysql, mi))
-    sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',\
-  replication started in log '%s' at position %s", mi->user,
-                    mi->host, mi->port,
-                    IO_RPL_LOG_NAME,
-                    llstr(mi->master_log_pos,llbuff));
+  {
+    sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',"
+                          "replication started in log '%s' at position %s",
+                          mi->user, mi->host, mi->port,
+			  IO_RPL_LOG_NAME,
+			  llstr(mi->master_log_pos,llbuff));
+  /*
+    Adding MAX_LOG_EVENT_HEADER_LEN to the max_packet_size on the I/O
+    thread, since a replication event can become this much larger than
+    the corresponding packet (query) sent from client to master.
+  */
+    mysql->net.max_packet_size= thd->net.max_packet_size+= MAX_LOG_EVENT_HEADER;
+  }
   else
   {
     sql_print_information("Slave I/O thread killed while connecting to master");
@@ -1994,15 +2007,16 @@
 
     while (!io_slave_killed(thd,mi))
     {
-      bool suppress_warnings= 0;
+      ulong event_len;
+      suppress_warnings= 0;
       /*
          We say "waiting" because read_event() will wait if there's nothing to
          read. But if there's something to read, it will not wait. The
          important thing is to not confuse users by saying "reading" whereas
          we're in fact receiving nothing.
       */
-      thd->proc_info = "Waiting for master to send event";
-      ulong event_len = read_event(mysql, mi, &suppress_warnings);
+      thd->proc_info= "Waiting for master to send event";
+      event_len= read_event(mysql, mi, &suppress_warnings);
       if (io_slave_killed(thd,mi))
       {
         if (global_system_variables.log_warnings)
@@ -2153,11 +2167,16 @@
   THD_CHECK_SENTRY(thd);
   delete thd;
   pthread_mutex_unlock(&LOCK_thread_count);
-  mi->abort_slave = 0;
-  mi->slave_running = 0;
-  mi->io_thd = 0;
-  pthread_mutex_unlock(&mi->run_lock);
+  mi->abort_slave= 0;
+  mi->slave_running= 0;
+  mi->io_thd= 0;
+  /*
+    Note: the order of the two following calls (first broadcast, then unlock)
+    is important. Otherwise a killer_thread can execute between the calls and
+    delete the mi structure leading to a crash! (see BUG#25306 for details)
+   */ 
   pthread_cond_broadcast(&mi->stop_cond);       // tell the world we are done
+  pthread_mutex_unlock(&mi->run_lock);
   my_thread_end();
   pthread_exit(0);
   DBUG_RETURN(0);                               // Can't return anything here
@@ -2407,9 +2426,14 @@
   THD_CHECK_SENTRY(thd);
   delete thd;
   pthread_mutex_unlock(&LOCK_thread_count);
+ /*
+  Note: the order of the broadcast and unlock calls below (first broadcast, then unlock)
+  is important. Otherwise a killer_thread can execute between the calls and
+  delete the mi structure leading to a crash! (see BUG#25306 for details)
+ */ 
   pthread_cond_broadcast(&rli->stop_cond);
-  // tell the world we are done
-  pthread_mutex_unlock(&rli->run_lock);
+  pthread_mutex_unlock(&rli->run_lock);  // tell the world we are done
+  
   my_thread_end();
   pthread_exit(0);
   DBUG_RETURN(0);                               // Can't return anything here
@@ -2554,7 +2578,7 @@
   /* Safe copy as 'rev' has been "sanitized" in Rotate_log_event's ctor */
   memcpy(mi->master_log_name, rev->new_log_ident, rev->ident_len+1);
   mi->master_log_pos= rev->pos;
-  DBUG_PRINT("info", ("master_log_pos: '%s' %d",
+  DBUG_PRINT("info", ("master_log_pos: '%s' %lu",
                       mi->master_log_name, (ulong) mi->master_log_pos));
 #ifndef DBUG_OFF
   /*
@@ -2672,7 +2696,7 @@
     int error = process_io_create_file(mi,(Create_file_log_event*)ev);
     delete ev;
     mi->master_log_pos += inc_pos;
-    DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
+    DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
     pthread_mutex_unlock(&mi->data_lock);
     my_free((char*)tmp_buf, MYF(0));
     DBUG_RETURN(error);
@@ -2699,7 +2723,7 @@
   }
   delete ev;
   mi->master_log_pos+= inc_pos;
-  DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
+  DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
   pthread_mutex_unlock(&mi->data_lock);
   DBUG_RETURN(0);
 }
@@ -2755,7 +2779,7 @@
   delete ev;
   mi->master_log_pos+= inc_pos;
 err:
-  DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
+  DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
   pthread_mutex_unlock(&mi->data_lock);
   DBUG_RETURN(0);
 }
@@ -2808,6 +2832,8 @@
   pthread_mutex_t *log_lock= rli->relay_log.get_log_lock();
   DBUG_ENTER("queue_event");
 
+  LINT_INIT(inc_pos);
+
   if (mi->rli.relay_log.description_event_for_queue->binlog_version<4 &&
       buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT /* a way to escape */)
     DBUG_RETURN(queue_old_event(mi,buf,event_len));
@@ -2928,7 +2954,8 @@
       rli->ign_master_log_pos_end= mi->master_log_pos;
     }
     rli->relay_log.signal_update(); // the slave SQL thread needs to re-check
-    DBUG_PRINT("info", ("master_log_pos: %d, event originating from the same server,
ignored", (ulong) mi->master_log_pos));
+    DBUG_PRINT("info", ("master_log_pos: %lu, event originating from the same server,
ignored",
+                        (ulong) mi->master_log_pos));
   }
   else
   {
@@ -2936,7 +2963,7 @@
     if (likely(!(rli->relay_log.appendv(buf,event_len,0))))
     {
       mi->master_log_pos+= inc_pos;
-      DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
+      DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
       rli->relay_log.harvest_bytes_written(&rli->log_space_total);
     }
     else
@@ -2948,7 +2975,7 @@
 
 err:
   pthread_mutex_unlock(&mi->data_lock);
-  DBUG_PRINT("info", ("error=%d", error));
+  DBUG_PRINT("info", ("error: %d", error));
   DBUG_RETURN(error);
 }
 
@@ -3057,8 +3084,8 @@
     {
       last_errno=mysql_errno(mysql);
       suppress_warnings= 0;
-      sql_print_error("Slave I/O thread: error %s to master \
-'%s@%s:%d': \
+      sql_print_error("Slave I/O thread: error %s to master "
+                      "'%s@%s:%d':                       \
 Error: '%s'  errno: %d  retry-time: %d  retries: %lu",
                       (reconnect ? "reconnecting" : "connecting"),
                       mi->user, mi->host, mi->port,
@@ -3268,7 +3295,13 @@
         hot_log=0;                              // Using old binary log
       }
     }
-
+    /* 
+      As there is no guarantee that the relay is open (for example, an I/O
+      error during a write by the slave I/O thread may have closed it), we
+      have to test it.
+    */
+    if (!my_b_inited(cur_log))
+      goto err;
 #ifndef DBUG_OFF
     {
       /* This is an assertion which sometimes fails, let's try to track it */
@@ -3596,6 +3629,70 @@
   DBUG_VOID_RETURN;
 }
 
+
+/**
+   Detects, based on master's version (as found in the relay log), if master
+   has a certain bug.
+   @param rli RELAY_LOG_INFO which tells the master's version
+   @param bug_id Number of the bug as found in bugs.mysql.com
+   @return TRUE if master has the bug, FALSE if it does not.
+*/
+bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id)
+{
+  struct st_version_range_for_one_bug {
+    uint        bug_id;
+    const uchar introduced_in[3]; // first version with bug
+    const uchar fixed_in[3];      // first version with fix
+  };
+  static struct st_version_range_for_one_bug versions_for_all_bugs[]=
+  {
+    {24432, { 5, 0, 24 }, { 5, 0, 38 } },
+    {24432, { 5, 1, 12 }, { 5, 1, 17 } }
+  };
+  const uchar *master_ver=
+    rli->relay_log.description_event_for_exec->server_version_split;
+
+ 
DBUG_ASSERT(sizeof(rli->relay_log.description_event_for_exec->server_version_split)
== 3);
+
+  for (uint i= 0;
+       i < sizeof(versions_for_all_bugs)/sizeof(*versions_for_all_bugs);i++)
+  {
+    const uchar *introduced_in= versions_for_all_bugs[i].introduced_in,
+      *fixed_in= versions_for_all_bugs[i].fixed_in;
+    if ((versions_for_all_bugs[i].bug_id == bug_id) &&
+        (memcmp(introduced_in, master_ver, 3) <= 0) &&
+        (memcmp(fixed_in,      master_ver, 3) >  0))
+    {
+      // a short message for SHOW SLAVE STATUS (message length constraints)
+      my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from"
+                      " http://bugs.mysql.com/bug.php?id=%u"
+                      " so slave stops; check error log on slave"
+                      " for more info", MYF(0), bug_id);
+      // a verbose message for the error log
+      slave_print_msg(ERROR_LEVEL, rli, ER_UNKNOWN_ERROR,
+                      "According to the master's version ('%s'),"
+                      " it is probable that master suffers from this bug:"
+                      " http://bugs.mysql.com/bug.php?id=%u"
+                      " and thus replicating the current binary log event"
+                      " may make the slave's data become different from the"
+                      " master's data."
+                      " To take no risk, slave refuses to replicate"
+                      " this event and stops."
+                      " We recommend that all updates be stopped on the"
+                      " master and slave, that the data of both be"
+                      " manually synchronized,"
+                      " that master's binary logs be deleted,"
+                      " that master be upgraded to a version at least"
+                      " equal to '%d.%d.%d'. Then replication can be"
+                      " restarted.",
+                      rli->relay_log.description_event_for_exec->server_version,
+                      bug_id,
+                      fixed_in[0], fixed_in[1], fixed_in[2]);
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
 
 #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
 template class I_List_iterator<i_string>;

--- 1.4/sql/rpl_rli.cc	2007-03-06 19:52:44 +01:00
+++ 1.5/sql/rpl_rli.cc	2007-03-06 19:52:44 +01:00
@@ -2,8 +2,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
+   the Free Software Foundation; version 2 of the License.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,6 +18,7 @@
 #include "rpl_rli.h"
 #include <my_dir.h>    // For MY_STAT
 #include "sql_repl.h"  // For check_binlog_magic
+#include "rpl_utility.h"
 
 static int count_relay_log_space(RELAY_LOG_INFO* rli);
 
@@ -402,7 +402,7 @@
                        bool look_for_description_event)
 {
   DBUG_ENTER("init_relay_log_pos");
-  DBUG_PRINT("info", ("pos=%lu", pos));
+  DBUG_PRINT("info", ("pos: %lu", (ulong) pos));
 
   *errmsg=0;
   pthread_mutex_t *log_lock=rli->relay_log.get_log_lock();
@@ -855,7 +855,7 @@
       Don't ask for disk deletion. For now, anyway they will be deleted when
       slave restarts, but it is a better intention to not delete them.
     */
-    DBUG_PRINT("info", ("table: %p", table));
+    DBUG_PRINT("info", ("table: 0x%lx", (long) table));
     close_temporary(table, 1, 0);
   }
   save_temporary_tables= 0;
@@ -1114,4 +1114,23 @@
   last_event_start_time= 0;
   DBUG_VOID_RETURN;
 }
+
+void st_relay_log_info::clear_tables_to_lock()
+{
+  while (tables_to_lock)
+  {
+    gptr to_free= reinterpret_cast<gptr>(tables_to_lock);
+    if (tables_to_lock->m_tabledef_valid)
+    {
+      tables_to_lock->m_tabledef.table_def::~table_def();
+      tables_to_lock->m_tabledef_valid= FALSE;
+    }
+    tables_to_lock=
+      static_cast<RPL_TABLE_LIST*>(tables_to_lock->next_global);
+    tables_to_lock_count--;
+    my_free(to_free, MYF(MY_WME));
+  }
+  DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0);
+}
+
 #endif

--- 1.8/mysql-test/r/rpl_row_tabledefs_2myisam.result	2007-03-06 19:52:44 +01:00
+++ 1.9/mysql-test/r/rpl_row_tabledefs_2myisam.result	2007-03-06 19:52:44 +01:00
@@ -121,7 +121,7 @@
 Replicate_Ignore_Table	
 Replicate_Wild_Do_Table	
 Replicate_Wild_Ignore_Table	
-Last_Errno	1364
+Last_Errno	1105
 Last_Error	Error in Write_rows event: error during transaction execution on table
test.t1_nodef
 Skip_Counter	0
 Exec_Master_Log_Pos	#

--- 1.9/sql/rpl_rli.h	2007-03-06 19:52:44 +01:00
+++ 1.10/sql/rpl_rli.h	2007-03-06 19:52:44 +01:00
@@ -2,8 +2,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
+   the Free Software Foundation; version 2 of the License.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,6 +20,8 @@
 
 #include "rpl_tblmap.h"
 
+struct RPL_TABLE_LIST;
+
 
 /****************************************************************************
 
@@ -280,7 +281,7 @@
 	    group_relay_log_pos);
   }
 
-  TABLE_LIST *tables_to_lock;               /* RBR: Tables to lock  */
+  RPL_TABLE_LIST *tables_to_lock;           /* RBR: Tables to lock  */
   uint tables_to_lock_count;        /* RBR: Count of tables to lock */
   table_mapping m_table_map;      /* RBR: Mapping table-id to table */
 
@@ -294,16 +295,7 @@
   bool cached_charset_compare(char *charset) const;
 
   void cleanup_context(THD *, bool);
-  void clear_tables_to_lock() {
-    while (tables_to_lock)
-    {
-      char *to_free= reinterpret_cast<gptr>(tables_to_lock);
-      tables_to_lock= tables_to_lock->next_global;
-      tables_to_lock_count--;
-      my_free(to_free, MYF(MY_WME));
-    }
-    DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0);
-  }
+  void clear_tables_to_lock();
 
   /*
     Used by row-based replication to detect that it should not stop at

--- 1.5/mysql-test/r/rpl_row_tabledefs_3innodb.result	2007-03-06 19:52:44 +01:00
+++ 1.6/mysql-test/r/rpl_row_tabledefs_3innodb.result	2007-03-06 19:52:44 +01:00
@@ -121,7 +121,7 @@
 Replicate_Ignore_Table	
 Replicate_Wild_Do_Table	
 Replicate_Wild_Ignore_Table	
-Last_Errno	1364
+Last_Errno	1105
 Last_Error	Error in Write_rows event: error during transaction execution on table
test.t1_nodef
 Skip_Counter	0
 Exec_Master_Log_Pos	#

--- 1.4/sql/rpl_utility.cc	2007-03-06 19:52:44 +01:00
+++ 1.5/sql/rpl_utility.cc	2007-03-06 19:52:44 +01:00
@@ -1,9 +1,8 @@
-/* Copyright 2006 MySQL AB. All rights reserved.
+/* Copyright (C) 2006 MySQL AB
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
+   the Free Software Foundation; version 2 of the License.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -12,7 +11,7 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
 
 #include "rpl_utility.h"
 
@@ -25,7 +24,7 @@
   switch (field_type) {
   case MYSQL_TYPE_DECIMAL:
   case MYSQL_TYPE_NEWDECIMAL:
-    length= ~0UL;
+    length= ~(uint32) 0;
     break;
   case MYSQL_TYPE_YEAR:
   case MYSQL_TYPE_TINY:
@@ -71,7 +70,7 @@
     break;
     break;
   case MYSQL_TYPE_BIT:
-    length= ~0UL;
+    length= ~(uint32) 0;
     break;
   default:
     /* This case should never be chosen */
@@ -85,7 +84,7 @@
   case MYSQL_TYPE_SET:
   case MYSQL_TYPE_VAR_STRING:
   case MYSQL_TYPE_VARCHAR:
-    length= ~0UL;                               // NYI
+    length= ~(uint32) 0;                               // NYI
     break;
 
   case MYSQL_TYPE_TINY_BLOB:
@@ -93,7 +92,7 @@
   case MYSQL_TYPE_LONG_BLOB:
   case MYSQL_TYPE_BLOB:
   case MYSQL_TYPE_GEOMETRY:
-    length= ~0UL;                               // NYI
+    length= ~(uint32) 0;                               // NYI
     break;
   }
 
@@ -132,7 +131,8 @@
     slave_print_msg(ERROR_LEVEL, rli, ER_BINLOG_ROW_WRONG_TABLE_DEF,
                     "Table width mismatch - "
                     "received %u columns, %s.%s has %u columns",
-                    size(), tsh->db.str, tsh->table_name.str, tsh->fields);
+                    (uint) size(), tsh->db.str, tsh->table_name.str,
+                    tsh->fields);
   }
 
   for (uint col= 0 ; col < cols_to_check ; ++col)

--- 1.3/sql/rpl_utility.h	2007-03-06 19:52:44 +01:00
+++ 1.4/sql/rpl_utility.h	2007-03-06 19:52:44 +01:00
@@ -1,9 +1,8 @@
-/* Copyright 2006 MySQL AB. All rights reserved.
+/* Copyright (C) 2006 MySQL AB
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
+   the Free Software Foundation; version 2 of the License.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -12,7 +11,7 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
 
 #ifndef RPL_UTILITY_H
 #define RPL_UTILITY_H
@@ -23,104 +22,117 @@
 
 #include "mysql_priv.h"
 
+struct st_relay_log_info;
+typedef st_relay_log_info RELAY_LOG_INFO;
+
 uint32
-field_length_from_packed(enum_field_types const field_type, 
-                         byte const *const data);
+field_length_from_packed(enum_field_types field_type, byte const *data);
 
-/*
+/**
   A table definition from the master.
 
-  RESPONSIBILITIES
-
+  The responsibilities of this class is:
   - Extract and decode table definition data from the table map event
   - Check if table definition in table map is compatible with table
     definition on slave
 
-  DESCRIPTION
-
-    Currently, the only field type data available is an array of the
-    type operators that are present in the table map event.
+  Currently, the only field type data available is an array of the
+  type operators that are present in the table map event.
 
-  TODO
-
-    Add type operands to this structure to allow detection of
-    difference between, e.g., BIT(5) and BIT(10).
+  @todo Add type operands to this structure to allow detection of
+     difference between, e.g., BIT(5) and BIT(10).
  */
 
 class table_def
 {
 public:
-  /*
+  /**
     Convenience declaration of the type of the field type data in a
     table map event.
   */
   typedef unsigned char field_type;
 
-  /*
+  /**
     Constructor.
 
-    SYNOPSIS
-      table_def()
-      types Array of types
-      size  Number of elements in array 'types'
+    @param types Array of types
+    @param size  Number of elements in array 'types'
    */
   table_def(field_type *types, my_size_t size)
-    : m_type(types), m_size(size)
+    : m_type(new unsigned char [size]), m_size(size)
   {
+    if (m_type)
+      memcpy(m_type, types, size);
+    else
+      m_size= 0;
   }
 
-  /*
-    Return the number of fields there is type data for.
+  ~table_def() {
+    if (m_type)
+      delete [] m_type;
+#ifndef DBUG_OFF
+    m_type= 0;
+    m_size= 0;
+#endif
+  }
 
-    SYNOPSIS
-      size()
+  /**
+    Return the number of fields there is type data for.
 
-    RETURN VALUE
-      The number of fields that there is type data for.
+    @return The number of fields that there is type data for.
    */
   my_size_t size() const { return m_size; }
 
+
   /*
     Return a representation of the type data for one field.
 
-    SYNOPSIS
-      type()
-      i   Field index to return data for
+    @param index Field index to return data for
 
-    RETURN VALUE
-
-      Will return a representation of the type data for field
-      'i'. Currently, only the type identifier is returned.
+    @return Will return a representation of the type data for field
+    <code>index</code>. Currently, only the type identifier is
+    returned.
    */
-  field_type type(my_ptrdiff_t i) const { return m_type[i]; }
+  field_type type(my_ptrdiff_t index) const
+  {
+    DBUG_ASSERT(0 <= index);
+    DBUG_ASSERT(static_cast<my_size_t>(index) < m_size);
+    return m_type[index];
+  }
 
-  /*
+  /**
     Decide if the table definition is compatible with a table.
 
-    SYNOPSIS
-      compatible_with()
-      rli   Pointer to relay log info
-      table Pointer to table to compare with.
-
-    DESCRIPTION
-
-      Compare the definition with a table to see if it is compatible
-      with it.  A table definition is compatible with a table if:
+    Compare the definition with a table to see if it is compatible
+    with it.
 
+    A table definition is compatible with a table if:
       - the columns types of the table definition is a (not
         necessarily proper) prefix of the column type of the table, or
-
       - the other way around
 
-    RETURN VALUE
-      1  if the table definition is not compatible with 'table'
-      0  if the table definition is compatible with 'table'
+    @param rli   Pointer to relay log info
+    @param table Pointer to table to compare with.
+
+    @retval 1  if the table definition is not compatible with @c table
+    @retval 0  if the table definition is compatible with @c table
   */
   int compatible_with(RELAY_LOG_INFO const *rli, TABLE *table) const;
 
 private:
   my_size_t m_size;           // Number of elements in the types array
   field_type *m_type;                     // Array of type descriptors
+};
+
+/**
+   Extend the normal table list with a few new fields needed by the
+   slave thread, but nowhere else.
+ */
+struct RPL_TABLE_LIST
+  : public st_table_list
+{
+  bool m_tabledef_valid;
+  table_def m_tabledef;
 };
 
 #endif /* RPL_UTILITY_H */

--- 1.103/sql/slave.h	2007-03-06 19:52:44 +01:00
+++ 1.104/sql/slave.h	2007-03-06 19:52:44 +01:00
@@ -2,8 +2,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
+   the Free Software Foundation; version 2 of the License.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -112,8 +111,6 @@
 #define MYSQL_SLAVE_RUN_NOT_CONNECT 1
 #define MYSQL_SLAVE_RUN_CONNECT     2
 
-static Log_event* next_event(RELAY_LOG_INFO* rli);
-
 #define RPL_LOG_NAME (rli->group_master_log_name[0] ? rli->group_master_log_name :\
  "FIRST")
 #define IO_RPL_LOG_NAME (mi->master_log_name[0] ? mi->master_log_name :\
@@ -162,6 +159,7 @@
 
 bool show_master_info(THD* thd, MASTER_INFO* mi);
 bool show_binlog_info(THD* thd);
+bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id);
 
 const char *print_slave_db_safe(const char *db);
 int check_expected_error(THD* thd, RELAY_LOG_INFO const *rli, int error_code);
Thread
bk commit into 5.1 tree (mats:1.2472)Mats Kindahl6 Mar