List:Commits« Previous MessageNext Message »
From:Luis Soares Date:September 30 2011 12:56pm
Subject:bzr push into mysql-trunk-wl3584-labs branch (luis.soares:3460 to 3461)
WL#3584
View as plain text  
 3461 Luis Soares	2011-09-30 [merge]
      WL#3584
      
      Merge from mysql-trunk-wl3584.

    added:
      mysql-test/suite/rpl/t/rpl_group_mysqlbinlog.test
      sql/zstring_appender.cc
    modified:
      client/client_priv.h
      client/mysqlbinlog.cc
      libmysqld/CMakeLists.txt
      sql/CMakeLists.txt
      sql/binlog.cc
      sql/handler.cc
      sql/log_event.cc
      sql/log_event.h
      sql/rpl_info_file.cc
      sql/share/errmsg-utf8.txt
      sql/sql_parse.cc
      sql/zappender.cc
      sql/zatom_file.cc
      sql/zcompact.cc
      sql/zgroup.cc
      sql/zgroup_cache.cc
      sql/zgroup_execution.cc
      sql/zgroup_log.cc
      sql/zgroup_set.cc
      sql/zgroups.h
      sql/zmutex_cond_array.cc
      sql/zowned_groups.cc
      sql/zreader.cc
      sql/zreturn.cc
      sql/zrot_file.cc
      sql/zsid_map.cc
      sql/zsubgroup_coder.cc
      sql/zugid_specification.cc
      sql/zuuid.cc
      unittest/gunit/rpl_group_set-t.cc
 3460 Luis Soares	2011-09-29 [merge]
      merge.

    modified:
      sql/binlog.cc
      sql/zcompact.cc
      sql/zgroup_set.cc
      sql/zgroups.h
=== modified file 'client/client_priv.h'
--- a/client/client_priv.h	2011-08-19 13:24:24 +0000
+++ b/client/client_priv.h	2011-09-30 12:13:10 +0000
@@ -91,7 +91,12 @@ enum options_client
   OPT_BINLOG_ROWS_EVENT_MAX_SIZE,
   OPT_BINARY_MODE,
   OPT_SSL_CRL, OPT_SSL_CRLPATH,
-  OPT_MAX_CLIENT_OPTION
+  OPT_MAX_CLIENT_OPTION,
+  OPT_MYSQLBINLOG_SKIP_UGIDS,
+  OPT_MYSQLBINLOG_INCLUDE_UGIDS,
+  OPT_MYSQLBINLOG_EXCLUDE_UGIDS,
+  OPT_MYSQLBINLOG_FIRST_LGID,
+  OPT_MYSQLBINLOG_LAST_LGID
 };
 
 /**

=== modified file 'client/mysqlbinlog.cc'
--- a/client/mysqlbinlog.cc	2011-07-11 16:21:46 +0000
+++ b/client/mysqlbinlog.cc	2011-09-30 12:13:10 +0000
@@ -36,10 +36,20 @@
 #include "sql_priv.h"
 #include <signal.h>
 #include <my_dir.h>
+
+/*
+  error() is used in macro BINLOG_ERROR which is invoked in
+  zgroups.h, hence the early forward declaration.
+*/
+static void error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+
+#include "zgroups.h"
 #include "log_event.h"
 #include "log_event_old.h"
 #include "sql_common.h"
 #include <welcome_copyright_notice.h> // ORACLE_WELCOME_COPYRIGHT_NOTICE
+#include "sql_string.h"
 
 #define BIN_LOG_HEADER_SIZE	4
 #define PROBE_HEADER_LEN	(EVENT_LEN_OFFSET+4)
@@ -64,9 +74,6 @@ static const char* default_dbug_option =
 #endif
 static const char *load_default_groups[]= { "mysqlbinlog","client",0 };
 
-static void error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
-static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
-
 static my_bool one_database=0, disable_log_bin= 0;
 static my_bool opt_hexdump= 0;
 const char *base64_output_mode_names[]=
@@ -76,6 +83,7 @@ TYPELIB base64_output_mode_typelib=
     base64_output_mode_names, NULL };
 static enum_base64_output_mode opt_base64_output_mode= BASE64_OUTPUT_UNSPEC;
 static char *opt_base64_output_mode_str= 0;
+static my_bool opt_skip_ugids= 0;
 static char *database= 0;
 static char *output_file= 0;
 static my_bool force_opt= 0, short_form= 0, remote_opt= 0;
@@ -132,6 +140,22 @@ enum Exit_status {
   OK_STOP
 };
 
+
+#ifdef HAVE_UGID
+static Checkable_rwlock sid_lock;
+static Sid_map sid_map(&sid_lock);
+static Group_log group_log(&sid_map);
+static bool have_group_log= false;
+Group_log::Group_log_reader group_log_reader(&group_log, &sid_map);
+
+static Group_set include_groups(&sid_map), exclude_groups(&sid_map);
+static bool include_anonymous= true, exclude_anonymous= false;
+static char *opt_include_ugids_str, *opt_exclude_ugids_str;
+static char *opt_first_lgid_str= NULL, *opt_last_lgid_str= NULL;
+static rpl_lgid opt_first_lgid= -1, opt_last_lgid= -1;
+#endif
+
+
 static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info,
                                           const char* logname);
 static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
@@ -1183,6 +1207,27 @@ static struct my_option my_long_options[
    /* def_value 4GB */ UINT_MAX, /* min_value */ 256,
    /* max_value */ ULONG_MAX, /* sub_size */ 0,
    /* block_size */ 256, /* app_type */ 0},
+  {"skip-ugids", OPT_MYSQLBINLOG_SKIP_UGIDS,
+   "Do not print universal group identifier information "
+   "(SET UGID_NEXT=... etc).",
+   &opt_skip_ugids, &opt_skip_ugids, 0,
+   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+  {"include-ugids", OPT_MYSQLBINLOG_INCLUDE_UGIDS,
+   "Include only the given universal group identifiers.",
+   &opt_include_ugids_str, &opt_include_ugids_str, 0,
+   GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"exclude-ugids", OPT_MYSQLBINLOG_EXCLUDE_UGIDS,
+   "Print all but the given universal group identifiers.",
+   &opt_exclude_ugids_str, &opt_exclude_ugids_str, 0,
+   GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"first-lgid", OPT_MYSQLBINLOG_FIRST_LGID,
+   "Ignore groups before the given Local Group ID.",
+   &opt_first_lgid_str, &opt_first_lgid_str, 0,
+   GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"last-lgid", OPT_MYSQLBINLOG_LAST_LGID,
+   "Ignore groups after the given Local Group ID.",
+   &opt_last_lgid_str, &opt_last_lgid_str, 0,
+   GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
 };
 
@@ -1450,11 +1495,12 @@ static Exit_status safe_connect()
 */
 static Exit_status dump_log_entries(const char* logname)
 {
+  DBUG_ENTER("dump_log_entries");
+
   Exit_status rc;
   PRINT_EVENT_INFO print_event_info;
-
   if (!print_event_info.init_ok())
-    return ERROR_STOP;
+    DBUG_RETURN(ERROR_STOP);
   /*
      Set safe delimiter, to dump things
      like CREATE PROCEDURE safely
@@ -1466,6 +1512,8 @@ static Exit_status dump_log_entries(cons
   strmov(print_event_info.delimiter, "/*!*/;");
   
   print_event_info.verbose= short_form ? 0 : verbose;
+  print_event_info.skip_ugids= opt_skip_ugids;
+  print_event_info.sid_map= &sid_map;
 
   rc= (remote_opt ? dump_remote_log_entries(&print_event_info, logname) :
        dump_local_log_entries(&print_event_info, logname));
@@ -1479,13 +1527,16 @@ static Exit_status dump_log_entries(cons
             "to an event in the middle of a statement. The event(s) "
             "from the partial statement have not been written to output.");
 
+  if (print_event_info.last_subgroup_printed)
+    fprintf(result_file, "SET UGID_NEXT='AUTOMATIC'%s\n",
+            print_event_info.delimiter);
   /* Set delimiter back to semicolon */
   if (!raw_mode)
   {
     fprintf(result_file, "DELIMITER ;\n");
     strmov(print_event_info.delimiter, ";");
   }
-  return rc;
+  DBUG_RETURN(rc);
 }
 
 
@@ -1501,6 +1552,7 @@ static Exit_status dump_log_entries(cons
 */
 static Exit_status check_master_version()
 {
+  DBUG_ENTER("check_master_version");
   MYSQL_RES* res = 0;
   MYSQL_ROW row;
   const char* version;
@@ -1510,7 +1562,7 @@ static Exit_status check_master_version(
   {
     error("Could not find server version: "
           "Query failed when checking master version: %s", mysql_error(mysql));
-    return ERROR_STOP;
+    DBUG_RETURN(ERROR_STOP);
   }
   if (!(row = mysql_fetch_row(res)))
   {
@@ -1567,11 +1619,11 @@ static Exit_status check_master_version(
   }
 
   mysql_free_result(res);
-  return OK_CONTINUE;
+  DBUG_RETURN(OK_CONTINUE);
 
 err:
   mysql_free_result(res);
-  return ERROR_STOP;
+  DBUG_RETURN(ERROR_STOP);
 }
 
 
@@ -1861,6 +1913,7 @@ static Exit_status check_header(IO_CACHE
                                 PRINT_EVENT_INFO *print_event_info,
                                 const char* logname)
 {
+  DBUG_ENTER("check_header");
   uchar header[BIN_LOG_HEADER_SIZE];
   uchar buf[PROBE_HEADER_LEN];
   my_off_t tmp_pos, pos;
@@ -1869,7 +1922,7 @@ static Exit_status check_header(IO_CACHE
   if (!(glob_description_event= new Format_description_log_event(3)))
   {
     error("Failed creating Format_description_log_event; out of memory?");
-    return ERROR_STOP;
+    DBUG_RETURN(ERROR_STOP);
   }
 
   pos= my_b_tell(file);
@@ -1877,12 +1930,12 @@ static Exit_status check_header(IO_CACHE
   if (my_b_read(file, header, sizeof(header)))
   {
     error("Failed reading header; probably an empty file.");
-    return ERROR_STOP;
+    DBUG_RETURN(ERROR_STOP);
   }
   if (memcmp(header, BINLOG_MAGIC, sizeof(header)))
   {
     error("File is not a binary log file.");
-    return ERROR_STOP;
+    DBUG_RETURN(ERROR_STOP);
   }
 
   /*
@@ -1907,7 +1960,7 @@ static Exit_status check_header(IO_CACHE
       {
         error("Could not read entry at offset %llu: "
               "Error in log format or read error.", (ulonglong)tmp_pos);
-        return ERROR_STOP;
+        DBUG_RETURN(ERROR_STOP);
       }
       /*
         Otherwise this is just EOF : this log currently contains 0-2
@@ -1942,7 +1995,7 @@ static Exit_status check_header(IO_CACHE
           {
             error("Failed creating Format_description_log_event; "
                   "out of memory?");
-            return ERROR_STOP;
+            DBUG_RETURN(ERROR_STOP);
           }
         }
         break;
@@ -1962,7 +2015,7 @@ static Exit_status check_header(IO_CACHE
           error("Could not read a Format_description_log_event event at "
                 "offset %llu; this could be a log format error or read error.",
                 (ulonglong)tmp_pos);
-          return ERROR_STOP;
+          DBUG_RETURN(ERROR_STOP);
         }
         if (opt_base64_output_mode == BASE64_OUTPUT_AUTO)
         {
@@ -1975,7 +2028,7 @@ static Exit_status check_header(IO_CACHE
                                             new_description_event, tmp_pos,
                                             logname);
           if (retval != OK_CONTINUE)
-            return retval;
+            DBUG_RETURN(retval);
         }
         else
         {
@@ -1995,7 +2048,7 @@ static Exit_status check_header(IO_CACHE
           error("Could not read a Rotate_log_event event at offset %llu;"
                 " this could be a log format error or read error.",
                 (ulonglong)tmp_pos);
-          return ERROR_STOP;
+          DBUG_RETURN(ERROR_STOP);
         }
         delete ev;
       }
@@ -2004,7 +2057,83 @@ static Exit_status check_header(IO_CACHE
     }
   }
   my_b_seek(file, pos);
-  return OK_CONTINUE;
+  DBUG_RETURN(OK_CONTINUE);
+}
+
+
+static Exit_status open_group_log(const char *binlog_filename)
+{
+  DBUG_ENTER("open_group_log");
+  // avoid assertion in sid_map
+  sid_lock.rdlock();
+
+  // open Sid_map
+  char sid_map_file[FN_REFLEN];
+  fn_format(sid_map_file, binlog_filename, "", "-sids", MY_REPLACE_EXT);
+  if (sid_map.open(sid_map_file, false) != RETURN_STATUS_OK)
+    DBUG_RETURN(OK_CONTINUE);
+
+  // open Group_log
+  char group_log_file[FN_REFLEN];
+  fn_format(group_log_file, binlog_filename, "", "-grp", MY_REPLACE_EXT);
+  if (group_log.open(group_log_file, false) != RETURN_STATUS_OK)
+    DBUG_RETURN(OK_CONTINUE);
+
+  // add include-ugids
+  if (opt_include_ugids_str != NULL)
+  {
+    if (include_groups.add(opt_include_ugids_str, &include_anonymous) !=
+        RETURN_STATUS_OK)
+    {
+      sid_lock.unlock();
+      error("Error adding include-groups '%s'.", opt_include_ugids_str);
+      DBUG_RETURN(ERROR_STOP);
+    }
+  }
+
+  // add include-ugids
+  if (opt_exclude_ugids_str != NULL)
+  {
+    if (exclude_groups.add(opt_exclude_ugids_str, &exclude_anonymous) !=
+        RETURN_STATUS_OK)
+    {
+      sid_lock.unlock();
+      error("Error adding exclude-groups '%s'.", opt_exclude_ugids_str);
+      DBUG_RETURN(ERROR_STOP);
+    }
+  }
+
+  // seek in group log
+  if (opt_include_ugids_str != NULL)
+    group_log_reader.seek(&include_groups, false, include_anonymous,
+                          opt_first_lgid, opt_last_lgid,
+                          0, start_position_mot);
+  else if (opt_exclude_ugids_str != NULL)
+    group_log_reader.seek(&exclude_groups, true, !exclude_anonymous,
+                          opt_first_lgid, opt_last_lgid,
+                          0, start_position_mot);
+  else
+    group_log_reader.seek(NULL, false, true,
+                          opt_first_lgid, opt_last_lgid,
+                          0, start_position_mot);
+
+  have_group_log= true;
+  DBUG_RETURN(OK_CONTINUE);
+}
+
+
+static void close_group_log()
+{
+  include_groups.clear();
+  exclude_groups.clear();
+  if (sid_map.is_open())
+  {
+    sid_map.close();
+    sid_map.clear();
+  }
+  if (group_log.is_open())
+    group_log.close();
+  sid_lock.unlock();
 }
 
 
@@ -2024,22 +2153,45 @@ static Exit_status check_header(IO_CACHE
 static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info,
                                           const char* logname)
 {
+  DBUG_ENTER("dump_local_log_entries");
   File fd = -1;
   IO_CACHE cache,*file= &cache;
   uchar tmp_buff[BIN_LOG_HEADER_SIZE];
   Exit_status retval= OK_CONTINUE;
+  bool opened_iocache= false;
+  Subgroup subgroup;
+  Subgroup *subgroup_p= NULL;
 
   if (logname && strcmp(logname, "-") != 0)
   {
     /* read from normal file */
+    if ((retval= open_group_log(logname)) != OK_CONTINUE)
+      goto end;
+    if (have_group_log)
+    {
+      /*
+        If using the group log, the first subgroup may appear after
+        --start-position (e.g. if start-position is before the initial
+        FD event, which is not part of any subgroup; or if
+        --include-ugids or --exclude-ugids causes the first subgroup
+        after start-position to be skipped).  So we change
+        start_position to the position of the first subgroup.
+      */
+      Subgroup *first_subgroup;
+      if (group_log_reader.peek_subgroup(&first_subgroup) != READ_OK)
+        goto end;
+      start_position= first_subgroup->binlog_pos;
+    }
+
     if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0)
-      return ERROR_STOP;
+      goto err;
     if (init_io_cache(file, fd, 0, READ_CACHE, start_position_mot, 0,
 		      MYF(MY_WME | MY_NABP)))
     {
       my_close(fd, MYF(MY_WME));
-      return ERROR_STOP;
+      goto err;
     }
+    opened_iocache= true;
     if ((retval= check_header(file, print_event_info, logname)) != OK_CONTINUE)
       goto end;
   }
@@ -2058,15 +2210,16 @@ static Exit_status dump_local_log_entrie
     if (_setmode(fileno(stdin), O_BINARY) == -1)
     {
       error("Could not set binary mode on stdin.");
-      return ERROR_STOP;
+      goto err;
     }
 #endif 
     if (init_io_cache(file, my_fileno(stdin), 0, READ_CACHE, (my_off_t) 0,
 		      0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE)))
     {
       error("Failed to init IO cache.");
-      return ERROR_STOP;
+      goto err;
     }
+    opened_iocache= true;
     if ((retval= check_header(file, print_event_info, logname)) != OK_CONTINUE)
       goto end;
     if (start_position)
@@ -2097,11 +2250,26 @@ static Exit_status dump_local_log_entrie
     error("Failed reading from file.");
     goto err;
   }
+
   for (;;)
   {
     char llbuff[21];
     my_off_t old_off = my_b_tell(file);
 
+    if (have_group_log && old_off >= start_position_mot &&
+        (subgroup_p == NULL ||
+         old_off >= (my_off_t)(subgroup_p->binlog_pos +
+                               subgroup_p->binlog_length)))
+    {
+      switch (group_log_reader.read_subgroup(&subgroup))
+      {
+      case READ_OK: break;
+      case READ_ERROR: case READ_TRUNCATED: goto err;
+      case READ_EOF: goto end;
+      }
+      subgroup_p= &subgroup;
+      my_b_seek(file, subgroup.binlog_pos);
+    }
     Log_event* ev = Log_event::read_log_event(file, glob_description_event,
                                               opt_verify_binlog_checksum);
     if (!ev)
@@ -2122,6 +2290,8 @@ static Exit_status dump_local_log_entrie
       // file->error == 0 means EOF, that's OK, we break in this case
       goto end;
     }
+    ev->subgroup= subgroup_p;
+    ev->event_end_position= my_b_tell(file);
     if ((retval= process_event(print_event_info, ev, old_off, logname)) !=
         OK_CONTINUE)
       goto end;
@@ -2135,8 +2305,10 @@ err:
 end:
   if (fd >= 0)
     my_close(fd, MYF(MY_WME));
-  end_io_cache(file);
-  return retval;
+  if (opened_iocache)
+    end_io_cache(file);
+  close_group_log();
+  DBUG_RETURN(retval);
 }
 
 /* Post processing of arguments to check for conflicts and other setups */
@@ -2170,6 +2342,41 @@ static int args_post_process(void)
     }
   }
 
+  if (opt_exclude_ugids_str != NULL && opt_include_ugids_str != NULL)
+  {
+    error("--exclude-ugids and --include-ugids cannot be combined.");
+    DBUG_RETURN(ERROR_STOP);
+  }
+
+  if (opt_first_lgid_str != NULL)
+  {
+    char *endp;
+    opt_first_lgid= strtoll(opt_first_lgid_str, &endp, 0);
+    if (*endp != 0 || opt_first_lgid <= 0 || opt_first_lgid == LLONG_MAX)
+    {
+      error("--first-lgid must be a positive integer.");
+      DBUG_RETURN(ERROR_STOP);
+    }
+  }
+
+  if (opt_last_lgid_str != NULL)
+  {
+    char *endp;
+    opt_last_lgid= strtoll(opt_last_lgid_str, &endp, 0);
+    if (*endp != 0 || opt_last_lgid <= 0 || opt_last_lgid == LLONG_MAX)
+    {
+      error("--last-lgid must be a positive integer.");
+      DBUG_RETURN(ERROR_STOP);
+    }
+  }
+
+  if (opt_first_lgid != -1 && opt_last_lgid != -1 &&
+      opt_first_lgid > opt_last_lgid)
+  {
+    error("--first-lgid must be smaller than or equal to --last-lgid.");
+    DBUG_RETURN(ERROR_STOP);
+  }
+
   DBUG_RETURN(OK_CONTINUE);
 }
 
@@ -2308,3 +2515,15 @@ int main(int argc, char** argv)
 #include "log_event.cc"
 #include "log_event_old.cc"
 #include "rpl_utility.cc"
+
+#include "zsid_map.cc"
+#include "zgroup_log.cc"
+#include "zreader.cc"
+#include "zappender.cc"
+#include "zrot_file.cc"
+#include "zreturn.cc"
+#include "zcompact.cc"
+#include "zsubgroup_coder.cc"
+#include "zuuid.cc"
+#include "zgroup_set.cc"
+#include "zgroup.cc"

=== modified file 'libmysqld/CMakeLists.txt'
--- a/libmysqld/CMakeLists.txt	2011-09-24 18:28:07 +0000
+++ b/libmysqld/CMakeLists.txt	2011-09-30 12:13:10 +0000
@@ -70,6 +70,7 @@ SET(SQL_EMBEDDED_SOURCES
   ../sql/zcompact.cc
   ../sql/zatom_file.cc
   ../sql/zrot_file.cc
+  ../sql/zstring_appender.cc
   ../sql/zuuid.cc
   ../sql/zgroup.cc
   ../sql/zreturn.cc

=== added file 'mysql-test/suite/rpl/t/rpl_group_mysqlbinlog.test'
--- a/mysql-test/suite/rpl/t/rpl_group_mysqlbinlog.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_group_mysqlbinlog.test	2011-09-30 12:13:10 +0000
@@ -0,0 +1,39 @@
+--source include/have_binlog_format_row.inc
+
+--let $rpl_topology= 1,2
+--source include/rpl_init.inc
+
+--let $server_uuid= `SELECT @@GLOBAL.SERVER_UUID`
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (2);
+INSERT INTO t1 VALUES (3);
+INSERT INTO t1 VALUES (4);
+CREATE TABLE t5 SELECT * FROM t1;
+SET UGID_NEXT = 'ANONYMOUS';
+INSERT INTO t1 VALUES (0);
+INSERT INTO t1 VALUES (6);
+INSERT INTO t1 VALUES (7);
+
+--let $datadir= `SELECT @@datadir`
+--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+--echo ================ FULL FILE ================
+--exec $MYSQL_BINLOG $datadir/$binlog_file | grep '^# Subgroup'
+--exec $MYSQL_BINLOG $datadir/$binlog_file
+
+--echo ================ 2,4-5 ================
+--exec $MYSQL_BINLOG --include-ugid=$server_uuid:2:4-5 $datadir/$binlog_file | grep '^# Subgroup'
+--exec $MYSQL_BINLOG --include-ugid=$server_uuid:2:4-5 $datadir/$binlog_file
+
+--echo ================ 1,3,ANON ================
+--exec $MYSQL_BINLOG --exclude-ugid=$server_uuid:2:4-5 $datadir/$binlog_file | grep '^# Subgroup'
+--exec $MYSQL_BINLOG --exclude-ugid=$server_uuid:2:4-5 $datadir/$binlog_file
+
+--echo ================ 1,3,ANON ================
+--exec $MYSQL_BINLOG --include-ugid=$server_uuid:1,ANONYMOUS,$server_uuid:3 $datadir/$binlog_file | grep '^# Subgroup'
+--exec $MYSQL_BINLOG --include-ugid=$server_uuid:1,ANONYMOUS,$server_uuid:3 $datadir/$binlog_file
+
+--echo ================ 2-5 ================
+--exec $MYSQL_BINLOG --first-lgid=2 --last-lgid=5 $datadir/$binlog_file | grep '^# Subgroup'
+--exec $MYSQL_BINLOG --first-lgid=2 --last-lgid=5 $datadir/$binlog_file
+
+DROP TABLE t1, t5;

=== modified file 'sql/CMakeLists.txt'
--- a/sql/CMakeLists.txt	2011-09-25 16:40:06 +0000
+++ b/sql/CMakeLists.txt	2011-09-30 12:13:10 +0000
@@ -190,7 +190,7 @@ TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATI
   ${SSL_LIBRARIES})
 
 SET (BINLOG_SOURCE zreader.cc zappender.cc zatom_file.cc zrot_file.cc
-                   zsubgroup_coder.cc
+                   zsubgroup_coder.cc zstring_appender.cc
                    zuuid.cc zgroup.cc zowner_id.cc
                    zreturn.cc
                    zcompact.cc zgroup_log.cc

=== modified file 'sql/binlog.cc'
--- a/sql/binlog.cc	2011-09-29 10:01:11 +0000
+++ b/sql/binlog.cc	2011-09-30 12:20:42 +0000
@@ -533,7 +533,7 @@ static int binlog_close_connection(handl
   DBUG_ASSERT(cache_mngr->trx_cache.is_binlog_empty() &&
               cache_mngr->stmt_cache.is_binlog_empty());
 #ifdef HAVE_UGID
-  DBUG_ASSERT(cache_mngr->trx_cache.is_group_cache_empty() && 
+  DBUG_ASSERT(cache_mngr->trx_cache.is_group_cache_empty() &&
               cache_mngr->stmt_cache.is_group_cache_empty());
 #endif  
   thd_set_ha_data(thd, binlog_hton, NULL);
@@ -546,6 +546,8 @@ static int binlog_close_connection(handl
 #ifdef HAVE_UGID
 static int binlog_flush_group_cache(THD *thd, binlog_cache_mngr *cache_mngr,
                                     binlog_cache_data *cache_data,
+                                    rpl_binlog_no binlog_no,
+                                    rpl_binlog_pos binlog_pos,
                                     rpl_binlog_pos offset_after_last_statement)
 {
   DBUG_ENTER("binlog_flush_group_cache");
@@ -556,6 +558,7 @@ static int binlog_flush_group_cache(THD
                                     &mysql_bin_log.group_log,
                                     &cache_data->group_cache,
                                     &cache_mngr->trx_cache.group_cache,
+                                    binlog_no, binlog_pos,
                                     offset_after_last_statement);
     DBUG_RETURN(ret);
   }
@@ -633,6 +636,7 @@ binlog_flush_cache(THD *thd, binlog_cach
   {
 #ifdef HAVE_UGID
     if (binlog_flush_group_cache(thd, cache_mngr, cache_data,
+                                 0/*binlog_no*/, 0/*binlog_pos*/,
                                  -1/*offset_after_last_statement*/))
       DBUG_RETURN(1);
 #endif
@@ -813,6 +817,7 @@ static int binlog_commit(handlerton *hto
 #ifdef HAVE_UGID
   else
     error= binlog_flush_group_cache(thd, cache_mngr, &cache_mngr->stmt_cache,
+                                    0/*binlog_no*/, 0/*binlog_pos*/,
                                     -1/*offset_after_last_statement*/);
 #endif
 
@@ -886,6 +891,7 @@ static int binlog_rollback(handlerton *h
 #ifdef HAVE_UGID
     else
       error= binlog_flush_group_cache(thd, cache_mngr, &cache_mngr->stmt_cache,
+                                      0/*binlog_no*/, 0/*binlog_pos*/,
                                       -1/*offset_after_last_statement*/);
 #endif
   }
@@ -1707,7 +1713,7 @@ int MYSQL_BIN_LOG::init_sid_map()
 {
   DBUG_ENTER("MYSQL_BIN_LOG::init_sid_map()");
   rpl_sid server_uuid_sid;
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   DBUG_ASSERT(server_uuid_sid.parse(server_uuid) == 0);
 #else
   server_uuid_sid.parse(server_uuid);
@@ -4339,6 +4345,8 @@ bool MYSQL_BIN_LOG::write_cache(THD *thd
 
 #ifdef HAVE_UGID
   if (binlog_flush_group_cache(thd, cache_mngr, cache_data,
+                               0/*binlog_no*/,
+                               my_b_tell(&log_file)/*binlog_pos*/,
                                offset_after_last_statement))
     goto err;
 #endif

=== modified file 'sql/handler.cc'
--- a/sql/handler.cc	2011-09-09 10:25:49 +0000
+++ b/sql/handler.cc	2011-09-30 12:13:10 +0000
@@ -1194,8 +1194,6 @@ int ha_commit_trans(THD *thd, bool all)
     DBUG_RETURN(2);
   }
 
-  DBUG_PRINT("sven", ("ha_info=%p is_real_trans=%d all=%d thd->transaction.all.ha_list=%p",
-                      ha_info, is_real_trans, all, thd->transaction.all.ha_list));
   if (ha_info)
   {
     uint rw_ha_count;
@@ -1212,8 +1210,6 @@ int ha_commit_trans(THD *thd, bool all)
     /* rw_trans is TRUE when we in a transaction changing data */
     rw_trans= is_real_trans && (rw_ha_count > 0);
 
-    DBUG_PRINT("sven", ("rw_ha_count=%d rw_trans=%d",
-                        rw_ha_count, rw_trans));
     if (rw_trans)
     {
       /*

=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc	2011-08-22 06:14:59 +0000
+++ b/sql/log_event.cc	2011-09-30 12:13:10 +0000
@@ -661,7 +661,8 @@ Log_event::Log_event(THD* thd_arg, uint1
   :log_pos(0), temp_buf(0), exec_time(0), flags(flags_arg),
   event_cache_type(cache_type_arg),
   event_logging_type(logging_type_arg),
-  crc(0), thd(thd_arg), checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
+  crc(0), thd(thd_arg),
+  checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
 {
   server_id=	thd->server_id;
   when=		thd->start_time;
@@ -700,7 +701,11 @@ Log_event::Log_event(const char* buf,
   :temp_buf(0), exec_time(0),
   event_cache_type(EVENT_INVALID_CACHE),
   event_logging_type(EVENT_INVALID_LOGGING),
-  crc(0), checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
+  crc(0),
+#if defined(HAVE_UGID) && defined(MYSQL_CLIENT)
+  subgroup(NULL),
+#endif
+  checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
 {
 #ifndef MYSQL_CLIENT
   thd = 0;
@@ -1576,6 +1581,79 @@ Log_event* Log_event::read_log_event(con
 
 #ifdef MYSQL_CLIENT
 
+
+void Log_event::print_subgroup_info(IO_CACHE *out, PRINT_EVENT_INFO *pei)
+{
+  if (pei->skip_ugids)
+    return;
+
+  if (subgroup == NULL)
+  {
+    if (!pei->last_subgroup_printed ||
+        pei->last_subgroup.type != ANONYMOUS_SUBGROUP)
+      my_b_printf(out, "SET UGID_NEXT='ANONYMOUS'%s\n", pei->delimiter);
+    return;
+  }
+
+  bool force= !pei->last_subgroup_printed;
+  bool printed_header= false;
+  Subgroup *prev= &pei->last_subgroup;
+
+  char header[2 + Subgroup::MAX_TEXT_LENGTH + 5 + 1]= "# ";
+  strcpy(header + 2 + subgroup->to_string(header + 2, pei->sid_map), "\nSET ");
+
+#define PRINT_HEADER                                            \
+  (my_b_printf(out, "%s", printed_header ? ", " : header),      \
+   printed_header= true)
+  if (force || subgroup->type != prev->type ||
+      (subgroup->type != ANONYMOUS_SUBGROUP &&
+       (subgroup->sidno != prev->sidno || subgroup->gno != prev->gno)))
+  {
+    PRINT_HEADER;
+    my_b_printf(out, "UGID_NEXT='");
+    switch (subgroup->type)
+    {
+    case ANONYMOUS_SUBGROUP:
+      my_b_printf(out, "ANONYMOUS");
+      break;
+    case DUMMY_SUBGROUP:
+      my_b_printf(out, "AUTOMATIC");
+      break;
+    case NORMAL_SUBGROUP:
+      Group g= { subgroup->sidno, subgroup->gno };
+      char buf[Group::MAX_TEXT_LENGTH + 1];
+      g.to_string(&sid_map, buf);
+      my_b_printf(out, "%s", buf);
+      break;
+    }
+    my_b_printf(out, "'");
+    prev->type= subgroup->type;
+    prev->sidno= subgroup->sidno;
+    prev->gno= subgroup->gno;
+  }
+  bool group_commit=
+    (subgroup->group_commit &&
+     event_end_position >= (subgroup->binlog_pos + subgroup->binlog_length -
+                            subgroup->binlog_offset_after_last_statement));
+  if (force || subgroup->group_end != prev->group_end)
+  {
+    PRINT_HEADER;
+    my_b_printf(out, "UGID_END=%d", subgroup->group_end ? 1 : 0);
+    prev->group_end= subgroup->group_end;
+  }
+  if (force || group_commit != prev->group_commit)
+  {
+    PRINT_HEADER;
+    my_b_printf(out, "UGID_COMMIT=%d", group_commit ? 1 : 0);
+    prev->group_commit= group_commit;
+  }
+  if (printed_header)
+    my_b_printf(out, "%s\n", pei->delimiter);
+
+  pei->last_subgroup_printed= true;
+}
+
+
 /*
   Log_event::print_header()
 */
@@ -1588,6 +1666,8 @@ void Log_event::print_header(IO_CACHE* f
   my_off_t hexdump_from= print_event_info->hexdump_from;
   DBUG_ENTER("Log_event::print_header");
 
+  print_subgroup_info(file, print_event_info);
+
   my_b_printf(file, "#");
   print_timestamp(file);
   my_b_printf(file, " server id %lu  end_log_pos %s ", (ulong) server_id,
@@ -11525,7 +11605,8 @@ st_print_event_info::st_print_event_info
    charset_database_number(ILLEGAL_CHARSET_INFO_NUMBER),
    thread_id(0), thread_id_printed(false),
    base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE),
-   have_unflushed_events(FALSE)
+   have_unflushed_events(FALSE),
+   last_subgroup_printed(false), skip_ugids(false), sid_map(NULL)
 {
   /*
     Currently we only use static PRINT_EVENT_INFO objects, so zeroed at

=== modified file 'sql/log_event.h'
--- a/sql/log_event.h	2011-08-19 13:04:28 +0000
+++ b/sql/log_event.h	2011-09-30 12:13:10 +0000
@@ -787,6 +787,13 @@ typedef struct st_print_event_info
   IO_CACHE body_cache;
   /* Indicate if the body cache has unflushed events */
   bool have_unflushed_events;
+
+#ifdef HAVE_UGID
+  Subgroup last_subgroup;
+  bool last_subgroup_printed;
+  bool skip_ugids;
+  Sid_map *sid_map;
+#endif
 } PRINT_EVENT_INFO;
 #endif
 
@@ -1153,23 +1160,38 @@ public:
   {
     return thd ? thd->db : 0;
   }
-#else
+#else // ifdef MYSQL_SERVER
   Log_event(enum_event_cache_type cache_type_arg= EVENT_INVALID_CACHE,
             enum_event_logging_type logging_type_arg= EVENT_INVALID_LOGGING)
   : temp_buf(0), event_cache_type(cache_type_arg),
-  event_logging_type(logging_type_arg) { }
+    event_logging_type(logging_type_arg), subgroup(NULL) { }
     /* avoid having to link mysqlbinlog against libpthread */
   static Log_event* read_log_event(IO_CACHE* file,
                                    const Format_description_log_event
                                    *description_event, my_bool crc_check);
   /* print*() functions are used by mysqlbinlog */
   virtual void print(FILE* file, PRINT_EVENT_INFO* print_event_info) = 0;
+protected:
   void print_timestamp(IO_CACHE* file, time_t *ts = 0);
   void print_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
                     bool is_more);
   void print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
                     bool is_more);
-#endif
+private:
+  /// Print 'SET UGID_*' statements.
+  void print_subgroup_info(IO_CACHE *out, PRINT_EVENT_INFO *print_event_info);
+public:
+  /**
+    The end of the event in *this* binary log.  This is not written to
+    the binary log and it is not subject to any transformations in
+    relay logs etc.  It is only used by mysqlbinlog.
+  */
+  rpl_binlog_pos event_end_position;
+  /**
+    Group information for the subgroup that this event is part of.
+  */
+  Subgroup *subgroup;
+#endif // ifdef MYSQL_SERVER ... else
   /* 
      The value is set by caller of FD constructor and
      Log_event::write_header() for the rest.

=== modified file 'sql/rpl_info_file.cc'
--- a/sql/rpl_info_file.cc	2011-08-19 13:04:28 +0000
+++ b/sql/rpl_info_file.cc	2011-09-30 12:13:10 +0000
@@ -130,7 +130,7 @@ int Rpl_info_file::do_prepare_info_for_w
 int Rpl_info_file::do_check_info(const ulong *uidx __attribute__((unused)),
                                  const uint nidx __attribute__((unused)))
 {
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   /*
     This function checks if the file exists and in other modules
     further actions are taken based on this. If the file exists

=== modified file 'sql/share/errmsg-utf8.txt'
--- a/sql/share/errmsg-utf8.txt	2011-09-25 16:40:06 +0000
+++ b/sql/share/errmsg-utf8.txt	2011-09-30 12:13:10 +0000
@@ -6534,13 +6534,13 @@ ER_PLUGIN_NO_INSTALL
   eng "Plugin '%s' is marked as not dynamically installable. You have to stop the server to install it."
 
 ER_VARIABLE_NOT_SETTABLE_IN_SF_OR_TRIGGER
-  eng "The system variable %s can not be set in stored functions or triggers."
+  eng "The system variable %.200s can not be set in stored functions or triggers."
 
 ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION
-  eng "The system variable %s can not be set when there is an ongoing transaction."
+  eng "The system variable %.200s can not be set when there is an ongoing transaction."
 
 ER_UGID_NEXT_IS_NOT_IN_UGID_NEXT_LIST
-  eng "The system variable @@SESSION.UGID_NEXT has value %s, which is not listed in @@SESSION.UGID_NEXT_LIST."
+  eng "The system variable @@SESSION.UGID_NEXT has value %.200s, which is not listed in @@SESSION.UGID_NEXT_LIST."
 
 ER_CANT_CHANGE_UGID_NEXT_IN_SUPER_GROUP_WHEN_UGID_NEXT_LIST_IS_NULL
   eng "When @@SESSION.UGID_NEXT_LIST == NULL and there is an ongoing transaction, the system variable @@SESSION.UGID_NEXT can not change until after @@SESSION.UGID_COMMIT is set to 1 and a statement executes."
@@ -6549,10 +6549,10 @@ ER_CANT_CHANGE_UGID_NEXT_LIST_IN_SUPER_G
   eng "When there is an ongoing transaction, the system variable @@SESSION.UGID_NEXT_LIST can not change until after @@SESSION.UGID_COMMIT is set to 1 and a statement executes."
 
 ER_SET_STATEMENT_CANNOT_INVOKE_FUNCTION
-  eng "The statement 'SET %s' cannot invoke a stored function."
+  eng "The statement 'SET %.200s' cannot invoke a stored function."
 
 ER_UGID_NEXT_IS_ENDED_IN_GROUP_CACHE
-  eng "The system variable @@SESSION.UGID_NEXT has value %s, which has already been ended in this client."
+  eng "The system variable @@SESSION.UGID_NEXT has value %.200s, which has already been ended in this client."
 
 ER_UGID_END_IS_ON_BUT_UGID_NEXT_IS_AUTO_OR_ANON
   eng "The system variable @@SESSION.UGID_END cannot be 1 when @@SESSION.UGID_NEXT is 'AUTOMATIC' or 'ANONYMOUS'."
@@ -6561,7 +6561,7 @@ ER_UGID_NEXT_CANT_BE_AUTOMATIC_IF_UGID_N
   eng "The system variable @@SESSION.UGID_NEXT cannot be 'AUTOMATIC' when @@SESSION.UGID_NEXT_LIST is non-NULL."
 
 ER_SKIPPING_LOGGED_GROUP
-  eng "Skipping group %s because it has already been executed and logged."
+  eng "Skipping group %.200s because it has already been executed and logged."
 
 ER_UGID_END_REQUIRES_UGID_COMMIT_WHEN_UGID_NEXT_LIST_IS_NULL
   eng "When UGID_NEXT_LIST = NULL and UGID_NEXT has the form UUID:NUMBER, UGID_END cannot be set if UGID_COMMIT is not set."
@@ -6573,10 +6573,10 @@ ER_MALFORMED_GROUP_SPECIFICATION
   eng "Malformed group specification '%.200s'."
 
 ER_FILE_FORMAT
-  eng "File '%250s' has an unknown format, it may be corrupt."
+  eng "File '%.200s' has an unknown format at position %lld, it may be corrupt."
 
 ER_FILE_TRUNCATED
-  eng "File '%250s' was truncated in the middle.
+  eng "File '%.200s' was truncated in the middle."
 
 ER_GNO_EXHAUSTED
   eng "Impossible to generate automatic GNO: the space of possible values is exhausted.  Restart the server with a new SID."

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2011-09-25 16:40:06 +0000
+++ b/sql/sql_parse.cc	2011-09-30 12:13:10 +0000
@@ -2299,7 +2299,6 @@ mysql_execute_command(THD *thd)
     */
     DBUG_ASSERT(! thd->in_sub_stmt);
     /* Commit or rollback the statement transaction. */
-    DBUG_PRINT("sven", ("before trans_commit_stmt 1"));
     thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
     /* Commit the normal transaction if one is active. */
     if (trans_commit_implicit(thd))

=== modified file 'sql/zappender.cc'
--- a/sql/zappender.cc	2011-09-24 18:28:07 +0000
+++ b/sql/zappender.cc	2011-09-30 12:13:10 +0000
@@ -32,7 +32,8 @@ enum_return_status Appender::truncate(my
   {
     if (new_position > old_position)
     {
-      my_error(EE_CANT_SEEK, MYF(0), get_source_name(), 0);
+      BINLOG_ERROR(("Can't seek in %.200s", get_source_name()),
+                   (EE_CANT_SEEK, MYF(0), get_source_name(), 0));
       RETURN_REPORTED_ERROR;
     }
     RETURN_OK;

=== modified file 'sql/zatom_file.cc'
--- a/sql/zatom_file.cc	2011-09-24 18:28:07 +0000
+++ b/sql/zatom_file.cc	2011-09-30 12:13:10 +0000
@@ -20,7 +20,8 @@
 #ifdef HAVE_UGID
 
 
-#include "mysqld_error.h"
+#include <mysqld_error.h>
+#include <my_dir.h>
 
 
 const char *Atom_file::OVERWRITE_FILE_SUFFIX= ".overwrite";
@@ -94,7 +95,10 @@ enum_return_status Atom_file::recover()
   if (b != 1 || stat.st_size < 9)
   {
     // file has invalid value or header is incomplete
-    my_error(ER_FILE_FORMAT, MYF(0), overwrite_filename);
+    BINLOG_ERROR(("File '%.200s' has an unknown format at position %lld, "
+                  "it may be corrupt.",
+                  overwrite_filename, 0),
+                 (ER_FILE_FORMAT, MYF(0), overwrite_filename, 0));
     goto error_close;
   }
 

=== modified file 'sql/zcompact.cc'
--- a/sql/zcompact.cc	2011-09-29 10:01:11 +0000
+++ b/sql/zcompact.cc	2011-09-30 12:20:42 +0000
@@ -116,7 +116,12 @@ Compact_coder::read_unsigned(Reader *rea
     }
   }
 file_format_error:
-  my_error(ER_FILE_FORMAT, MYF(0), reader->get_source_name());
+  my_off_t ofs;
+  reader->tell(&ofs);
+  BINLOG_ERROR(("File '%.250s' has an unknown format at position %lld, "
+                "it may be corrupt.",
+                reader->get_source_name(), ofs),
+               (ER_FILE_FORMAT, MYF(0), reader->get_source_name(), ofs));
   DBUG_RETURN(READ_ERROR);
 }
 
@@ -160,7 +165,12 @@ Compact_coder::read_unsigned(Reader *rea
     }
   }
 file_format_error:
-  my_error(ER_FILE_FORMAT, MYF(0), reader->get_source_name());
+  my_off_t ofs;
+  reader->tell(&ofs);
+  BINLOG_ERROR(("File '%.250s' has an unknown format at position %lld, "
+                "it may be corrupt.",
+                reader->get_source_name(), ofs),
+               (ER_FILE_FORMAT, MYF(0), reader->get_source_name(), ofs));
   DBUG_RETURN(READ_ERROR);
 }
 
@@ -184,27 +194,38 @@ enum_read_status Compact_coder::read_sig
 enum_read_status
 Compact_coder::read_type_code(Reader *reader,
                               int min_fatal, int min_ignorable,
-                              uchar *out)
+                              uchar *out, int code)
 {
   DBUG_ENTER("Compact_coder::read_type_code");
   DBUG_ASSERT((min_fatal & 1) == 0);
   DBUG_ASSERT((min_ignorable & 1) == 1);
 
-  PROPAGATE_READ_STATUS(reader->read(out, 1));
+  if (code != -1)
+    *out= code;
+  else
+  {
+    PROPAGATE_READ_STATUS(reader->read(out, 1));
+    code= *out;
+  }
 
-  if ((*out & 1) == 0)
+  if ((code & 1) == 0)
   {
     // even type code
-    if (*out < min_fatal)
+    if (code < min_fatal)
       DBUG_RETURN(READ_OK);
     // unknown even type code: fatal
-    my_error(ER_FILE_FORMAT, MYF(0), reader->get_source_name());
+    my_off_t ofs;
+    reader->tell(&ofs);
+    BINLOG_ERROR(("File '%.200s' has an unknown format at position %lld, "
+                  "it may be corrupt.",
+                  reader->get_source_name(), ofs),
+                 (ER_FILE_FORMAT, MYF(0), reader->get_source_name(), ofs));
     DBUG_RETURN(READ_ERROR);
   }
   else
   {
     // odd type code
-    if (*out < min_ignorable)
+    if (code < min_ignorable)
       DBUG_RETURN(READ_OK);
     // unknown odd type code: ignorable
     ulonglong skip_len;
@@ -220,9 +241,9 @@ enum_read_status Compact_coder::read_str
                                             bool null_terminated)
 {
   DBUG_ENTER("Compact_coder::read_string(Reader *, uchar *, size_t *, size_t, bool)");
-  ulonglong length_ulonglong;
   if (null_terminated)
     max_length--;
+  ulonglong length_ulonglong;
   PROPAGATE_READ_STATUS(read_unsigned(reader, &length_ulonglong, max_length));
   *length= (size_t) length_ulonglong;
   PROPAGATE_READ_STATUS_NOEOF(reader->read(buf, *length));

=== modified file 'sql/zgroup.cc'
--- a/sql/zgroup.cc	2011-09-18 16:55:17 +0000
+++ b/sql/zgroup.cc	2011-09-30 12:13:10 +0000
@@ -47,7 +47,8 @@ enum_return_status Group::parse(Sid_map
     if (gno > 0 && *text == 0)
       RETURN_OK;
   }
-  my_error(ER_MALFORMED_GROUP_SPECIFICATION, MYF(0), text);
+  BINLOG_ERROR(("Malformed group specification: %.200s", text),
+               (ER_MALFORMED_GROUP_SPECIFICATION, MYF(0), text));
   RETURN_REPORTED_ERROR;
 }
 

=== modified file 'sql/zgroup_cache.cc'
--- a/sql/zgroup_cache.cc	2011-09-28 14:17:30 +0000
+++ b/sql/zgroup_cache.cc	2011-09-30 12:20:42 +0000
@@ -71,7 +71,7 @@ enum_return_status Group_cache::add_subg
   // if sub-group could not be merged with previous sub-group, append it
   if (insert_dynamic(&subgroups, group) != 0)
   {
-    my_error(ER_OUT_OF_RESOURCES, MYF(0));
+    BINLOG_ERROR(("Out of memory."), (ER_OUT_OF_RESOURCES, MYF(0)));
     RETURN_REPORTED_ERROR;
   }
 
@@ -320,7 +320,7 @@ Group_cache::write_to_log_prepare(Group_
     }
   }
 
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   /*
     Assert that UGID is valid for all groups. This ensures that group
     numbers have been generated for automatic subgroups.
@@ -328,9 +328,9 @@ Group_cache::write_to_log_prepare(Group_
   {
     for (int i= 0; i < n_subgroups; i++)
     {
-      DBUG_ASSERT(get_unsafe_pointer(i)->type == ANONYMOUS_SUBGROUP || 
-                  (get_unsafe_pointer(i)->sidno > 0 && 
-                   get_unsafe_pointer(i)->gno > 0));
+      Cached_subgroup *cs= get_unsafe_pointer(i);
+      DBUG_ASSERT(cs->type == ANONYMOUS_SUBGROUP ||
+                  (cs->sidno > 0 && cs->gno > 0));
     }
   }
 #endif
@@ -341,7 +341,7 @@ Group_cache::write_to_log_prepare(Group_
   */
   // offset_after_last_statement is -1 if this Group_cache contains
   // only dummy groups.
-#ifdef NO_DBUG
+#ifdef DBUG_OFF
   if (offset_after_last_statement != -1)
 #endif
   {
@@ -366,10 +366,11 @@ Group_cache::write_to_log_prepare(Group_
 
 
 enum_return_status
-Group_cache::write_to_log(const THD *thd, Group_cache *trx_group_cache,
+Group_cache::write_to_log(const THD *thd,
+                          Group_log *group_log, Group_cache *trx_group_cache,
+                          rpl_binlog_no binlog_no, rpl_binlog_pos binlog_pos,
                           rpl_binlog_pos offset_after_last_statement,
-                          bool group_commit,
-                          Group_log *group_log)
+                          bool group_commit)
 {
   DBUG_ENTER("Group_cache::write_to_log");
   Cached_subgroup *last_non_dummy_subgroup;
@@ -384,11 +385,12 @@ Group_cache::write_to_log(const THD *thd
   for (int i= 0; i < n_subgroups; i++)
   {
     Cached_subgroup *cs= get_unsafe_pointer(i);
-    if (cs == last_non_dummy_subgroup)
-      group_log->write_subgroup(cs, group_commit, offset_after_last_statement,
-                                thd);
+    if (i == n_subgroups - 1)
+      group_log->write_subgroup(thd, cs, binlog_no, binlog_pos,
+                                offset_after_last_statement, true);
     else
-      group_log->write_subgroup(cs, false, 0, thd);
+      group_log->write_subgroup(thd, cs, binlog_no, binlog_pos, 0, false);
+    binlog_pos+= cs->binlog_length;
   }
 
   RETURN_OK;

=== modified file 'sql/zgroup_execution.cc'
--- a/sql/zgroup_execution.cc	2011-09-26 08:54:32 +0000
+++ b/sql/zgroup_execution.cc	2011-09-30 12:13:10 +0000
@@ -389,15 +389,17 @@ int ugid_flush_group_cache(THD *thd, Che
                            Group_log *gl,
                            Group_cache *gc,
                            Group_cache *trx_cache,
+                           rpl_binlog_no binlog_no, rpl_binlog_pos binlog_pos,
                            rpl_binlog_pos offset_after_last_statement)
 {
   DBUG_ENTER("ugid_flush_group_cache");
   lock->rdlock();
   PROPAGATE_REPORTED_ERROR_INT(gc->generate_automatic_gno(thd, gls));
   PROPAGATE_REPORTED_ERROR_INT(
-    gc->write_to_log(thd, trx_cache,
+    gc->write_to_log(thd, gl, trx_cache,
+                     binlog_no, binlog_pos,
                      offset_after_last_statement,
-                     thd->variables.ugid_commit ? true : false, gl));
+                     thd->variables.ugid_commit ? true : false));
   PROPAGATE_REPORTED_ERROR_INT(gc->update_group_log_state(thd, gls));
   lock->unlock();
   gc->clear();

=== modified file 'sql/zgroup_log.cc'
--- a/sql/zgroup_log.cc	2011-09-26 08:54:32 +0000
+++ b/sql/zgroup_log.cc	2011-09-30 12:13:10 +0000
@@ -22,92 +22,160 @@
 #include "mysqld_error.h"
 
 
-int Group_log::open(const char *filename)
+enum_return_status Group_log::open(const char *filename, bool writable)
 {
   DBUG_ENTER("Group_log::open(const char *)");
-  int ret= rot_file.open(filename, true);
-  DBUG_RETURN(ret);
+  PROPAGATE_REPORTED_ERROR(rot_file.open(filename, writable));
+  RETURN_OK;
+}
+
+
+enum_return_status Group_log::close()
+{
+  DBUG_ENTER("Group_log::close()");
+  PROPAGATE_REPORTED_ERROR(rot_file.close());
+  RETURN_OK;
 }
 
 
+#ifndef MYSQL_CLIENT
 enum_return_status
-Group_log::write_subgroup(const Cached_subgroup *subgroup,
+Group_log::write_subgroup(const THD *thd, const Cached_subgroup *subgroup,
+                          rpl_binlog_no binlog_no, rpl_binlog_pos binlog_pos,
                           rpl_binlog_pos offset_after_last_statement,
-                          bool group_commit, const THD *thd)
+                          bool group_commit)
 {
   DBUG_ENTER("Group_log::write_subgroup(const Subgroup *)");
 
   if (!rot_file.is_open())
   {
-    my_error(ER_ERROR_ON_WRITE, MYF(0), rot_file.get_source_name(), errno);
+    BINLOG_ERROR(("Error writing file '%-.200s' (errno: %d)",
+                  rot_file.get_source_name(), errno),
+                 (ER_ERROR_ON_WRITE, MYF(0), rot_file.get_source_name(),
+                  errno));
     RETURN_REPORTED_ERROR;
   }
 
   Rpl_owner_id id;
   id.copy_from(thd);
-  if (encoder.append(&rot_file, subgroup, offset_after_last_statement,
-                     group_commit, id.owner_type) != APPEND_OK)
+  if (encoder.append(&rot_file, subgroup, binlog_no, binlog_pos,
+                     offset_after_last_statement, group_commit,
+                     id.owner_type) != APPEND_OK)
   {
     rot_file.close();
     RETURN_REPORTED_ERROR;
   }
   RETURN_OK;
 }
+#endif // ifndef MYSQL_CLIENT
 
 
-Group_log::Group_log_reader::Group_log_reader(
-  Group_log *_group_log, const Group_set *group_set,
-  rpl_binlog_no binlog_no, rpl_binlog_pos binlog_pos, enum_read_status *status)
-  : output_sid_map(group_set->get_sid_map()),
+Group_log::Group_log_reader::Group_log_reader(Group_log *_group_log,
+                                              Sid_map *_output_sid_map)
+  : output_sid_map(_output_sid_map),
     group_log(_group_log),
     rot_file_reader(&group_log->rot_file, 0),
-    has_peeked(true)
+    has_peeked(false)
 {
-  DBUG_ENTER("Group_log::Reader::Reader");
-  bool found_group= false, found_pos= false;
+  DBUG_ASSERT(output_sid_map != NULL);
+}
+
+
+enum_read_status Group_log::Group_log_reader::seek(const Group_set *_group_set,
+                                                   bool _exclude,
+                                                   bool _include_anonymous,
+                                                   rpl_lgid _first_lgid,
+                                                   rpl_lgid _last_lgid,
+                                                   rpl_binlog_no binlog_no,
+                                                   rpl_binlog_pos binlog_pos)
+{
+  DBUG_ENTER("Group_log::Group_log_reader::seek");
+  DBUG_ASSERT(group_set == NULL || output_sid_map == group_set->get_sid_map());
+
+  group_set= _group_set;
+  exclude_group_set= _exclude;
+  include_anonymous= _include_anonymous;
+  first_lgid= _first_lgid;
+  last_lgid= _last_lgid;
   do
   {
-    *status= do_read_subgroup(&peeked_subgroup);
-    if (*status != READ_OK)
-      DBUG_VOID_RETURN;
-    found_group= found_group || group_set->contains_group(peeked_subgroup.sidno,
-                                                          peeked_subgroup.gno);
-    found_pos= found_pos || (peeked_subgroup.binlog_no > binlog_no ||
-                             (peeked_subgroup.binlog_no == binlog_no &&
-                              peeked_subgroup.binlog_pos >= binlog_pos));
-  } while (!found_pos || !found_group);
-  DBUG_VOID_RETURN;
+    PROPAGATE_READ_STATUS(do_read_subgroup(&peeked_subgroup, NULL));
+    char *str= peeked_subgroup.to_string();
+    free(str);
+    has_peeked= true;
+  } while (binlog_no != 0 &&
+           (peeked_subgroup.binlog_no < binlog_no ||
+            (peeked_subgroup.binlog_no == binlog_no &&
+             peeked_subgroup.binlog_pos < binlog_pos)));
+  DBUG_RETURN(READ_OK);
+}
+
+
+bool Group_log::Group_log_reader::subgroup_in_valid_set(Subgroup *subgroup)
+{
+  if (first_lgid != -1 && subgroup->lgid < first_lgid)
+    return false;
+  if (last_lgid != -1 && subgroup->lgid > last_lgid)
+    return false;
+  if (group_set == NULL)
+    return true;
+  if (subgroup->type == ANONYMOUS_SUBGROUP)
+    return include_anonymous;
+  return group_set->contains_group(subgroup->sidno, subgroup->gno) !=
+    exclude_group_set;
 }
 
 
 enum_read_status Group_log::Group_log_reader::do_read_subgroup(
-  Subgroup *subgroup)
+  Subgroup *subgroup, uint32 *owner_type)
 {
   DBUG_ENTER("Group_log::Reader::do_read_subgroup(Subgroup *)");
-  PROPAGATE_READ_STATUS(decoder.read(&rot_file_reader, subgroup));
-  const Sid_map *log_sid_map= group_log->get_sid_map();
-  if (output_sid_map != log_sid_map)
-  {
-    const rpl_sid *sid= log_sid_map->sidno_to_sid(subgroup->sidno);
-    READER_CHECK_FORMAT(&rot_file_reader, sid != NULL);
-    subgroup->sidno= output_sid_map->add_permanent(sid);
-    READER_CHECK_FORMAT(&rot_file_reader, subgroup->sidno < 1);
-  }
+
+  do
+  {
+    // read one subgroup
+    PROPAGATE_READ_STATUS(decoder.read(&rot_file_reader, subgroup, owner_type));
+    const Sid_map *log_sid_map= group_log->get_sid_map();
+    if (output_sid_map != log_sid_map)
+    {
+      const rpl_sid *sid= log_sid_map->sidno_to_sid(subgroup->sidno);
+      READER_CHECK_FORMAT(&rot_file_reader, sid != NULL);
+      subgroup->sidno= output_sid_map->add_permanent(sid);
+      READER_CHECK_FORMAT(&rot_file_reader, subgroup->sidno < 1);
+    }
+    // skip sub-groups that are outside the valid set of groups
+  } while (!subgroup_in_valid_set(subgroup));
+
   DBUG_RETURN(READ_OK);
 }
 
 
-enum_read_status Group_log::Group_log_reader::read_subgroup(Subgroup *subgroup)
+enum_read_status Group_log::Group_log_reader::read_subgroup(
+  Subgroup *subgroup, uint32 *owner_type)
 {
   DBUG_ENTER("Group_log::Reader::read_subgroup(Subgroup *)");
   if (has_peeked)
   {
     *subgroup= peeked_subgroup;
     has_peeked= false;
-    DBUG_RETURN(READ_OK);
   }
-  enum_read_status status= do_read_subgroup(subgroup);
-  DBUG_RETURN(status);
+  else
+    PROPAGATE_READ_STATUS(do_read_subgroup(subgroup, owner_type));
+  DBUG_RETURN(READ_OK);
+}
+
+
+enum_read_status Group_log::Group_log_reader::peek_subgroup(
+  Subgroup **subgroup_p, uint32 *owner_type)
+{
+  DBUG_ENTER("Group_log::Reader::read_subgroup(Subgroup *)");
+  if (!has_peeked)
+  {
+    PROPAGATE_READ_STATUS(do_read_subgroup(&peeked_subgroup, owner_type));
+    has_peeked= true;
+  }
+  *subgroup_p= &peeked_subgroup;
+  DBUG_RETURN(READ_OK);
 }
 
 

=== modified file 'sql/zgroup_set.cc'
--- a/sql/zgroup_set.cc	2011-09-29 10:01:11 +0000
+++ b/sql/zgroup_set.cc	2011-09-30 12:20:42 +0000
@@ -21,7 +21,6 @@
 
 
 #include <ctype.h>
-#include "mysqld.h"
 #include "my_dbug.h"
 #include "mysqld_error.h"
 
@@ -74,7 +73,7 @@ void Group_set::init(Sid_map *_sid_map,
   chunks= NULL;
   free_intervals= NULL;
   my_init_dynamic_array(&intervals, sizeof(Interval *), 0, 8);
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   n_chunks= 0;
 #endif
   DBUG_VOID_RETURN;
@@ -140,7 +139,7 @@ enum_return_status Group_set::ensure_sid
   }
   RETURN_OK;
 error:
-  my_error(ER_OUT_OF_RESOURCES, MYF(0));
+  BINLOG_ERROR(("Out of memory."), (ER_OUT_OF_RESOURCES, MYF(0)));
   RETURN_REPORTED_ERROR;
 }
 
@@ -169,13 +168,13 @@ enum_return_status Group_set::create_new
                              sizeof(Interval) * (size - 1));
   if (new_chunk == NULL)
   {
-    my_error(ER_OUT_OF_RESOURCES, MYF(0));
+    BINLOG_ERROR(("Out of memory."), (ER_OUT_OF_RESOURCES, MYF(0)));
     RETURN_REPORTED_ERROR;
   }
   // store the chunk in the list of chunks
   new_chunk->next= chunks;
   chunks= new_chunk;
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   n_chunks++;
 #endif
   // add the intervals in the chunk to the list of free intervals
@@ -376,7 +375,7 @@ int format_gno(char *s, rpl_gno gno)
 }
 
 
-enum_return_status Group_set::add(const char *text)
+enum_return_status Group_set::add(const char *text, bool *anonymous)
 {
 #define SKIP_WHITESPACE() while (isspace(*s)) s++
   DBUG_ENTER("Group_set::add(const char*)");
@@ -384,6 +383,9 @@ enum_return_status Group_set::add(const
 
   DBUG_PRINT("info", ("adding '%s'", text));
 
+  if (anonymous != NULL)
+    *anonymous= false;
+
   SKIP_WHITESPACE();
   if (*s == 0)
     RETURN_OK;
@@ -418,50 +420,58 @@ enum_return_status Group_set::add(const
       RETURN_OK;
 
     // Parse SID.
-    rpl_sid sid;
-    if (sid.parse(s) != 0)
-      goto parse_error;
-    s += rpl_sid::TEXT_LENGTH;
-    rpl_sidno sidno= sid_map->add_permanent(&sid, 0);
-    if (sidno <= 0)
-      RETURN_REPORTED_ERROR;
-    PROPAGATE_REPORTED_ERROR(ensure_sidno(sidno));
-    SKIP_WHITESPACE();
-
-    // Iterate over intervals.
-    Interval_iterator ivit(this, sidno);
-    while (*s == ':')
+    if (anonymous != NULL && strncmp(s, "ANONYMOUS", 9) == 0)
     {
-      // Skip ':'.
-      s++;
-
-      // Read start of interval.
-      rpl_gno start= parse_gno(&s);
-      if (start == 0)
+      *anonymous= true;
+      s+= 9;
+    }
+    else
+    {
+      rpl_sid sid;
+      if (sid.parse(s) != 0)
         goto parse_error;
+      s += rpl_sid::TEXT_LENGTH;
+      rpl_sidno sidno= sid_map->add_permanent(&sid, 0);
+      if (sidno <= 0)
+        RETURN_REPORTED_ERROR;
+      PROPAGATE_REPORTED_ERROR(ensure_sidno(sidno));
       SKIP_WHITESPACE();
 
-      // Read end of interval.
-      rpl_gno end;
-      if (*s == '-')
+      // Iterate over intervals.
+      Interval_iterator ivit(this, sidno);
+      while (*s == ':')
       {
+        // Skip ':'.
         s++;
-        end= parse_gno(&s);
-        if (end == 0)
+
+        // Read start of interval.
+        rpl_gno start= parse_gno(&s);
+        if (start == 0)
           goto parse_error;
-        end++;
         SKIP_WHITESPACE();
-      }
-      else
-        end= start + 1;
 
-      // Add interval.  Use the existing iterator position if the
-      // current interval does not begin before it.  Otherwise iterate
-      // from the beginning.
-      Interval *current= ivit.get();
-      if (current == NULL || start < current->start)
-        ivit.init(this, sidno);
-      PROPAGATE_REPORTED_ERROR(add(&ivit, start, end));
+        // Read end of interval.
+        rpl_gno end;
+        if (*s == '-')
+        {
+          s++;
+          end= parse_gno(&s);
+          if (end == 0)
+            goto parse_error;
+          end++;
+          SKIP_WHITESPACE();
+        }
+        else
+          end= start + 1;
+
+        // Add interval.  Use the existing iterator position if the
+        // current interval does not begin before it.  Otherwise iterate
+        // from the beginning.
+        Interval *current= ivit.get();
+        if (current == NULL || start < current->start)
+          ivit.init(this, sidno);
+        PROPAGATE_REPORTED_ERROR(add(&ivit, start, end));
+      }
     }
 
     // Must be end of string or comma. (Commas are consumed and
@@ -472,7 +482,8 @@ enum_return_status Group_set::add(const
   DBUG_ASSERT(0);
 
 parse_error:
-  my_error(ER_MALFORMED_GROUP_SET_SPECIFICATION, MYF(0), text);
+  BINLOG_ERROR(("Malformed group set specification '%.200s'.", text),
+               (ER_MALFORMED_GROUP_SET_SPECIFICATION, MYF(0), text));
   RETURN_REPORTED_ERROR;
 }
 
@@ -647,7 +658,7 @@ int Group_set::to_string(char *buf, cons
 {
   DBUG_ENTER("Group_set::to_string");
   if (sf == NULL)
-    sf= &default_string_format;;
+    sf= &default_string_format;
   rpl_sidno map_max_sidno= sid_map->get_max_sidno();
   memcpy(buf, sf->begin, sf->begin_length);
   char *s= buf + sf->begin_length;

=== modified file 'sql/zgroups.h'
--- a/sql/zgroups.h	2011-09-29 10:01:11 +0000
+++ b/sql/zgroups.h	2011-09-30 12:20:42 +0000
@@ -18,15 +18,16 @@
 #define RPL_GROUPS_H_INCLUDED
 
 
-#include "my_base.h"
-#include "mysqld_error.h"
+#include <m_string.h>
+#include <my_base.h>
+#include <mysqld_error.h>
 
 
 /*
   In the current version, enable UGID only in debug builds.  We will
   enable it fully when it is more complete.
 */
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
 /*
   The group log can only be correctly truncated if my_chsize actually
   truncates the file. So disable UGIDs on platforms that don't support
@@ -38,16 +39,38 @@
 #endif
 
 
+/**
+  Report an error from code that can be linked into either the server
+  or mysqlbinlog.  There is no common error reporting mechanism, so we
+  have to duplicate the error message (write it out in the source file
+  for mysqlbinlog, write it in share/errmsg-utf8.txt for the server).
+
+  @param MYSQLBINLOG_ERROR arguments to mysqlbinlog's 'error'
+  function, including the function call parentheses
+  @param SERVER_ERROR arguments to my_error, including the function
+  call parentheses.
+*/
+#ifdef MYSQL_CLIENT
+#define BINLOG_ERROR(MYSQLBINLOG_ERROR, SERVER_ERROR) error MYSQLBINLOG_ERROR
+#else
+#define BINLOG_ERROR(MYSQLBINLOG_ERROR, SERVER_ERROR) my_error SERVER_ERROR
+#endif
+
+
 #ifdef HAVE_UGID
 
-#include "mysqld.h"
+//#include "mysqld.h"
+//#include "sql_string.h"
 #include "hash.h"
 #include "lf.h"
 #include "my_atomic.h"
 
 
-class MYSQL_BIN_LOG;
+#ifndef MYSQL_CLIENT
+class String;
 class Group_log;
+class THD;
+#endif // ifndef MYSQL_CLIENT
 
 
 /// Type of SIDNO (source ID number, first component of UGID)
@@ -170,7 +193,7 @@ enum enum_return_status
   Lowest level macro used in the PROPAGATE_* and RETURN_* macros
   below.
 
-  If NO_DBUG is defined, does nothing. Otherwise, if STATUS is
+  If DBUG_OFF is defined, does nothing. Otherwise, if STATUS is
   RETURN_STATUS_OK, does nothing; otherwise, make a dbug printout and
   (if ALLOW_UNREPORTED==0) assert that STATUS !=
   RETURN_STATUS_UNREPORTED.
@@ -183,7 +206,7 @@ enum enum_return_status
   @param ALLOW_UNREPORTED If false, the macro asserts that STATUS is
   not RETURN_STATUS_UNREPORTED_ERROR.
 */
-#ifdef NO_DBUG
+#ifdef DBUG_OFF
 #define __CHECK_RETURN_STATUS(STATUS, ACTION, STATUS_NAME, ALLOW_UNREPORTED)
 #else
 extern void check_return_status(enum_return_status status,
@@ -384,9 +407,13 @@ public:
     if (ret != READ_OK)
     {
       if (ret == READ_EOF)
-        my_error(ER_UNEXPECTED_EOF, MYF(0), get_source_name(), my_errno);
+        BINLOG_ERROR(("Unexpected EOF found when reading file '%-.192s' (errno: %d)",
+                      get_source_name(), my_errno),
+                     (ER_UNEXPECTED_EOF, MYF(0), get_source_name(), my_errno));
       else if (ret == READ_TRUNCATED)
-        my_error(ER_FILE_TRUNCATED, MYF(0), get_source_name());
+        BINLOG_ERROR(("File '%.200s' was truncated in the middle.",
+                      get_source_name()),
+                     (ER_FILE_TRUNCATED, MYF(0), get_source_name()));
     }
     DBUG_RETURN(ret);
   }
@@ -433,7 +460,14 @@ protected:
     {                                                                   \
       if (!(CONDITION))                                                 \
       {                                                                 \
-        my_error(ER_FILE_FORMAT, MYF(0), (READER)->get_source_name());  \
+        Reader *_reader_check_format_reader= (READER);                  \
+        my_off_t ofs;                                                   \
+        _reader_check_format_reader->tell(&ofs);                        \
+        BINLOG_ERROR(("File '%.200s' has an unknown format at position %lld, " \
+                      "it may be corrupt.",                             \
+                      (READER)->get_source_name(), ofs),                \
+                     (ER_FILE_FORMAT, MYF(0),                           \
+                      (READER)->get_source_name(), ofs));               \
         DBUG_RETURN(READ_ERROR);                                        \
       }                                                                 \
     } while (0)
@@ -557,6 +591,8 @@ protected:
     DBUG_ENTER("Appender::file_truncate");
     if (my_chsize(fd, position, 0, MYF(MY_WME)) != 0)
       RETURN_REPORTED_ERROR;
+    if (my_seek(fd, position, SEEK_SET, MYF(MY_WME)) != 0)
+      RETURN_REPORTED_ERROR;
     RETURN_OK;
   }
 };
@@ -568,9 +604,6 @@ public:
   Memory_reader(size_t _length, const uchar *_data,
                 const char *_source_name= "<Memory buffer>")
     : source_name(_source_name), data_length(_length), data(_data), pos(0) {}
-  Memory_reader(String *str, const char *_source_name= "<String *>")
-    : source_name(_source_name),
-    data_length(str->length()), data((uchar *)str->ptr()), pos(0) {}
   enum_read_status read(uchar *buffer, size_t length)
   {
     DBUG_ENTER("Memory_reader::read");
@@ -719,6 +752,7 @@ private:
 };
 
 
+#ifndef MYSQL_CLIENT
 /**
   Appender class where the output is stored in a String object.
 */
@@ -726,25 +760,14 @@ class String_appender : public Appender
 {
 public:
   String_appender(String *_str) : str(_str) {}
-  enum_return_status append(const uchar *buf, size_t length)
-  {
-    DBUG_ENTER("String_appender::append");
-    if (str->append((const char *)buf, length))
-    {
-      my_error(ER_OUT_OF_RESOURCES, MYF(0));
-      RETURN_REPORTED_ERROR;
-    }
-    RETURN_OK;
-  }
-  enum_return_status truncate(my_off_t new_position)
-  {
-    DBUG_ENTER("String_appender::truncate");
-    str->length(new_position);
-    RETURN_OK;
-  }
+protected:
+  enum_return_status do_append(const uchar *buf, size_t length);
+  enum_return_status do_truncate(my_off_t new_position);
+  enum_return_status do_tell(my_off_t *position) const;
 private:
   String *str;
 };
+#endif // ifndef MYSQL_CLIENT
 
 
 /**
@@ -757,10 +780,12 @@ struct Rpl_owner_id
 {
   uint32 owner_type;
   uint32 thread_id;
-  void copy_from(const THD *thd);
   void set_to_dead_client() { owner_type= thread_id= 0; }
   void set_to_none() { owner_type= NO_OWNER_TYPE; thread_id= NO_THREAD_ID; }
+#ifndef MYSQL_CLIENT
+  void copy_from(const THD *thd);
   bool equals(const THD *thd) const;
+#endif // ifndef MYSQL_CLIENT
   bool is_sql_thread() const
   { return owner_type >= 1 && owner_type != NO_OWNER_TYPE; }
   bool is_none() const { return owner_type == NO_OWNER_TYPE; }
@@ -810,8 +835,8 @@ struct Uuid
 
     @retval 36 - the length of the resulting string.
   */
-  int to_string(char *buf) const;
-#ifndef NO_DBUG
+  size_t to_string(char *buf) const;
+#ifndef DBUG_OFF
   void print() const
   {
     char buf[TEXT_LENGTH + 1];
@@ -874,7 +899,7 @@ public:
   /// Initialize this Checkable_rwlock.
   Checkable_rwlock()
   {
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
     my_atomic_rwlock_init(&atomic_lock);
     lock_state= 0;
 #endif
@@ -883,7 +908,7 @@ public:
   /// Destroy this Checkable_lock.
   ~Checkable_rwlock()
   {
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
     my_atomic_rwlock_destroy(&atomic_lock);
 #endif
     mysql_rwlock_destroy(&rwlock);
@@ -894,7 +919,7 @@ public:
   {
     mysql_rwlock_rdlock(&rwlock);
     assert_no_wrlock();
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
     my_atomic_rwlock_wrlock(&atomic_lock);
     my_atomic_add32(&lock_state, 1);
     my_atomic_rwlock_wrunlock(&atomic_lock);
@@ -905,7 +930,7 @@ public:
   {
     mysql_rwlock_wrlock(&rwlock);
     assert_no_lock();
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
     my_atomic_rwlock_wrlock(&atomic_lock);
     my_atomic_store32(&lock_state, -1);
     my_atomic_rwlock_wrunlock(&atomic_lock);
@@ -915,7 +940,7 @@ public:
   inline void unlock()
   {
     assert_some_lock();
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
     my_atomic_rwlock_wrlock(&atomic_lock);
     int val= my_atomic_load32(&lock_state);
     if (val > 0)
@@ -949,7 +974,7 @@ public:
   { DBUG_ASSERT(get_state() == 0); }
 
 private:
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   /**
     The state of the lock:
     0 - not locked
@@ -1011,7 +1036,7 @@ public:
     @param base_filename The base of the filename, i.e., without "-sids".
     @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
   */
-  enum_return_status open(const char *base_filename);
+  enum_return_status open(const char *base_filename, bool writable= true);
   /**
     Close the disk file.
     @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
@@ -1107,6 +1132,10 @@ public:
     sid_lock->assert_some_lock();
     return _sidno_to_sid.elements;
   }
+  /**
+    Return true iff open() has been (successfully) called.
+  */
+  bool is_open() { return status == OPEN; }
 
 private:
   /// Node pointed to by both the hash and the array.
@@ -1117,11 +1146,6 @@ private:
   };
 
   /**
-    Return true iff open() has been (successfully) called.
-  */
-  bool is_open() { return fd != -1; }
-
-  /**
     Create a Node from the given SIDNO and SID and add it to
     _sidno_to_sid, _sid_to_sidno, and _sorted.
 
@@ -1163,6 +1187,11 @@ private:
   File fd;
   /// Appender object to write to the file.
   File_appender appender;
+  enum sid_map_status
+  {
+    CLOSED_OK, CLOSED_ERROR, OPEN
+  };
+  sid_map_status status;
 };
 
 
@@ -1213,21 +1242,21 @@ public:
   }
   /**
     Assert that this thread owns the n'th mutex.
-    This is a no-op if NO_DBUG is on.
+    This is a no-op if DBUG_OFF is on.
   */
   inline void assert_owner(int n) const
   {
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
     mysql_mutex_assert_owner(&get_mutex_cond(n)->mutex);
 #endif
   }
   /**
     Assert that this thread does not own the n'th mutex.
-    This is a no-op if NO_DBUG is on.
+    This is a no-op if DBUG_OFF is on.
   */
   inline void assert_not_owner(int n) const
   {
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
     mysql_mutex_assert_not_owner(&get_mutex_cond(n)->mutex);
 #endif
   }
@@ -1240,9 +1269,11 @@ public:
     mysql_cond_wait(&mutex_cond->cond, &mutex_cond->mutex);
     DBUG_VOID_RETURN;
   }
+#ifndef MYSQL_CLIENT
   /// Execute THD::enter_cond for the n'th condition variable.
   void enter_cond(THD *thd, int n, PSI_stage_info *stage,
                   PSI_stage_info *old_stage) const;
+#endif // ifndef MYSQL_CLIENT
   /// Return the greatest addressable index in this Mutex_cond_array.
   inline int get_max_index() const
   {
@@ -1301,7 +1332,7 @@ struct Group
   */
   enum_return_status parse(Sid_map *sid_map, const char *text);
 
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   void print(const Sid_map *sid_map) const
   {
     char buf[MAX_TEXT_LENGTH + 1];
@@ -1419,6 +1450,7 @@ public:
     or more of the following:
 
        XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX(:NUMBER+(-NUMBER)?)*
+       | ANONYMOUS
 
        Each X is a hexadecimal digit (upper- or lowercase).
        NUMBER is a decimal, 0xhex, or 0oct number.
@@ -1430,9 +1462,12 @@ public:
     short period when the lock is not held at all.
 
     @param text The string to parse.
+    @param anonymous[in,out] If this is NULL, ANONYMOUS is not
+    allowed.  If this is not NULL, it will be set to true if the
+    anonymous group was found; false otherwise.
     @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
   */
-  enum_return_status add(const char *text);
+  enum_return_status add(const char *text, bool *anonymous= NULL);
   /**
     Decodes a Group_set from the given string.
 
@@ -1511,8 +1546,8 @@ public:
     Returns true if the given string is a valid specification of a Group_set, false otherwise.
   */
   static bool is_valid(const char *text);
-#ifndef NO_DBUG
-  char *get_string() const
+#ifndef DBUG_OFF
+  char *to_string() const
   {
     char *str= (char *)malloc(get_string_length() + 1);
     DBUG_ASSERT(str != NULL);
@@ -1522,7 +1557,7 @@ public:
   /// Print this group set to stdout.
   void print() const
   {
-    char *str= get_string();
+    char *str= to_string();
     printf("%s\n", str);
     free(str);
   }
@@ -1890,7 +1925,7 @@ private:
   mutable int cached_string_length;
   /// The String_format that was used when cached_string_length was computed.
   mutable const String_format *cached_string_format;
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   /**
     The number of chunks.  Used only to check some invariants when
     DBUG is on.
@@ -2081,7 +2116,7 @@ public:
   */
   enum_return_status get_partial_groups(Group_set *gs) const;
 
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   int to_string(const Sid_map *sm, char *out) const
   {
     char *p= out;
@@ -2269,8 +2304,10 @@ public:
     @param owner The thread that will own the group.
     @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
   */
+#ifndef MYSQL_CLIENT
   enum_return_status acquire_ownership(rpl_sidno sidno, rpl_gno gno,
                                        const THD *thd);
+#endif // ifndef MYSQL_CLIENT
   /**
     Ends the given group, i.e., moves it from the set of 'owned
     groups' to the set of 'ended groups'.
@@ -2296,7 +2333,9 @@ public:
   /// Broadcasts updates for the given SIDNO.
   void broadcast_sidno(rpl_sidno sidno) { sid_locks.broadcast(sidno); }
   /// Waits for updates on the given SIDNO.
+#ifndef MYSQL_CLIENT
   void wait_for_sidno(THD *thd, const Sid_map *sm, Group g, Rpl_owner_id owner);
+#endif // ifndef MYSQL_CLIENT
   /**
     Locks one mutex for each SIDNO where the given Group_set has at
     least one group. If the Group_set is not given, locks all
@@ -2331,7 +2370,7 @@ public:
   const Owned_groups *get_owned_groups() { return &owned_groups; }
   /// Return Sid_map used by this Group_log_state.
   const Sid_map *get_sid_map() { return sid_map; }
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   size_t get_string_length() const
   {
     return owned_groups.get_string_length() +
@@ -2427,7 +2466,7 @@ struct Ugid_specification
   static enum_type get_type(const char *text);
   /// Returns true if the given string is a valid Ugid_specification.
   static bool is_valid(const char *text) { return get_type(text) != INVALID; }
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   void print() const
   {
     char buf[MAX_TEXT_LENGTH + 1];
@@ -2456,6 +2495,47 @@ struct Subgroup
   rpl_lgid lgid;
   bool group_commit;
   bool group_end;
+  size_t to_string(char *buf, Sid_map *sm= NULL) const
+  {
+    char *s= buf;
+    s+= sprintf(s, "Subgroup(#%lld, ", lgid);
+    if (type == ANONYMOUS_SUBGROUP)
+      s+= sprintf(s, "ANONYMOUS");
+    else
+    {
+      char sid_buf[Uuid::TEXT_LENGTH + 1];
+      if (sm == NULL)
+        sprintf(sid_buf, "%d", sidno);
+      else
+        sm->sidno_to_sid(sidno)->to_string(sid_buf);
+      s+= sprintf(s, "%s:%lld%s", sid_buf, gno, group_end ? ", END" : "");
+    }
+    if (group_commit)
+      s+= sprintf(s, ", COMMIT");
+    if (type == DUMMY_SUBGROUP)
+      s+= sprintf(s, " DUMMY");
+    else
+      s+= sprintf(s, ", binlog(no=%lld, pos=%lld, len=%lld, oals=%lld)",
+                  binlog_no, binlog_pos, binlog_length,
+                  binlog_offset_after_last_statement);
+    return s - buf;
+  }
+  static const size_t MAX_TEXT_LENGTH= 1024;
+#ifndef DBUG_OFF
+  void print(Sid_map *sm= NULL) const
+  {
+    char buf[MAX_TEXT_LENGTH];
+    to_string(buf, sm);
+    printf("%s\n", buf);
+  }
+  char *to_string(Sid_map *sm= NULL) const
+  {
+    char *ret= (char *)malloc(MAX_TEXT_LENGTH);
+    DBUG_ASSERT(ret != NULL);
+    to_string(ret, sm);
+    return ret;
+  }
+#endif
 };
 
 
@@ -2504,8 +2584,10 @@ public:
     @param binlog_length Length of group in binary log.
     @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
   */
+#ifndef MYSQL_CLIENT
   enum_return_status add_logged_subgroup(const THD *thd,
                                          my_off_t binlog_length);
+#endif // ifndef MYSQL_CLIENT
   /**
     Adds a dummy group with the given SIDNO, GNO, and GROUP_END to this cache.
 
@@ -2541,6 +2623,7 @@ public:
   enum_return_status
     add_dummy_subgroups_if_missing(const Group_log_state *gls,
                                    const Group_set *group_set);
+#ifndef MYSQL_CLIENT
   /**
     Update the binary log's Group_log_state to the state after this
     cache has been flushed.
@@ -2567,10 +2650,11 @@ public:
     sub-groups of the group.
     @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
   */
-  enum_return_status write_to_log(const THD *thd, Group_cache *trx_group_cache,
-                                  rpl_binlog_pos offset_after_last_statement,
-                                  bool group_commit,
-                                  Group_log *group_log);
+  enum_return_status
+    write_to_log(const THD *thd, Group_log *group_log,
+                 Group_cache *trx_group_cache,
+                 rpl_binlog_no binlog_no, rpl_binlog_pos binlog_pos,
+                 rpl_binlog_pos offset_after_last_statement, bool group_commit);
   /**
     Generates GNO for all groups that are committed for the first time
     in this Group_cache.
@@ -2585,6 +2669,7 @@ public:
   */
   enum_return_status generate_automatic_gno(const THD *thd,
                                             Group_log_state *gls);
+#endif // ifndef MYSQL_CLIENT
   /**
     Return true if this Group_cache contains the given group.
 
@@ -2625,42 +2710,43 @@ public:
   */
   enum_return_status get_ended_groups(Group_set *gs) const;
 
-#ifndef NO_DBUG
-  void get_string(const Sid_map *sm, char *buf) const
+#ifndef DBUG_OFF
+  size_t to_string(const Sid_map *sm, char *buf) const
   {
     int n_subgroups= get_n_subgroups();
+    char *s= buf;
 
-    buf += sprintf(buf, "%d sub-groups = {\n", n_subgroups);
+    s += sprintf(s, "%d sub-groups = {\n", n_subgroups);
     for (int i= 0; i < n_subgroups; i++)
     {
       Cached_subgroup *cs= get_unsafe_pointer(i);
       char uuid[Uuid::TEXT_LENGTH + 1]= "[]";
       if (cs->sidno)
         sm->sidno_to_sid(cs->sidno)->to_string(uuid);
-      buf +=
-        sprintf(buf, "  %s:%lld%s [%lld bytes] - %s\n",
-                uuid, cs->gno, cs->group_end ? "-END":"", cs->binlog_length,
-                cs->type == NORMAL_SUBGROUP ? "NORMAL" :
-                cs->type == ANONYMOUS_SUBGROUP ? "ANON" :
-                cs->type == DUMMY_SUBGROUP ? "DUMMY" :
-                "INVALID-SUBGROUP-TYPE");
+      s += sprintf(s, "  %s:%lld%s [%lld bytes] - %s\n",
+                   uuid, cs->gno, cs->group_end ? "-END":"", cs->binlog_length,
+                   cs->type == NORMAL_SUBGROUP ? "NORMAL" :
+                   cs->type == ANONYMOUS_SUBGROUP ? "ANON" :
+                   cs->type == DUMMY_SUBGROUP ? "DUMMY" :
+                   "INVALID-SUBGROUP-TYPE");
     }
-    sprintf(buf, "}\n");
+    sprintf(s, "}\n");
+    return s - buf;
   }
   size_t get_string_length() const
   {
     return (2 + Uuid::TEXT_LENGTH + 1 + MAX_GNO_TEXT_LENGTH + 4 + 2 +
             40 + 10 + 21 + 1 + 100/*margin*/) * get_n_subgroups() + 100/*margin*/;
   }
-  char *get_string(const Sid_map *sm) const
+  char *to_string(const Sid_map *sm) const
   {
     char *str= (char *)malloc(get_string_length());
-    get_string(sm, str);
+    to_string(sm, str);
     return str;
   }
   void print(const Sid_map *sm) const
   {
-    char *str= get_string(sm);
+    char *str= to_string(sm);
     printf("%s\n", str);
     free(str);
   }
@@ -2744,6 +2830,7 @@ enum enum_ugid_statement_status
 };
 
 
+#ifndef MYSQL_CLIENT
 /**
   Before a loggable statement begins, this function:
 
@@ -2769,7 +2856,9 @@ int ugid_flush_group_cache(THD *thd, Che
                            Group_log_state *gls,
                            Group_log *gl,
                            Group_cache *gc, Group_cache *trx_cache,
+                           rpl_binlog_no binlog_no, rpl_binlog_pos binlog_pos,
                            rpl_binlog_pos offset_after_last_statement);
+#endif // ifndef MYSQL_CLIENT
 
 
 /**
@@ -3013,9 +3102,8 @@ public:
     enum_read_status read(uchar *buffer, size_t length)
     {
       DBUG_ENTER("Rot_file_reader::read");
-      PROPAGATE_READ_STATUS(file_pread(rot_file->sub_file.fd, buffer,
-                                       rot_file->sub_file.header_length + pos,
-                                       length));
+      PROPAGATE_READ_STATUS(file_pread(rot_file->sub_file.fd, buffer, length,
+                                       rot_file->sub_file.header_length + pos));
       pos+= length;
       DBUG_RETURN(READ_OK);
     }
@@ -3082,23 +3170,22 @@ private:
 class Subgroup_coder
 {
 public:
-  Subgroup_coder() : lgid(0), offset(0), binlog_pos(0), binlog_no(0) {}
+  Subgroup_coder() : lgid(0), offset(0) {}
   enum_append_status append(Appender *appender, const Cached_subgroup *cs,
+                            rpl_binlog_no binlog_no, rpl_binlog_pos binlog_pos,
                             rpl_binlog_pos offset_after_last_statement,
                             bool group_commit, uint32 owner_type);
-  enum_read_status read(Reader *reader, Subgroup *s);
+  enum_read_status read(Reader *reader, Subgroup *s, uint32 *owner_type);
 private:
   rpl_lgid lgid;
   my_off_t offset;
-  rpl_binlog_pos binlog_pos;
-  rpl_binlog_no binlog_no;
   /// Constants used to encode groups.
   static const int TINY_SUBGROUP_MAX= 246, TINY_SUBGROUP_MIN= -123;
   static const int NORMAL_SUBGROUP_MIN= 247,
     NORMAL_SUBGROUP_SIDNO_BIT= 0, NORMAL_SUBGROUP_GNO_BIT= 1,
     NORMAL_SUBGROUP_BINLOG_LENGTH_BIT= 2;
   static const int SPECIAL_TYPE= 255;
-  static const int DUMMY_SUBGRUOP_MAX= 15;
+  static const int DUMMY_SUBGROUP_MAX= 15;
   static const int ANONYMOUS_SUBGROUP_COMMIT= 16,
     ANONYMOUS_SUBGROUP_NO_COMMIT= 17;
   static const int BINLOG_ROTATE= 18;
@@ -3107,8 +3194,9 @@ private:
   static const int FLIP_OWNER= 21, SET_OWNER= 22;
   static const int FULL_SUBGROUP= 26;
   static const int MIN_IGNORABLE_TYPE= 25;
+  static const int MIN_FATAL_TYPE= 28;
   static const my_off_t FULL_SUBGROUP_SIZE=
-    1 + 1 + 4 + 8 + 8 + 8 + 8 + 8 + 4 + 1 + 1;
+    1 + 4 + 8 + 8 + 8 + 8 + 8 + 4 + 1 + 1;
 };
 
 
@@ -3119,18 +3207,32 @@ public:
   /**
     Open the disk file.
     @param filename Name of file to open.
-    @retval 0 Success.
-    @retval 1 Error. This function calls my_error.
+    @param writable If false, the group log is opened in read-only mode.
+    @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
   */
-  int open(const char *filename);
+  enum_return_status open(const char *filename, bool writable= true);
+  /**
+    Close the disk file.
+    @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+  */
+  enum_return_status close();
+  /**
+    Return true iff this Group_log has been (successfully) opened.
+  */
+  bool is_open() { return rot_file.is_open(); }
+#ifndef MYSQL_CLIENT
   /**
     Writes the given Subgroup to the log.
     @param Subgroup Subgroup to write.
     @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
   */
-  enum_return_status write_subgroup(const Cached_subgroup *subgroup,
+  enum_return_status write_subgroup(const THD *thd,
+                                    const Cached_subgroup *subgroup,
+                                    rpl_binlog_no binlog_no,
+                                    rpl_binlog_pos binlog_pos,
                                     rpl_binlog_pos offset_after_last_statement,
-                                    bool group_commit, const THD *thd);
+                                    bool group_commit);
+#endif // ifndef MYSQL_CLIENT
 
   /**
     Iterator class that sequentially reads groups from the log.
@@ -3150,35 +3252,102 @@ public:
       @param status Will be set to 0 on success, 1 on error. This
       function calls my_error.
     */
-    Group_log_reader(Group_log *group_log, const Group_set *group_set,
-                     rpl_binlog_no binlog_no, rpl_binlog_pos binlog_pos,
-                     enum_read_status *status);
+    Group_log_reader(Group_log *group_log, Sid_map *sid_map);
     /// Destroy this Group_log_reader.
-    ~Group_log_reader();
+    ~Group_log_reader() {}
+    /**
+      Move the reading position forwards to the given position.
+
+      @param group_set If group_set is not NULL, seek and subsequent
+      calls to read_subgroup will ignore groups that are NOT IN
+      group_set (if exclude_group_set == false) respectively groups
+      that are IN group_set (if exclude_group_set == true.)
+      @param exclude_group_set Determines if groups in group_set are
+      to be included or excluded; see above.
+      @param include_anonymous If true, anonymous subgroups will be
+      included.
+      @param first_lgid First LGID to include (or -1 to start from the
+      beginning).
+      @param last_lgid Last LGID to include (or -1 to continue to the
+      end).
+      @param binlog_no If this is not 0, will seek to this binary log
+      (or longer, depending on group_set).
+      @param binlog_pos If binlog_no is not 0, will seek to this
+      position within the binary log (or longer, depending on
+      group_set).
+      my_b_read(). This is used in mysqlbinlog if the binary log is
+      stdin.
+    */
+    enum_read_status seek(const Group_set *group_set, bool exclude_group_set,
+                          bool include_anonymous,
+                          rpl_lgid first_lgid, rpl_lgid last_lgid,
+                          rpl_binlog_no binlog_no, rpl_binlog_pos binlog_pos);
     /**
       Read the next subgroup from this Group_log.
       @param[out] subgroup Subgroup object where the subgroup will be stored.
+      @param[out] owner_type If this is not NULL, the owner_type will
+      be stored here.
+      @retval READ_EOF End of file.
+      @retval READ_SUCCES Ok.
+      @retval READ_ERROR IO error. This function calls my_error.
+    */
+    enum_read_status read_subgroup(Subgroup *subgroup,
+                                   uint32 *owner_type= NULL);
+    /**
+      Read the next subgroup from this Group_log but do not advance
+      the read position.  The subgroup is stored in an internal buffer
+      and the caller is given the pointer to this buffer.  The
+      internal buffer will be overwritten by subsequent calls to
+      peek_subgroup() or read_subgroup() or seek().
+
+      @param[out] subgroup Subgroup* object where the subgroup will be stored.
+      @param[out] owner_type If this is not NULL, the owner_type will
+      be stored here.
       @retval READ_EOF End of file.
       @retval READ_SUCCES Ok.
       @retval READ_ERROR IO error. This function calls my_error.
     */
-    enum_read_status read_subgroup(Subgroup *subgroup);
+    enum_read_status peek_subgroup(Subgroup **subgroup,
+                                   uint32 *owner_type= NULL);
     const char *get_current_filename() const
     { return rot_file_reader.get_source_name(); }
   private:
     /**
+      Return true if the given subgroup is in the set specified by the
+      arguments to seek().
+    */
+    bool subgroup_in_valid_set(Subgroup *subgroup);
+    /**
       Unconditionally read subgroup from file.
 
       This is different from read_subgroup(), because read_subgroup()
       will re-use the subgroup from peeked_subgroup if has_peeked is
       true.
     */
-    enum_read_status do_read_subgroup(Subgroup *subgroup);
+    enum_read_status do_read_subgroup(Subgroup *subgroup,
+                                      uint32 *owner_type= NULL);
 
     /// Sid_map to use in returned Sub_groups.
     Sid_map *output_sid_map;
     /// Sid_map used by Sub_groups in the log.
     Group_log *group_log;
+    /**
+      Only include groups in this set, or only include groups outside
+      this set if exclude_group_set == false.  If this is NULL,
+      include all groups.
+    */
+    const Group_set *group_set;
+    /**
+      If false, this reader only returns groups IN group_set.  If
+      true, this reader only returns groups NOT IN group_set.
+    */
+    bool exclude_group_set;
+    /// If true, this reader will return any anonymous groups it finds.
+    bool include_anonymous;
+    /// First LGID to include, or -1 to start from the beginning.
+    rpl_lgid first_lgid;
+    /// Last LGID to include, or -1 to continue to the end.
+    rpl_lgid last_lgid;
     /// Reader object from which we read raw bytes.
     Rot_file::Rot_file_reader rot_file_reader;
     /// State needed to decode groups.
@@ -3266,7 +3435,12 @@ public:
     PROPAGATE_READ_STATUS(read_unsigned(reader, out));
     if (*out > max_value)
     {
-      my_error(ER_FILE_FORMAT, MYF(0), reader->get_source_name());
+      my_off_t ofs;
+      reader->tell(&ofs);
+      BINLOG_ERROR(("File '%.200s' has an unknown format at position %lld, "
+                    "it may be corrupt.",
+                    reader->get_source_name(), ofs),
+                   (ER_FILE_FORMAT, MYF(0), reader->get_source_name(), ofs));
       DBUG_RETURN(READ_ERROR);
     }
     DBUG_RETURN(READ_OK);
@@ -3281,7 +3455,12 @@ public:
     PROPAGATE_READ_STATUS(read_unsigned(reader, out));
     if (*out > max_value)
     {
-      my_error(ER_FILE_FORMAT, MYF(0), reader->get_source_name());
+      my_off_t ofs;
+      reader->tell(&ofs);
+      BINLOG_ERROR(("File '%.200s' has an unknown format at position %lld, "
+                    "it may be corrupt.",
+                    reader->get_source_name(), ofs),
+                   (ER_FILE_FORMAT, MYF(0), reader->get_source_name(), ofs));
       DBUG_RETURN(READ_ERROR);
     }
     DBUG_RETURN(READ_OK);
@@ -3322,10 +3501,20 @@ public:
     compact-encoded integer, N, followed by N bytes of data.  If an
     odd unknown type code is found, this function seeks past the data
     and returns ok.
+
+    @param reader Reader object to read from.
+    @param min_fatal The smallest even number that is a fatal error.
+    @param min_ignorable The smallest odd number that is not a
+    recognized type code and shall be ignored.
+    @param[out] out The type code will be stored here.
+    @param code By default, the code is read from the first byte of
+    the Reader.  If this parameter is given, the code is instead the
+    value of this parameter and it is assumed that the Reader is
+    positioned after the type code.
   */
   static enum_read_status read_type_code(Reader *reader,
                                          int min_fatal, int min_ignorable,
-                                         uchar *out);
+                                         uchar *out, int code= -1);
   /**
     Read a compact-encoded integer - the string length - followed by a
     string of that length.

=== modified file 'sql/zmutex_cond_array.cc'
--- a/sql/zmutex_cond_array.cc	2011-09-18 16:55:17 +0000
+++ b/sql/zmutex_cond_array.cc	2011-09-30 12:13:10 +0000
@@ -95,7 +95,7 @@ enum_return_status Mutex_cond_array::ens
 error:
   global_lock->unlock();
   global_lock->rdlock();
-  my_error(ER_OUT_OF_RESOURCES, MYF(0));
+  BINLOG_ERROR(("Out of memory."), (ER_OUT_OF_RESOURCES, MYF(0)));
   RETURN_REPORTED_ERROR;
 }
 

=== modified file 'sql/zowned_groups.cc'
--- a/sql/zowned_groups.cc	2011-09-18 16:55:17 +0000
+++ b/sql/zowned_groups.cc	2011-09-30 12:13:10 +0000
@@ -86,7 +86,7 @@ enum_return_status Owned_groups::ensure_
 error:
   sid_lock->unlock();
   sid_lock->rdlock();
-  my_error(ER_OUT_OF_RESOURCES, MYF(0));
+  BINLOG_ERROR(("Out of memory."), (ER_OUT_OF_RESOURCES, MYF(0)));
   RETURN_REPORTED_ERROR;
 }
 
@@ -132,7 +132,7 @@ Owned_groups::add(rpl_sidno sidno, rpl_g
   }
   RETURN_OK;
 error:
-  my_error(ER_OUT_OF_RESOURCES, MYF(0));
+  BINLOG_ERROR(("Out of memory."), (ER_OUT_OF_RESOURCES, MYF(0)));
   RETURN_REPORTED_ERROR;
 }
 
@@ -147,7 +147,7 @@ void Owned_groups::remove(rpl_sidno sidn
   Node *node= get_node(hash, gno);
   if (node != NULL)
   {
-#ifdef NO_DBUG
+#ifdef DBUG_OFF
     my_hash_delete(hash, (uchar *)node);
 #else
     // my_hash_delete returns nonzero if the element does not exist

=== modified file 'sql/zreader.cc'
--- a/sql/zreader.cc	2011-09-26 08:54:32 +0000
+++ b/sql/zreader.cc	2011-09-30 12:13:10 +0000
@@ -20,7 +20,7 @@
 
 
 #include "mysqld_error.h"
-#include "sql_class.h"
+#include "my_dir.h"
 
 
 enum_read_status Reader::file_pread(File fd, uchar *buffer,
@@ -34,13 +34,18 @@ enum_read_status Reader::file_pread(File
     if (abort_loop || current_thd->killed)
     {
       /// @todo: report other error?
-      my_error(ER_ERROR_ON_READ, MYF(0), get_source_name(), errno);
+      BINLOG_ERROR(("Error reading file '%-.200s' (errno: %d)",
+                    my_filename(io_cache->file), errno),
+                   (ER_ERROR_ON_READ, MYF(0),
+                    my_filename(io_cache->file), errno));
       DBUG_RETURN(READ_ERROR);
     }
     */
     if (read_bytes == MY_FILE_ERROR)
     {
-      my_error(ER_ERROR_ON_READ, MYF(0), get_source_name(), errno);
+      BINLOG_ERROR(("Error reading file '%-.200s' (errno: %d)",
+                    my_filename(fd), errno),
+                   (ER_ERROR_ON_READ, MYF(0), my_filename(fd), errno));
       DBUG_RETURN(READ_ERROR);
     }
     if (read_bytes == 0)

=== modified file 'sql/zreturn.cc'
--- a/sql/zreturn.cc	2011-09-28 14:17:30 +0000
+++ b/sql/zreturn.cc	2011-09-30 12:20:42 +0000
@@ -17,10 +17,12 @@
 #include "zgroups.h"
 
 
-#if defined(HAVE_UGID) && !defined(NO_DBUG)
+#if defined(HAVE_UGID) && !defined(DBUG_OFF)
 
 
+#ifndef MYSQL_CLIENT
 #include "sql_class.h"
+#endif // ifndef MYSQL_CLIENT
 
 
 void check_return_status(enum_return_status status, const char *action,
@@ -31,12 +33,15 @@ void check_return_status(enum_return_sta
     DBUG_ASSERT(allow_unreported || status == RETURN_STATUS_REPORTED_ERROR);
     if (status == RETURN_STATUS_REPORTED_ERROR)
     {
-      DBUG_ASSERT(current_thd == NULL ||
-                  current_thd->get_stmt_da()->status() == Diagnostics_area::DA_ERROR);
+#if !defined(MYSQL_CLIENT) && !defined(DBUG_OFF)
+      THD *thd= current_thd;
+      DBUG_ASSERT(thd == NULL ||
+                  thd->get_stmt_da()->status() == Diagnostics_area::DA_ERROR);
+#endif
     }
     DBUG_PRINT("info", ("%s error %d (%s)", action, status, status_name));
   }
 }
 
 
-#endif // HAVE_UGID && ! NO_DBUG
+#endif // HAVE_UGID && ! DBUG_OFF

=== modified file 'sql/zrot_file.cc'
--- a/sql/zrot_file.cc	2011-09-26 08:54:32 +0000
+++ b/sql/zrot_file.cc	2011-09-30 12:13:10 +0000
@@ -36,7 +36,8 @@ enum_return_status Rot_file::open(const
                       base_filename, sub_file.filename, writable));
 
   sub_file.fd= my_open(sub_file.filename,
-                       (writable ? O_RDWR | O_CREAT : O_RDONLY) | O_BINARY,
+                       (writable ? O_RDWR | O_CREAT | O_APPEND : O_RDONLY) |
+                       O_BINARY,
                        MYF(MY_WME));
   if (sub_file.fd < 0)
     RETURN_REPORTED_ERROR;
@@ -51,6 +52,7 @@ enum_return_status Rot_file::open(const
   switch (read_status)
   {
   case READ_OK:
+    PROPAGATE_REPORTED_ERROR(reader.tell(&sub_file.header_length));
     break;
   case READ_ERROR:
     RETURN_REPORTED_ERROR;
@@ -61,8 +63,8 @@ enum_return_status Rot_file::open(const
     // file was empty: write the header
     if (Compact_coder::append_unsigned(&appender, 0) != APPEND_OK)
       RETURN_REPORTED_ERROR;
+    PROPAGATE_REPORTED_ERROR(appender.tell(&sub_file.header_length));
   }
-  PROPAGATE_REPORTED_ERROR(appender.tell(&sub_file.header_length));
 
   _is_open= true;
   RETURN_OK;

=== modified file 'sql/zsid_map.cc'
--- a/sql/zsid_map.cc	2011-09-26 13:11:26 +0000
+++ b/sql/zsid_map.cc	2011-09-30 12:13:10 +0000
@@ -25,7 +25,7 @@
 
 
 Sid_map::Sid_map(Checkable_rwlock *_sid_lock)
-  : sid_lock(_sid_lock), fd(-1)
+  : sid_lock(_sid_lock), fd(-1), status(CLOSED_OK)
 {
   DBUG_ENTER("Sid_map::Sid_map");
   my_init_dynamic_array(&_sidno_to_sid, sizeof(Node *), 8, 8);
@@ -57,12 +57,13 @@ enum_return_status Sid_map::clear()
                my_free, 0);
   reset_dynamic(&_sidno_to_sid);
   reset_dynamic(&_sorted);
-  PROPAGATE_REPORTED_ERROR(appender.truncate(0));
+  if (is_open())
+    PROPAGATE_REPORTED_ERROR(appender.truncate(0));
   RETURN_OK;
 }
 
 
-enum_return_status Sid_map::open(const char *_filename)
+enum_return_status Sid_map::open(const char *_filename, bool writable)
 {
   DBUG_ENTER("Sid_map::open(const char *)");
   if (is_open())
@@ -71,12 +72,17 @@ enum_return_status Sid_map::open(const c
   strcpy(filename, _filename);
   DBUG_ASSERT(strlen(_filename) < FN_REFLEN);
 
-  fd= my_open(filename, O_RDWR | O_CREAT | O_BINARY, MYF(MY_WME));
+  fd= my_open(filename, (writable ? O_RDWR | O_CREAT : O_RDONLY) | O_BINARY,
+              MYF(MY_WME));
   if (fd == -1)
+  {
+    status= CLOSED_ERROR;
     RETURN_REPORTED_ERROR;
+  }
   File_reader reader;
   reader.set_file(fd);
-  appender.set_file(fd);
+  if (writable)
+    appender.set_file(fd);
   my_off_t pos= 0;
   rpl_sidno sidno= 0;
   uchar type_code;
@@ -90,7 +96,7 @@ enum_return_status Sid_map::open(const c
     {
     case READ_ERROR: goto error;
     case READ_TRUNCATED: goto truncate;
-    case READ_EOF: RETURN_OK;
+    case READ_EOF: goto ok;
     case READ_OK: break;
     }
     DBUG_ASSERT(type_code == 0);
@@ -106,11 +112,22 @@ enum_return_status Sid_map::open(const c
   }
 
 truncate:
-  if (appender.truncate(pos) != RETURN_STATUS_OK)
-    goto error;
+  if (writable)
+    if (appender.truncate(pos) != RETURN_STATUS_OK)
+      goto error;
+  /*FALLTHROUGH*/
+ok:
+  if (writable)
+    status= OPEN;
+  else
+  {
+    close();
+    status= CLOSED_OK;
+  }
   RETURN_OK;
 
 error:
+  status= CLOSED_ERROR;
   close();
   RETURN_REPORTED_ERROR;
 }
@@ -119,7 +136,7 @@ error:
 rpl_sidno Sid_map::add_permanent(const rpl_sid *sid, bool _sync)
 {
   DBUG_ENTER("Sid_map::add_permanent");
-#ifndef NO_DBUG
+#ifndef DBUG_OFF
   char buf[Uuid::TEXT_LENGTH + 1];
   sid->to_string(buf);
   DBUG_PRINT("info", ("SID=%s", buf));
@@ -128,10 +145,10 @@ rpl_sidno Sid_map::add_permanent(const r
   Node *node= (Node *)my_hash_search(&_sid_to_sidno, sid->bytes,
                                      rpl_sid::BYTE_LENGTH);
   if (node != NULL)
+  {
+    DBUG_PRINT("info", ("existed as sidno=%d", node->sidno));
     DBUG_RETURN(node->sidno);
-
-  if (fd == -1)
-    DBUG_RETURN(-1);
+  }
 
   sid_lock->unlock();
   sid_lock->wrlock();
@@ -147,6 +164,7 @@ rpl_sidno Sid_map::add_permanent(const r
         write_to_disk(sidno, sid) != RETURN_STATUS_OK ||
         (_sync && sync() != RETURN_STATUS_OK))
       sidno= -1;
+    /// @todo: remove node on write error /sven
   }
 
   sid_lock->unlock();
@@ -158,12 +176,31 @@ rpl_sidno Sid_map::add_permanent(const r
 enum_return_status Sid_map::write_to_disk(rpl_sidno sidno, const rpl_sid *sid)
 {
   DBUG_ENTER("Sid_map::write_to_disk");
+  if (status == CLOSED_OK)
+    RETURN_OK;
+  else if (status == CLOSED_ERROR)
+  {
+    /// @todo : make error message correct
+    BINLOG_ERROR(("Error writing file '%-.200s' (errno: %d)",
+                  appender.get_source_name(), 0),
+                 (ER_ERROR_ON_WRITE, MYF(0), appender.get_source_name(), 0));
+    RETURN_REPORTED_ERROR;
+  }
   sid_lock->assert_some_lock();
+  char buf[Uuid::TEXT_LENGTH + 1];
+  sid->to_string(buf);
+  DBUG_PRINT("info", ("Writing sidno=%d sid=%s\n", sidno, buf));
   if (!appender.is_open())
   {
-    my_error(ER_ERROR_ON_WRITE, MYF(0));
+    BINLOG_ERROR(("Error writing file '%-.200s' (errno: %d)",
+                  appender.get_source_name(), errno),
+                 (ER_ERROR_ON_WRITE, MYF(0), appender.get_source_name(),
+                  errno));
     RETURN_REPORTED_ERROR;
   }
+  my_off_t position;
+  appender.tell(&position);
+  DBUG_PRINT("info", ("At position %llu\n", position));
   uchar type_code= 0;
   if (appender.append(&type_code, 1) != APPEND_OK ||
       sid->append(&appender) != APPEND_OK)
@@ -217,7 +254,7 @@ enum_return_status Sid_map::add_node(rpl
     }
     free(node);
   }
-  my_error(ER_OUT_OF_RESOURCES, MYF(0));
+  BINLOG_ERROR(("Out of memory."), (ER_OUT_OF_RESOURCES, MYF(0)));
   RETURN_REPORTED_ERROR;
 }
 
@@ -225,6 +262,16 @@ enum_return_status Sid_map::add_node(rpl
 enum_return_status Sid_map::sync()
 {
   DBUG_ENTER("Sid_map::flush()");
+  if (status == CLOSED_OK)
+    RETURN_OK;
+  else if (status == CLOSED_ERROR)
+  {
+    /// @todo : make error message correct
+    BINLOG_ERROR(("Error writing file '%-.200s' (errno: %d)",
+                  appender.get_source_name(), 0),
+                 (ER_ERROR_ON_WRITE, MYF(0), appender.get_source_name(), 0));
+    RETURN_REPORTED_ERROR;
+  }
   if (my_sync(fd, MYF(MY_WME)) != 0)
   {
     close(); // this is a fatal error, file may be corrupt
@@ -241,7 +288,11 @@ enum_return_status Sid_map::close()
   fd= -1;
   appender.set_file(-1);
   if (ret != 0)
+  {
+    status= CLOSED_ERROR;
     RETURN_REPORTED_ERROR;
+  }
+  status= CLOSED_OK;
   RETURN_OK;
 }
 

=== added file 'sql/zstring_appender.cc'
--- a/sql/zstring_appender.cc	1970-01-01 00:00:00 +0000
+++ b/sql/zstring_appender.cc	2011-09-30 12:13:10 +0000
@@ -0,0 +1,54 @@
+/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+
+   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; 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
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+
+#include "zgroups.h"
+
+
+#ifdef HAVE_UGID
+
+
+#include "sql_string.h"
+
+
+enum_return_status String_appender::do_append(const uchar *buf, size_t length)
+{
+  DBUG_ENTER("String_appender::append");
+  if (str->append((const char *)buf, length))
+  {
+    my_error(ER_OUT_OF_RESOURCES, MYF(0));
+    RETURN_REPORTED_ERROR;
+  }
+  RETURN_OK;
+}
+
+
+enum_return_status String_appender::do_truncate(my_off_t new_position)
+{
+  DBUG_ENTER("String_appender::truncate");
+  str->length(new_position);
+  RETURN_OK;
+}
+
+
+enum_return_status String_appender::do_tell(my_off_t *position) const
+{
+  DBUG_ENTER("String_appender::tell");
+  *position= str->length();
+  RETURN_OK;
+}
+
+
+#endif

=== modified file 'sql/zsubgroup_coder.cc'
--- a/sql/zsubgroup_coder.cc	2011-09-26 08:54:32 +0000
+++ b/sql/zsubgroup_coder.cc	2011-09-30 12:13:10 +0000
@@ -21,11 +21,12 @@
 
 enum_append_status Subgroup_coder::append(
   Appender *appender, const Cached_subgroup *cs,
+  rpl_binlog_no binlog_no, rpl_binlog_pos _binlog_pos,
   rpl_binlog_pos offset_after_last_statement,
   bool group_commit, uint32 owner_type)
 {
   DBUG_ENTER("Subgroup_coder::append");
-  uchar buf[FULL_SUBGROUP_SIZE];
+  uchar buf[2 + FULL_SUBGROUP_SIZE];
   uchar *p= buf;
   // write type code
   *p= SPECIAL_TYPE, p++;
@@ -35,41 +36,50 @@ enum_append_status Subgroup_coder::appen
   int4store(p, cs->sidno); p+= 4;
   int8store(p, cs->gno); p+= 8;
   int8store(p, binlog_no); p+= 8;
-  int8store(p, binlog_pos); p+= 8;
+  int8store(p, _binlog_pos); p+= 8;
   int8store(p, cs->binlog_length); p+= 8;
   int8store(p, offset_after_last_statement); p+= 8;
   int4store(p, owner_type); p+= 4;
   *p= cs->group_end ? 1 : 0, p++;
   *p= group_commit ? 1 : 0, p++;
   // append
+  DBUG_ASSERT(p - buf == 2 + FULL_SUBGROUP_SIZE);
   PROPAGATE_APPEND_STATUS(appender->append(buf, p - buf));
   // update state
   lgid++;
-  binlog_pos+= cs->binlog_length;
   DBUG_RETURN(APPEND_OK);
 }
 
 
-enum_read_status Subgroup_coder::read(Reader *reader, Subgroup *out)
+enum_read_status Subgroup_coder::read(Reader *reader, Subgroup *out, uint32 *owner_type)
 {
   DBUG_ENTER("Subgroup_coder::read");
   uchar buf[FULL_SUBGROUP_SIZE];
   PROPAGATE_READ_STATUS(reader->read(buf, 2));
-  READER_CHECK_FORMAT(reader,
-                      buf[0] == SPECIAL_TYPE &&
-                      (buf[1] == FULL_SUBGROUP ||
-                       (buf[1] >= MIN_IGNORABLE_TYPE && (buf[1] & 1) == 1)));
+  READER_CHECK_FORMAT(reader, buf[0] == SPECIAL_TYPE);
+  PROPAGATE_READ_STATUS(Compact_coder::read_type_code(reader, MIN_FATAL_TYPE,
+                                                      MIN_IGNORABLE_TYPE,
+                                                      &(buf[1]), buf[1]));
+  READER_CHECK_FORMAT(reader, buf[1] == FULL_SUBGROUP || (buf[1] & 1) == 1);
   PROPAGATE_READ_STATUS_NOEOF(reader->read(buf, FULL_SUBGROUP_SIZE));
 #define UNPACK(FIELD, N) out->FIELD= sint ## N ## korr(p), p += N
   uchar *p= buf;
+  out->type= (enum_subgroup_type)*p;
+  p++;
   UNPACK(sidno, 4);
   UNPACK(gno, 8);
   UNPACK(binlog_no, 8);
   UNPACK(binlog_pos, 8);
   UNPACK(binlog_length, 8);
   UNPACK(binlog_offset_after_last_statement, 8);
+  if (owner_type != NULL)
+    *owner_type= uint4korr(p);
+  p+= 4;
+  READER_CHECK_FORMAT(reader, (*p == 0 || *p == 1) && (p[1] == 0 || p[1] == 1));
   out->group_end= *p == 1 ? true : false, p++;
   out->group_commit= *p == 1 ? true : false, p++;
+  DBUG_ASSERT(p - buf == (int)FULL_SUBGROUP_SIZE);
+  // update state
   out->lgid= ++lgid;
   DBUG_RETURN(READ_OK);
 }

=== modified file 'sql/zugid_specification.cc'
--- a/sql/zugid_specification.cc	2011-09-28 14:17:30 +0000
+++ b/sql/zugid_specification.cc	2011-09-30 12:20:42 +0000
@@ -43,7 +43,8 @@ enum_return_status Ugid_specification::p
   {
     if (group.parse(&mysql_bin_log.sid_map, text) != 0)
     {
-      my_error(ER_MALFORMED_GROUP_SPECIFICATION, MYF(0), text);
+      BINLOG_ERROR(("Malformed group specification '%.200s'.", text),
+                   (ER_MALFORMED_GROUP_SPECIFICATION, MYF(0), text));
       RETURN_REPORTED_ERROR;
     }
     type= UGID;

=== modified file 'sql/zuuid.cc'
--- a/sql/zuuid.cc	2011-09-26 08:54:32 +0000
+++ b/sql/zuuid.cc	2011-09-30 12:13:10 +0000
@@ -102,7 +102,7 @@ bool Uuid::is_valid(const char *s)
 }
 
 
-int Uuid::to_string(char *buf) const
+size_t Uuid::to_string(char *buf) const
 {
   DBUG_ENTER("Uuid::to_string");
   static const char byte_to_hex[]= "0123456789ABCDEF";

=== modified file 'unittest/gunit/rpl_group_set-t.cc'
--- a/unittest/gunit/rpl_group_set-t.cc	2011-09-25 16:01:57 +0000
+++ b/unittest/gunit/rpl_group_set-t.cc	2011-09-30 12:13:10 +0000
@@ -814,6 +814,7 @@ TEST_F(GroupTest, Group_containers)
           ugid_flush_group_cache(thd, &lock,
                                  &group_log_state, NULL/*group log*/,
                                  &stmt_cache, &trx_cache,
+                                 1/*binlog_no*/, 1/*binlog_pos*/,
                                  stmt_contains_logged_subgroup ?
                                  20 + rand() % 99 : -1
                                  /*offset_after_last_statement*/);
@@ -837,6 +838,7 @@ TEST_F(GroupTest, Group_containers)
             ugid_flush_group_cache(thd, &lock, 
                                    &group_log_state, NULL/*group log*/,
                                    &trx_cache, &trx_cache,
+                                   1/*binlog_no*/, 1/*binlog_pos*/,
                                    trx_contains_logged_subgroup ?
                                    20 + rand() % 99 : -1
                                    /*offset_after_last_statement*/);

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk-wl3584-labs branch (luis.soares:3460 to 3461)WL#3584Luis Soares2 Oct