List:Commits« Previous MessageNext Message »
From:Sven Sandberg Date:May 3 2011 5:52pm
Subject:bzr commit into mysql-trunk branch (sven.sandberg:3338)
View as plain text  
#At file:///home/sven/bzr/w3584-ugid/trunk/ based on revid:sven.sandberg@stripped

 3338 Sven Sandberg	2011-05-03
      x

    added:
      sql/zgroup_cache.cc
      sql/zgroup_execution.cc
    modified:
      libmysqld/CMakeLists.txt
      sql/CMakeLists.txt
      sql/binlog.cc
      sql/binlog.h
      sql/share/errmsg-utf8.txt
      sql/sys_vars.cc
      sql/zgroup_log_state.cc
      sql/zgroup_set.cc
      sql/zgroups.h
      sql/zowned_groups.cc
      sql/zsid_map.cc
      unittest/gunit/rpl_group_set-t.cc
=== modified file 'libmysqld/CMakeLists.txt'
--- a/libmysqld/CMakeLists.txt	2011-04-28 16:16:51 +0000
+++ b/libmysqld/CMakeLists.txt	2011-05-03 17:52:32 +0000
@@ -71,7 +71,8 @@ SET(SQL_EMBEDDED_SOURCES
   ../sql/zugid_specification.cc
   ../sql/zgroup_log_state.cc
   ../sql/zowned_groups.cc
-  ${IMPORTED_SOURCES}
+  ../sql/zgroup_cache.cc
+ ${IMPORTED_SOURCES}
 )
 
 

=== modified file 'sql/CMakeLists.txt'
--- a/sql/CMakeLists.txt	2011-04-28 16:16:51 +0000
+++ b/sql/CMakeLists.txt	2011-05-03 17:52:32 +0000
@@ -185,7 +185,7 @@ TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATI
   ${SSL_LIBRARIES})
 
 SET (BINLOG_SOURCE zuuid.cc zsid_map.cc zgroup_set.cc zugid_specification.cc
-                   zgroup_log_state.cc zowned_groups.cc
+                   zgroup_log_state.cc zowned_groups.cc zgroup_cache.cc
                    log_event.cc log_event_old.cc binlog.cc sql_binlog.cc
 		   rpl_filter.cc rpl_record.cc rpl_record_old.cc rpl_utility.cc
 		   rpl_injector.cc)

=== modified file 'sql/binlog.cc'
--- a/sql/binlog.cc	2011-04-04 08:47:25 +0000
+++ b/sql/binlog.cc	2011-05-03 17:52:32 +0000
@@ -1557,6 +1557,9 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_
    checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF),
    relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF),
    description_event_for_exec(0), description_event_for_queue(0)
+#ifdef HAVE_UGID
+  , sid_map(), group_log_state(&sid_map, &group_log_state_init_status)
+#endif
 {
   /*
     We don't want to initialize locks here as such initialization depends on
@@ -1568,6 +1571,7 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_
   bzero((char*) &index_file, sizeof(index_file));
   bzero((char*) &purge_index_file, sizeof(purge_index_file));
   bzero((char*) &crash_safe_index_file, sizeof(crash_safe_index_file));
+  DBUG_ASSERT(group_log_state_init_status == GS_SUCCESS);
 }
 
 /* this is called only once */

=== modified file 'sql/binlog.h'
--- a/sql/binlog.h	2011-04-28 16:16:51 +0000
+++ b/sql/binlog.h	2011-05-03 17:52:32 +0000
@@ -309,7 +309,9 @@ public:
   inline uint32 get_open_count() { return open_count; }
 
 #ifdef HAVE_UGID
+  enum_group_status group_log_state_init_status;
   Sid_map sid_map;
+  Group_log_state group_log_state;
 #endif
 };
 

=== modified file 'sql/share/errmsg-utf8.txt'
--- a/sql/share/errmsg-utf8.txt	2011-04-26 10:48:53 +0000
+++ b/sql/share/errmsg-utf8.txt	2011-05-03 17:52:32 +0000
@@ -6501,3 +6501,6 @@ ER_CHANGE_RPL_INFO_REPOSITORY_FAILURE
 
 ER_VARIABLE_NOT_SETTABLE_IN_SUB_STATEMENT
   eng "The system variable %s can only be set in top-level statements."
+
+ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION
+  eng "The system variable %s can not be set when there is an ongoing transaction."

=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc	2011-04-28 16:16:51 +0000
+++ b/sql/sys_vars.cc	2011-05-03 17:52:32 +0000
@@ -363,21 +363,30 @@ static bool check_has_super(sys_var *sel
 }
 
 #ifdef HAVE_UGID
-static bool is_top_level_stmt(sys_var *self, THD *thd, set_var *var)
+static bool check_top_level_stmt(sys_var *self, THD *thd, set_var *var)
 {
   if (thd->in_sub_stmt)
   {
     my_error(ER_VARIABLE_NOT_SETTABLE_IN_SUB_STATEMENT, MYF(0), var->var->name);
-    return false;
+    return true;
   }
-  return true;
+  return false;
 }
 
-static bool is_top_level_stmt_and_super(sys_var *self, THD *thd, set_var *var)
+static bool check_top_level_stmt_and_super(sys_var *self, THD *thd, set_var *var)
 {
-  // settable only by super in top-level statements
   return (check_has_super(self, thd, var) ||
-          !is_top_level_stmt(self, thd, var));
+          check_top_level_stmt(self, thd, var));
+}
+
+static bool check_outside_transaction(sys_var *self, THD *thd, set_var *var)
+{  
+  if (thd->in_active_multi_stmt_transaction())
+  {
+    my_error(ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION, MYF(0), var->var->name);
+    return true;
+  }
+  return false;
 }
 #endif
 
@@ -3424,37 +3433,43 @@ static Sys_var_tz Sys_time_zone(
 
 
 #ifdef HAVE_UGID
-/*static bool fix_ugid_next(sys_var *self, THD *thd, enum_var_type type)
-{
-  
-}*/
 
-static Sys_var_ugid_specification Sys_ugid_next(
-       "ugid_next",
-       "The Universal Group Identifier for the following statement.",
-       SESSION_ONLY(ugid_next), NO_CMD_LINE,
-       DEFAULT("AUTOMATIC"), NO_MUTEX_GUARD,
-       NOT_IN_BINLOG, ON_CHECK(is_top_level_stmt_and_super));
+static bool check_ugid_next_list(sys_var *self, THD *thd, set_var *var)
+{
+  if (check_top_level_stmt_and_super(self, thd, var) ||
+      check_outside_transaction(self, thd, var))
+    return true;
+  // TODO: check that there is no ongoing super-group (i.e., check
+  // that we don't own any groups)
+  return false;
+}
 
 static Sys_var_group_set Sys_ugid_next_list(
        "ugid_next_list",
        "The set of groups that will be part of the following super-group.",
        SESSION_ONLY(ugid_next_list), NO_CMD_LINE,
        DEFAULT(NULL), NO_MUTEX_GUARD,
-       NOT_IN_BINLOG, ON_CHECK(is_top_level_stmt_and_super));
+       NOT_IN_BINLOG, ON_CHECK(check_ugid_next_list));
+
+static Sys_var_ugid_specification Sys_ugid_next(
+       "ugid_next",
+       "The Universal Group Identifier for the following statement.",
+       SESSION_ONLY(ugid_next), NO_CMD_LINE,
+       DEFAULT("AUTOMATIC"), NO_MUTEX_GUARD,
+       NOT_IN_BINLOG, ON_CHECK(check_top_level_stmt_and_super));
 
 static Sys_var_mybool Sys_ugid_end(
        "ugid_end",
-       "If true, the next statement is the last one in an ending sub-group.",
+       "If true, the next statement is the last one in a group.",
        SESSION_ONLY(ugid_end), NO_CMD_LINE,
        DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG,
-       ON_CHECK(is_top_level_stmt_and_super));
+       ON_CHECK(check_top_level_stmt_and_super));
 
 static Sys_var_mybool Sys_ugid_commit(
        "ugid_commit",
-       "If true, the next statement is the last one in an ending sub-group.",
+       "If true, the next statement is the last one in a super-group.",
        SESSION_ONLY(ugid_commit), NO_CMD_LINE,
        DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG,
-       ON_CHECK(is_top_level_stmt_and_super));
+       ON_CHECK(check_top_level_stmt_and_super));
 
 #endif

=== added file 'sql/zgroup_cache.cc'
--- a/sql/zgroup_cache.cc	1970-01-01 00:00:00 +0000
+++ b/sql/zgroup_cache.cc	2011-05-03 17:52:32 +0000
@@ -0,0 +1,153 @@
+/* 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
+
+/**
+  Creates the used_sidnos bitmap, with space for the given max_sidno.
+
+  @param max_sidno The maximum sidno that will be supported by this Group_cache.
+  @return GS_SUCCESS or GS_OUT_OF_MEMORY.
+*/
+enum_group_status Group_cache::allocate_bitmap(rpl_sidno max_sidno)
+{
+  return my_bitmap_init(&used_sidnos, NULL,
+                        /* round up to nearest sizeof(my_bitmap_map) bytes */
+                        (max_sidno + sizeof(my_bitmap_map) * CHAR_BIT - 1) &
+                        ~(sizeof(my_bitmap_map) * CHAR_BIT - 1), 0) ?
+    GS_ERROR_OUT_OF_MEMORY : GS_SUCCESS;
+}
+
+Group_cache::Group_cache(rpl_sidno default_max_sidno, enum_group_status *error)
+{
+  DBUG_ENTER("Group_cache::Group_cache");
+  my_init_dynamic_array(&groups, sizeof(Cached_subgroup), 8, 8);
+  *error= allocate_bitmap(default_max_sidno);
+  DBUG_VOID_RETURN;
+}
+
+enum_group_status Group_cache::append(Cached_subgroup *group)
+{
+  DBUG_ENTER("Group_cache::append");
+  if (insert_dynamic(&groups, group) == 0)
+    DBUG_RETURN(GS_ERROR_OUT_OF_MEMORY);
+  bitmap_set_bit(&used_sidnos, group->sidno);
+  DBUG_RETURN(GS_SUCCESS);
+}
+
+enum_group_status Group_cache::clear(rpl_sidno max_sidno)
+{
+  DBUG_ENTER("Group_cache::clear");
+  groups.elements= 0;
+  if (max_sidno > used_sidnos.n_bits)
+  {
+    bitmap_free(&used_sidnos);
+    DBUG_RETURN(allocate_bitmap(max_sidno));
+  }
+  DBUG_RETURN(GS_SUCCESS);
+}
+
+/**
+  Return true if this Group_cache contains the given group.
+
+  @param sidno The SIDNO of the group to check.
+  @param gno The GNO of the group to check.
+  @retval true The group exists in this cache.
+  @retval false The group does not exist in this cache.
+*/
+bool Group_cache::contains_group(rpl_sidno sidno, rpl_gno gno) const
+{
+  for (int i= 0; i < n_subgroups; i++)
+  {
+    Cached_subgroup *cs= get_unsafe_pointer(i);
+    if (cs->gno == gno && cs->sidno= sidno)
+      return true;
+  }
+  return false;
+}
+
+enum_group_status
+Group_cache::add_dummy_group_for_missing_group(Group_log_state *gls,
+                                               rpl_sidno sidno, rpl_gno gno)
+{
+  if (!gls->is_ended(sidno, gno) && !gls->is_partial(sidno, gno) &&
+      !contains_group(sidno, gno))
+  {
+    Cached_subgroup cs=
+      {
+        DUMMY_SUBGROUP, sidno, gno,
+        0/*binlog_pos*/, 0/*binlog_length*/,
+        0/*binlog_offset_after_last_statement*/,
+        false/*group_end*/
+      };
+    GROUP_STATUS_THROW(append(&cs));
+  }
+}
+
+enum_group_status
+Group_cache::add_dummy_groups_for_missing_groups(Group_log_state *gls,
+                                                 Group_set *group_set)
+{
+  DBUG_ENTER("Group_cache::add_dummy_groups_for_missing_groups");
+  Group_set::Group_iterator git(group_set);
+  /*
+    @todo: This algorithm is
+    O(n_groups_in_cache*n_groups_in_group_set) because contains_group
+    is O(n_groups_in_cache).  We can optimize this to
+    O(n_groups_in_cache+n_groups_in_group_set), as follows: create a
+    HASH and copy all groups from the cache to the HASH.  Then use the
+    HASH to detect if the group is in the cache or not.  This has some
+    overhead so should only be used if
+    n_groups_in_cache*n_groups_in_group_set is significantly bigger
+    than n_groups_in_cache+n_groups_in_group_set elements. /Sven
+  */
+  for (Group g= git.get(); g.sidno; git.next())
+    GROUP_STATUS_THROW(add_dummy_group_for_missing_group(gls, g.sidno, g.gno));
+  DBUG_RETURN(GS_SUCCESS);
+}
+
+enum_group_status Group_cache::update_group_log_state(Group_log_state *gls)
+{
+  gls->lock_sidnos(&used_sidnos);
+
+  enum_group_status ret= GS_SUCCESS;
+  bool updated= false;
+  int n_subgroups= get_n_subgroups();
+
+  for (int i= 0; i < n_subgroups; i++)
+  {
+    Cached_subgroup *cs= get_unsafe_pointer(i);
+    if (cs->group_end)
+    {
+      updated= true;
+      ret= gls->end_group(cs->sidno, cs->gno);
+      if (ret != GS_SUCCESS)
+        break;
+    }
+    else if (!gls->mark_partial(cs->sidno, cs->gno))
+      updated= true;
+  }
+
+  if (updated)
+    gls->broadcast_sidnos(&used_sidnos);
+  else
+    gls->unlock_sidnos(&used_sidnos);
+
+  return ret;
+}
+
+#endif

=== added file 'sql/zgroup_execution.cc'
--- a/sql/zgroup_execution.cc	1970-01-01 00:00:00 +0000
+++ b/sql/zgroup_execution.cc	2011-05-03 17:52:32 +0000
@@ -0,0 +1,41 @@
+/* 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
+
+void flush_cache(THD *thd, Group_cache *gc)
+{
+  Group_set *ugid_next_list= thd->variables.ugid_next_list.group_set;
+  if (ugid_next_list != NULL)
+    gc->add_dummy_groups_for_missing_groups(&mysql_bin_log.group_log_state,
+                                            ugid_next_list);
+  else
+  {
+    Ugid_specification *ugid_next= &thd->variables.ugid_next;
+    if (ugid_next->type == UGID)
+      gc->add_dummy_group_for_missing_group(&mysql_bin_log.group_log_state,
+                                            ugid_next->sidno, ugid_next->gno);
+  }
+  /*
+    @todo: We have not yet implemented the group log. When that is
+    done, uncomment the next line. /Sven
+  */
+  // gc->write_to_log();
+  gc->update_group_log_state(&mysql_bin_log.group_log_state);
+}
+
+#endif

=== modified file 'sql/zgroup_log_state.cc'
--- a/sql/zgroup_log_state.cc	2011-04-28 16:16:51 +0000
+++ b/sql/zgroup_log_state.cc	2011-05-03 17:52:32 +0000
@@ -17,18 +17,6 @@
 
 #ifdef HAVE_UGID
 
-Group_log_state::enum_group_completeness
-Group_log_state::get_group_completeness(rpl_sidno sidno, rpl_gno gno,
-                                        rpl_thread_id *owner) const
-{
-  if (ended_groups.contains(sidno, gno))
-    return ENDED;
-  else if ((*owner= owned_groups.get_owner(sidno, gno)) != NO_THREAD)
-    return OWNED;
-  else
-    return FREE;
-}
-
 enum_group_status
 Group_log_state::acquire_ownership(rpl_sidno sidno, rpl_gno gno,
                                    rpl_thread_id owner)
@@ -46,7 +34,7 @@ enum_group_status Group_log_state::end_g
   DBUG_RETURN(ended_groups.add(sidno, gno));
 }
 
-rpl_gno Group_log_state::end_automatic_group(rpl_sidno sidno)
+rpl_gno Group_log_state::get_automatic_gno(rpl_sidno sidno)
 {
   DBUG_ENTER("Group_log_state::end_automatic_group");
   ended_groups.ensure_sidno(sidno);
@@ -67,4 +55,28 @@ rpl_gno Group_log_state::end_automatic_g
   }
 }
 
+void Group_log_state::lock_sidnos(MY_BITMAP *set)
+{
+  rpl_sidno n_bits= set->n_bits;
+  for (rpl_sidno i= 0; i < n_bits; i++)
+    if (bitmap_is_set(set, i))
+      lock_sidno(i);
+}
+
+void Group_log_state::unlock_sidnos(MY_BITMAP *set)
+{
+  rpl_sidno n_bits= set->n_bits;
+  for (rpl_sidno i= 0; i < n_bits; i++)
+    if (bitmap_is_set(set, i))
+      unlock_sidno(i);
+}
+
+void Group_log_state::broadcast_sidnos(MY_BITMAP *set)
+{
+  rpl_sidno n_bits= set->n_bits;
+  for (rpl_sidno i= 0; i < n_bits; i++)
+    if (bitmap_is_set(set, i))
+      broadcast_sidno(i);
+}
+
 #endif /* HAVE_UGID */

=== modified file 'sql/zgroup_set.cc'
--- a/sql/zgroup_set.cc	2011-04-28 16:16:51 +0000
+++ b/sql/zgroup_set.cc	2011-05-03 17:52:32 +0000
@@ -81,12 +81,6 @@ Group_set::~Group_set()
   DBUG_VOID_RETURN;
 }
 
-/**
-  Allocates space for the given sidno in the intervals array.
-
-  @param sidno The SIDNO.
-  @return GS_SUCCESS or GS_ERROR_OUT_OF_MEMORY.
-*/
 enum_group_status Group_set::ensure_sidno(rpl_sidno sidno)
 {
   DBUG_ENTER("Group_set::ensure_sidno");

=== modified file 'sql/zgroups.h'
--- a/sql/zgroups.h	2011-04-28 16:16:51 +0000
+++ b/sql/zgroups.h	2011-05-03 17:52:32 +0000
@@ -29,6 +29,7 @@
 
 #include "mysqld.h"
 #include "hash.h"
+#include "lf.h"
 
 typedef int64 rpl_gno;
 typedef int32 rpl_sidno;
@@ -68,6 +69,16 @@ const rpl_thread_id NO_THREAD= -1;
 const int MAX_GNO_TEXT_LENGTH= 19;
 
 /**
+  Holds information about a group: the sidno and the gno.
+*/
+class Group
+{
+public:
+  rpl_sidno sidno;
+  rpl_gno gno;
+};
+
+/**
   Parse a GNO from a string.
 
   @param s Pointer to the string. *s will advance to the end of the
@@ -101,11 +112,11 @@ public:
   /**
     Copies the given 16-byte data to this UUID.
   */
-  void copy_from(const unsigned char *data) { memcpy(uuid, data, BYTE_LENGTH); };
+  void copy_from(const unsigned char *data) { memcpy(uuid, data, BYTE_LENGTH); }
   /**
     Copies the given UUID object to this UUID.
   */
-  void copy_from(const Uuid *data) { copy_from(data->uuid); };
+  void copy_from(const Uuid *data) { copy_from(data->uuid); }
   /**
     Returns true if this UUID is equal the given UUID.
   */
@@ -209,43 +220,100 @@ public:
   */
   int get_max_sidno() const { return _sidno_to_sid.elements; }
 
-  void lock(rpl_sidno sidno)
+private:
+  class Node
   {
-    mysql_mutex_lock(&sidno_to_node(sidno)->mutex);
+  public:
+    rpl_sidno sidno;
+    rpl_sid sid;
   };
-  void unlock(rpl_sidno sidno)
+
+  inline Node *sidno_to_node(rpl_sidno sidno) const
   {
-    mysql_mutex_unlock(&sidno_to_node(sidno)->mutex);
+    return *dynamic_element(&_sidno_to_sid, sidno - 1, Node **);
   }
-  void broadcast(rpl_sidno sidno)
+
+  DYNAMIC_ARRAY _sidno_to_sid;
+  HASH _sid_to_sidno;
+  DYNAMIC_ARRAY _sorted;
+};
+
+class Mutex_cond_array
+{
+public:
+  Mutex_cond_array()
   {
-    mysql_cond_broadcast(&sidno_to_node(sidno)->cond);
+    lf_dynarray_init(&array, sizeof(Mutex_cond));
+    mysql_mutex_init(NULL, &internal_update_mutex, NULL);
   }
-  void wait_for_update(rpl_sidno sidno)
+  ~Mutex_cond_array()
   {
-    DBUG_ENTER("Group_set::wait_for_update");
-    Node *node= sidno_to_node(sidno);
-    mysql_cond_wait(&node->cond, &node->mutex);
+    for (int i= 0; i < max_index; i++)
+    {
+      Mutex_cond *mutex_cond= get_mutex_cond(i);
+      if (mutex_cond)
+      {
+        mysql_mutex_destroy(&mutex_cond->mutex);
+        mysql_cond_destroy(&mutex_cond->cond);
+      }
+    }
+    lf_dynarray_destroy(&array);
+    mysql_mutex_destroy(&internal_update_mutex);
+  }
+  inline void lock(int n)
+  {
+    mysql_mutex_lock(&get_mutex_cond(n)->mutex);
+  }
+  inline void unlock(int n)
+  {
+    mysql_mutex_unlock(&get_mutex_cond(n)->mutex);
+  }
+  inline void broadcast(int n)
+  {
+    mysql_cond_broadcast(&get_mutex_cond(n)->cond);
+  }
+  inline void wait(int n)
+  {
+    DBUG_ENTER("Mutex_cond_array::wait");
+    Mutex_cond *mutex_cond= get_mutex_cond(n);
+    mysql_cond_wait(&mutex_cond->cond, &mutex_cond->mutex);
     DBUG_VOID_RETURN;
   }
 private:
-  class Node
+  class Mutex_cond
   {
   public:
-    rpl_sidno sidno;
-    rpl_sid sid;
     mysql_mutex_t mutex;
     mysql_cond_t cond;
   };
+  mysql_mutex_t internal_update_mutex;
+  int max_index;
 
-  inline Node *sidno_to_node(rpl_sidno sidno) const
+  LF_DYNARRAY array;
+  inline Mutex_cond *get_mutex_cond(int n)
   {
-    return *dynamic_element(&_sidno_to_sid, sidno - 1, Node **);
+    Mutex_cond *mutex_cond= (Mutex_cond *)lf_dynarray_value(&array, n);
+    if (!mutex_cond)
+    {
+      mysql_mutex_lock(&internal_update_mutex);
+      // try again: maybe another thread created this mutex_cond just before
+      // we took the global mutex
+      mutex_cond= (Mutex_cond *)lf_dynarray_value(&array, n);
+      if (!mutex_cond)
+      {
+        mutex_cond= (Mutex_cond *)lf_dynarray_lvalue(&array, n);
+        if (mutex_cond)
+        {
+          mysql_mutex_init(NULL, &mutex_cond->mutex, NULL);
+          mysql_cond_init(NULL, &mutex_cond->cond, NULL);
+          if (n > max_index)
+            max_index= n;
+        }
+      }
+      mysql_mutex_unlock(&internal_update_mutex);
+    }
+    return mutex_cond;
   }
-
-  DYNAMIC_ARRAY _sidno_to_sid;
-  HASH _sid_to_sidno;
-  DYNAMIC_ARRAY _sorted;
 };
 
 /**
@@ -334,6 +402,14 @@ public:
   */
   rpl_sidno get_max_sidno() const { return intervals.elements - 1; }
   /**
+    Allocates space for all sidnos up to the given sidno in the array of intervals.
+    The sidno must exist in the Sid_map associated with this Group_set.
+
+    @param sidno The SIDNO.
+    @return GS_SUCCESS or GS_ERROR_OUT_OF_MEMORY.
+  */
+  enum_group_status ensure_sidno(rpl_sidno sidno);
+  /**
     Returns true if this Group_set is equal to the other Group_set.
   */
   bool equals(const Group_set *other) const;
@@ -403,37 +479,37 @@ public:
   };
 
   /**
-    Iterator over intervals.
+    Iterator over intervals for a given SIDNO.
 
-    The iterator always points to a current_elem.  As a special case,
-    the current_interval may be the empty position after the last
-    interval.
+    The iterator always points to a current_elem, which is either one
+    of the intervals for the SIDNO or the empty position after the
+    last interval.
   */
   class Interval_iterator
   {
   public:
     /**
-      Construct a new iterator where current_elem is the first element
-      of the list.
+      Construct a new iterator where current_elem is the first
+      interval for the given sidno.
     */
     Interval_iterator(const Group_set *group_set, rpl_sidno sidno)
-      : p(dynamic_element(&group_set->intervals, sidno, Interval **))
     {
       DBUG_ASSERT(sidno >= 1 && sidno <= group_set->get_max_sidno());
+      init(group_set, sidno);
     }
-    /**
-      Construct a new iterator over the free intervals.
-    */
-    Interval_iterator(const Group_set *group_set)
-      : p(dynamic_element(&group_set->intervals, 0, Interval **)) {}
+    /** Construct a new iterator over the free intervals. */
+    Interval_iterator(const Group_set *group_set) { init(group_set, 0); }
+    /** Reset this iterator. */
+    inline void init(const Group_set *group_set, rpl_sidno sidno)
+    { p= get_pointer_to_head(group_set, sidno); }
     /** Advance current_elem one step. */
     inline void next()
     {
       DBUG_ASSERT(*p != NULL);
       p= &(*p)->next;
-    };
+    }
     /** Insert the given element before current_elem. */
-    inline void insert(Interval *iv) { iv->next= *p; set(iv); };
+    inline void insert(Interval *iv) { iv->next= *p; set(iv); }
     /** Remove current_elem. */
     inline void remove(Group_set *group_set)
     {
@@ -443,18 +519,79 @@ public:
       set(next);
     }
     /** Return current_elem. */
-    inline Interval *get() const { return *p; };
+    inline Interval *get() const { return *p; }
     /** Set current_elem to the given Interval but do not touch the next pointer of the given Interval. */
-    inline void set(Interval *iv) { *p= iv; };
+    inline void set(Interval *iv) { *p= iv; }
   private:
+    inline Interval **get_pointer_to_head(const Group_set *group_set,
+                                          rpl_sidno sidno)
+    { return dynamic_element(&group_set->intervals, 0, Interval **); }
     /**
-      This holds the address of the 'next' pointer of the previous
-      element, or the address of the initial pointer into the list, if
-      the current element is the first element.
+      Holds the address of the 'next' pointer of the previous element,
+      or the address of the initial pointer into the list, if the
+      current element is the first element.
     */
     Interval **p;
   };
-  enum_group_status ensure_sidno(rpl_sidno sidno);
+
+  /** Iterator over all groups in this Group_set. */
+  class Group_iterator
+  {
+  public:
+    Group_iterator(Group_set *gs)
+      : group_set(gs), sidno(0), ivit(gs) { next_sidno(); }
+    /** Advance to next group. */
+    inline void next()
+    {
+      DBUG_ASSERT(gno > 0 && sidno > 0);
+      // go to next group in current interval
+      gno++;
+      // end of interval? then go to next interval for this sidno
+      if (gno == ivit.get()->end)
+      {
+        ivit.next();
+        Interval *iv= ivit.get();
+        // last interval for this sidno? then go to next sidno
+        if (iv == NULL)
+        {
+          next_sidno();
+          // last sidno? then don't try more
+          if (sidno == 0)
+            return;
+          iv= ivit.get();
+        }
+        gno= iv->start;
+      }
+    }
+    /** Return next group, or {0,0} if we reached the end. */
+    inline Group get()
+    {
+      Group ret= { sidno, gno };
+      return ret;
+    }
+  private:
+    /** Find next sidno that has one or more intervals. */
+    inline void next_sidno()
+    {
+      Interval *iv;
+      do {
+        sidno++;
+        if (sidno > group_set->get_max_sidno())
+        {
+          sidno= 0;
+          gno= 0;
+          return;
+        }
+        ivit.init(group_set, sidno);
+        iv= ivit.get();
+      } while (iv == NULL);
+      gno= iv->start;
+    }
+    Group_set *group_set;
+    rpl_sidno sidno;
+    rpl_gno gno;
+    Interval_iterator ivit;
+  };
 
 private:
   static const int CHUNK_GROW_SIZE = 8;
@@ -513,19 +650,14 @@ public:
 };
 
 /**
-  Holds information about a group: the sidno and the gno.
-*/
-class Group
-{
-public:
-  rpl_sidno sidno;
-  rpl_gno gno;
-};
-
-/**
   Represents the set of groups that are owned by some thread.
 
-  This has the form of a map from (SID, GNO) to thread_id.
+  This consists of all partial groups and a subset of the unlogged
+  groups.  Each group has a flag that indicates whether it is partial
+  or not.
+
+  The internal representation is a map (HASH) from (SID, GNO) to
+  (thread_id, bool).
 */
 class Owned_groups
 {
@@ -536,6 +668,8 @@ public:
   rpl_thread_id get_owner(rpl_sidno sidno, rpl_gno gno) const;
   enum_group_status set_owner(rpl_sidno sidno, rpl_gno gno, rpl_thread_id owner);
   void remove(rpl_sidno sidno, rpl_gno gno);
+  bool mark_partial(rpl_sidno sidno, rpl_gno gno);
+  bool is_partial(rpl_sidno sidno, rpl_gno gno) const;
 
 private:
   class Node
@@ -543,6 +677,7 @@ private:
   public:
     Group group;
     rpl_thread_id owner;
+    bool is_partial;
   };
   Node *get_node(rpl_sidno sidno, rpl_gno gno) const
   {
@@ -561,28 +696,47 @@ private:
 class Group_log_state
 {
 public:
-  enum enum_group_completeness
-  {
-    ENDED, OWNED, FREE
-  };
   Group_log_state(Sid_map *sid_map, enum_group_status *error)
-    : ended_groups(sid_map, error) {};
+    : ended_groups(sid_map, error) {}
+  /**
+    Returns true if the given group is ended.
+
+    @param sidno The SIDNO to check.
+    @param gno The GNO to check.
+    @retval true The group is ended in the group log.
+    @retval false The group is partial or unlogged in the group log.
+  */
+  bool is_ended(rpl_sidno sidno, rpl_gno gno) const
+  { return ended_groups.contains(sidno, gno); }
+  /**
+    Returns true if the given group is partial.
+
+    @param sidno The SIDNO to check.
+    @param gno The GNO to check.
+    @retval true The group is partial in the group log.
+    @retval false The group is ended or unlogged in the group log.
+  */
+  bool is_partial(rpl_sidno sidno, rpl_gno gno) const
+  { return owned_groups.is_partial(sidno, gno); }
   /**
-    Returns the status for the given group: ENDED, PARTIAL, or UNLOGGED.
+    Returns the owner of the given group.
 
     @param sidno The SIDNO to check.
     @param gno The GNO to check.
-    @param owner If the status is PARTIAL, the owner of the group will
-    be stored here.
+    @retval NO_THREAD if the group is not owned.
+    @retval other ID of the thread that owns the group.
+  */
+  rpl_thread_id get_owner(rpl_sidno sidno, rpl_gno gno) const
+  { return owned_groups.get_owner(sidno, gno); }
+  /**
+    Marks the given group as partial.
 
-    @retval ENDED The group has been ended in the group log.
-    @retval OWNED The group is partial or unlogged in the group log,
-    and is owned by some client.
-    @retval FREE The group is unlogged in the group log and is not
-    owned by any client.
+    @param sidno The SIDNO of the group.
+    @param gno The GNO of the group.
+    @return The value of is_partial() before this call.
   */
-  enum_group_completeness get_group_completeness(rpl_sidno sidno, rpl_gno gno,
-                                                 rpl_thread_id *owner) const;
+  bool mark_partial(rpl_sidno sidno, rpl_gno gno)
+  { return owned_groups.mark_partial(sidno, gno); }
   /**
     Acquires ownership of the given group, on behalf of the given thread.
 
@@ -612,9 +766,18 @@ public:
     @retval negative the numeric value of GS_OUT_OF_MEMORY
     @retval other The GNO for the group.
   */
-  rpl_gno end_automatic_group(rpl_sidno sidno);
+  rpl_gno get_automatic_gno(rpl_sidno sidno);
+  void lock_sidno(rpl_sidno sidno) { sid_locks.lock(sidno); }
+  void unlock_sidno(rpl_sidno sidno) { sid_locks.unlock(sidno); }
+  void broadcast_sidno(rpl_sidno sidno) { sid_locks.broadcast(sidno); }
+  void wait_for_sidno(rpl_sidno sidno) { sid_locks.wait(sidno); }
+  void lock_sidnos(MY_BITMAP *set);
+  void unlock_sidnos(MY_BITMAP *set);
+  void broadcast_sidnos(MY_BITMAP *set);
 private:
-  mysql_rwlock_t main_lock;
+  bool contains_group(rpl_sidno sidno, rpl_gno gno) const;
+  //bool contains_sidno(rpl_sidno sidno) const { return used_sidnos
+  Mutex_cond_array sid_locks;
   Group_set ended_groups;
   Owned_groups owned_groups;
 };
@@ -660,7 +823,7 @@ public:
 
 enum enum_subgroup_type
 {
-  NORMAL_SUBGROUP, ANONYMOUS_SUBGROUP, DUMMY_SUBGROUP
+  NORMAL_SUBGROUP, ANONYMOUS_SUBGROUP, DUMMY_SUBGROUP, AUTOMATIC_SUBGROUP
 };
 /**
    Holds information about a sub-group.
@@ -685,13 +848,15 @@ class Subgroup
 /**
   Represents a sub-group in the group cache.
 
-  Groups in the group cache are slightly different from other sub-groups 
+  Groups in the group cache are slightly different from other
+  sub-groups, because not all information about them is known.
 */
 class Cached_subgroup
 {
+public:
   enum_subgroup_type type;
-  rpl_gno gno;
   rpl_sidno sidno;
+  rpl_gno gno;
   rpl_binlog_pos binlog_pos;
   rpl_binlog_pos binlog_length;
   rpl_binlog_pos binlog_offset_after_last_statement;
@@ -700,12 +865,78 @@ class Cached_subgroup
 };
 
 /**
-   Represents a group cache: either the statement group cache or the
-   transaction group cache.
+  Represents a group cache: either the statement group cache or the
+  transaction group cache.
 */
 class Group_cache
 {
+public:
+  /**
+    Constructs a new Group_cache.
+
+    @param default_max_sidno The maximal SIDNO to allocate space for initially.
+    @param error Will be set to GS_SUCCESS or GS_ERROR_OUT_OF_MEMORY.
+  */
+  Group_cache(rpl_sidno default_max_sidno, enum_group_status *error);
+  /**
+    Appends the given sub-group to this cache.
+  */
+  enum_group_status append(Cached_subgroup *subgroup);
+  /**
+    Removes all sub-groups from this cache.
+  */
+  void clear(rpl_sidno max_sidno);
+  /**
+    Return the number of sub-groups in this group cache.
+  */
+  int get_n_subgroups() { return groups.elements; }
+  /**
+    If the cache does not contain the given group, and the group is
+    unlogged, add a dummy group to the cache.
+
+    @param gls Group_log_state, used to determine if the group is
+    unlogged or not.
+    @param sidno SIDNO of the group.
+    @param gno GNO of the group.
+    @return GS_SUCCESS or GS_OUT_OF_MEMORY.
+  */
+  enum_group_status
+    add_dummy_group_for_missing_group(Group_log_state *gls,
+                                      rpl_sidno sidno, rpl_gno gno);
+  /**
+    For each group in the given Group_set, if the cache does not
+    contain the group, and the group is unlogged, add a dummy group to
+    the cache.
+
+    @param gls Group_log_state, used to determine if the group is
+    unlogged or not.
+    @param group_set The set of groups to possibly add.
+    @return GS_SUCCESS or GS_OUT_OF_MEMORY.
+  */
+  enum_group_status
+    add_dummy_groups_for_missing_groups(Group_log_state *gls,
+                                        Group_set *group_set);
+  /**
+    Writes all sub-groups in the cache to the group log.
+
+    @todo The group log is not yet implemented /Sven
+  */
+  //enum_group_status write_to_log();
 private:
+  enum_group_status allocate_bitmap(rpl_sidno max_sidno);
+  /**
+    Returns a pointer to the given subgroup.  The pointer is only
+    valid until the next call to append().
+
+    @param index Index of the element: 0 <= index < get_n_elements().
+  */
+  Cached_subgroup *get_unsafe_pointer(int index)
+  {
+    DBUG_ASSERT(index >= 0 && index < get_n_subgroups());
+    return dynamic_element(&groups, index, Cached_subgroup *);
+  }
+  bool contains_group(rpl_sidno sidno, rpl_gno gno) const;
+  MY_BITMAP used_sidnos;
   DYNAMIC_ARRAY groups;
 };
 

=== modified file 'sql/zowned_groups.cc'
--- a/sql/zowned_groups.cc	2011-04-28 16:16:51 +0000
+++ b/sql/zowned_groups.cc	2011-05-03 17:52:32 +0000
@@ -73,4 +73,22 @@ enum_group_status Owned_groups::set_owne
   DBUG_RETURN(GS_SUCCESS);
 }
 
+bool Owned_groups::mark_partial(rpl_sidno sidno, rpl_gno gno)
+{
+  DBUG_ENTER("Owned_groups::mark_partial");
+  Node *n= get_node(sidno, gno);
+  DBUG_ASSERT(n != NULL);
+  bool old_partial= n->is_partial;
+  n->is_partial= true;
+  DBUG_RETURN(old_partial);
+}
+
+bool Owned_groups::is_partial(rpl_sidno sidno, rpl_gno gno) const
+{
+  DBUG_ENTER("Owned_groups::is_partial");
+  Node *n= get_node(sidno, gno);
+  DBUG_ASSERT(n != NULL);
+  DBUG_RETURN(n->is_partial);
+}
+
 #endif /* HAVE_UGID */

=== modified file 'sql/zsid_map.cc'
--- a/sql/zsid_map.cc	2011-04-28 16:16:51 +0000
+++ b/sql/zsid_map.cc	2011-05-03 17:52:32 +0000
@@ -53,8 +53,8 @@ rpl_sidno Sid_map::add_permanent(rpl_sid
     rpl_sidno sidno= get_max_sidno() + 1;
     node->sidno= sidno;
     node->sid= *sid;
-    mysql_mutex_init(NULL, &node->mutex, 0);
-    mysql_cond_init(NULL, &node->cond, 0);
+    //mysql_mutex_init(NULL, &node->mutex, 0);
+    //mysql_cond_init(NULL, &node->cond, 0);
     if (insert_dynamic(&_sidno_to_sid, &node) == 0)
     {
       if (insert_dynamic(&_sorted, &sidno) == 0)

=== modified file 'unittest/gunit/rpl_group_set-t.cc'
--- a/unittest/gunit/rpl_group_set-t.cc	2011-04-26 10:48:53 +0000
+++ b/unittest/gunit/rpl_group_set-t.cc	2011-05-03 17:52:32 +0000
@@ -259,5 +259,5 @@ TEST_F(GroupTest, Group_log_state)
   sm.add_permanent(&sids[0]);
   sm.add_permanent(&sids[1]);
 
-  gls.end_automatic_group(1);
+  gls.get_automatic_gno(1);
 }


Attachment: [text/bzr-bundle] bzr/sven.sandberg@oracle.com-20110503175232-a9n1534kveasz6di.bundle
Thread
bzr commit into mysql-trunk branch (sven.sandberg:3338) Sven Sandberg3 May