List:Commits« Previous MessageNext Message »
From:Ingo Struewing Date:July 13 2007 5:59pm
Subject:bk commit into 5.1 tree (istruewing:1.2530) BUG#26379
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of istruewing. When istruewing does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-07-13 17:58:57+02:00, istruewing@stripped +9 -0
  Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
              corrupts a MERGE table
  
  Not to be pushed. This is a partial implementation of a new
  MERGE table open approach. For intermediate review only.
  
  When opening a MERGE table in open_tables() we do now add the
  child tables to the list of tables to be opened by open_tables().
  Nothing else is done with the MERGE table at this stage.
  I.e. no storage engine object is created yet.
  When the last child is opened, we remove the children from the
  list again and finalize the MERGE table open. This behaves 
  basically like the old open. However it does not open the MyISAM
  tables directly, but grabs them from the already open children.

  include/my_base.h@stripped, 2007-07-13 17:58:53+02:00, istruewing@stripped +6 -1
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added HA_EXTRA_FINALIZE_MERGE_OPEN.

  include/myisammrg.h@stripped, 2007-07-13 17:58:53+02:00, istruewing@stripped +8 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added declarations for myrg_prepare_open() and myrg_finalize_open()
    for the new MERGE table open approach.

  sql/sql_base.cc@stripped, 2007-07-13 17:58:53+02:00, istruewing@stripped +58 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added support for MERGE table open to open_tables().

  sql/table.h@stripped, 2007-07-13 17:58:53+02:00, istruewing@stripped +4 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added elements for MERGE tables to TABLE and TABLE_LIST.

  storage/myisam/ha_myisam.cc@stripped, 2007-07-13 17:58:53+02:00, istruewing@stripped
+18 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added new function myisam_engine_handle() to extract a MI_INFO
    pointer from a handler object.

  storage/myisam/ha_myisam.h@stripped, 2007-07-13 17:58:53+02:00, istruewing@stripped +4
-0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Declared new function myisam_engine_handle() as friend of
    ha_myisam class.

  storage/myisammrg/ha_myisammrg.cc@stripped, 2007-07-13 17:58:53+02:00,
istruewing@stripped +243 -21
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added callback functions to support initial and final open
    of MERGE tables.
    Changed MERGE table open to create a list of child tables only.
    Added ha_myisammrg::finalize_open(), which does now the main part
    of MERGE open.
    Added a call to ::finalize_open() to ::extra() on
    HA_EXTRA_FINALIZE_MERGE_OPEN.

  storage/myisammrg/ha_myisammrg.h@stripped, 2007-07-13 17:58:54+02:00,
istruewing@stripped +8 -0
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Added elements to class ha_myisammrg to support the new
    open approach.
    Added table_ptr() method to use with callback functions.

  storage/myisammrg/myrg_open.c@stripped, 2007-07-13 17:58:54+02:00, istruewing@stripped
+243 -7
    Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
                corrupts a MERGE table
    Removed an always true condition in myrg_open().
    Added myrg_prepare_open() and myrg_finalize_open() for the new
    MERGE table open approach.

diff -Nrup a/include/my_base.h b/include/my_base.h
--- a/include/my_base.h	2007-07-02 05:55:19 +02:00
+++ b/include/my_base.h	2007-07-13 17:58:53 +02:00
@@ -185,7 +185,12 @@ enum ha_extra_function {
     Inform handler that an "INSERT...ON DUPLICATE KEY UPDATE" will be
     executed. This condition is unset by HA_EXTRA_NO_IGNORE_DUP_KEY.
   */
-  HA_EXTRA_INSERT_WITH_UPDATE
+  HA_EXTRA_INSERT_WITH_UPDATE,
+  /*
+    Inform MERGE handler that now its child tables are open and the
+    parent table open can be finialized.
+  */
+  HA_EXTRA_FINALIZE_MERGE_OPEN
 };
 
 	/* The following is parameter to ha_panic() */
diff -Nrup a/include/myisammrg.h b/include/myisammrg.h
--- a/include/myisammrg.h	2007-05-10 11:59:24 +02:00
+++ b/include/myisammrg.h	2007-07-13 17:58:53 +02:00
@@ -80,6 +80,14 @@ typedef struct st_myrg_info
 extern int myrg_close(MYRG_INFO *file);
 extern int myrg_delete(MYRG_INFO *file,const uchar *buff);
 extern MYRG_INFO *myrg_open(const char *name,int mode,int wait_if_locked);
+extern int myrg_prepare_open(const char *parent_name,
+                             int (*callback)(void*, const char*),
+                             void *callback_param,
+                             int *insert_method_p);
+extern MYRG_INFO *myrg_finalize_open(uint children, int handle_locking,
+                                     int insert_method,
+                                     MI_INFO *(*callback)(void*),
+                                     void *callback_param);
 extern int myrg_panic(enum ha_panic_function function);
 extern int myrg_rfirst(MYRG_INFO *file,uchar *buf,int inx);
 extern int myrg_rlast(MYRG_INFO *file,uchar *buf,int inx);
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc	2007-06-21 21:02:11 +02:00
+++ b/sql/sql_base.cc	2007-07-13 17:58:53 +02:00
@@ -3531,6 +3531,9 @@ int open_tables(THD *thd, TABLE_LIST **s
   */
   for (tables= *start; tables ;tables= tables->next_global)
   {
+    DBUG_PRINT("table", ("opening table: '%s'.'%s'",
+                         tables->db, tables->table_name));
+
     safe_to_ignore_table= FALSE;                // 'FALSE', as per coding style
 
     if (tables->lock_type == TL_WRITE_DEFAULT)
@@ -3586,6 +3589,61 @@ int open_tables(THD *thd, TABLE_LIST **s
       }
       else
         tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags);
+
+      /* We may need to handle MERGE tables specially. */
+      /* Comment to be deleted: It may be good to make a function from below. */
+      if (tables->table)
+      {
+        if (tables->table->child_l)
+        {
+          /* 'table' is a MERGE parent table. */
+          TABLE *parent= tables->table;
+          TABLE_LIST *child_l;
+          DBUG_PRINT("myrg", ("MERGE parent: '%s' 0x%lx",
+                              parent->s->table_name.str, (long) parent));
+
+          /* Fix children.*/
+          for (child_l= parent->child_l; child_l; child_l= child_l->next_global)
+          {
+            /* Set lock type. */
+            child_l->lock_type= tables->lock_type;
+            /* Set parent reference. */
+            child_l->parent_l= tables;
+          }
+
+          /* Insert children into the table list. */
+          *parent->child_last_l= tables->next_global;
+          tables->next_global= parent->child_l;
+          /*
+            Do not fix the prev_global pointers. We will remove the
+            chain soon anyway.
+          */
+        }
+        else if (tables->parent_l && (&tables->next_global ==
+                                      tables->parent_l->table->child_last_l))
+        {
+          /* 'tables' lists the last MERGE child, finalize parent open. */
+          TABLE *parent= tables->parent_l->table;
+          DBUG_PRINT("myrg", ("MERGE child: '%s' 0x%lx",
+                              tables->table->s->table_name.str,
+                              (long) tables->table));
+
+          if (parent->file->extra(HA_EXTRA_FINALIZE_MERGE_OPEN))
+          {
+            DBUG_PRINT("error", ("finalizing MERGE open failed: %d", my_errno));
+            result= -1;
+            goto err;
+          }
+
+          /* Remove children from the table list. */
+          tables->parent_l->next_global= *parent->child_last_l;
+          /*
+            Do not fix the last childs next_global pointer. It is needed
+            for the enclosing loop.
+            Do not fix prev_global pointers. We did not set them.
+          */
+        }
+      }
     }
 
     if (!tables->table)
diff -Nrup a/sql/table.h b/sql/table.h
--- a/sql/table.h	2007-06-05 21:19:57 +02:00
+++ b/sql/table.h	2007-07-13 17:58:53 +02:00
@@ -325,6 +325,8 @@ struct st_table {
   struct st_table *open_next, **open_prev;	/* Link to open tables */
 #endif
   struct st_table *next, *prev;
+  struct st_table_list *child_l;        /* Set in MERGE parent tables */
+  struct st_table_list **child_last_l;  /* Set in MERGE parent tables */
 
   THD	*in_use;                        /* Which thread uses this */
   Field **field;			/* Pointer to fields */
@@ -844,6 +846,8 @@ typedef struct st_table_list
     (non-zero only for merged underlying tables of a view).
   */
   st_table_list	*referencing_view;
+  /* Set in MERGE child tables. */
+  st_table_list *parent_l;
   /*
     Security  context (non-zero only for tables which belong
     to view with SQL SECURITY DEFINER)
diff -Nrup a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
--- a/storage/myisam/ha_myisam.cc	2007-06-18 12:09:22 +02:00
+++ b/storage/myisam/ha_myisam.cc	2007-07-13 17:58:53 +02:00
@@ -2075,3 +2075,21 @@ mysql_declare_plugin(myisam)
 }
 mysql_declare_plugin_end;
 
+
+/**
+  @brief Extract the MyISAM table structure pointer from a handler object.
+
+  @param[in]      handler_handle        pointer to handler object
+
+  @return       MyISAM table structure pointer
+    @retval     NULL if handler has not a MyISAM table open
+*/
+
+MI_INFO *myisam_engine_handle(handler *handler_handle)
+{
+  DBUG_ENTER("myisam_engine_handle");
+  if (strcmp(handler_handle->table_type(), "MyISAM"))
+    DBUG_RETURN(NULL);
+  DBUG_RETURN(((ha_myisam*) handler_handle)->file);
+}
+
diff -Nrup a/storage/myisam/ha_myisam.h b/storage/myisam/ha_myisam.h
--- a/storage/myisam/ha_myisam.h	2007-05-10 11:59:32 +02:00
+++ b/storage/myisam/ha_myisam.h	2007-07-13 17:58:53 +02:00
@@ -33,6 +33,8 @@ extern ulong myisam_sort_buffer_size;
 extern TYPELIB myisam_recover_typelib;
 extern ulong myisam_recover_options;
 
+extern MI_INFO *myisam_engine_handle(handler *handler_handle);
+
 class ha_myisam: public handler
 {
   MI_INFO *file;
@@ -137,4 +139,6 @@ class ha_myisam: public handler
   int dump(THD* thd, int fd);
   int net_read_dump(NET* net);
 #endif
+
+  friend MI_INFO *myisam_engine_handle(handler *handler_handle);
 };
diff -Nrup a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc
--- a/storage/myisammrg/ha_myisammrg.cc	2007-06-14 13:19:44 +02:00
+++ b/storage/myisammrg/ha_myisammrg.cc	2007-07-13 17:58:53 +02:00
@@ -22,6 +22,7 @@
 #include "mysql_priv.h"
 #include <mysql/plugin.h>
 #include <m_ctype.h>
+#include "../myisam/ha_myisam.h"
 #include "ha_myisammrg.h"
 #include "myrg_def.h"
 
@@ -89,7 +90,212 @@ const char *ha_myisammrg::index_type(uin
 }
 
 
-int ha_myisammrg::open(const char *name, int mode, uint test_if_locked)
+/**
+  @brief Callback function for preparing open of a MERGE child table.
+
+  @detail This function adds a TABLE_LIST object for a MERGE child table
+    to a list of tables of the parent TABLE object. It is called for
+    each child table.
+
+    TABLE::child_l -> TABLE_LIST::next_global -> TABLE_LIST::next_global
+    #                 #               ^          #               ^
+    #                 #               |          #               |
+    #                 #               +--------- TABLE_LIST::prev_global
+    #                 #                                          |
+    #           +---- TABLE_LIST::prev_global                    |
+    #           v                                                |
+    TABLE::child_last_l -----------------------------------------+
+
+  @param[in]    callback_param  data pointer as given to myrg_open()
+  @param[in]    filename        file name of MyISAM table
+                                without extension.
+
+  @return status
+    @retval     0               OK
+    @retval     != 0            Error
+*/
+
+static int myisammrg_prepare_open_callback(void *callback_param,
+                                           const char *filename)
+{
+  ha_myisammrg  *ha_myrg;
+  THD           *thd;
+  TABLE         *parent;
+  TABLE_LIST    *child_l;
+  const char    *db;
+  const char    *table_name;
+  uint          dirlen;
+  char          dir_path[FN_REFLEN];
+  DBUG_ENTER("myisammrg_open_callback");
+
+  /* Extract child table name and database name from filename. */
+  dirlen= dirname_length(filename);
+  if (dirlen >= FN_REFLEN)
+  {
+    DBUG_PRINT("error", ("name too long: '%.64s'", filename));
+    my_errno= ENAMETOOLONG;
+    DBUG_RETURN(1);
+  }
+  table_name= filename + dirlen;
+  dirlen--; /* Strip off trailing '/'. */
+  memcpy(dir_path, filename, dirlen);
+  dir_path[dirlen]= '\0';
+  db= base_name(dir_path);
+  dirlen-= db - dir_path; /* This is now the length of 'db'. */
+  DBUG_PRINT("myrg", ("open: '%s'.'%s'", db, table_name));
+
+  ha_myrg= (ha_myisammrg*) callback_param;
+  parent= ha_myrg->table_ptr();
+  thd= parent->in_use ? parent->in_use : current_thd;
+
+  /* Allocate TABLE_LIST object in threads mem_root and clear it. */
+  if (!(child_l= (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
+  {
+    DBUG_PRINT("error", ("thd->calloc error: %d", my_errno));
+    DBUG_RETURN(1);
+  }
+  /* Set database (schema) name. */
+  child_l->db_length= dirlen;
+  child_l->db= thd->strmake(db, dirlen);
+  /* Set table name. */
+  child_l->table_name_length= strlen(table_name);
+  child_l->table_name= thd->strmake(table_name, child_l->table_name_length);
+  /* Convert to lowercase if required. */
+  if (lower_case_table_names && child_l->table_name_length)
+    child_l->table_name_length= my_casedn_str(files_charset_info,
+                                              child_l->table_name);
+  /* Set alias. */
+  child_l->alias= child_l->table_name;
+
+  /* Link TABLE_LIST object into the parent list. */
+  if (!parent->child_last_l)
+  {
+    /* Initialize parent->child_last_l when handling first child. */
+    parent->child_last_l= &parent->child_l;
+  }
+  *parent->child_last_l= child_l;
+  child_l->prev_global= parent->child_last_l;
+  parent->child_last_l= &child_l->next_global;
+
+  /* Count children. */
+  ha_myrg->children++;
+
+  DBUG_RETURN(0);
+}
+
+
+/**
+  @brief Callback function for finalizing open of a MERGE child table.
+
+  @detail This function retrieves the MyISAM table handle from the
+    next child table. It is called for each child table.
+
+  @param[in]    callback_param      data pointer as given to myrg_open()
+
+  @return pointer to opened MyISAM table structure
+    @retval     NULL on end of child tables, my_errno == 0
+    @retval     NULL on error, my_errno != 0
+*/
+
+static
+MI_INFO *myisammrg_finalize_open_callback(void *callback_param)
+{
+  ha_myisammrg  *ha_myrg;
+  TABLE         *parent;
+  TABLE_LIST    *child_l;
+  MI_INFO       *myisam;
+  DBUG_ENTER("myisammrg_finalize_open_callback");
+
+  my_errno= 0;
+  ha_myrg= (ha_myisammrg*) callback_param;
+  parent= ha_myrg->table_ptr();
+
+  /* Get child list item. */
+  child_l= ha_myrg->next_child_open;
+  if (!child_l)
+  {
+    DBUG_PRINT("myrg", ("No more children to open"));
+    DBUG_RETURN(NULL);
+  }
+  /*
+    Prepare for next child. Used as child_l in next call to this function.
+    We cannot rely on a NULL-terminated chain.
+  */
+  if (&child_l->next_global == parent->child_last_l)
+  {
+    DBUG_PRINT("myrg", ("Opening last child"));
+    ha_myrg->next_child_open= NULL;
+  }
+  else
+    ha_myrg->next_child_open= child_l->next_global;
+
+  /* Extract the MyISAM table structure pointer from the handler object. */
+  if (!(myisam= myisam_engine_handle(child_l->table->file)))
+  {
+    DBUG_PRINT("error", ("no MyISAM handle for table: '%s' 0x%lx",
+                         child_l->table->s->table_name.str,
+                         (long) child_l->table));
+    my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+  }
+  DBUG_PRINT("myrg", ("MyISAM handle: 0x%lx  my_errno: %d",
+                      (long) myisam, my_errno));
+  DBUG_RETURN(my_errno ? NULL : myisam);
+}
+
+
+/**
+  @brief Prepare for open of a MERGE table.
+
+  @detail This function adds a children list of TABLE_LIST to the
+    parent TABLE.
+
+  @param[in]    name            MERGE table path name
+  @param[in]    mode            read/write mode, unused
+  @param[in]    test_if_locked  open flags
+
+  @return pointer to opened MyISAM table structure
+    @retval     NULL on end of child tables, my_errno == 0
+    @retval     NULL on error, my_errno != 0
+*/
+
+int ha_myisammrg::open(const char *name, int mode __attribute__((unused)),
+                       uint test_if_locked)
+{
+  DBUG_ENTER("ha_myisammrg::open");
+  DBUG_PRINT("myrg", ("name: '%s'", name));
+
+  /* Save for later use. */
+  this->test_if_locked= test_if_locked;
+
+  /* Initialize child count for increment by callback function. */
+  this->children= 0;
+
+  /* retrieve children table list. */
+  my_errno= 0;
+  if (myrg_prepare_open(name, myisammrg_prepare_open_callback, this,
+                        &this->insert_method))
+  {
+    DBUG_PRINT("error", ("my_errno %d", my_errno));
+    DBUG_RETURN(my_errno ? my_errno : -1);
+  }
+  DBUG_PRINT("myrg", ("children added: %u", this->children));
+  DBUG_RETURN(0);
+}
+
+
+/**
+  @brief Finalize open of a MERGE table.
+
+  @detail Let the storage engine create its data objects.
+    Check table definitions for consistency.
+
+  @return status
+    @retval     0               OK
+    @retval     != 0            Error, my_errno gives reason
+*/
+
+
+int ha_myisammrg::finalize_open(void)
 {
   MI_KEYDEF *keyinfo;
   MI_COLUMNDEF *recinfo;
@@ -97,30 +303,32 @@ int ha_myisammrg::open(const char *name,
   uint recs;
   uint keys= table->s->keys;
   int error;
-  char name_buff[FN_REFLEN];
+  DBUG_ENTER("ha_myisammrg::finalize_open");
+  DBUG_PRINT("myrg", ("table: '%s'", table->s->table_name.str));
 
-  DBUG_PRINT("info", ("ha_myisammrg::open"));
-  if (!(file=myrg_open(fn_format(name_buff,name,"","",
-                                 MY_UNPACK_FILENAME|MY_APPEND_EXT),
-                       mode, test_if_locked)))
+  next_child_open= table->child_l;
+  my_errno= 0;
+  if (!(file= myrg_finalize_open(this->children, this->test_if_locked,
+                                 this->insert_method,
+                                 myisammrg_finalize_open_callback, this)))
   {
-    DBUG_PRINT("info", ("ha_myisammrg::open exit %d", my_errno));
-    return (my_errno ? my_errno : -1);
+    DBUG_PRINT("error", ("my_errno %d", my_errno));
+    DBUG_RETURN(my_errno ? my_errno : -1);
   }
-  DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc..."));
+  DBUG_PRINT("myrg", ("calling myrg_extrafunc"));
   myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref);
-  if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
-	test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
-    myrg_extra(file,HA_EXTRA_NO_WAIT_LOCK,0);
+  if (!(this->test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
+        this->test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
+    myrg_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0);
   info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
-  if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
-    myrg_extra(file,HA_EXTRA_WAIT_LOCK,0);
+  if (!(this->test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
+    myrg_extra(file, HA_EXTRA_WAIT_LOCK, 0);
 
   if (table->s->reclength != stats.mean_rec_length &&
stats.mean_rec_length)
   {
-    DBUG_PRINT("error",("reclength: %lu  mean_rec_length: %lu",
-			table->s->reclength, stats.mean_rec_length));
-    if (test_if_locked & HA_OPEN_FOR_REPAIR)
+    DBUG_PRINT("error", ("reclength: %lu  mean_rec_length: %lu",
+                         table->s->reclength, stats.mean_rec_length));
+    if (this->test_if_locked & HA_OPEN_FOR_REPAIR)
       myrg_print_wrong_table(file->open_tables->table->filename);
     error= HA_ERR_WRONG_MRG_TABLE_DEF;
     goto err;
@@ -128,7 +336,7 @@ int ha_myisammrg::open(const char *name,
   if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
   {
     /* purecov: begin inspected */
-    DBUG_PRINT("error", ("Failed to convert TABLE object to MyISAM "
+    DBUG_PRINT("error", ("failed to convert TABLE object to MyISAM "
                          "key and column definition"));
     goto err;
     /* purecov: end */
@@ -140,8 +348,10 @@ int ha_myisammrg::open(const char *name,
                          u_table->table->s->base.keys,
                          u_table->table->s->base.fields, false))
     {
+      DBUG_PRINT("error", ("table definition mismatch: '%s'",
+                           u_table->table->filename));
       error= HA_ERR_WRONG_MRG_TABLE_DEF;
-      if (test_if_locked & HA_OPEN_FOR_REPAIR)
+      if (this->test_if_locked & HA_OPEN_FOR_REPAIR)
         myrg_print_wrong_table(u_table->table->filename);
       else
       {
@@ -157,15 +367,17 @@ int ha_myisammrg::open(const char *name,
   /* Merge table has more than 2G rows */
   if (table->s->crashed)
   {
+    DBUG_PRINT("error", ("MERGE table marked crashed"));
     error= HA_ERR_WRONG_MRG_TABLE_DEF;
     goto err;
   }
 #endif
-  return (0);
+  DBUG_RETURN(0);
+
 err:
   myrg_close(file);
   file=0;
-  return (my_errno= error);
+  DBUG_RETURN(my_errno= error);
 }
 
 int ha_myisammrg::close(void)
@@ -387,6 +599,16 @@ int ha_myisammrg::info(uint flag)
 
 int ha_myisammrg::extra(enum ha_extra_function operation)
 {
+  if (operation == HA_EXTRA_FINALIZE_MERGE_OPEN)
+  {
+    int rc= finalize_open();
+    if (!rc)
+      (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
+    return(rc);
+  }
+  else if (!file)
+    return(0); /* MERGE table and children not open yet. */
+
   /* As this is just a mapping, we don't have to force the underlying
      tables to be closed */
   if (operation == HA_EXTRA_FORCE_REOPEN ||
diff -Nrup a/storage/myisammrg/ha_myisammrg.h b/storage/myisammrg/ha_myisammrg.h
--- a/storage/myisammrg/ha_myisammrg.h	2007-06-18 12:09:22 +02:00
+++ b/storage/myisammrg/ha_myisammrg.h	2007-07-13 17:58:54 +02:00
@@ -27,6 +27,12 @@ class ha_myisammrg: public handler
   MYRG_INFO *file;
 
  public:
+  struct st_table_list *next_child_open;        /* next child to open */
+  uint children;                                /* number of child tables */
+  uint test_if_locked;                          /* flags from ::open() */
+  int  insert_method;                           /* MERGE insert method */
+
+ public:
   ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg);
   ~ha_myisammrg() {}
   const char *table_type() const { return "MRG_MyISAM"; }
@@ -53,6 +59,7 @@ class ha_myisammrg: public handler
   { return ulonglong2double(stats.data_file_length) / IO_SIZE + file->tables; }
 
   int open(const char *name, int mode, uint test_if_locked);
+  int finalize_open(void);
   int close(void);
   int write_row(uchar * buf);
   int update_row(const uchar * old_data, uchar * new_data);
@@ -86,4 +93,5 @@ class ha_myisammrg: public handler
   MYRG_INFO *myrg_info() { return file; }
   bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
   int check(THD* thd, HA_CHECK_OPT* check_opt);
+  TABLE *table_ptr() { return table; }
 };
diff -Nrup a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c
--- a/storage/myisammrg/myrg_open.c	2007-06-07 10:39:11 +02:00
+++ b/storage/myisammrg/myrg_open.c	2007-07-13 17:58:54 +02:00
@@ -107,13 +107,11 @@ MYRG_INFO *myrg_open(const char *name, i
                                            key_parts*sizeof(long),
                                            MYF(MY_WME|MY_ZEROFILL))))
         goto err;
-      if (files)
-      {
-        m_info->open_tables=(MYRG_TABLE *) (m_info+1);
-        m_info->rec_per_key_part=(ulong *) (m_info->open_tables+files);
-        m_info->tables= files;
-        files= 0;
-      }
+      DBUG_ASSERT(files);
+      m_info->open_tables=(MYRG_TABLE *) (m_info+1);
+      m_info->rec_per_key_part=(ulong *) (m_info->open_tables+files);
+      m_info->tables= files;
+      files= 0;
       m_info->reclength=isam->s->base.reclength;
       min_keys= isam->s->base.keys;
       errpos=3;
@@ -187,5 +185,243 @@ err:
     VOID(my_close(fd,MYF(0)));
   }
   my_errno=save_errno;
+  DBUG_RETURN (NULL);
+}
+
+
+/**
+  @brief Prepare for open of a MyISAM MERGE table.
+
+  @detail Open MERGE meta file to get the table name paths for the
+    child tables. Call a callback function for each child table.
+
+  @param[in]    parent_name     merge table name path as "database/table"
+  @param[in]    callback        function to call for each child table
+  @param[in]    callback_param  data pointer to give to the callback
+  @param[out]   insert_method_p pointer to insert method variable
+
+  @return status
+    @retval     0       OK
+    @retval     != 0    Error
+*/
+
+
+int myrg_prepare_open(const char *parent_name,
+                      int (*callback)(void*, const char*),
+                      void *callback_param,
+                      int *insert_method_p)
+{
+  int       rc;
+  int       errpos;
+  int       save_errno;
+  uint      length;
+  uint      dir_length;
+  size_t    name_buff_length;
+  File      fd;
+  IO_CACHE  file_cache;
+  char      parent_name_buff[FN_REFLEN * 2];
+  char      child_name_buff[FN_REFLEN];
+  DBUG_ENTER("myrg_prepare_open");
+
+  rc= 1;
+  errpos= 0;
+  bzero((char*) &file_cache, sizeof(file_cache));
+
+  if ((fd= my_open(fn_format(parent_name_buff, parent_name, "", MYRG_NAME_EXT,
+                             MY_UNPACK_FILENAME|MY_APPEND_EXT),
+                   O_RDONLY | O_SHARE, MYF(0))) < 0)
+    goto err;
+  errpos= 1;
+
+  if (init_io_cache(&file_cache, fd, 4 * IO_SIZE, READ_CACHE, 0, 0,
+                    MYF(MY_WME | MY_NABP)))
+    goto err;
+  errpos= 2;
+
+  dir_length= dirname_part(parent_name_buff, parent_name, &name_buff_length);
+
+  while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1)))
+  {
+    /* Remove line terminator. */
+    if (child_name_buff[length - 1] == '\n')
+      child_name_buff[--length]= '\0';
+
+    /* Skip empty lines. */
+    if (!child_name_buff[0])
+      continue;
+
+    /* Skip comments, but evaluate insert method. */
+    if (child_name_buff[0] == '#')
+    {
+      if (!strncmp(child_name_buff + 1, "INSERT_METHOD=", 14))
+      {
+        /* Compare buffer with global methods list: merge_insert_method. */
+        *insert_method_p= find_type(child_name_buff + 15,
+                                    &merge_insert_method, 2);
+      }
+      continue;
+    }
+
+    if (!has_path(child_name_buff))
+    {
+      VOID(strmake(parent_name_buff + dir_length, child_name_buff,
+                   sizeof(parent_name_buff) - 1 - dir_length));
+      VOID(cleanup_dirname(child_name_buff, parent_name_buff));
+    }
+    else
+      fn_format(child_name_buff, child_name_buff, "", "", 0);
+    DBUG_PRINT("info", ("child: '%s'", child_name_buff));
+
+    if ((rc= (*callback)(callback_param, child_name_buff)))
+      goto err;
+  }
+
+  VOID(my_close(fd, MYF(0)));
+  end_io_cache(&file_cache);
+  DBUG_RETURN(0);
+
+err:
+  save_errno= my_errno;
+  switch (errpos) {
+  case 2:
+    end_io_cache(&file_cache);
+    /* Fall through */
+  case 1:
+    VOID(my_close(fd, MYF(0)));
+  }
+  my_errno= save_errno;
+  DBUG_RETURN (rc);
+}
+
+
+/**
+  @brief Finalize open of a MyISAM MERGE table.
+
+  @detail Call a callback function for each child table.
+    The callback returns the MyISAM table handle of the child table.
+    Create the MYRG_INFO object and initialize it.
+
+  @param[in]    children        number of child tables
+  @param[in]    handle_locking  if contains HA_OPEN_FOR_REPAIR, warn about
+                                incompatible child tables, but continue
+  @param[in]    callback        function to call for each child table
+  @param[in]    callback_param  data pointer to give to the callback
+
+  @return MERGE table handle
+    @retval     != NULL         OK
+    @retval     NULL            Error
+*/
+
+
+MYRG_INFO *myrg_finalize_open(uint children, int handle_locking,
+                              int insert_method,
+                              MI_INFO *(*callback)(void*),
+                              void *callback_param)
+{
+  ulonglong  file_offset;
+  MYRG_INFO  *m_info;
+  MI_INFO    *myisam;
+  int        rc;
+  int        errpos;
+  int        save_errno;
+  uint       idx;
+  uint       child_nr;
+  uint       key_parts;
+  uint       min_keys;
+  const char *filename;
+  DBUG_ENTER("myrg_finalize_open");
+
+  rc= 1;
+  errpos= 0;
+  file_offset= 0;
+  LINT_INIT(key_parts);
+  min_keys= 0;
+  m_info= NULL;
+  child_nr= 0;
+  while ((myisam= (*callback)(callback_param)))
+  {
+    DBUG_ASSERT(child_nr < children);
+
+    /* Allocate MERGE table handle after first MyISAM table. */
+    if (!m_info)
+    {
+      key_parts= myisam->s->base.key_parts;
+      if (!(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO) +
+                                           children * sizeof(MYRG_TABLE) +
+                                           key_parts * sizeof(long),
+                                           MYF(MY_WME | MY_ZEROFILL))))
+        goto err;
+      m_info->open_tables= (MYRG_TABLE*) (m_info + 1);
+      m_info->rec_per_key_part= (ulong*) (m_info->open_tables + children);
+      m_info->tables= children;
+      m_info->reclength= myisam->s->base.reclength;
+      min_keys= myisam->s->base.keys;
+      errpos= 3;
+    }
+
+    /* Add MyISAM table info. */
+    m_info->open_tables[child_nr].table= myisam;
+    m_info->open_tables[child_nr].file_offset= (my_off_t) file_offset;
+    file_offset+= myisam->state->data_file_length;
+
+    /* Check table definition match. */
+    if (m_info->reclength != myisam->s->base.reclength)
+    {
+      my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+      if (handle_locking & HA_OPEN_FOR_REPAIR)
+      {
+        myrg_print_wrong_table(myisam->filename);
+        continue;
+      }
+      goto err;
+    }
+
+    m_info->options|= myisam->s->options;
+    m_info->records+= myisam->state->records;
+    m_info->del+= myisam->state->del;
+    m_info->data_file_length+= myisam->state->data_file_length;
+    if (min_keys > myisam->s->base.keys)
+      min_keys= myisam->s->base.keys;
+    for (idx=0; idx < key_parts; idx++)
+      m_info->rec_per_key_part[idx]+= (myisam->s->state.rec_per_key_part[idx] /
+                                       m_info->tables);
+
+    child_nr++;
+  }
+
+  if (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF)
+    goto err;
+  if (!m_info && !(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO),
+                                                  MYF(MY_WME | MY_ZEROFILL))))
+    goto err;
+  /* Don't mark table readonly, for ALTER TABLE ... UNION=(...) to work */
+  m_info->options&= ~(HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA);
+  m_info->merge_insert_method= insert_method > 0 ? insert_method : 0;
+
+  if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L)
+  {
+    my_errno= HA_ERR_RECORD_FILE_FULL;
+    goto err;
+  }
+  m_info->keys= min_keys;
+  bzero((char*) &m_info->by_key, sizeof(m_info->by_key));
+
+  /* this works ok if the table list is empty */
+  m_info->end_table= m_info->open_tables + children;
+  m_info->last_used_table= m_info->open_tables;
+
+  m_info->open_list.data= (void*) m_info;
+  pthread_mutex_lock(&THR_LOCK_open);
+  myrg_open_list= list_add(myrg_open_list, &m_info->open_list);
+  pthread_mutex_unlock(&THR_LOCK_open);
+  DBUG_RETURN(m_info);
+
+err:
+  save_errno= my_errno;
+  switch (errpos) {
+  case 3:
+    my_free((char*) m_info, MYF(0));
+  }
+  my_errno= save_errno;
   DBUG_RETURN (NULL);
 }
Thread
bk commit into 5.1 tree (istruewing:1.2530) BUG#26379Ingo Struewing13 Jul