summaryrefslogtreecommitdiff
path: root/storage/myisammrg
diff options
context:
space:
mode:
Diffstat (limited to 'storage/myisammrg')
-rw-r--r--storage/myisammrg/.cvsignore3
-rwxr-xr-xstorage/myisammrg/CMakeLists.txt28
-rw-r--r--storage/myisammrg/Makefile.am44
-rw-r--r--storage/myisammrg/ha_myisammrg.cc1326
-rw-r--r--storage/myisammrg/ha_myisammrg.h105
-rwxr-xr-xstorage/myisammrg/make-ccc3
-rw-r--r--storage/myisammrg/myrg_close.c68
-rw-r--r--storage/myisammrg/myrg_create.c75
-rw-r--r--storage/myisammrg/myrg_def.h35
-rw-r--r--storage/myisammrg/myrg_delete.c26
-rw-r--r--storage/myisammrg/myrg_extra.c91
-rw-r--r--storage/myisammrg/myrg_info.c88
-rw-r--r--storage/myisammrg/myrg_locking.c51
-rw-r--r--storage/myisammrg/myrg_open.c526
-rw-r--r--storage/myisammrg/myrg_panic.c45
-rw-r--r--storage/myisammrg/myrg_queue.c80
-rw-r--r--storage/myisammrg/myrg_range.c35
-rw-r--r--storage/myisammrg/myrg_records.c27
-rw-r--r--storage/myisammrg/myrg_rfirst.c48
-rw-r--r--storage/myisammrg/myrg_rkey.c97
-rw-r--r--storage/myisammrg/myrg_rlast.c49
-rw-r--r--storage/myisammrg/myrg_rnext.c52
-rw-r--r--storage/myisammrg/myrg_rnext_same.c50
-rw-r--r--storage/myisammrg/myrg_rprev.c52
-rw-r--r--storage/myisammrg/myrg_rrnd.c116
-rw-r--r--storage/myisammrg/myrg_rsame.c27
-rw-r--r--storage/myisammrg/myrg_static.c29
-rw-r--r--storage/myisammrg/myrg_update.c26
-rw-r--r--storage/myisammrg/myrg_write.c29
-rw-r--r--storage/myisammrg/plug.in6
30 files changed, 3237 insertions, 0 deletions
diff --git a/storage/myisammrg/.cvsignore b/storage/myisammrg/.cvsignore
new file mode 100644
index 00000000000..e9955884756
--- /dev/null
+++ b/storage/myisammrg/.cvsignore
@@ -0,0 +1,3 @@
+.deps
+Makefile
+Makefile.in
diff --git a/storage/myisammrg/CMakeLists.txt b/storage/myisammrg/CMakeLists.txt
new file mode 100755
index 00000000000..4bb581c1298
--- /dev/null
+++ b/storage/myisammrg/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright (c) 2006-2008 MySQL AB, 2009 Sun Microsystems, Inc.
+# Use is subject to license terms.
+#
+# 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX")
+SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX")
+
+INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake")
+
+SET(MYISAMMRG_SOURCES myrg_close.c myrg_create.c myrg_delete.c myrg_extra.c myrg_info.c
+ ha_myisammrg.cc
+ myrg_locking.c myrg_open.c myrg_panic.c myrg_queue.c myrg_range.c
+ myrg_rfirst.c myrg_rkey.c myrg_rlast.c myrg_rnext.c myrg_rnext_same.c
+ myrg_rprev.c myrg_rrnd.c myrg_rsame.c myrg_static.c myrg_update.c
+ myrg_write.c myrg_records.c)
+
+MYSQL_STORAGE_ENGINE(MYISAMMRG)
diff --git a/storage/myisammrg/Makefile.am b/storage/myisammrg/Makefile.am
new file mode 100644
index 00000000000..1ca51bc9d03
--- /dev/null
+++ b/storage/myisammrg/Makefile.am
@@ -0,0 +1,44 @@
+# Copyright (C) 2000-2002, 2005-2006 MySQL AB
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+MYSQLDATAdir = $(localstatedir)
+MYSQLSHAREdir = $(pkgdatadir)
+MYSQLBASEdir= $(prefix)
+MYSQLLIBdir= $(pkglibdir)
+INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include \
+ -I$(top_srcdir)/regex \
+ -I$(top_srcdir)/sql \
+ -I$(srcdir)
+WRAPLIBS=
+
+LDADD =
+
+DEFS = @DEFS@
+pkglib_LIBRARIES = libmyisammrg.a
+noinst_HEADERS = myrg_def.h ha_myisammrg.h
+noinst_LIBRARIES = libmyisammrg.a
+libmyisammrg_a_SOURCES = myrg_open.c myrg_extra.c myrg_info.c myrg_locking.c \
+ myrg_rrnd.c myrg_update.c myrg_delete.c myrg_rsame.c \
+ myrg_panic.c myrg_close.c myrg_create.c myrg_static.c \
+ myrg_rkey.c myrg_rfirst.c myrg_rlast.c myrg_rnext.c \
+ myrg_rprev.c myrg_queue.c myrg_write.c myrg_range.c \
+ ha_myisammrg.cc \
+ myrg_rnext_same.c myrg_records.c
+
+
+EXTRA_DIST = CMakeLists.txt plug.in
+
+# Don't update the files from bitkeeper
+%::SCCS/s.%
diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc
new file mode 100644
index 00000000000..fdefcddb2a4
--- /dev/null
+++ b/storage/myisammrg/ha_myisammrg.cc
@@ -0,0 +1,1326 @@
+/* Copyright (c) 2000, 2010, 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+
+/*
+ MyISAM MERGE tables
+
+ A MyISAM MERGE table is kind of a union of zero or more MyISAM tables.
+
+ Besides the normal form file (.frm) a MERGE table has a meta file
+ (.MRG) with a list of tables. These are paths to the MyISAM table
+ files. The last two components of the path contain the database name
+ and the table name respectively.
+
+ When a MERGE table is open, there exists an TABLE object for the MERGE
+ table itself and a TABLE object for each of the MyISAM tables. For
+ abbreviated writing, I call the MERGE table object "parent" and the
+ MyISAM table objects "children".
+
+ A MERGE table is almost always opened through open_and_lock_tables()
+ and hence through open_tables(). When the parent appears in the list
+ of tables to open, the initial open of the handler does nothing but
+ read the meta file and collect a list of TABLE_LIST objects for the
+ children. This list is attached to the parent TABLE object as
+ TABLE::child_l. The end of the children list is saved in
+ TABLE::child_last_l.
+
+ Back in open_tables(), add_merge_table_list() is called. It updates
+ each list member with the lock type and a back pointer to the parent
+ TABLE_LIST object TABLE_LIST::parent_l. The list is then inserted in
+ the list of tables to open, right behind the parent. Consequently,
+ open_tables() opens the children, one after the other. The TABLE
+ references of the TABLE_LIST objects are implicitly set to the open
+ tables. The children are opened as independent MyISAM tables, right as
+ if they are used by the SQL statement.
+
+ TABLE_LIST::parent_l is required to find the parent 1. when the last
+ child has been opened and children are to be attached, and 2. when an
+ error happens during child open and the child list must be removed
+ from the queuery list. In these cases the current child does not have
+ TABLE::parent set or does not have a TABLE at all respectively.
+
+ When the last child is open, attach_merge_children() is called. It
+ removes the list of children from the open list. Then the children are
+ "attached" to the parent. All required references between parent and
+ children are set up.
+
+ The MERGE storage engine sets up an array with references to the
+ low-level MyISAM table objects (MI_INFO). It remembers the state of
+ the table in MYRG_INFO::children_attached.
+
+ Every child TABLE::parent references the parent TABLE object. That way
+ TABLE objects belonging to a MERGE table can be identified.
+ TABLE::parent is required because the parent and child TABLE objects
+ can live longer than the parent TABLE_LIST object. So the path
+ child->pos_in_table_list->parent_l->table can be broken.
+
+ If necessary, the compatibility of parent and children is checked.
+ This check is necessary when any of the objects are reopend. This is
+ detected by comparing the current table def version against the
+ remembered child def version. On parent open, the list members are
+ initialized to an "impossible"/"undefined" version value. So the check
+ is always executed on the first attach.
+
+ The version check is done in myisammrg_attach_children_callback(),
+ which is called for every child. ha_myisammrg::attach_children()
+ initializes 'need_compat_check' to FALSE and
+ myisammrg_attach_children_callback() sets it ot TRUE if a table
+ def version mismatches the remembered child def version.
+
+ Finally the parent TABLE::children_attached is set.
+
+ ---
+
+ On parent open the storage engine structures are allocated and initialized.
+ They stay with the open table until its final close.
+
+
+*/
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+#define MYSQL_SERVER 1
+#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"
+
+
+static handler *myisammrg_create_handler(handlerton *hton,
+ TABLE_SHARE *table,
+ MEM_ROOT *mem_root)
+{
+ return new (mem_root) ha_myisammrg(hton, table);
+}
+
+
+/**
+ @brief Constructor
+*/
+
+ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg)
+ :handler(hton, table_arg), file(0), is_cloned(0)
+{}
+
+
+/**
+ @brief Destructor
+*/
+
+ha_myisammrg::~ha_myisammrg(void)
+{}
+
+
+static const char *ha_myisammrg_exts[] = {
+ ".MRG",
+ NullS
+};
+extern int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
+ MI_COLUMNDEF **recinfo_out, uint *records_out);
+extern int check_definition(MI_KEYDEF *t1_keyinfo, MI_COLUMNDEF *t1_recinfo,
+ uint t1_keys, uint t1_recs,
+ MI_KEYDEF *t2_keyinfo, MI_COLUMNDEF *t2_recinfo,
+ uint t2_keys, uint t2_recs, bool strict,
+ TABLE *table_arg);
+static void split_file_name(const char *file_name,
+ LEX_STRING *db, LEX_STRING *name);
+
+
+extern "C" void myrg_print_wrong_table(const char *table_name)
+{
+ LEX_STRING db= {NULL, 0}, name;
+ char buf[FN_REFLEN];
+ split_file_name(table_name, &db, &name);
+ memcpy(buf, db.str, db.length);
+ buf[db.length]= '.';
+ memcpy(buf + db.length + 1, name.str, name.length);
+ buf[db.length + name.length + 1]= 0;
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_ADMIN_WRONG_MRG_TABLE, ER(ER_ADMIN_WRONG_MRG_TABLE),
+ buf);
+}
+
+
+const char **ha_myisammrg::bas_ext() const
+{
+ return ha_myisammrg_exts;
+}
+
+
+const char *ha_myisammrg::index_type(uint key_number)
+{
+ return ((table->key_info[key_number].flags & HA_FULLTEXT) ?
+ "FULLTEXT" :
+ (table->key_info[key_number].flags & HA_SPATIAL) ?
+ "SPATIAL" :
+ (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ?
+ "RTREE" :
+ "BTREE");
+}
+
+
+/**
+ @brief Callback function for open of a MERGE parent 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.
+
+ The list of child TABLE_LIST objects is kept in the TABLE object of
+ the parent for the whole life time of the MERGE table. It is
+ inserted in the statement list behind the MERGE parent TABLE_LIST
+ object when the MERGE table is opened. It is removed from the
+ statement list after the last child is opened.
+
+ All memeory used for the child TABLE_LIST objects and the strings
+ referred by it are taken from the parent TABLE::mem_root. Thus they
+ are all freed implicitly at the final close of the table.
+
+ TABLE::child_l -> TABLE_LIST::next_global -> TABLE_LIST::next_global
+ # # ^ # ^
+ # # | # |
+ # # +--------- TABLE_LIST::prev_global
+ # # |
+ # |<--- TABLE_LIST::prev_global |
+ # |
+ TABLE::child_last_l -----------------------------------------+
+
+ @param[in] callback_param data pointer as given to myrg_parent_open()
+ @param[in] filename file name of MyISAM table
+ without extension.
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error
+*/
+
+static int myisammrg_parent_open_callback(void *callback_param,
+ const char *filename)
+{
+ ha_myisammrg *ha_myrg= (ha_myisammrg*) callback_param;
+ TABLE *parent= ha_myrg->table_ptr();
+ TABLE_LIST *child_l;
+ size_t dirlen;
+ char dir_path[FN_REFLEN];
+ char name_buf[NAME_LEN];
+ DBUG_ENTER("myisammrg_parent_open_callback");
+
+ /* Get a TABLE_LIST object. */
+ if (!(child_l= (TABLE_LIST*) alloc_root(&parent->mem_root,
+ sizeof(TABLE_LIST))))
+ {
+ /* purecov: begin inspected */
+ DBUG_PRINT("error", ("my_malloc error: %d", my_errno));
+ DBUG_RETURN(1);
+ /* purecov: end */
+ }
+ bzero((char*) child_l, sizeof(TABLE_LIST));
+
+ /*
+ Depending on MySQL version, filename may be encoded by table name to
+ file name encoding or not. Always encoded if parent table is created
+ by 5.1.46+. Encoded if parent is created by 5.1.6+ and child table is
+ in different database.
+ */
+ if (!has_path(filename))
+ {
+ /* Child is in the same database as parent. */
+ child_l->db_length= parent->s->db.length;
+ child_l->db= strmake_root(&parent->mem_root, parent->s->db.str,
+ child_l->db_length);
+ /* Child table name is encoded in parent dot-MRG starting with 5.1.46. */
+ if (parent->s->mysql_version >= 50146)
+ {
+ child_l->table_name_length= filename_to_tablename(filename, name_buf,
+ sizeof(name_buf));
+ child_l->table_name= strmake_root(&parent->mem_root, name_buf,
+ child_l->table_name_length);
+ }
+ else
+ {
+ child_l->table_name_length= strlen(filename);
+ child_l->table_name= strmake_root(&parent->mem_root, filename,
+ child_l->table_name_length);
+ }
+ }
+ else
+ {
+ DBUG_ASSERT(strlen(filename) < sizeof(dir_path));
+ fn_format(dir_path, filename, "", "", 0);
+ /* Extract child table name and database name from filename. */
+ dirlen= dirname_length(dir_path);
+ /* Child db/table name is encoded in parent dot-MRG starting with 5.1.6. */
+ if (parent->s->mysql_version >= 50106)
+ {
+ child_l->table_name_length= filename_to_tablename(dir_path + dirlen,
+ name_buf,
+ sizeof(name_buf));
+ child_l->table_name= strmake_root(&parent->mem_root, name_buf,
+ child_l->table_name_length);
+ dir_path[dirlen - 1]= 0;
+ dirlen= dirname_length(dir_path);
+ child_l->db_length= filename_to_tablename(dir_path + dirlen, name_buf,
+ sizeof(name_buf));
+ child_l->db= strmake_root(&parent->mem_root, name_buf, child_l->db_length);
+ }
+ else
+ {
+ child_l->table_name_length= strlen(dir_path + dirlen);
+ child_l->table_name= strmake_root(&parent->mem_root, dir_path + dirlen,
+ child_l->table_name_length);
+ dir_path[dirlen - 1]= 0;
+ dirlen= dirname_length(dir_path);
+ child_l->db_length= strlen(dir_path + dirlen);
+ child_l->db= strmake_root(&parent->mem_root, dir_path + dirlen,
+ child_l->db_length);
+ }
+ }
+
+ DBUG_PRINT("myrg", ("open: '%.*s'.'%.*s'", (int) child_l->db_length,
+ child_l->db, (int) child_l->table_name_length,
+ child_l->table_name));
+
+ /* 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;
+
+ /* Initialize table map to 'undefined'. */
+ child_l->init_child_def_version();
+
+ /* 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;
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief Callback function for attaching 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_attach_children()
+
+ @return pointer to open MyISAM table structure
+ @retval !=NULL OK, returning pointer
+ @retval NULL, my_errno == 0 Ok, no more child tables
+ @retval NULL, my_errno != 0 error
+*/
+
+static MI_INFO *myisammrg_attach_children_callback(void *callback_param)
+{
+ ha_myisammrg *ha_myrg;
+ TABLE *parent;
+ TABLE *child;
+ TABLE_LIST *child_l;
+ MI_INFO *UNINIT_VAR(myisam);
+ DBUG_ENTER("myisammrg_attach_children_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_attach;
+ if (!child_l)
+ {
+ DBUG_PRINT("myrg", ("No more children to attach"));
+ DBUG_RETURN(NULL);
+ }
+ child= child_l->table;
+ DBUG_PRINT("myrg", ("child table: '%s'.'%s' 0x%lx", child->s->db.str,
+ child->s->table_name.str, (long) child));
+ /*
+ 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", ("attaching last child"));
+ ha_myrg->next_child_attach= NULL;
+ }
+ else
+ ha_myrg->next_child_attach= child_l->next_global;
+
+ /* Set parent reference. */
+ child->parent= parent;
+
+ /*
+ Do a quick compatibility check. The table def version is set when
+ the table share is created. The child def version is copied
+ from the table def version after a sucessful compatibility check.
+ We need to repeat the compatibility check only if a child is opened
+ from a different share than last time it was used with this MERGE
+ table.
+ */
+ DBUG_PRINT("myrg", ("table_def_version last: %lu current: %lu",
+ (ulong) child_l->get_child_def_version(),
+ (ulong) child->s->get_table_def_version()));
+ if (child_l->get_child_def_version() != child->s->get_table_def_version())
+ ha_myrg->need_compat_check= TRUE;
+
+ /*
+ If parent is temporary, children must be temporary too and vice
+ versa. This check must be done for every child on every open because
+ the table def version can overlap between temporary and
+ non-temporary tables. We need to detect the case where a
+ non-temporary table has been replaced with a temporary table of the
+ same version. Or vice versa. A very unlikely case, but it could
+ happen.
+ */
+ if (child->s->tmp_table != parent->s->tmp_table)
+ {
+ DBUG_PRINT("error", ("temporary table mismatch parent: %d child: %d",
+ parent->s->tmp_table, child->s->tmp_table));
+ my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+ goto err;
+ }
+
+ /* Extract the MyISAM table structure pointer from the handler object. */
+ if ((child->file->ht->db_type != DB_TYPE_MYISAM) ||
+ !(myisam= ((ha_myisam*) child->file)->file_ptr()))
+ {
+ DBUG_PRINT("error", ("no MyISAM handle for child table: '%s'.'%s' 0x%lx",
+ child->s->db.str, child->s->table_name.str,
+ (long) child));
+ my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+ }
+ DBUG_PRINT("myrg", ("MyISAM handle: 0x%lx my_errno: %d",
+ my_errno ? 0L : (long) myisam, my_errno));
+
+ err:
+ DBUG_RETURN(my_errno ? NULL : myisam);
+}
+
+
+/**
+ @brief Open a MERGE parent table, not its children.
+
+ @detail This function initializes the MERGE storage engine structures
+ and adds a child 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_arg open flags
+
+ @return status
+ @retval 0 OK
+ @retval -1 Error, my_errno gives reason
+*/
+
+int ha_myisammrg::open(const char *name, int mode __attribute__((unused)),
+ uint test_if_locked_arg)
+{
+ DBUG_ENTER("ha_myisammrg::open");
+ DBUG_PRINT("myrg", ("name: '%s' table: 0x%lx", name, (long) table));
+ DBUG_PRINT("myrg", ("test_if_locked_arg: %u", test_if_locked_arg));
+
+ /* Save for later use. */
+ test_if_locked= test_if_locked_arg;
+
+ /* retrieve children table list. */
+ my_errno= 0;
+ if (is_cloned)
+ {
+ /*
+ Open and attaches the MyISAM tables,that are under the MERGE table
+ parent, on the MyISAM storage engine interface directly within the
+ MERGE engine. The new MyISAM table instances, as well as the MERGE
+ clone itself, are not visible in the table cache. This is not a
+ problem because all locking is handled by the original MERGE table
+ from which this is cloned of.
+ */
+ if (!(file= myrg_open(table->s->normalized_path.str, table->db_stat,
+ HA_OPEN_IGNORE_IF_LOCKED)))
+ {
+ DBUG_PRINT("error", ("my_errno %d", my_errno));
+ DBUG_RETURN(my_errno ? my_errno : -1);
+ }
+
+ file->children_attached= TRUE;
+
+ info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
+ }
+ else if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this)))
+ {
+ DBUG_PRINT("error", ("my_errno %d", my_errno));
+ DBUG_RETURN(my_errno ? my_errno : -1);
+ }
+ DBUG_PRINT("myrg", ("MYRG_INFO: 0x%lx", (long) file));
+ DBUG_RETURN(0);
+}
+
+/**
+ Returns a cloned instance of the current handler.
+
+ @return A cloned handler instance.
+ */
+handler *ha_myisammrg::clone(MEM_ROOT *mem_root)
+{
+ MYRG_TABLE *u_table,*newu_table;
+ ha_myisammrg *new_handler=
+ (ha_myisammrg*) get_new_handler(table->s, mem_root, table->s->db_type());
+ if (!new_handler)
+ return NULL;
+
+ /* Inform ha_myisammrg::open() that it is a cloned handler */
+ new_handler->is_cloned= TRUE;
+ /*
+ Allocate handler->ref here because otherwise ha_open will allocate it
+ on this->table->mem_root and we will not be able to reclaim that memory
+ when the clone handler object is destroyed.
+ */
+ if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2)))
+ {
+ delete new_handler;
+ return NULL;
+ }
+
+ if (new_handler->ha_open(table, table->s->normalized_path.str, table->db_stat,
+ HA_OPEN_IGNORE_IF_LOCKED))
+ {
+ delete new_handler;
+ return NULL;
+ }
+
+ /*
+ Iterate through the original child tables and
+ copy the state into the cloned child tables.
+ We need to do this because all the child tables
+ can be involved in delete.
+ */
+ newu_table= new_handler->file->open_tables;
+ for (u_table= file->open_tables; u_table < file->end_table; u_table++)
+ {
+ newu_table->table->state= u_table->table->state;
+ newu_table++;
+ }
+
+ return new_handler;
+ }
+
+
+/**
+ @brief Attach children to a MERGE table.
+
+ @detail Let the storage engine attach its children through a callback
+ function. Check table definitions for consistency.
+
+ @note Special thd->open_options may be in effect. We can make use of
+ them in attach. I.e. we use HA_OPEN_FOR_REPAIR to report the names
+ of mismatching child tables. We cannot transport these options in
+ ha_myisammrg::test_if_locked because they may change after the
+ parent is opened. The parent is kept open in the table cache over
+ multiple statements and can be used by other threads. Open options
+ can change over time.
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error, my_errno gives reason
+*/
+
+int ha_myisammrg::attach_children(void)
+{
+ MYRG_TABLE *u_table;
+ MI_COLUMNDEF *recinfo;
+ MI_KEYDEF *keyinfo;
+ uint recs;
+ uint keys= table->s->keys;
+ int error;
+ DBUG_ENTER("ha_myisammrg::attach_children");
+ DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
+ table->s->table_name.str, (long) table));
+ DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked));
+ DBUG_ASSERT(!this->file->children_attached);
+
+ /*
+ Initialize variables that are used, modified, and/or set by
+ myisammrg_attach_children_callback().
+ 'next_child_attach' traverses the chain of TABLE_LIST objects
+ that has been compiled during myrg_parent_open(). Every call
+ to myisammrg_attach_children_callback() moves the pointer to
+ the next object.
+ 'need_compat_check' is set by myisammrg_attach_children_callback()
+ if a child fails the table def version check.
+ 'my_errno' is set by myisammrg_attach_children_callback() in
+ case of an error.
+ */
+ next_child_attach= table->child_l;
+ need_compat_check= FALSE;
+ my_errno= 0;
+
+ if (myrg_attach_children(this->file, this->test_if_locked |
+ current_thd->open_options,
+ myisammrg_attach_children_callback, this,
+ (my_bool *) &need_compat_check))
+ {
+ DBUG_PRINT("error", ("my_errno %d", my_errno));
+ DBUG_RETURN(my_errno ? my_errno : -1);
+ }
+ 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);
+ 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);
+
+ /*
+ The compatibility check is required only if one or more children do
+ not match their table def version from the last check. This will
+ always happen at the first attach because the reference child def
+ version is initialized to 'undefined' at open.
+ */
+ DBUG_PRINT("myrg", ("need_compat_check: %d", need_compat_check));
+ if (need_compat_check)
+ {
+ TABLE_LIST *child_l;
+
+ 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)
+ myrg_print_wrong_table(file->open_tables->table->filename);
+ error= HA_ERR_WRONG_MRG_TABLE_DEF;
+ goto err;
+ }
+ /*
+ Both recinfo and keyinfo are allocated by my_multi_malloc(), thus
+ only recinfo must be freed.
+ */
+ if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
+ {
+ /* purecov: begin inspected */
+ DBUG_PRINT("error", ("failed to convert TABLE object to MyISAM "
+ "key and column definition"));
+ goto err;
+ /* purecov: end */
+ }
+ for (u_table= file->open_tables; u_table < file->end_table; u_table++)
+ {
+ if (check_definition(keyinfo, recinfo, keys, recs,
+ u_table->table->s->keyinfo, u_table->table->s->rec,
+ u_table->table->s->base.keys,
+ u_table->table->s->base.fields, false, NULL))
+ {
+ DBUG_PRINT("error", ("table definition mismatch: '%s'",
+ u_table->table->filename));
+ error= HA_ERR_WRONG_MRG_TABLE_DEF;
+ if (!(this->test_if_locked & HA_OPEN_FOR_REPAIR))
+ {
+ my_free((uchar*) recinfo, MYF(0));
+ goto err;
+ }
+ myrg_print_wrong_table(u_table->table->filename);
+ }
+ }
+ my_free((uchar*) recinfo, MYF(0));
+ if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
+ goto err;
+
+ /* All checks passed so far. Now update child def version. */
+ for (child_l= table->child_l; ; child_l= child_l->next_global)
+ {
+ child_l->set_child_def_version(
+ child_l->table->s->get_table_def_version());
+
+ if (&child_l->next_global == table->child_last_l)
+ break;
+ }
+ }
+#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
+ /* 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
+ DBUG_RETURN(0);
+
+err:
+ myrg_detach_children(file);
+ DBUG_RETURN(my_errno= error);
+}
+
+
+/**
+ @brief Detach all children from a MERGE table.
+
+ @note Detach must not touch the children in any way.
+ They may have been closed at ths point already.
+ All references to the children should be removed.
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error, my_errno gives reason
+*/
+
+int ha_myisammrg::detach_children(void)
+{
+ DBUG_ENTER("ha_myisammrg::detach_children");
+ DBUG_ASSERT(this->file && this->file->children_attached);
+
+ if (myrg_detach_children(this->file))
+ {
+ /* purecov: begin inspected */
+ DBUG_PRINT("error", ("my_errno %d", my_errno));
+ DBUG_RETURN(my_errno ? my_errno : -1);
+ /* purecov: end */
+ }
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief Close a MERGE parent table, not its children.
+
+ @note The children are expected to be closed separately by the caller.
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error, my_errno gives reason
+*/
+
+int ha_myisammrg::close(void)
+{
+ int rc;
+ DBUG_ENTER("ha_myisammrg::close");
+ /*
+ Children must not be attached here. Unless the MERGE table has no
+ children or the handler instance has been cloned. In these cases
+ children_attached is always true.
+ */
+ DBUG_ASSERT(!this->file->children_attached || !this->file->tables || this->is_cloned);
+ rc= myrg_close(file);
+ file= 0;
+ DBUG_RETURN(rc);
+}
+
+int ha_myisammrg::write_row(uchar * buf)
+{
+ DBUG_ENTER("ha_myisammrg::write_row");
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_write_count);
+
+ if (file->merge_insert_method == MERGE_INSERT_DISABLED || !file->tables)
+ DBUG_RETURN(HA_ERR_TABLE_READONLY);
+
+ if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
+ table->timestamp_field->set_time();
+ if (table->next_number_field && buf == table->record[0])
+ {
+ int error;
+ if ((error= update_auto_increment()))
+ DBUG_RETURN(error); /* purecov: inspected */
+ }
+ DBUG_RETURN(myrg_write(file,buf));
+}
+
+int ha_myisammrg::update_row(const uchar * old_data, uchar * new_data)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_update_count);
+ if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
+ table->timestamp_field->set_time();
+ return myrg_update(file,old_data,new_data);
+}
+
+int ha_myisammrg::delete_row(const uchar * buf)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_delete_count);
+ return myrg_delete(file,buf);
+}
+
+int ha_myisammrg::index_read_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_read_key_count);
+ int error=myrg_rkey(file,buf,active_index, key, keypart_map, find_flag);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisammrg::index_read_idx_map(uchar * buf, uint index, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_read_key_count);
+ int error=myrg_rkey(file,buf,index, key, keypart_map, find_flag);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisammrg::index_read_last_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_read_key_count);
+ int error=myrg_rkey(file,buf,active_index, key, keypart_map,
+ HA_READ_PREFIX_LAST);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisammrg::index_next(uchar * buf)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_read_next_count);
+ int error=myrg_rnext(file,buf,active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisammrg::index_prev(uchar * buf)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_read_prev_count);
+ int error=myrg_rprev(file,buf, active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisammrg::index_first(uchar * buf)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_read_first_count);
+ int error=myrg_rfirst(file, buf, active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisammrg::index_last(uchar * buf)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_read_last_count);
+ int error=myrg_rlast(file, buf, active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisammrg::index_next_same(uchar * buf,
+ const uchar *key __attribute__((unused)),
+ uint length __attribute__((unused)))
+{
+ int error;
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_read_next_count);
+ do
+ {
+ error= myrg_rnext_same(file,buf);
+ } while (error == HA_ERR_RECORD_DELETED);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+
+int ha_myisammrg::rnd_init(bool scan)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ return myrg_reset(file);
+}
+
+
+int ha_myisammrg::rnd_next(uchar *buf)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_read_rnd_next_count);
+ int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+
+int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ha_statistic_increment(&SSV::ha_read_rnd_count);
+ int error=myrg_rrnd(file, buf, my_get_ptr(pos,ref_length));
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+void ha_myisammrg::position(const uchar *record)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ ulonglong row_position= myrg_position(file);
+ my_store_ptr(ref, ref_length, (my_off_t) row_position);
+}
+
+
+ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key,
+ key_range *max_key)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ return (ha_rows) myrg_records_in_range(file, (int) inx, min_key, max_key);
+}
+
+
+int ha_myisammrg::info(uint flag)
+{
+ MYMERGE_INFO mrg_info;
+ DBUG_ASSERT(this->file->children_attached);
+ (void) myrg_status(file,&mrg_info,flag);
+ /*
+ The following fails if one has not compiled MySQL with -DBIG_TABLES
+ and one has more than 2^32 rows in the merge tables.
+ */
+ stats.records = (ha_rows) mrg_info.records;
+ stats.deleted = (ha_rows) mrg_info.deleted;
+#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
+ if ((mrg_info.records >= (ulonglong) 1 << 32) ||
+ (mrg_info.deleted >= (ulonglong) 1 << 32))
+ table->s->crashed= 1;
+#endif
+ stats.data_file_length= mrg_info.data_file_length;
+ if (mrg_info.errkey >= (int) table_share->keys)
+ {
+ /*
+ If value of errkey is higher than the number of keys
+ on the table set errkey to MAX_KEY. This will be
+ treated as unknown key case and error message generator
+ won't try to locate key causing segmentation fault.
+ */
+ mrg_info.errkey= MAX_KEY;
+ }
+ table->s->keys_in_use.set_prefix(table->s->keys);
+ stats.mean_rec_length= mrg_info.reclength;
+
+ /*
+ The handler::block_size is used all over the code in index scan cost
+ calculations. It is used to get number of disk seeks required to
+ retrieve a number of index tuples.
+ If the merge table has N underlying tables, then (assuming underlying
+ tables have equal size, the only "simple" approach we can use)
+ retrieving X index records from a merge table will require N times more
+ disk seeks compared to doing the same on a MyISAM table with equal
+ number of records.
+ In the edge case (file_tables > myisam_block_size) we'll get
+ block_size==0, and index calculation code will act as if we need one
+ disk seek to retrieve one index tuple.
+
+ TODO: In 5.2 index scan cost calculation will be factored out into a
+ virtual function in class handler and we'll be able to remove this hack.
+ */
+ stats.block_size= 0;
+ if (file->tables)
+ stats.block_size= myisam_block_size / file->tables;
+
+ stats.update_time= 0;
+#if SIZEOF_OFF_T > 4
+ ref_length=6; // Should be big enough
+#else
+ ref_length=4; // Can't be > than my_off_t
+#endif
+ if (flag & HA_STATUS_CONST)
+ {
+ if (table->s->key_parts && mrg_info.rec_per_key)
+ {
+#ifdef HAVE_purify
+ /*
+ valgrind may be unhappy about it, because optimizer may access values
+ between file->keys and table->key_parts, that will be uninitialized.
+ It's safe though, because even if opimizer will decide to use a key
+ with such a number, it'll be an error later anyway.
+ */
+ bzero((char*) table->key_info[0].rec_per_key,
+ sizeof(table->key_info[0].rec_per_key[0]) * table->s->key_parts);
+#endif
+ memcpy((char*) table->key_info[0].rec_per_key,
+ (char*) mrg_info.rec_per_key,
+ sizeof(table->key_info[0].rec_per_key[0]) *
+ min(file->keys, table->s->key_parts));
+ }
+ }
+ if (flag & HA_STATUS_ERRKEY)
+ {
+ errkey= mrg_info.errkey;
+ my_store_ptr(dup_ref, ref_length, mrg_info.dupp_key_pos);
+ }
+ return 0;
+}
+
+
+int ha_myisammrg::extra(enum ha_extra_function operation)
+{
+ if (operation == HA_EXTRA_ATTACH_CHILDREN)
+ {
+ int rc= attach_children();
+ if (!rc)
+ (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
+ return(rc);
+ }
+ else if (operation == HA_EXTRA_DETACH_CHILDREN)
+ {
+ /*
+ Note that detach must not touch the children in any way.
+ They may have been closed at ths point already.
+ */
+ int rc= detach_children();
+ return(rc);
+ }
+
+ /* As this is just a mapping, we don't have to force the underlying
+ tables to be closed */
+ if (operation == HA_EXTRA_FORCE_REOPEN ||
+ operation == HA_EXTRA_PREPARE_FOR_DROP)
+ return 0;
+ return myrg_extra(file,operation,0);
+}
+
+int ha_myisammrg::reset(void)
+{
+ return myrg_reset(file);
+}
+
+/* To be used with WRITE_CACHE, EXTRA_CACHE and BULK_INSERT_BEGIN */
+
+int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE)
+ return 0;
+ return myrg_extra(file, operation, (void*) &cache_size);
+}
+
+int ha_myisammrg::external_lock(THD *thd, int lock_type)
+{
+ DBUG_ASSERT(this->file->children_attached);
+ return myrg_lock_database(file,lock_type);
+}
+
+uint ha_myisammrg::lock_count(void) const
+{
+ /*
+ Return the real lock count even if the children are not attached.
+ This method is used for allocating memory. If we would return 0
+ to another thread (e.g. doing FLUSH TABLE), and attach the children
+ before the other thread calls store_lock(), then we would return
+ more locks in store_lock() than we claimed by lock_count(). The
+ other tread would overrun its memory.
+ */
+ return file->tables;
+}
+
+
+THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd,
+ THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+{
+ MYRG_TABLE *open_table;
+
+ /*
+ This method can be called while another thread is attaching the
+ children. If the processor reorders instructions or write to memory,
+ 'children_attached' could be set before 'open_tables' has all the
+ pointers to the children. Use of a mutex here and in
+ myrg_attach_children() forces consistent data.
+ */
+ pthread_mutex_lock(&this->file->mutex);
+
+ /*
+ When MERGE table is open, but not yet attached, other threads
+ could flush it, which means call mysql_lock_abort_for_thread()
+ on this threads TABLE. 'children_attached' is FALSE in this
+ situaton. Since the table is not locked, return no lock data.
+ */
+ if (!this->file->children_attached)
+ goto end; /* purecov: tested */
+
+ for (open_table=file->open_tables ;
+ open_table != file->end_table ;
+ open_table++)
+ {
+ *(to++)= &open_table->table->lock;
+ if (lock_type != TL_IGNORE && open_table->table->lock.type == TL_UNLOCK)
+ open_table->table->lock.type=lock_type;
+ }
+
+ end:
+ pthread_mutex_unlock(&this->file->mutex);
+ return to;
+}
+
+
+/* Find out database name and table name from a filename */
+
+static void split_file_name(const char *file_name,
+ LEX_STRING *db, LEX_STRING *name)
+{
+ size_t dir_length, prefix_length;
+ char buff[FN_REFLEN];
+
+ db->length= 0;
+ strmake(buff, file_name, sizeof(buff)-1);
+ dir_length= dirname_length(buff);
+ if (dir_length > 1)
+ {
+ /* Get database */
+ buff[dir_length-1]= 0; // Remove end '/'
+ prefix_length= dirname_length(buff);
+ db->str= (char*) file_name+ prefix_length;
+ db->length= dir_length - prefix_length -1;
+ }
+ name->str= (char*) file_name+ dir_length;
+ name->length= (uint) (fn_ext(name->str) - name->str);
+}
+
+
+void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info)
+{
+ DBUG_ENTER("ha_myisammrg::update_create_info");
+
+ if (!(create_info->used_fields & HA_CREATE_USED_UNION))
+ {
+ MYRG_TABLE *open_table;
+ THD *thd=current_thd;
+
+ create_info->merge_list.next= &create_info->merge_list.first;
+ create_info->merge_list.elements=0;
+
+ for (open_table=file->open_tables ;
+ open_table != file->end_table ;
+ open_table++)
+ {
+ TABLE_LIST *ptr;
+ LEX_STRING db, name;
+ LINT_INIT(db.str);
+
+ if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
+ goto err;
+ split_file_name(open_table->table->filename, &db, &name);
+ if (!(ptr->table_name= thd->strmake(name.str, name.length)))
+ goto err;
+ if (db.length && !(ptr->db= thd->strmake(db.str, db.length)))
+ goto err;
+
+ create_info->merge_list.elements++;
+ (*create_info->merge_list.next) = ptr;
+ create_info->merge_list.next= &ptr->next_local;
+ }
+ *create_info->merge_list.next=0;
+ }
+ if (!(create_info->used_fields & HA_CREATE_USED_INSERT_METHOD))
+ {
+ create_info->merge_insert_method = file->merge_insert_method;
+ }
+ DBUG_VOID_RETURN;
+
+err:
+ create_info->merge_list.elements=0;
+ create_info->merge_list.first=0;
+ DBUG_VOID_RETURN;
+}
+
+
+int ha_myisammrg::create(const char *name, register TABLE *form,
+ HA_CREATE_INFO *create_info)
+{
+ char buff[FN_REFLEN];
+ const char **table_names, **pos;
+ TABLE_LIST *tables= create_info->merge_list.first;
+ THD *thd= current_thd;
+ size_t dirlgt= dirname_length(name);
+ DBUG_ENTER("ha_myisammrg::create");
+
+ /* Allocate a table_names array in thread mem_root. */
+ if (!(table_names= (const char**)
+ thd->alloc((create_info->merge_list.elements+1) * sizeof(char*))))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ /* Create child path names. */
+ for (pos= table_names; tables; tables= tables->next_local)
+ {
+ const char *table_name= buff;
+
+ /*
+ Construct the path to the MyISAM table. Try to meet two conditions:
+ 1.) Allow to include MyISAM tables from different databases, and
+ 2.) allow for moving DATADIR around in the file system.
+ The first means that we need paths in the .MRG file. The second
+ means that we should not have absolute paths in the .MRG file.
+ The best, we can do, is to use 'mysql_data_home', which is '.'
+ in mysqld and may be an absolute path in an embedded server.
+ This means that it might not be possible to move the DATADIR of
+ an embedded server without changing the paths in the .MRG file.
+
+ Do the same even for temporary tables. MERGE children are now
+ opened through the table cache. They are opened by db.table_name,
+ not by their path name.
+ */
+ uint length= build_table_filename(buff, sizeof(buff),
+ tables->db, tables->table_name, "", 0);
+ /*
+ If a MyISAM table is in the same directory as the MERGE table,
+ we use the table name without a path. This means that the
+ DATADIR can easily be moved even for an embedded server as long
+ as the MyISAM tables are from the same database as the MERGE table.
+ */
+ if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt))
+ {
+ table_name+= dirlgt;
+ length-= dirlgt;
+ }
+ if (!(table_name= thd->strmake(table_name, length)))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
+
+ *pos++= table_name;
+ }
+ *pos=0;
+
+ /* Create a MERGE meta file from the table_names array. */
+ DBUG_RETURN(myrg_create(fn_format(buff,name,"","",
+ MY_RESOLVE_SYMLINKS|
+ MY_UNPACK_FILENAME|MY_APPEND_EXT),
+ table_names,
+ create_info->merge_insert_method,
+ (my_bool) 0));
+}
+
+
+void ha_myisammrg::append_create_info(String *packet)
+{
+ const char *current_db;
+ size_t db_length;
+ THD *thd= current_thd;
+ TABLE_LIST *open_table, *first;
+
+ if (file->merge_insert_method != MERGE_INSERT_DISABLED)
+ {
+ packet->append(STRING_WITH_LEN(" INSERT_METHOD="));
+ packet->append(get_type(&merge_insert_method,file->merge_insert_method-1));
+ }
+ /*
+ There is no sence adding UNION clause in case there is no underlying
+ tables specified.
+ */
+ if (file->open_tables == file->end_table)
+ return;
+ packet->append(STRING_WITH_LEN(" UNION=("));
+
+ current_db= table->s->db.str;
+ db_length= table->s->db.length;
+
+ for (first= open_table= table->child_l;;
+ open_table= open_table->next_global)
+ {
+ LEX_STRING db= { open_table->db, open_table->db_length };
+
+ if (open_table != first)
+ packet->append(',');
+ /* Report database for mapped table if it isn't in current database */
+ if (db.length &&
+ (db_length != db.length ||
+ strncmp(current_db, db.str, db.length)))
+ {
+ append_identifier(thd, packet, db.str, db.length);
+ packet->append('.');
+ }
+ append_identifier(thd, packet, open_table->table_name,
+ open_table->table_name_length);
+ if (&open_table->next_global == table->child_last_l)
+ break;
+ }
+ packet->append(')');
+}
+
+
+bool ha_myisammrg::check_if_incompatible_data(HA_CREATE_INFO *info,
+ uint table_changes)
+{
+ /*
+ For myisammrg, we should always re-generate the mapping file as this
+ is trivial to do
+ */
+ return COMPATIBLE_DATA_NO;
+}
+
+
+int ha_myisammrg::check(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ return HA_ADMIN_OK;
+}
+
+
+ha_rows ha_myisammrg::records()
+{
+ return myrg_records(file);
+}
+
+
+extern int myrg_panic(enum ha_panic_function flag);
+int myisammrg_panic(handlerton *hton, ha_panic_function flag)
+{
+ return myrg_panic(flag);
+}
+
+static int myisammrg_init(void *p)
+{
+ handlerton *myisammrg_hton;
+
+ myisammrg_hton= (handlerton *)p;
+
+ myisammrg_hton->db_type= DB_TYPE_MRG_MYISAM;
+ myisammrg_hton->create= myisammrg_create_handler;
+ myisammrg_hton->panic= myisammrg_panic;
+ myisammrg_hton->flags= HTON_NO_PARTITION;
+
+ return 0;
+}
+
+struct st_mysql_storage_engine myisammrg_storage_engine=
+{ MYSQL_HANDLERTON_INTERFACE_VERSION };
+
+mysql_declare_plugin(myisammrg)
+{
+ MYSQL_STORAGE_ENGINE_PLUGIN,
+ &myisammrg_storage_engine,
+ "MRG_MYISAM",
+ "MySQL AB",
+ "Collection of identical MyISAM tables",
+ PLUGIN_LICENSE_GPL,
+ myisammrg_init, /* Plugin Init */
+ NULL, /* Plugin Deinit */
+ 0x0100, /* 1.0 */
+ NULL, /* status variables */
+ NULL, /* system variables */
+ NULL /* config options */
+}
+mysql_declare_plugin_end;
diff --git a/storage/myisammrg/ha_myisammrg.h b/storage/myisammrg/ha_myisammrg.h
new file mode 100644
index 00000000000..957848e3e5e
--- /dev/null
+++ b/storage/myisammrg/ha_myisammrg.h
@@ -0,0 +1,105 @@
+/*
+ Copyright (c) 2000-2008 MySQL AB, 2009 Sun Microsystems, Inc.
+ Use is subject to license terms.
+
+ 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+/* class for the the myisam merge handler */
+
+#include <myisammrg.h>
+
+class ha_myisammrg: public handler
+{
+ MYRG_INFO *file;
+ my_bool is_cloned; /* This instance has been cloned */
+
+ public:
+ TABLE_LIST *next_child_attach; /* next child to attach */
+ uint test_if_locked; /* flags from ::open() */
+ bool need_compat_check; /* if need compatibility check */
+
+ ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg);
+ ~ha_myisammrg();
+ const char *table_type() const { return "MRG_MyISAM"; }
+ const char **bas_ext() const;
+ const char *index_type(uint key_number);
+ ulonglong table_flags() const
+ {
+ return (HA_REC_NOT_IN_SEQ | HA_AUTO_PART_KEY | HA_NO_TRANSACTIONS |
+ HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE |
+ HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_FILE_BASED |
+ HA_ANY_INDEX_MAY_BE_UNIQUE | HA_CAN_BIT_FIELD |
+ HA_HAS_RECORDS |
+ HA_NO_COPY_ON_ALTER |
+ HA_DUPLICATE_POS);
+ }
+ ulong index_flags(uint inx, uint part, bool all_parts) const
+ {
+ return ((table_share->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) ?
+ 0 : HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE |
+ HA_READ_ORDER | HA_KEYREAD_ONLY);
+ }
+ uint max_supported_keys() const { return MI_MAX_KEY; }
+ uint max_supported_key_length() const { return MI_MAX_KEY_LENGTH; }
+ uint max_supported_key_part_length() const { return MI_MAX_KEY_LENGTH; }
+ double scan_time()
+ { return ulonglong2double(stats.data_file_length) / IO_SIZE + file->tables; }
+
+ int open(const char *name, int mode, uint test_if_locked);
+ int attach_children(void);
+ int detach_children(void);
+ virtual handler *clone(MEM_ROOT *mem_root);
+ int close(void);
+ int write_row(uchar * buf);
+ int update_row(const uchar * old_data, uchar * new_data);
+ int delete_row(const uchar * buf);
+ int index_read_map(uchar *buf, const uchar *key, key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ int index_read_idx_map(uchar *buf, uint index, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ int index_read_last_map(uchar *buf, const uchar *key, key_part_map keypart_map);
+ int index_next(uchar * buf);
+ int index_prev(uchar * buf);
+ int index_first(uchar * buf);
+ int index_last(uchar * buf);
+ int index_next_same(uchar *buf, const uchar *key, uint keylen);
+ int rnd_init(bool scan);
+ int rnd_next(uchar *buf);
+ int rnd_pos(uchar * buf, uchar *pos);
+ void position(const uchar *record);
+ ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key);
+ int info(uint);
+ int reset(void);
+ int extra(enum ha_extra_function operation);
+ int extra_opt(enum ha_extra_function operation, ulong cache_size);
+ int external_lock(THD *thd, int lock_type);
+ uint lock_count(void) const;
+ int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info);
+ THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type);
+ void update_create_info(HA_CREATE_INFO *create_info);
+ void append_create_info(String *packet);
+ MYRG_INFO *myrg_info() { return file; }
+ TABLE *table_ptr() { return table; }
+ bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
+ int check(THD* thd, HA_CHECK_OPT* check_opt);
+ ha_rows records();
+};
diff --git a/storage/myisammrg/make-ccc b/storage/myisammrg/make-ccc
new file mode 100755
index 00000000000..a7e3dfc3cdb
--- /dev/null
+++ b/storage/myisammrg/make-ccc
@@ -0,0 +1,3 @@
+ccc -I./../include -I../include -DDBUG_OFF -fast -O3 -c myrg_close.c myrg_create.c myrg_delete.c myrg_extra.c myrg_info.c myrg_locking.c myrg_open.c myrg_panic.c myrg_rrnd.c myrg_rsame.c myrg_static.c myrg_update.c
+rm libmyisammrg.a
+ar -cr libmyisammrg.a myrg_close.o
diff --git a/storage/myisammrg/myrg_close.c b/storage/myisammrg/myrg_close.c
new file mode 100644
index 00000000000..97216ed47fe
--- /dev/null
+++ b/storage/myisammrg/myrg_close.c
@@ -0,0 +1,68 @@
+/* Copyright (C) 2000-2001 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* close a isam-database */
+
+#include "myrg_def.h"
+
+int myrg_close(MYRG_INFO *info)
+{
+ int error=0,new_error;
+ MYRG_TABLE *file;
+ DBUG_ENTER("myrg_close");
+
+ /*
+ Assume that info->children_attached means that this is called from
+ direct use of MERGE, not from a MySQL server. In this case the
+ children must be closed and info->rec_per_key_part is part of the
+ 'info' multi_alloc.
+ If info->children_attached is false, this is called from a MySQL
+ server. Children are closed independently but info->rec_per_key_part
+ must be freed.
+ Just in case of a server panic (myrg_panic()) info->children_attached
+ might be true. We would close the children though they should be
+ closed independently and info->rec_per_key_part is not freed.
+ This should be acceptable for a panic.
+ In case of a MySQL server and no children, children_attached is
+ always true. In this case no rec_per_key_part has been allocated.
+ So it is correct to use the branch where an empty list of tables is
+ (not) closed.
+ */
+ if (info->children_attached)
+ {
+ for (file= info->open_tables; file != info->end_table; file++)
+ {
+ /* purecov: begin inspected */
+ if ((new_error= mi_close(file->table)))
+ error= new_error;
+ else
+ file->table= NULL;
+ /* purecov: end */
+ }
+ }
+ else
+ my_free((uchar*) info->rec_per_key_part, MYF(MY_ALLOW_ZERO_PTR));
+ delete_queue(&info->by_key);
+ pthread_mutex_lock(&THR_LOCK_open);
+ myrg_open_list=list_delete(myrg_open_list,&info->open_list);
+ pthread_mutex_unlock(&THR_LOCK_open);
+ VOID(pthread_mutex_destroy(&info->mutex));
+ my_free((uchar*) info,MYF(0));
+ if (error)
+ {
+ DBUG_RETURN(my_errno=error);
+ }
+ DBUG_RETURN(0);
+}
diff --git a/storage/myisammrg/myrg_create.c b/storage/myisammrg/myrg_create.c
new file mode 100644
index 00000000000..2720aeae894
--- /dev/null
+++ b/storage/myisammrg/myrg_create.c
@@ -0,0 +1,75 @@
+/*
+ Copyright (c) 2000, 2001, 2005-2007 MySQL AB, 2009 Sun Microsystems, Inc.
+ Use is subject to license terms.
+
+ 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/* Create a MYMERGE_-file */
+
+#include "myrg_def.h"
+
+ /* create file named 'name' and save filenames in it
+ table_names should be NULL or a vector of string-pointers with
+ a NULL-pointer last
+ */
+
+int myrg_create(const char *name, const char **table_names,
+ uint insert_method, my_bool fix_names)
+{
+ int save_errno;
+ uint errpos;
+ File file;
+ char buff[FN_REFLEN],*end;
+ DBUG_ENTER("myrg_create");
+
+ errpos=0;
+ if ((file = my_create(fn_format(buff,name,"",MYRG_NAME_EXT,
+ MY_UNPACK_FILENAME|MY_APPEND_EXT),0,
+ O_RDWR | O_EXCL | O_NOFOLLOW,MYF(MY_WME))) < 0)
+ goto err;
+ errpos=1;
+ if (table_names)
+ {
+ for ( ; *table_names ; table_names++)
+ {
+ strmov(buff,*table_names);
+ if (fix_names)
+ fn_same(buff,name,4);
+ *(end=strend(buff))='\n';
+ end[1]=0;
+ if (my_write(file,(uchar*) buff,(uint) (end-buff+1),
+ MYF(MY_WME | MY_NABP)))
+ goto err;
+ }
+ }
+ if (insert_method != MERGE_INSERT_DISABLED)
+ {
+ end=strxmov(buff,"#INSERT_METHOD=",
+ get_type(&merge_insert_method,insert_method-1),"\n",NullS);
+ if (my_write(file, (uchar*) buff,(uint) (end-buff),MYF(MY_WME | MY_NABP)))
+ goto err;
+ }
+ if (my_close(file,MYF(0)))
+ goto err;
+ DBUG_RETURN(0);
+
+err:
+ save_errno=my_errno ? my_errno : -1;
+ switch (errpos) {
+ case 1:
+ VOID(my_close(file,MYF(0)));
+ }
+ DBUG_RETURN(my_errno=save_errno);
+} /* myrg_create */
diff --git a/storage/myisammrg/myrg_def.h b/storage/myisammrg/myrg_def.h
new file mode 100644
index 00000000000..9c69da1424d
--- /dev/null
+++ b/storage/myisammrg/myrg_def.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2000-2001, 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* This file is included by all myisam-merge files */
+
+#ifndef N_MAXKEY
+#include "../myisam/myisamdef.h"
+#endif
+
+#include "myisammrg.h"
+
+extern LIST *myrg_open_list;
+
+#ifdef THREAD
+extern pthread_mutex_t THR_LOCK_open;
+#endif
+
+int _myrg_init_queue(MYRG_INFO *info,int inx,enum ha_rkey_function search_flag);
+int _myrg_mi_read_record(MI_INFO *info, uchar *buf);
+#ifdef __cplusplus
+extern "C"
+#endif
+void myrg_print_wrong_table(const char *table_name);
diff --git a/storage/myisammrg/myrg_delete.c b/storage/myisammrg/myrg_delete.c
new file mode 100644
index 00000000000..93d45198b36
--- /dev/null
+++ b/storage/myisammrg/myrg_delete.c
@@ -0,0 +1,26 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Delete last read record */
+
+#include "myrg_def.h"
+
+int myrg_delete(MYRG_INFO *info, const uchar *record)
+{
+ if (!info->current_table)
+ return (my_errno= HA_ERR_NO_ACTIVE_RECORD);
+
+ return mi_delete(info->current_table->table,record);
+}
diff --git a/storage/myisammrg/myrg_extra.c b/storage/myisammrg/myrg_extra.c
new file mode 100644
index 00000000000..3d14f6a56e6
--- /dev/null
+++ b/storage/myisammrg/myrg_extra.c
@@ -0,0 +1,91 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Extra functions we want to do with a database
+ - All flags, exept record-cache-flags, are set in all used databases
+ record-cache-flags are set in myrg_rrnd when we are changing database.
+*/
+
+#include "myrg_def.h"
+
+int myrg_extra(MYRG_INFO *info,enum ha_extra_function function,
+ void *extra_arg)
+{
+ int error,save_error=0;
+ MYRG_TABLE *file;
+ DBUG_ENTER("myrg_extra");
+ DBUG_PRINT("info",("function: %lu", (ulong) function));
+
+ if (!info->children_attached)
+ DBUG_RETURN(1);
+ if (function == HA_EXTRA_CACHE)
+ {
+ info->cache_in_use=1;
+ info->cache_size= (extra_arg ? *(ulong*) extra_arg :
+ my_default_record_cache_size);
+ }
+ else
+ {
+ if (function == HA_EXTRA_NO_CACHE ||
+ function == HA_EXTRA_PREPARE_FOR_UPDATE)
+ info->cache_in_use=0;
+ if (function == HA_EXTRA_RESET_STATE)
+ {
+ info->current_table=0;
+ info->last_used_table=info->open_tables;
+ }
+ for (file=info->open_tables ; file != info->end_table ; file++)
+ {
+ if ((error=mi_extra(file->table, function, extra_arg)))
+ save_error=error;
+ }
+ }
+ DBUG_RETURN(save_error);
+}
+
+
+void myrg_extrafunc(MYRG_INFO *info, invalidator_by_filename inv)
+{
+ MYRG_TABLE *file;
+ DBUG_ENTER("myrg_extrafunc");
+
+ for (file=info->open_tables ; file != info->end_table ; file++)
+ file->table->s->invalidator = inv;
+
+ DBUG_VOID_RETURN;
+}
+
+
+int myrg_reset(MYRG_INFO *info)
+{
+ int save_error= 0;
+ MYRG_TABLE *file;
+ DBUG_ENTER("myrg_reset");
+
+ if (!info->children_attached)
+ DBUG_RETURN(1);
+ info->cache_in_use=0;
+ info->current_table=0;
+ info->last_used_table= info->open_tables;
+
+ for (file=info->open_tables ; file != info->end_table ; file++)
+ {
+ int error;
+ if ((error= mi_reset(file->table)))
+ save_error=error;
+ }
+ DBUG_RETURN(save_error);
+}
diff --git a/storage/myisammrg/myrg_info.c b/storage/myisammrg/myrg_info.c
new file mode 100644
index 00000000000..67a403e7643
--- /dev/null
+++ b/storage/myisammrg/myrg_info.c
@@ -0,0 +1,88 @@
+/*
+ Copyright (c) 2000-2002, 2005, 2006 MySQL AB, 2009 Sun Microsystems, Inc.
+ Use is subject to license terms.
+
+ 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "myrg_def.h"
+
+ulonglong myrg_position(MYRG_INFO *info)
+{
+ MYRG_TABLE *current_table;
+
+ if (!(current_table = info->current_table) &&
+ info->open_tables != info->end_table)
+ current_table = info->open_tables;
+ return current_table ?
+ current_table->table->lastpos + current_table->file_offset :
+ ~(ulonglong) 0;
+}
+
+int myrg_status(MYRG_INFO *info,register MYMERGE_INFO *x,int flag)
+{
+ MYRG_TABLE *current_table;
+ DBUG_ENTER("myrg_status");
+
+ if (!(current_table = info->current_table) &&
+ info->open_tables != info->end_table)
+ current_table = info->open_tables;
+ x->recpos = info->current_table ?
+ info->current_table->table->lastpos + info->current_table->file_offset :
+ (ulong) -1L;
+ if (flag != HA_STATUS_POS)
+ {
+ MYRG_TABLE *file;
+
+ info->records=info->del=info->data_file_length=0;
+ for (file=info->open_tables ; file != info->end_table ; file++)
+ {
+ file->file_offset=info->data_file_length;
+ info->data_file_length+=file->table->s->state.state.data_file_length;
+ info->records+=file->table->s->state.state.records;
+ info->del+=file->table->s->state.state.del;
+ DBUG_PRINT("info2",("table: %s, offset: %lu",
+ file->table->filename,(ulong) file->file_offset));
+ }
+ x->records= info->records;
+ x->deleted= info->del;
+ x->data_file_length= info->data_file_length;
+ x->reclength= info->reclength;
+ x->options= info->options;
+ if (current_table)
+ {
+ /*
+ errkey is set to the index number of the myisam tables. But
+ since the MERGE table can have less keys than the MyISAM
+ tables, errkey cannot be be used as an index into the key_info
+ on the server. This value will be overwritten with MAX_KEY by
+ the MERGE engine.
+ */
+ x->errkey= current_table->table->errkey;
+ /*
+ Calculate the position of the duplicate key to be the sum of the
+ offset of the myisam file and the offset into the file at which
+ the duplicate key is located.
+ */
+ x->dupp_key_pos= current_table->file_offset + current_table->table->dupp_key_pos;
+ }
+ else
+ {
+ x->errkey= 0;
+ x->dupp_key_pos= 0;
+ }
+ x->rec_per_key = info->rec_per_key_part;
+ }
+ DBUG_RETURN(0);
+}
diff --git a/storage/myisammrg/myrg_locking.c b/storage/myisammrg/myrg_locking.c
new file mode 100644
index 00000000000..4f1e3f844a1
--- /dev/null
+++ b/storage/myisammrg/myrg_locking.c
@@ -0,0 +1,51 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Lock databases against read or write.
+*/
+
+#include "myrg_def.h"
+
+int myrg_lock_database(MYRG_INFO *info, int lock_type)
+{
+ int error,new_error;
+ MYRG_TABLE *file;
+
+ error=0;
+ for (file=info->open_tables ; file != info->end_table ; file++)
+ {
+#ifdef __WIN__
+ /*
+ Make sure this table is marked as owned by a merge table.
+ The semaphore is never released as long as table remains
+ in memory. This should be refactored into a more generic
+ approach (observer pattern)
+ */
+ (file->table)->owned_by_merge = TRUE;
+#endif
+ if ((new_error=mi_lock_database(file->table,lock_type)))
+ {
+ error=new_error;
+ if (lock_type != F_UNLCK)
+ {
+ while (--file >= info->open_tables)
+ mi_lock_database(file->table, F_UNLCK);
+ break;
+ }
+ }
+ }
+ return(error);
+}
diff --git a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c
new file mode 100644
index 00000000000..ac8b753b317
--- /dev/null
+++ b/storage/myisammrg/myrg_open.c
@@ -0,0 +1,526 @@
+/*
+ Copyright (c) 2000, 2010, 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/* open a MyISAM MERGE table */
+
+#include "myrg_def.h"
+#include <stddef.h>
+#include <errno.h>
+#ifdef VMS
+#include "mrg_static.c"
+#endif
+
+/*
+ open a MyISAM MERGE table
+ if handle_locking is 0 then exit with error if some table is locked
+ if handle_locking is 1 then wait if table is locked
+
+ NOTE: This function is not used in the MySQL server. It is for
+ MERGE use independent from MySQL. Currently there is some code
+ duplication between myrg_open() and myrg_parent_open() +
+ myrg_attach_children(). Please duplicate changes in these
+ functions or make common sub-functions.
+*/
+
+MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking)
+{
+ int save_errno,errpos=0;
+ uint files= 0, i, dir_length, length, UNINIT_VAR(key_parts), min_keys= 0;
+ ulonglong file_offset=0;
+ char name_buff[FN_REFLEN*2],buff[FN_REFLEN],*end;
+ MYRG_INFO *m_info=0;
+ File fd;
+ IO_CACHE file;
+ MI_INFO *isam=0;
+ uint found_merge_insert_method= 0;
+ size_t name_buff_length;
+ my_bool bad_children= FALSE;
+ DBUG_ENTER("myrg_open");
+
+ bzero((char*) &file,sizeof(file));
+ if ((fd=my_open(fn_format(name_buff,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, fd, 4*IO_SIZE, READ_CACHE, 0, 0,
+ MYF(MY_WME | MY_NABP)))
+ goto err;
+ errpos=2;
+ dir_length=dirname_part(name_buff, name, &name_buff_length);
+ while ((length=my_b_gets(&file,buff,FN_REFLEN-1)))
+ {
+ if ((end=buff+length)[-1] == '\n')
+ end[-1]='\0';
+ if (buff[0] && buff[0] != '#')
+ files++;
+ }
+
+ my_b_seek(&file, 0);
+ while ((length=my_b_gets(&file,buff,FN_REFLEN-1)))
+ {
+ if ((end=buff+length)[-1] == '\n')
+ *--end='\0';
+ if (!buff[0])
+ continue; /* Skip empty lines */
+ if (buff[0] == '#')
+ {
+ if (!strncmp(buff+1,"INSERT_METHOD=",14))
+ { /* Lookup insert method */
+ int tmp=find_type(buff+15,&merge_insert_method,2);
+ found_merge_insert_method = (uint) (tmp >= 0 ? tmp : 0);
+ }
+ continue; /* Skip comments */
+ }
+
+ if (!has_path(buff))
+ {
+ VOID(strmake(name_buff+dir_length,buff,
+ sizeof(name_buff)-1-dir_length));
+ VOID(cleanup_dirname(buff,name_buff));
+ }
+ else
+ fn_format(buff, buff, "", "", 0);
+ if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0))))
+ {
+ if (handle_locking & HA_OPEN_FOR_REPAIR)
+ {
+ myrg_print_wrong_table(buff);
+ bad_children= TRUE;
+ continue;
+ }
+ goto bad_children;
+ }
+ if (!m_info) /* First file */
+ {
+ key_parts=isam->s->base.key_parts;
+ if (!(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO) +
+ files*sizeof(MYRG_TABLE) +
+ key_parts*sizeof(long),
+ MYF(MY_WME|MY_ZEROFILL))))
+ goto err;
+ 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;
+ }
+ m_info->open_tables[files].table= isam;
+ m_info->open_tables[files].file_offset=(my_off_t) file_offset;
+ file_offset+=isam->state->data_file_length;
+ files++;
+ if (m_info->reclength != isam->s->base.reclength)
+ {
+ if (handle_locking & HA_OPEN_FOR_REPAIR)
+ {
+ myrg_print_wrong_table(buff);
+ bad_children= TRUE;
+ continue;
+ }
+ goto bad_children;
+ }
+ m_info->options|= isam->s->options;
+ m_info->records+= isam->state->records;
+ m_info->del+= isam->state->del;
+ m_info->data_file_length+= isam->state->data_file_length;
+ if (min_keys > isam->s->base.keys)
+ min_keys= isam->s->base.keys;
+ for (i=0; i < key_parts; i++)
+ m_info->rec_per_key_part[i]+= (isam->s->state.rec_per_key_part[i] /
+ m_info->tables);
+ }
+
+ if (bad_children)
+ goto bad_children;
+ 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= found_merge_insert_method;
+
+ 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+files;
+ m_info->last_used_table=m_info->open_tables;
+ m_info->children_attached= TRUE;
+
+ VOID(my_close(fd,MYF(0)));
+ end_io_cache(&file);
+ VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST));
+ 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);
+
+bad_children:
+ my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+err:
+ save_errno=my_errno;
+ switch (errpos) {
+ case 3:
+ while (files)
+ (void) mi_close(m_info->open_tables[--files].table);
+ my_free((char*) m_info,MYF(0));
+ /* Fall through */
+ case 2:
+ end_io_cache(&file);
+ /* Fall through */
+ case 1:
+ VOID(my_close(fd,MYF(0)));
+ }
+ my_errno=save_errno;
+ DBUG_RETURN (NULL);
+}
+
+
+/**
+ @brief Open parent table of a MyISAM MERGE table.
+
+ @detail Open MERGE meta file to get the table name paths for the child
+ tables. Count the children. Allocate and initialize MYRG_INFO
+ structure. 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
+
+ @return MYRG_INFO pointer
+ @retval != NULL OK
+ @retval NULL Error
+
+ @note: Currently there is some code duplication between myrg_open()
+ and myrg_parent_open() + myrg_attach_children(). Please duplicate
+ changes in these functions or make common sub-functions.
+*/
+
+MYRG_INFO *myrg_parent_open(const char *parent_name,
+ int (*callback)(void*, const char*),
+ void *callback_param)
+{
+ MYRG_INFO *UNINIT_VAR(m_info);
+ int rc;
+ int errpos;
+ int save_errno;
+ int insert_method;
+ uint length;
+ uint child_count;
+ File fd;
+ IO_CACHE file_cache;
+ char parent_name_buff[FN_REFLEN * 2];
+ char child_name_buff[FN_REFLEN];
+ DBUG_ENTER("myrg_parent_open");
+
+ rc= 1;
+ errpos= 0;
+ bzero((char*) &file_cache, sizeof(file_cache));
+
+ /* Open MERGE meta file. */
+ 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; /* purecov: inspected */
+ errpos= 1;
+
+ if (init_io_cache(&file_cache, fd, 4 * IO_SIZE, READ_CACHE, 0, 0,
+ MYF(MY_WME | MY_NABP)))
+ goto err; /* purecov: inspected */
+ errpos= 2;
+
+ /* Count children. Determine insert method. */
+ child_count= 0;
+ insert_method= 0;
+ 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; /* purecov: inspected */
+
+ /* 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= find_type(child_name_buff + 15,
+ &merge_insert_method, 2);
+ }
+ continue;
+ }
+
+ /* Count the child. */
+ child_count++;
+ }
+
+ /* Allocate MERGE parent table structure. */
+ if (!(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO) +
+ child_count * sizeof(MYRG_TABLE),
+ MYF(MY_WME | MY_ZEROFILL))))
+ goto err; /* purecov: inspected */
+ errpos= 3;
+ m_info->open_tables= (MYRG_TABLE*) (m_info + 1);
+ m_info->tables= child_count;
+ m_info->merge_insert_method= insert_method > 0 ? insert_method : 0;
+ /* This works even if the table list is empty. */
+ m_info->end_table= m_info->open_tables + child_count;
+ if (!child_count)
+ {
+ /* Do not attach/detach an empty child list. */
+ m_info->children_attached= TRUE;
+ }
+
+ /* Call callback for each child. */
+ my_b_seek(&file_cache, 0);
+ 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 and comments. */
+ if (!child_name_buff[0] || (child_name_buff[0] == '#'))
+ continue;
+
+ DBUG_PRINT("info", ("child: '%s'", child_name_buff));
+
+ /* Callback registers child with handler table. */
+ if ((rc= (*callback)(callback_param, child_name_buff)))
+ goto err; /* purecov: inspected */
+ }
+
+ end_io_cache(&file_cache);
+ VOID(my_close(fd, MYF(0)));
+ VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST));
+
+ 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);
+
+ /* purecov: begin inspected */
+ err:
+ save_errno= my_errno;
+ switch (errpos) {
+ case 3:
+ my_free((char*) m_info, MYF(0));
+ /* Fall through */
+ case 2:
+ end_io_cache(&file_cache);
+ /* Fall through */
+ case 1:
+ VOID(my_close(fd, MYF(0)));
+ }
+ my_errno= save_errno;
+ DBUG_RETURN (NULL);
+ /* purecov: end */
+}
+
+
+/**
+ @brief Attach children to a MyISAM MERGE parent table.
+
+ @detail Call a callback function for each child table.
+ The callback returns the MyISAM table handle of the child table.
+ Check table definition match.
+
+ @param[in] m_info MERGE parent table structure
+ @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
+ @param[in] need_compat_check pointer to ha_myisammrg::need_compat_check
+ (we need this one to decide if previously
+ allocated buffers can be reused).
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error
+
+ @note: Currently there is some code duplication between myrg_open()
+ and myrg_parent_open() + myrg_attach_children(). Please duplicate
+ changes in these functions or make common sub-functions.
+*/
+
+int myrg_attach_children(MYRG_INFO *m_info, int handle_locking,
+ MI_INFO *(*callback)(void*),
+ void *callback_param, my_bool *need_compat_check)
+{
+ ulonglong file_offset;
+ MI_INFO *myisam;
+ int errpos;
+ int save_errno;
+ uint idx;
+ uint child_nr;
+ uint UNINIT_VAR(key_parts);
+ uint min_keys;
+ my_bool bad_children= FALSE;
+ DBUG_ENTER("myrg_attach_children");
+ DBUG_PRINT("myrg", ("handle_locking: %d", handle_locking));
+
+ /*
+ This function can be called while another thread is trying to abort
+ locks of this MERGE table. If the processor reorders instructions or
+ write to memory, 'children_attached' could be set before
+ 'open_tables' has all the pointers to the children. Use of a mutex
+ here and in ha_myisammrg::store_lock() forces consistent data.
+ */
+ pthread_mutex_lock(&m_info->mutex);
+ errpos= 0;
+ file_offset= 0;
+ min_keys= 0;
+ child_nr= 0;
+ while ((myisam= (*callback)(callback_param)))
+ {
+ DBUG_PRINT("myrg", ("child_nr: %u table: '%s'",
+ child_nr, myisam->filename));
+ DBUG_ASSERT(child_nr < m_info->tables);
+
+ /* Special handling when the first child is attached. */
+ if (!child_nr)
+ {
+ m_info->reclength= myisam->s->base.reclength;
+ min_keys= myisam->s->base.keys;
+ key_parts= myisam->s->base.key_parts;
+ if (*need_compat_check && m_info->rec_per_key_part)
+ {
+ my_free((char *) m_info->rec_per_key_part, MYF(0));
+ m_info->rec_per_key_part= NULL;
+ }
+ if (!m_info->rec_per_key_part)
+ {
+ if(!(m_info->rec_per_key_part= (ulong*)
+ my_malloc(key_parts * sizeof(long), MYF(MY_WME))))
+ goto err; /* purecov: inspected */
+ errpos= 1;
+ }
+ bzero((char*) m_info->rec_per_key_part, key_parts * sizeof(long));
+ }
+
+ /* 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)
+ {
+ DBUG_PRINT("error", ("definition mismatch table: '%s' repair: %d",
+ myisam->filename,
+ (handle_locking & HA_OPEN_FOR_REPAIR)));
+ if (handle_locking & HA_OPEN_FOR_REPAIR)
+ {
+ myrg_print_wrong_table(myisam->filename);
+ bad_children= TRUE;
+ continue;
+ }
+ goto bad_children;
+ }
+
+ 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; /* purecov: inspected */
+ 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 (bad_children)
+ goto bad_children;
+ /* Note: callback() resets my_errno, so it is safe to check it here */
+ if (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF)
+ goto err;
+ if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L)
+ {
+ my_errno= HA_ERR_RECORD_FILE_FULL;
+ 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->keys= min_keys;
+ m_info->last_used_table= m_info->open_tables;
+ m_info->children_attached= TRUE;
+ pthread_mutex_unlock(&m_info->mutex);
+ DBUG_RETURN(0);
+
+bad_children:
+ my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+err:
+ save_errno= my_errno;
+ switch (errpos) {
+ case 1:
+ my_free((char*) m_info->rec_per_key_part, MYF(0));
+ m_info->rec_per_key_part= NULL;
+ }
+ pthread_mutex_unlock(&m_info->mutex);
+ my_errno= save_errno;
+ DBUG_RETURN(1);
+}
+
+
+/**
+ @brief Detach children from a MyISAM MERGE parent table.
+
+ @param[in] m_info MERGE parent table structure
+
+ @note Detach must not touch the children in any way.
+ They may have been closed at ths point already.
+ All references to the children should be removed.
+
+ @return status
+ @retval 0 OK
+*/
+
+int myrg_detach_children(MYRG_INFO *m_info)
+{
+ DBUG_ENTER("myrg_detach_children");
+ /* For symmetry with myrg_attach_children() we use the mutex here. */
+ pthread_mutex_lock(&m_info->mutex);
+ if (m_info->tables)
+ {
+ /* Do not attach/detach an empty child list. */
+ m_info->children_attached= FALSE;
+ bzero((char*) m_info->open_tables, m_info->tables * sizeof(MYRG_TABLE));
+ }
+ m_info->records= 0;
+ m_info->del= 0;
+ m_info->data_file_length= 0;
+ m_info->options= 0;
+ pthread_mutex_unlock(&m_info->mutex);
+ DBUG_RETURN(0);
+}
+
diff --git a/storage/myisammrg/myrg_panic.c b/storage/myisammrg/myrg_panic.c
new file mode 100644
index 00000000000..0b1b7476873
--- /dev/null
+++ b/storage/myisammrg/myrg_panic.c
@@ -0,0 +1,45 @@
+/* Copyright (C) 2000-2001 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "myrg_def.h"
+
+ /* if flag == HA_PANIC_CLOSE then all misam files are closed */
+ /* if flag == HA_PANIC_WRITE then all misam files are unlocked and
+ all changed data in single user misam is written to file */
+ /* if flag == HA_PANIC_READ then all misam files that was locked when
+ mi_panic(HA_PANIC_WRITE) was done is locked. A mi_readinfo() is
+ done for all single user files to get changes in database */
+
+
+int myrg_panic(enum ha_panic_function flag)
+{
+ int error=0;
+ LIST *list_element,*next_open;
+ MYRG_INFO *info;
+ DBUG_ENTER("myrg_panic");
+
+ for (list_element=myrg_open_list ; list_element ; list_element=next_open)
+ {
+ next_open=list_element->next; /* Save if close */
+ info=(MYRG_INFO*) list_element->data;
+ if (flag == HA_PANIC_CLOSE && myrg_close(info))
+ error=my_errno;
+ }
+ if (myrg_open_list && flag != HA_PANIC_CLOSE)
+ DBUG_RETURN(mi_panic(flag));
+ if (error)
+ my_errno=error;
+ DBUG_RETURN(error);
+}
diff --git a/storage/myisammrg/myrg_queue.c b/storage/myisammrg/myrg_queue.c
new file mode 100644
index 00000000000..d2579053784
--- /dev/null
+++ b/storage/myisammrg/myrg_queue.c
@@ -0,0 +1,80 @@
+/* Copyright (C) 2000-2003, 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "myrg_def.h"
+
+static int queue_key_cmp(void *keyseg, uchar *a, uchar *b)
+{
+ MYRG_TABLE *ma= (MYRG_TABLE *)a;
+ MYRG_TABLE *mb= (MYRG_TABLE *)b;
+ MI_INFO *aa= ma->table;
+ MI_INFO *bb= mb->table;
+ uint not_used[2];
+ int ret= ha_key_cmp((HA_KEYSEG *)keyseg, aa->lastkey, bb->lastkey,
+ USE_WHOLE_KEY, SEARCH_FIND, not_used);
+ if (ret < 0)
+ return -1;
+ if (ret > 0)
+ return 1;
+
+ /*
+ If index tuples have the same values, let the record with least rowid
+ value be "smaller", so index scans return records ordered by (keytuple,
+ rowid). This is used by index_merge access method, grep for ROR in
+ sql/opt_range.cc for details.
+ */
+ return (ma->file_offset < mb->file_offset)? -1 : (ma->file_offset >
+ mb->file_offset) ? 1 : 0;
+} /* queue_key_cmp */
+
+
+int _myrg_init_queue(MYRG_INFO *info,int inx,enum ha_rkey_function search_flag)
+{
+ int error=0;
+ QUEUE *q= &(info->by_key);
+
+ if (inx < (int) info->keys)
+ {
+ if (!is_queue_inited(q))
+ {
+ if (init_queue(q,info->tables, 0,
+ (myisam_readnext_vec[search_flag] == SEARCH_SMALLER),
+ queue_key_cmp,
+ info->open_tables->table->s->keyinfo[inx].seg))
+ error=my_errno;
+ }
+ else
+ {
+ if (reinit_queue(q,info->tables, 0,
+ (myisam_readnext_vec[search_flag] == SEARCH_SMALLER),
+ queue_key_cmp,
+ info->open_tables->table->s->keyinfo[inx].seg))
+ error=my_errno;
+ }
+ }
+ else
+ my_errno= error= HA_ERR_WRONG_INDEX;
+ return error;
+}
+
+int _myrg_mi_read_record(MI_INFO *info, uchar *buf)
+{
+ if (!(*info->read_record)(info,info->lastpos,buf))
+ {
+ info->update|= HA_STATE_AKTIV; /* Record is read */
+ return 0;
+ }
+ return my_errno;
+}
diff --git a/storage/myisammrg/myrg_range.c b/storage/myisammrg/myrg_range.c
new file mode 100644
index 00000000000..26aa465e7d1
--- /dev/null
+++ b/storage/myisammrg/myrg_range.c
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002, 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "myrg_def.h"
+
+ha_rows myrg_records_in_range(MYRG_INFO *info, int inx,
+ key_range *min_key, key_range *max_key)
+{
+ ha_rows records=0, res;
+ MYRG_TABLE *table;
+
+ for (table=info->open_tables ; table != info->end_table ; table++)
+ {
+ res= mi_records_in_range(table->table, inx, min_key, max_key);
+ if (res == HA_POS_ERROR)
+ return HA_POS_ERROR;
+ if (records > HA_POS_ERROR - res)
+ return HA_POS_ERROR-1;
+ records+=res;
+ }
+ return records;
+}
+
diff --git a/storage/myisammrg/myrg_records.c b/storage/myisammrg/myrg_records.c
new file mode 100644
index 00000000000..03815d934a8
--- /dev/null
+++ b/storage/myisammrg/myrg_records.c
@@ -0,0 +1,27 @@
+/* Copyright (C) 2008 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "myrg_def.h"
+
+ha_rows myrg_records(MYRG_INFO *info)
+{
+ ha_rows records=0;
+ MYRG_TABLE *file;
+ DBUG_ENTER("myrg_records");
+
+ for (file=info->open_tables ; file != info->end_table ; file++)
+ records+= file->table->s->state.state.records;
+ DBUG_RETURN(records);
+}
diff --git a/storage/myisammrg/myrg_rfirst.c b/storage/myisammrg/myrg_rfirst.c
new file mode 100644
index 00000000000..9d7b0f9e83f
--- /dev/null
+++ b/storage/myisammrg/myrg_rfirst.c
@@ -0,0 +1,48 @@
+/* Copyright (C) 2000-2001, 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "myrg_def.h"
+
+ /* Read first row according to specific key */
+
+int myrg_rfirst(MYRG_INFO *info, uchar *buf, int inx)
+{
+ MYRG_TABLE *table;
+ MI_INFO *mi;
+ int err;
+
+ if (_myrg_init_queue(info,inx,HA_READ_KEY_OR_NEXT))
+ return my_errno;
+
+ for (table=info->open_tables ; table != info->end_table ; table++)
+ {
+ if ((err=mi_rfirst(table->table,NULL,inx)))
+ {
+ if (err == HA_ERR_END_OF_FILE)
+ continue;
+ return err;
+ }
+ /* adding to queue */
+ queue_insert(&(info->by_key),(uchar *)table);
+ }
+ /* We have done a read in all tables */
+ info->last_used_table=table;
+
+ if (!info->by_key.elements)
+ return HA_ERR_END_OF_FILE;
+
+ mi=(info->current_table=(MYRG_TABLE *)queue_top(&(info->by_key)))->table;
+ return _myrg_mi_read_record(mi,buf);
+}
diff --git a/storage/myisammrg/myrg_rkey.c b/storage/myisammrg/myrg_rkey.c
new file mode 100644
index 00000000000..3b14e8ce3b6
--- /dev/null
+++ b/storage/myisammrg/myrg_rkey.c
@@ -0,0 +1,97 @@
+/*
+ Copyright (c) 2000-2003, 2005-2008 MySQL AB, 2009 Sun Microsystems, Inc.
+ Use is subject to license terms.
+
+ 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/* Read record based on a key */
+
+/*
+ * HA_READ_KEY_EXACT => SEARCH_BIGGER
+ * HA_READ_KEY_OR_NEXT => SEARCH_BIGGER
+ * HA_READ_AFTER_KEY => SEARCH_BIGGER
+ * HA_READ_PREFIX => SEARCH_BIGGER
+ * HA_READ_KEY_OR_PREV => SEARCH_SMALLER
+ * HA_READ_BEFORE_KEY => SEARCH_SMALLER
+ * HA_READ_PREFIX_LAST => SEARCH_SMALLER
+ */
+
+
+#include "myrg_def.h"
+
+/* todo: we could store some additional info to speedup lookups:
+ column (key, keyseg) can be constant per table
+ it can also be increasing (table1.val > table2.val > ...),
+ or decreasing, <=, >=, etc.
+ SerG
+*/
+
+int myrg_rkey(MYRG_INFO *info,uchar *buf,int inx, const uchar *key,
+ key_part_map keypart_map, enum ha_rkey_function search_flag)
+{
+ uchar *UNINIT_VAR(key_buff);
+ uint UNINIT_VAR(pack_key_length);
+ uint16 UNINIT_VAR(last_used_keyseg);
+ MYRG_TABLE *table;
+ MI_INFO *mi;
+ int err;
+ DBUG_ENTER("myrg_rkey");
+
+ if (_myrg_init_queue(info,inx,search_flag))
+ DBUG_RETURN(my_errno);
+
+ for (table=info->open_tables ; table != info->end_table ; table++)
+ {
+ mi=table->table;
+
+ if (table == info->open_tables)
+ {
+ err=mi_rkey(mi, 0, inx, key, keypart_map, search_flag);
+ /* Get the saved packed key and packed key length. */
+ key_buff=(uchar*) mi->lastkey+mi->s->base.max_key_length;
+ pack_key_length=mi->pack_key_length;
+ last_used_keyseg= mi->last_used_keyseg;
+ }
+ else
+ {
+ mi->once_flags|= USE_PACKED_KEYS;
+ mi->last_used_keyseg= last_used_keyseg;
+ err=mi_rkey(mi, 0, inx, key_buff, pack_key_length, search_flag);
+ }
+ info->last_used_table=table+1;
+
+ if (err)
+ {
+ if (err == HA_ERR_KEY_NOT_FOUND)
+ continue;
+ DBUG_PRINT("exit", ("err: %d", err));
+ DBUG_RETURN(err);
+ }
+ /* adding to queue */
+ queue_insert(&(info->by_key),(uchar *)table);
+
+ }
+
+ DBUG_PRINT("info", ("tables with matches: %u", info->by_key.elements));
+ if (!info->by_key.elements)
+ DBUG_RETURN(HA_ERR_KEY_NOT_FOUND);
+
+ mi=(info->current_table=(MYRG_TABLE *)queue_top(&(info->by_key)))->table;
+ mi->once_flags|= RRND_PRESERVE_LASTINX;
+ DBUG_PRINT("info", ("using table no: %d",
+ (int) (info->current_table - info->open_tables + 1)));
+ DBUG_DUMP("result key", (uchar*) mi->lastkey, mi->lastkey_length);
+ DBUG_RETURN(_myrg_mi_read_record(mi,buf));
+}
diff --git a/storage/myisammrg/myrg_rlast.c b/storage/myisammrg/myrg_rlast.c
new file mode 100644
index 00000000000..8086a2f8104
--- /dev/null
+++ b/storage/myisammrg/myrg_rlast.c
@@ -0,0 +1,49 @@
+/* Copyright (C) 2000-2001, 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "myrg_def.h"
+
+ /* Read last row with the same key as the previous read. */
+
+int myrg_rlast(MYRG_INFO *info, uchar *buf, int inx)
+{
+ MYRG_TABLE *table;
+ MI_INFO *mi;
+ int err;
+
+ if (_myrg_init_queue(info,inx, HA_READ_KEY_OR_PREV))
+ return my_errno;
+
+ for (table=info->open_tables ; table < info->end_table ; table++)
+ {
+ if ((err=mi_rlast(table->table,NULL,inx)))
+ {
+ if (err == HA_ERR_END_OF_FILE)
+ continue;
+ return err;
+ }
+ /* adding to queue */
+ queue_insert(&(info->by_key),(uchar *)table);
+ }
+ /* We have done a read in all tables */
+ info->last_used_table=table;
+
+ if (!info->by_key.elements)
+ return HA_ERR_END_OF_FILE;
+
+ mi=(info->current_table=(MYRG_TABLE *)queue_top(&(info->by_key)))->table;
+ return _myrg_mi_read_record(mi,buf);
+}
+
diff --git a/storage/myisammrg/myrg_rnext.c b/storage/myisammrg/myrg_rnext.c
new file mode 100644
index 00000000000..82d5cbf38b1
--- /dev/null
+++ b/storage/myisammrg/myrg_rnext.c
@@ -0,0 +1,52 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "myrg_def.h"
+
+ /*
+ Read next row with the same key as previous read
+ */
+
+int myrg_rnext(MYRG_INFO *info, uchar *buf, int inx)
+{
+ int err;
+ MI_INFO *mi;
+
+ if (!info->current_table)
+ return (HA_ERR_KEY_NOT_FOUND);
+
+ /* at first, do rnext for the table found before */
+ if ((err=mi_rnext(info->current_table->table,NULL,inx)))
+ {
+ if (err == HA_ERR_END_OF_FILE)
+ {
+ queue_remove(&(info->by_key),0);
+ if (!info->by_key.elements)
+ return HA_ERR_END_OF_FILE;
+ }
+ else
+ return err;
+ }
+ else
+ {
+ /* Found here, adding to queue */
+ queue_top(&(info->by_key))=(uchar *)(info->current_table);
+ queue_replaced(&(info->by_key));
+ }
+
+ /* now, mymerge's read_next is as simple as one queue_top */
+ mi=(info->current_table=(MYRG_TABLE *)queue_top(&(info->by_key)))->table;
+ return _myrg_mi_read_record(mi,buf);
+}
diff --git a/storage/myisammrg/myrg_rnext_same.c b/storage/myisammrg/myrg_rnext_same.c
new file mode 100644
index 00000000000..ad7bbfb0f6e
--- /dev/null
+++ b/storage/myisammrg/myrg_rnext_same.c
@@ -0,0 +1,50 @@
+/* Copyright (C) 2002, 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "myrg_def.h"
+
+
+int myrg_rnext_same(MYRG_INFO *info, uchar *buf)
+{
+ int err;
+ MI_INFO *mi;
+
+ if (!info->current_table)
+ return (HA_ERR_KEY_NOT_FOUND);
+
+ /* at first, do rnext for the table found before */
+ if ((err=mi_rnext_same(info->current_table->table,NULL)))
+ {
+ if (err == HA_ERR_END_OF_FILE)
+ {
+ queue_remove(&(info->by_key),0);
+ if (!info->by_key.elements)
+ return HA_ERR_END_OF_FILE;
+ }
+ else
+ return err;
+ }
+ else
+ {
+ /* Found here, adding to queue */
+ queue_top(&(info->by_key))=(uchar *)(info->current_table);
+ queue_replaced(&(info->by_key));
+ }
+
+ /* now, mymerge's read_next is as simple as one queue_top */
+ mi=(info->current_table=(MYRG_TABLE *)queue_top(&(info->by_key)))->table;
+ return _myrg_mi_read_record(mi,buf);
+}
+
diff --git a/storage/myisammrg/myrg_rprev.c b/storage/myisammrg/myrg_rprev.c
new file mode 100644
index 00000000000..66c94974940
--- /dev/null
+++ b/storage/myisammrg/myrg_rprev.c
@@ -0,0 +1,52 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "myrg_def.h"
+
+ /*
+ Read previous row with the same key as previous read
+ */
+
+int myrg_rprev(MYRG_INFO *info, uchar *buf, int inx)
+{
+ int err;
+ MI_INFO *mi;
+
+ if (!info->current_table)
+ return (HA_ERR_KEY_NOT_FOUND);
+
+ /* at first, do rprev for the table found before */
+ if ((err=mi_rprev(info->current_table->table,NULL,inx)))
+ {
+ if (err == HA_ERR_END_OF_FILE)
+ {
+ queue_remove(&(info->by_key),0);
+ if (!info->by_key.elements)
+ return HA_ERR_END_OF_FILE;
+ }
+ else
+ return err;
+ }
+ else
+ {
+ /* Found here, adding to queue */
+ queue_top(&(info->by_key))=(uchar *)(info->current_table);
+ queue_replaced(&(info->by_key));
+ }
+
+ /* now, mymerge's read_prev is as simple as one queue_top */
+ mi=(info->current_table=(MYRG_TABLE *)queue_top(&(info->by_key)))->table;
+ return _myrg_mi_read_record(mi,buf);
+}
diff --git a/storage/myisammrg/myrg_rrnd.c b/storage/myisammrg/myrg_rrnd.c
new file mode 100644
index 00000000000..b598563680c
--- /dev/null
+++ b/storage/myisammrg/myrg_rrnd.c
@@ -0,0 +1,116 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Read a record with random-access. The position to the record must
+ get by myrg_info(). The next record can be read with pos= -1 */
+
+
+#include "myrg_def.h"
+
+static MYRG_TABLE *find_table(MYRG_TABLE *start,MYRG_TABLE *end,ulonglong pos);
+
+/*
+ If filepos == HA_OFFSET_ERROR, read next
+ Returns same as mi_rrnd:
+ 0 = Ok.
+ HA_ERR_RECORD_DELETED = Record is deleted.
+ HA_ERR_END_OF_FILE = EOF.
+*/
+
+int myrg_rrnd(MYRG_INFO *info,uchar *buf,ulonglong filepos)
+{
+ int error;
+ MI_INFO *isam_info;
+ DBUG_ENTER("myrg_rrnd");
+ DBUG_PRINT("info",("offset: %lu", (ulong) filepos));
+
+ if (filepos == HA_OFFSET_ERROR)
+ {
+ if (!info->current_table)
+ {
+ if (info->open_tables == info->end_table)
+ { /* No tables */
+ DBUG_RETURN(my_errno=HA_ERR_END_OF_FILE);
+ }
+ isam_info=(info->current_table=info->open_tables)->table;
+ if (info->cache_in_use)
+ mi_extra(isam_info,HA_EXTRA_CACHE,(uchar*) &info->cache_size);
+ filepos=isam_info->s->pack.header_length;
+ isam_info->lastinx= (uint) -1; /* Can't forward or backward */
+ }
+ else
+ {
+ isam_info=info->current_table->table;
+ filepos= isam_info->nextpos;
+ }
+
+ for (;;)
+ {
+ isam_info->update&= HA_STATE_CHANGED;
+ if ((error=(*isam_info->s->read_rnd)(isam_info,(uchar*) buf,
+ (my_off_t) filepos,1)) !=
+ HA_ERR_END_OF_FILE)
+ DBUG_RETURN(error);
+ if (info->cache_in_use)
+ mi_extra(info->current_table->table, HA_EXTRA_NO_CACHE,
+ (uchar*) &info->cache_size);
+ if (info->current_table+1 == info->end_table)
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ info->current_table++;
+ info->last_used_table=info->current_table;
+ if (info->cache_in_use)
+ mi_extra(info->current_table->table, HA_EXTRA_CACHE,
+ (uchar*) &info->cache_size);
+ info->current_table->file_offset=
+ info->current_table[-1].file_offset+
+ info->current_table[-1].table->state->data_file_length;
+
+ isam_info=info->current_table->table;
+ filepos=isam_info->s->pack.header_length;
+ isam_info->lastinx= (uint) -1;
+ }
+ }
+ info->current_table=find_table(info->open_tables,
+ info->end_table-1,filepos);
+ isam_info=info->current_table->table;
+ isam_info->update&= HA_STATE_CHANGED;
+ DBUG_RETURN((*isam_info->s->read_rnd)
+ (isam_info, (uchar*) buf,
+ (my_off_t) (filepos - info->current_table->file_offset),
+ 0));
+}
+
+
+ /* Find which table to use according to file-pos */
+
+static MYRG_TABLE *find_table(MYRG_TABLE *start, MYRG_TABLE *end,
+ ulonglong pos)
+{
+ MYRG_TABLE *mid;
+ DBUG_ENTER("find_table");
+
+ while (start != end)
+ {
+ mid=start+((uint) (end-start)+1)/2;
+ if (mid->file_offset > pos)
+ end=mid-1;
+ else
+ start=mid;
+ }
+ DBUG_PRINT("info",("offset: %lu, table: %s",
+ (ulong) pos, start->table->filename));
+ DBUG_RETURN(start);
+}
diff --git a/storage/myisammrg/myrg_rsame.c b/storage/myisammrg/myrg_rsame.c
new file mode 100644
index 00000000000..2f7523759dc
--- /dev/null
+++ b/storage/myisammrg/myrg_rsame.c
@@ -0,0 +1,27 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "myrg_def.h"
+
+int myrg_rsame(MYRG_INFO *info,uchar *record,int inx)
+{
+ if (inx) /* not yet used, should be 0 */
+ return (my_errno=HA_ERR_WRONG_INDEX);
+
+ if (!info->current_table)
+ return (my_errno=HA_ERR_NO_ACTIVE_RECORD);
+
+ return mi_rsame(info->current_table->table,record,inx);
+}
diff --git a/storage/myisammrg/myrg_static.c b/storage/myisammrg/myrg_static.c
new file mode 100644
index 00000000000..c20d2be4396
--- /dev/null
+++ b/storage/myisammrg/myrg_static.c
@@ -0,0 +1,29 @@
+/* Copyright (C) 2000-2001, 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Static variables for pisam library. All definied here for easy making of
+ a shared library
+*/
+
+#ifndef stdin
+#include "myrg_def.h"
+#endif
+
+LIST *myrg_open_list=0;
+static const char *merge_insert_methods[] =
+{ "FIRST", "LAST", NullS };
+TYPELIB merge_insert_method= { array_elements(merge_insert_methods)-1,"",
+ merge_insert_methods, 0};
diff --git a/storage/myisammrg/myrg_update.c b/storage/myisammrg/myrg_update.c
new file mode 100644
index 00000000000..5d883be8484
--- /dev/null
+++ b/storage/myisammrg/myrg_update.c
@@ -0,0 +1,26 @@
+/* Copyright (C) 2000-2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Update last read record */
+
+#include "myrg_def.h"
+
+int myrg_update(register MYRG_INFO *info,const uchar *oldrec, uchar *newrec)
+{
+ if (!info->current_table)
+ return (my_errno=HA_ERR_NO_ACTIVE_RECORD);
+
+ return mi_update(info->current_table->table,oldrec,newrec);
+}
diff --git a/storage/myisammrg/myrg_write.c b/storage/myisammrg/myrg_write.c
new file mode 100644
index 00000000000..27534df2821
--- /dev/null
+++ b/storage/myisammrg/myrg_write.c
@@ -0,0 +1,29 @@
+/* Copyright (C) 2001-2002, 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Write a row to a MyISAM MERGE table */
+
+#include "myrg_def.h"
+
+int myrg_write(register MYRG_INFO *info, uchar *rec)
+{
+ /* [phi] MERGE_WRITE_DISABLED is handled by the else case */
+ if (info->merge_insert_method == MERGE_INSERT_TO_FIRST)
+ return mi_write((info->current_table=info->open_tables)->table,rec);
+ else if (info->merge_insert_method == MERGE_INSERT_TO_LAST)
+ return mi_write((info->current_table=info->end_table-1)->table,rec);
+ else /* unsupported insertion method */
+ return (my_errno= HA_ERR_WRONG_COMMAND);
+}
diff --git a/storage/myisammrg/plug.in b/storage/myisammrg/plug.in
new file mode 100644
index 00000000000..1f94e07d881
--- /dev/null
+++ b/storage/myisammrg/plug.in
@@ -0,0 +1,6 @@
+MYSQL_STORAGE_ENGINE(myisammrg,no,[MyISAM MERGE Engine],
+ [Merge multiple MySQL tables into one])
+MYSQL_PLUGIN_DIRECTORY(myisammrg,[storage/myisammrg])
+MYSQL_PLUGIN_STATIC(myisammrg, [libmyisammrg.a])
+MYSQL_PLUGIN_MANDATORY(myisammrg)
+MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS(myisammrg, [ha_myisammrg.cc])