diff options
Diffstat (limited to 'storage/myisammrg')
29 files changed, 907 insertions, 490 deletions
diff --git a/storage/myisammrg/.cvsignore b/storage/myisammrg/.cvsignore deleted file mode 100644 index e9955884756..00000000000 --- a/storage/myisammrg/.cvsignore +++ /dev/null @@ -1,3 +0,0 @@ -.deps -Makefile -Makefile.in diff --git a/storage/myisammrg/CMakeLists.txt b/storage/myisammrg/CMakeLists.txt index 4bb581c1298..eec3ca783dc 100755..100644 --- a/storage/myisammrg/CMakeLists.txt +++ b/storage/myisammrg/CMakeLists.txt @@ -1,5 +1,4 @@ -# Copyright (c) 2006-2008 MySQL AB, 2009 Sun Microsystems, Inc. -# Use is subject to license terms. +# Copyright (c) 2006, 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 @@ -12,11 +11,7 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 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") +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA SET(MYISAMMRG_SOURCES myrg_close.c myrg_create.c myrg_delete.c myrg_extra.c myrg_info.c ha_myisammrg.cc @@ -25,4 +20,4 @@ SET(MYISAMMRG_SOURCES myrg_close.c myrg_create.c myrg_delete.c myrg_extra.c myr 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) +MYSQL_ADD_PLUGIN(myisammrg ${MYISAMMRG_SOURCES} STORAGE_ENGINE MANDATORY RECOMPILE_FOR_EMBEDDED) diff --git a/storage/myisammrg/Makefile.am b/storage/myisammrg/Makefile.am deleted file mode 100644 index 091362bf7fc..00000000000 --- a/storage/myisammrg/Makefile.am +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2000-2002, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 index 75c96a3527e..b7e043c99f1 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -11,8 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* @@ -34,40 +33,29 @@ 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. This list is attached to the handler object as + ha_myisammrg::children_l. The end of the children list is saved in + ha_myisammrg::children_last_l. + + Back in open_tables(), handler::extra(HA_EXTRA_ADD_CHILDREN_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 by open_table(). The children are opened as + independent MyISAM tables, right as if they are used by the SQL + statement. + + When all tables from the statement query list are open, + handler::extra(HA_EXTRA_ATTACH_CHILDREN) is called. It "attaches" the + children 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 @@ -81,14 +69,20 @@ 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. + The children chain remains in the statement query list until the table + is closed or the children are detached. This is done so that the + children are locked by lock_tables(). + + At statement end the children are detached. At the next statement + begin the open-add-attach sequence repeats. There is no exception for + LOCK TABLES. The fresh establishment of the parent-child relationship + before every statement catches numerous cases of ALTER/FLUSH/DROP/etc + of parent or children during LOCK TABLES. --- 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 @@ -96,13 +90,20 @@ #endif #define MYSQL_SERVER 1 -#include "mysql_priv.h" +#include "sql_priv.h" +#include "unireg.h" +#include "sql_cache.h" // query_cache_* +#include "sql_show.h" // append_identifier +#include "sql_table.h" // build_table_filename +#include "probes_mysql.h" #include <mysql/plugin.h> #include <m_ctype.h> #include "../myisam/ha_myisam.h" #include "ha_myisammrg.h" #include "myrg_def.h" - +#include "thr_malloc.h" // int_sql_alloc +#include "sql_class.h" // THD +#include "debug_sync.h" static handler *myisammrg_create_handler(handlerton *hton, TABLE_SHARE *table, @@ -118,7 +119,10 @@ static handler *myisammrg_create_handler(handlerton *hton, ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg) :handler(hton, table_arg), file(0), is_cloned(0) -{} +{ + init_sql_alloc(&children_mem_root, + FN_REFLEN + ALLOC_ROOT_MIN_BLOCK_SIZE, 0); +} /** @@ -126,7 +130,9 @@ ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg) */ ha_myisammrg::~ha_myisammrg(void) -{} +{ + free_root(&children_mem_root, MYF(0)); +} static const char *ha_myisammrg_exts[] = { @@ -153,9 +159,14 @@ extern "C" void myrg_print_wrong_table(const char *table_name) 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); + /* + Push an error to be reported as part of CHECK/REPAIR result-set. + Note that calling my_error() from handler is a hack which is kept + here to avoid refactoring. Normally engines should report errors + through return value which will be interpreted by caller using + handler::print_error() call. + */ + my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), buf); } @@ -178,62 +189,61 @@ const char *ha_myisammrg::index_type(uint key_number) /** - @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 -----------------------------------------+ + Callback function for open of a MERGE parent table. @param[in] callback_param data pointer as given to myrg_parent_open() + this is used to pass the handler handle @param[in] filename file name of MyISAM table without extension. @return status @retval 0 OK @retval != 0 Error + + @detail + + This function adds a TABLE_LIST object for a MERGE child table to a + list of tables in the parent handler object. It is called for each + child table. + + The list of child TABLE_LIST objects is kept in the handler object + of the parent for the whole life time of the MERGE table. It is + inserted in the statement query list behind the MERGE parent + TABLE_LIST object when the MERGE table is opened. It is removed from + the statement query list at end of statement or at children detach. + + All memory used for the child TABLE_LIST objects and the strings + referred by it are taken from the parent + ha_myisammrg::children_mem_root. Thus they are all freed implicitly at + the final close of the table. + + children_l -> TABLE_LIST::next_global -> TABLE_LIST::next_global + # # ^ # ^ + # # | # | + # # +--------- TABLE_LIST::prev_global + # # | + # |<--- TABLE_LIST::prev_global | + # | + children_last_l -----------------------------------------+ */ -static int myisammrg_parent_open_callback(void *callback_param, - const char *filename) +CPP_UNNAMED_NS_START + +extern "C" 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; + Mrg_child_def *mrg_child_def; + char *db; + char *table_name; + uint dirlen; + uint db_length; + uint table_name_length; 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 @@ -243,22 +253,21 @@ static int myisammrg_parent_open_callback(void *callback_param, 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); + db_length= parent->s->db.length; + db= strmake_root(&ha_myrg->children_mem_root, parent->s->db.str, 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); + table_name_length= filename_to_tablename(filename, name_buf, + sizeof(name_buf)); + table_name= strmake_root(&ha_myrg->children_mem_root, name_buf, + 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); + table_name_length= strlen(filename); + table_name= strmake_root(&ha_myrg->children_mem_root, filename, + table_name_length); } } else @@ -270,140 +279,401 @@ static int myisammrg_parent_open_callback(void *callback_param, /* 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); + table_name_length= filename_to_tablename(dir_path + dirlen, name_buf, + sizeof(name_buf)); + table_name= strmake_root(&ha_myrg->children_mem_root, name_buf, + 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); + db_length= filename_to_tablename(dir_path + dirlen, name_buf, sizeof(name_buf)); + db= strmake_root(&ha_myrg->children_mem_root, name_buf, 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); + table_name_length= strlen(dir_path + dirlen); + table_name= strmake_root(&ha_myrg->children_mem_root, dir_path + dirlen, + 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); + db_length= strlen(dir_path + dirlen); + db= strmake_root(&ha_myrg->children_mem_root, dir_path + dirlen, + 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)); + if (! db || ! table_name) + DBUG_RETURN(1); + + DBUG_PRINT("myrg", ("open: '%.*s'.'%.*s'", (int) db_length, db, + (int) table_name_length, 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; + if (lower_case_table_names && table_name_length) + { + /* purecov: begin tested */ + table_name_length= my_casedn_str(files_charset_info, table_name); + /* purecov: end */ + } + + mrg_child_def= new (&ha_myrg->children_mem_root) + Mrg_child_def(db, db_length, table_name, table_name_length); + + if (! mrg_child_def || + ha_myrg->child_def_list.push_back(mrg_child_def, + &ha_myrg->children_mem_root)) + { + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + +CPP_UNNAMED_NS_END + + +/** + Open a MERGE parent table, but not its children. + + @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 + + @detail + This function initializes the MERGE storage engine structures + and adds a child list of TABLE_LIST to the parent handler. +*/ + +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)); + + /* Must not be used when table is open. */ + DBUG_ASSERT(!this->file); + + /* Save for later use. */ + test_if_locked= test_if_locked_arg; + + /* In case this handler was open and closed before, free old data. */ + free_root(&this->children_mem_root, MYF(MY_MARK_BLOCKS_FREE)); + + /* + Initialize variables that are used, modified, and/or set by + myisammrg_parent_open_callback(). + 'children_l' is the head of the children chain. + 'children_last_l' points to the end of the children chain. + 'my_errno' is set by myisammrg_parent_open_callback() in + case of an error. + */ + children_l= NULL; + children_last_l= NULL; + child_def_list.empty(); + my_errno= 0; + + /* retrieve children table list. */ + 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(name, table->db_stat, HA_OPEN_IGNORE_IF_LOCKED))) + { + DBUG_PRINT("error", ("my_errno %d", my_errno)); + DBUG_RETURN(my_errno ? my_errno : -1); + } - /* Initialize table map to 'undefined'. */ - child_l->init_child_def_version(); + file->children_attached= TRUE; - /* Link TABLE_LIST object into the parent list. */ - if (!parent->child_last_l) + info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); + } + else if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this))) { - /* Initialize parent->child_last_l when handling first child. */ - parent->child_last_l= &parent->child_l; + /* purecov: begin inspected */ + DBUG_PRINT("error", ("my_errno %d", my_errno)); + DBUG_RETURN(my_errno ? my_errno : -1); + /* purecov: end */ + } + DBUG_PRINT("myrg", ("MYRG_INFO: 0x%lx child tables: %u", + (long) file, file->tables)); + DBUG_RETURN(0); +} + + +/** + Add list of MERGE children to a TABLE_LIST chain. + + @return status + @retval 0 OK + @retval != 0 Error + + @detail + When a MERGE parent table has just been opened, insert the + TABLE_LIST chain from the MERGE handler into the table list used for + opening tables for this statement. This lets the children be opened + too. +*/ + +int ha_myisammrg::add_children_list(void) +{ + TABLE_LIST *parent_l= this->table->pos_in_table_list; + THD *thd= table->in_use; + List_iterator_fast<Mrg_child_def> it(child_def_list); + Mrg_child_def *mrg_child_def; + DBUG_ENTER("ha_myisammrg::add_children_list"); + DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", this->table->s->db.str, + this->table->s->table_name.str, (long) this->table)); + + /* Must call this with open table. */ + DBUG_ASSERT(this->file); + + /* Ignore this for empty MERGE tables (UNION=()). */ + if (!this->file->tables) + { + DBUG_PRINT("myrg", ("empty merge table union")); + goto end; + } + + /* Must not call this with attached children. */ + DBUG_ASSERT(!this->file->children_attached); + + /* Must not call this with children list in place. */ + DBUG_ASSERT(this->children_l == NULL); + + /* + Prevent inclusion of another MERGE table, which could make infinite + recursion. + */ + if (parent_l->parent_l) + { + my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), parent_l->alias); + DBUG_RETURN(1); + } + + while ((mrg_child_def= it++)) + { + TABLE_LIST *child_l; + char *db; + char *table_name; + + child_l= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST)); + db= (char*) thd->memdup(mrg_child_def->db.str, mrg_child_def->db.length+1); + table_name= (char*) thd->memdup(mrg_child_def->name.str, + mrg_child_def->name.length+1); + + if (child_l == NULL || db == NULL || table_name == NULL) + DBUG_RETURN(1); + + child_l->init_one_table(db, mrg_child_def->db.length, + table_name, mrg_child_def->name.length, + table_name, parent_l->lock_type); + /* Set parent reference. Used to detect MERGE in children list. */ + child_l->parent_l= parent_l; + /* Copy select_lex. Used in unique_table() at least. */ + child_l->select_lex= parent_l->select_lex; + /* Set the expected table version, to not cause spurious re-prepare. */ + child_l->set_table_ref_id(mrg_child_def->get_child_table_ref_type(), + mrg_child_def->get_child_def_version()); + /* + For statements which acquire a SNW metadata lock on a parent table and + then later try to upgrade it to an X lock (e.g. ALTER TABLE), SNW + locks should be also taken on the children tables. + + Otherwise we end up in a situation where the thread trying to upgrade SNW + to X lock on the parent also holds a SR metadata lock and a read + thr_lock.c lock on the child. As a result, another thread might be + blocked on the thr_lock.c lock for the child after successfully acquiring + a SR or SW metadata lock on it. If at the same time this second thread + has a shared metadata lock on the parent table or there is some other + thread which has a shared metadata lock on the parent and is waiting for + this second thread, we get a deadlock. This deadlock cannot be properly + detected by the MDL subsystem as part of the waiting happens within + thr_lock.c. By taking SNW locks on the child tables we ensure that any + thread which waits for a thread doing SNW -> X upgrade, does this within + the MDL subsystem and thus potential deadlocks are exposed to the deadlock + detector. + + We don't do the same thing for SNRW locks as this would allow + DDL on implicitly locked underlying tables of a MERGE table. + */ + if (! thd->locked_tables_mode && + parent_l->mdl_request.type == MDL_SHARED_NO_WRITE) + child_l->mdl_request.set_type(MDL_SHARED_NO_WRITE); + /* Link TABLE_LIST object into the children list. */ + if (this->children_last_l) + child_l->prev_global= this->children_last_l; + else + { + /* Initialize children_last_l when handling first child. */ + this->children_last_l= &this->children_l; + } + *this->children_last_l= child_l; + this->children_last_l= &child_l->next_global; } - *parent->child_last_l= child_l; - child_l->prev_global= parent->child_last_l; - parent->child_last_l= &child_l->next_global; + /* Insert children into the table list. */ + if (parent_l->next_global) + parent_l->next_global->prev_global= this->children_last_l; + *this->children_last_l= parent_l->next_global; + parent_l->next_global= this->children_l; + this->children_l->prev_global= &parent_l->next_global; + /* + We have to update LEX::query_tables_last if children are added to + the tail of the table list in order to be able correctly add more + elements to it (e.g. as part of prelocking process). + */ + if (thd->lex->query_tables_last == &parent_l->next_global) + thd->lex->query_tables_last= this->children_last_l; + /* + The branch below works only when re-executing a prepared + statement or a stored routine statement: + We've just modified query_tables_last. Keep it in sync with + query_tables_last_own, if it was set by the prelocking code. + This ensures that the check that prohibits double updates (*) + can correctly identify what tables belong to the main statement. + (*) A double update is, e.g. when a user issues UPDATE t1 and + t1 has an AFTER UPDATE trigger that also modifies t1. + */ + if (thd->lex->query_tables_own_last == &parent_l->next_global) + thd->lex->query_tables_own_last= this->children_last_l; + +end: DBUG_RETURN(0); } /** - @brief Callback function for attaching a MERGE child table. + A context of myrg_attach_children() callback. +*/ - @detail This function retrieves the MyISAM table handle from the - next child table. It is called for each child table. +class Mrg_attach_children_callback_param +{ +public: + /** + 'need_compat_check' is set by myisammrg_attach_children_callback() + if a child fails the table def version check. + */ + bool need_compat_check; + /** TABLE_LIST identifying this merge parent. */ + TABLE_LIST *parent_l; + /** Iterator position, the current child to attach. */ + TABLE_LIST *next_child_attach; + List_iterator_fast<Mrg_child_def> def_it; + Mrg_child_def *mrg_child_def; +public: + Mrg_attach_children_callback_param(TABLE_LIST *parent_l_arg, + TABLE_LIST *first_child, + List<Mrg_child_def> &child_def_list) + :need_compat_check(FALSE), + parent_l(parent_l_arg), + next_child_attach(first_child), + def_it(child_def_list), + mrg_child_def(def_it++) + {} + void next() + { + next_child_attach= next_child_attach->next_global; + if (next_child_attach && next_child_attach->parent_l != parent_l) + next_child_attach= NULL; + if (mrg_child_def) + mrg_child_def= def_it++; + } +}; - @param[in] callback_param data pointer as given to - myrg_attach_children() + +/** + Callback function for attaching a MERGE child table. + + @param[in] callback_param data pointer as given to myrg_attach_children() + this is used to pass the handler handle @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 + @retval NULL, Error. + + @detail + This function retrieves the MyISAM table handle from the + next child table. It is called for each child table. */ -static MI_INFO *myisammrg_attach_children_callback(void *callback_param) +CPP_UNNAMED_NS_START + +extern "C" MI_INFO *myisammrg_attach_children_callback(void *callback_param) { - ha_myisammrg *ha_myrg; - TABLE *parent; + Mrg_attach_children_callback_param *param= + (Mrg_attach_children_callback_param*) callback_param; + TABLE *parent= param->parent_l->table; TABLE *child; - TABLE_LIST *child_l; - MI_INFO *UNINIT_VAR(myisam); + TABLE_LIST *child_l= param->next_child_attach; + Mrg_child_def *mrg_child_def= param->mrg_child_def; + MI_INFO *myisam= NULL; DBUG_ENTER("myisammrg_attach_children_callback"); - my_errno= 0; - ha_myrg= (ha_myisammrg*) callback_param; - parent= ha_myrg->table_ptr(); + /* + Number of children in the list and MYRG_INFO::tables_count, + which is used by caller of this function, should always match. + */ + DBUG_ASSERT(child_l); - /* 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. */ + param->next(); + /* - Prepare for next child. Used as child_l in next call to this function. - We cannot rely on a NULL-terminated chain. + When MERGE table is opened for CHECK or REPAIR TABLE statements, + failure to open any of underlying tables is ignored until this moment + (this is needed to provide complete list of the problematic underlying + tables in CHECK/REPAIR TABLE output). + Here we detect such a situation and report an appropriate error. */ - if (&child_l->next_global == parent->child_last_l) + if (! child) { - DBUG_PRINT("myrg", ("attaching last child")); - ha_myrg->next_child_attach= NULL; + DBUG_PRINT("error", ("failed to open underlying table '%s'.'%s'", + child_l->db, child_l->table_name)); + /* This should only happen inside of CHECK/REPAIR TABLE. */ + DBUG_ASSERT(current_thd->open_options & HA_OPEN_FOR_REPAIR); + goto end; } - 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. + from the table def version after a successful 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) mrg_child_def->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 (mrg_child_def->get_child_def_version() != child->s->get_table_def_version()) + param->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 is temporary, parent must be temporary as well. Other + parent/child combinations are allowed. 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. (Note that the condition was different from + 5.1.23/6.0.4(Bug#19627) to 5.5.6 (Bug#36171): child->s->tmp_table != + parent->s->tmp_table. Tables were required to have the same status.) */ - if (child->s->tmp_table != parent->s->tmp_table) + 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; + goto end; } /* Extract the MyISAM table structure pointer from the handler object. */ @@ -413,72 +683,33 @@ static MI_INFO *myisammrg_attach_children_callback(void *callback_param) 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. + DBUG_PRINT("myrg", ("MyISAM handle: 0x%lx", (long) myisam)); - @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; + end: - /* retrieve children table list. */ - my_errno= 0; - if (is_cloned) + if (!myisam && + (current_thd->open_options & HA_OPEN_FOR_REPAIR)) { + char buf[2*NAME_LEN + 1 + 1]; + strxnmov(buf, sizeof(buf) - 1, child_l->db, ".", child_l->table_name, NULL); /* - 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. + Push an error to be reported as part of CHECK/REPAIR result-set. + Note that calling my_error() from handler is a hack which is kept + here to avoid refactoring. Normally engines should report errors + through return value which will be interpreted by caller using + handler::print_error() call. */ - if (!(file= myrg_open(name, 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); + my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), buf); } - DBUG_PRINT("myrg", ("MYRG_INFO: 0x%lx", (long) file)); - DBUG_RETURN(0); + + DBUG_RETURN(myisam); } +CPP_UNNAMED_NS_END + + /** Returns a cloned instance of the current handler. @@ -530,22 +761,24 @@ handler *ha_myisammrg::clone(const char *name, MEM_ROOT *mem_root) /** - @brief Attach children to a MERGE table. + Attach children to a MERGE table. + + @return status + @retval 0 OK + @retval != 0 Error, my_errno gives reason - @detail Let the storage engine attach its children through a callback + @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 + @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) @@ -555,36 +788,42 @@ int ha_myisammrg::attach_children(void) MI_KEYDEF *keyinfo; uint recs; uint keys= table->s->keys; + TABLE_LIST *parent_l= table->pos_in_table_list; int error; + Mrg_attach_children_callback_param param(parent_l, this->children_l, child_def_list); 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); + + /* Must call this with open table. */ + DBUG_ASSERT(this->file); /* - 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. + A MERGE table with no children (empty union) is always seen as + attached internally. */ - next_child_attach= table->child_l; - need_compat_check= FALSE; - my_errno= 0; + if (!this->file->tables) + { + DBUG_PRINT("myrg", ("empty merge table union")); + goto end; + } + DBUG_PRINT("myrg", ("child tables: %u", this->file->tables)); + + /* Must not call this with attached children. */ + DBUG_ASSERT(!this->file->children_attached); + + DEBUG_SYNC(current_thd, "before_myisammrg_attach"); + /* Must call this with children list in place. */ + DBUG_ASSERT(this->table->pos_in_table_list->next_global == this->children_l); if (myrg_attach_children(this->file, this->test_if_locked | current_thd->open_options, - myisammrg_attach_children_callback, this, - (my_bool *) &need_compat_check)) + myisammrg_attach_children_callback, ¶m, + (my_bool *) ¶m.need_compat_check)) { - DBUG_PRINT("error", ("my_errno %d", my_errno)); - DBUG_RETURN(my_errno ? my_errno : -1); + error= my_errno; + goto err; } DBUG_PRINT("myrg", ("calling myrg_extrafunc")); myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref); @@ -601,8 +840,8 @@ int ha_myisammrg::attach_children(void) 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) + DBUG_PRINT("myrg", ("need_compat_check: %d", param.need_compat_check)); + if (param.need_compat_check) { TABLE_LIST *child_l; @@ -611,7 +850,11 @@ int ha_myisammrg::attach_children(void) DBUG_PRINT("error",("reclength: %lu mean_rec_length: %lu", table->s->reclength, stats.mean_rec_length)); if (test_if_locked & HA_OPEN_FOR_REPAIR) + { + /* purecov: begin inspected */ myrg_print_wrong_table(file->open_tables->table->filename); + /* purecov: end */ + } error= HA_ERR_WRONG_MRG_TABLE_DEF; goto err; } @@ -639,23 +882,28 @@ int ha_myisammrg::attach_children(void) error= HA_ERR_WRONG_MRG_TABLE_DEF; if (!(this->test_if_locked & HA_OPEN_FOR_REPAIR)) { - my_free((uchar*) recinfo, MYF(0)); + my_free(recinfo); goto err; } + /* purecov: begin inspected */ myrg_print_wrong_table(u_table->table->filename); + /* purecov: end */ } } - my_free((uchar*) recinfo, MYF(0)); + my_free(recinfo); if (error == HA_ERR_WRONG_MRG_TABLE_DEF) - goto err; + goto err; /* purecov: inspected */ - /* All checks passed so far. Now update child def version. */ - for (child_l= table->child_l; ; child_l= child_l->next_global) + List_iterator_fast<Mrg_child_def> def_it(child_def_list); + DBUG_ASSERT(this->children_l); + for (child_l= this->children_l; ; child_l= child_l->next_global) { - child_l->set_child_def_version( + Mrg_child_def *mrg_child_def= def_it++; + mrg_child_def->set_child_def_version( + child_l->table->s->get_table_ref_type(), child_l->table->s->get_table_def_version()); - if (&child_l->next_global == table->child_last_l) + if (&child_l->next_global == this->children_last_l) break; } } @@ -668,50 +916,141 @@ int ha_myisammrg::attach_children(void) goto err; } #endif + + end: DBUG_RETURN(0); err: - myrg_detach_children(file); + DBUG_PRINT("error", ("attaching MERGE children failed: %d", error)); + print_error(error, MYF(0)); + detach_children(); 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. + Detach all children from a MERGE table and from the query list of tables. @return status @retval 0 OK @retval != 0 Error, my_errno gives reason + + @note + Detach must not touch the child TABLE objects in any way. + They may have been closed at ths point already. + All references to the children should be removed. */ int ha_myisammrg::detach_children(void) { + TABLE_LIST *child_l; DBUG_ENTER("ha_myisammrg::detach_children"); - DBUG_ASSERT(this->file && this->file->children_attached); + + /* Must call this with open table. */ + DBUG_ASSERT(this->file); + + /* A MERGE table with no children (empty union) cannot be detached. */ + if (!this->file->tables) + { + DBUG_PRINT("myrg", ("empty merge table union")); + goto end; + } + + if (this->children_l) + { + THD *thd= table->in_use; + + /* Clear TABLE references. */ + for (child_l= this->children_l; ; child_l= child_l->next_global) + { + /* + Do not DBUG_ASSERT(child_l->table); open_tables might be + incomplete. + + Clear the table reference. + */ + child_l->table= NULL; + /* Similarly, clear the ticket reference. */ + child_l->mdl_request.ticket= NULL; + + /* Break when this was the last child. */ + if (&child_l->next_global == this->children_last_l) + break; + } + /* + Remove children from the table list. This won't fail if called + twice. The list is terminated after removal. + + If the parent is LEX::query_tables_own_last and pre-locked tables + follow (tables used by stored functions or triggers), the children + are inserted behind the parent and before the pre-locked tables. But + we do not adjust LEX::query_tables_own_last. The pre-locked tables + could have chopped off the list by clearing + *LEX::query_tables_own_last. This did also chop off the children. If + we would copy the reference from *this->children_last_l in this + case, we would put the chopped off pre-locked tables back to the + list. So we refrain from copying it back, if the destination has + been set to NULL meanwhile. + */ + if (this->children_l->prev_global && *this->children_l->prev_global) + *this->children_l->prev_global= *this->children_last_l; + if (*this->children_last_l) + (*this->children_last_l)->prev_global= this->children_l->prev_global; + + /* + If table elements being removed are at the end of table list we + need to adjust LEX::query_tables_last member to point to the + new last element of the list. + */ + if (thd->lex->query_tables_last == this->children_last_l) + thd->lex->query_tables_last= this->children_l->prev_global; + + /* + If the statement requires prelocking, and prelocked + tables were added right after merge children, modify the + last own table pointer to point at prev_global of the merge + parent. + */ + if (thd->lex->query_tables_own_last == this->children_last_l) + thd->lex->query_tables_own_last= this->children_l->prev_global; + + /* Terminate child list. So it cannot be tried to remove again. */ + *this->children_last_l= NULL; + this->children_l->prev_global= NULL; + + /* Forget about the children, we don't own their memory. */ + this->children_l= NULL; + this->children_last_l= NULL; + } + + if (!this->file->children_attached) + { + DBUG_PRINT("myrg", ("merge children are already detached")); + goto end; + } if (myrg_detach_children(this->file)) { /* purecov: begin inspected */ - DBUG_PRINT("error", ("my_errno %d", my_errno)); + print_error(my_errno, MYF(0)); DBUG_RETURN(my_errno ? my_errno : -1); /* purecov: end */ } + + 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. + Close a MERGE parent table, but not its children. @return status @retval 0 OK @retval != 0 Error, my_errno gives reason + + @note + The children are expected to be closed separately by the caller. */ int ha_myisammrg::close(void) @@ -719,11 +1058,12 @@ 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. + There are cases where children are not explicitly detached before + close. detach_children() protects itself against double detach. */ - DBUG_ASSERT(!this->file->children_attached || !this->file->tables || this->is_cloned); + if (!is_cloned) + detach_children(); + rc= myrg_close(file); file= 0; DBUG_RETURN(rc); @@ -770,9 +1110,11 @@ int ha_myisammrg::index_read_map(uchar * buf, const uchar * key, enum ha_rkey_function find_flag) { DBUG_ASSERT(this->file->children_attached); + MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); 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; + MYSQL_INDEX_READ_ROW_DONE(error); return error; } @@ -781,9 +1123,11 @@ int ha_myisammrg::index_read_idx_map(uchar * buf, uint index, const uchar * key, enum ha_rkey_function find_flag) { DBUG_ASSERT(this->file->children_attached); + MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); 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; + MYSQL_INDEX_READ_ROW_DONE(error); return error; } @@ -791,46 +1135,56 @@ int ha_myisammrg::index_read_last_map(uchar *buf, const uchar *key, key_part_map keypart_map) { DBUG_ASSERT(this->file->children_attached); + MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); 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; + MYSQL_INDEX_READ_ROW_DONE(error); return error; } int ha_myisammrg::index_next(uchar * buf) { DBUG_ASSERT(this->file->children_attached); + MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); ha_statistic_increment(&SSV::ha_read_next_count); int error=myrg_rnext(file,buf,active_index); table->status=error ? STATUS_NOT_FOUND: 0; + MYSQL_INDEX_READ_ROW_DONE(error); return error; } int ha_myisammrg::index_prev(uchar * buf) { DBUG_ASSERT(this->file->children_attached); + MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); ha_statistic_increment(&SSV::ha_read_prev_count); int error=myrg_rprev(file,buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; + MYSQL_INDEX_READ_ROW_DONE(error); return error; } int ha_myisammrg::index_first(uchar * buf) { DBUG_ASSERT(this->file->children_attached); + MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); ha_statistic_increment(&SSV::ha_read_first_count); int error=myrg_rfirst(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; + MYSQL_INDEX_READ_ROW_DONE(error); return error; } int ha_myisammrg::index_last(uchar * buf) { DBUG_ASSERT(this->file->children_attached); + MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); ha_statistic_increment(&SSV::ha_read_last_count); int error=myrg_rlast(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; + MYSQL_INDEX_READ_ROW_DONE(error); return error; } @@ -840,12 +1194,14 @@ int ha_myisammrg::index_next_same(uchar * buf, { int error; DBUG_ASSERT(this->file->children_attached); + MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); 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; + MYSQL_INDEX_READ_ROW_DONE(error); return error; } @@ -860,9 +1216,12 @@ int ha_myisammrg::rnd_init(bool scan) int ha_myisammrg::rnd_next(uchar *buf) { DBUG_ASSERT(this->file->children_attached); + MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str, + TRUE); 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; + MYSQL_READ_ROW_DONE(error); return error; } @@ -870,9 +1229,12 @@ int ha_myisammrg::rnd_next(uchar *buf) int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos) { DBUG_ASSERT(this->file->children_attached); + MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str, + TRUE); 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; + MYSQL_READ_ROW_DONE(error); return error; } @@ -892,6 +1254,22 @@ ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key, } +int ha_myisammrg::truncate() +{ + int err= 0; + MYRG_TABLE *table; + DBUG_ENTER("ha_myisammrg::truncate"); + + for (table= file->open_tables; table != file->end_table; table++) + { + if ((err= mi_delete_all_rows(table->table))) + break; + } + + DBUG_RETURN(err); +} + + int ha_myisammrg::info(uint flag) { MYMERGE_INFO mrg_info; @@ -979,13 +1357,23 @@ int ha_myisammrg::info(uint flag) int ha_myisammrg::extra(enum ha_extra_function operation) { - if (operation == HA_EXTRA_ATTACH_CHILDREN) + if (operation == HA_EXTRA_ADD_CHILDREN_LIST) + { + int rc= add_children_list(); + return(rc); + } + else 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_IS_ATTACHED_CHILDREN) + { + /* For the upper layer pretend empty MERGE union is never attached. */ + return(file && file->tables && file->children_attached); + } else if (operation == HA_EXTRA_DETACH_CHILDREN) { /* @@ -1001,11 +1389,14 @@ int ha_myisammrg::extra(enum ha_extra_function operation) if (operation == HA_EXTRA_FORCE_REOPEN || operation == HA_EXTRA_PREPARE_FOR_DROP) return 0; + if (operation == HA_EXTRA_MMAP && !opt_myisam_use_mmap) + return 0; return myrg_extra(file,operation,0); } int ha_myisammrg::reset(void) { + /* This is normally called with detached children. */ return myrg_reset(file); } @@ -1021,21 +1412,24 @@ int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong 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); + /* + This can be called with no children attached. E.g. FLUSH TABLES + unlocks and re-locks tables under LOCK TABLES, but it does not open + them first. So they are detached all the time. But locking of the + children should work anyway because thd->open_tables is not changed + during FLUSH TABLES. + + If this handler instance has been cloned, we still must call + myrg_lock_database(). + */ + if (is_cloned) + return myrg_lock_database(file, lock_type); + return 0; } 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; + return 0; } @@ -1043,37 +1437,6 @@ 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; } @@ -1162,7 +1525,7 @@ int ha_myisammrg::create(const char *name, register TABLE *form, /* 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); + DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */ /* Create child path names. */ for (pos= table_names; tables; tables= tables->next_local) @@ -1237,7 +1600,7 @@ void ha_myisammrg::append_create_info(String *packet) current_db= table->s->db.str; db_length= table->s->db.length; - for (first= open_table= table->child_l;; + for (first= open_table= children_l;; open_table= open_table->next_global) { LEX_STRING db= { open_table->db, open_table->db_length }; @@ -1254,7 +1617,7 @@ void ha_myisammrg::append_create_info(String *packet) } append_identifier(thd, packet, open_table->table_name, open_table->table_name_length); - if (&open_table->next_global == table->child_last_l) + if (&open_table->next_global == children_last_l) break; } packet->append(')'); @@ -1274,7 +1637,7 @@ bool ha_myisammrg::check_if_incompatible_data(HA_CREATE_INFO *info, int ha_myisammrg::check(THD* thd, HA_CHECK_OPT* check_opt) { - return HA_ADMIN_OK; + return this->file->children_attached ? HA_ADMIN_OK : HA_ADMIN_CORRUPT; } @@ -1296,6 +1659,10 @@ static int myisammrg_init(void *p) myisammrg_hton= (handlerton *)p; +#ifdef HAVE_PSI_INTERFACE + init_myisammrg_psi_keys(); +#endif + myisammrg_hton->db_type= DB_TYPE_MRG_MYISAM; myisammrg_hton->create= myisammrg_create_handler; myisammrg_hton->panic= myisammrg_panic; @@ -1320,6 +1687,7 @@ mysql_declare_plugin(myisammrg) 0x0100, /* 1.0 */ NULL, /* status variables */ NULL, /* system variables */ - NULL /* config options */ + NULL, /* config options */ + 0, /* flags */ } mysql_declare_plugin_end; diff --git a/storage/myisammrg/ha_myisammrg.h b/storage/myisammrg/ha_myisammrg.h index a2761069bcc..21974e91581 100644 --- a/storage/myisammrg/ha_myisammrg.h +++ b/storage/myisammrg/ha_myisammrg.h @@ -12,8 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef USE_PRAGMA_INTERFACE @@ -24,15 +23,62 @@ #include <myisammrg.h> +/** + Represents one name of a MERGE child. + + @todo: Add MYRG_SHARE and store chlidren names in the + share. +*/ + +class Mrg_child_def: public Sql_alloc +{ + /* Remembered MERGE child def version. See top comment in ha_myisammrg.cc */ + enum_table_ref_type m_child_table_ref_type; + ulong m_child_def_version; +public: + LEX_STRING db; + LEX_STRING name; + + /* Access MERGE child def version. See top comment in ha_myisammrg.cc */ + inline enum_table_ref_type get_child_table_ref_type() + { + return m_child_table_ref_type; + } + inline ulong get_child_def_version() + { + return m_child_def_version; + } + inline void set_child_def_version(enum_table_ref_type child_table_ref_type, + ulong version) + { + m_child_table_ref_type= child_table_ref_type; + m_child_def_version= version; + } + + Mrg_child_def(char *db_arg, size_t db_len_arg, + char *table_name_arg, size_t table_name_len_arg) + { + db.str= db_arg; + db.length= db_len_arg; + name.str= table_name_arg; + name.length= table_name_len_arg; + m_child_def_version= ~0UL; + m_child_table_ref_type= TABLE_REF_NULL; + } +}; + + 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 */ +public: + MEM_ROOT children_mem_root; /* mem root for children list */ + List<Mrg_child_def> child_def_list; + TABLE_LIST *children_l; /* children list */ + TABLE_LIST **children_last_l; /* children list end */ 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(); @@ -62,6 +108,7 @@ class ha_myisammrg: public handler { return ulonglong2double(stats.data_file_length) / IO_SIZE + file->tables; } int open(const char *name, int mode, uint test_if_locked); + int add_children_list(void); int attach_children(void); int detach_children(void); virtual handler *clone(const char *name, MEM_ROOT *mem_root); @@ -85,6 +132,7 @@ class ha_myisammrg: public handler 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 truncate(); int info(uint); int reset(void); int extra(enum ha_extra_function operation); diff --git a/storage/myisammrg/make-ccc b/storage/myisammrg/make-ccc deleted file mode 100755 index a7e3dfc3cdb..00000000000 --- a/storage/myisammrg/make-ccc +++ /dev/null @@ -1,3 +0,0 @@ -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 index f720ecb7de1..97c35619067 100644 --- a/storage/myisammrg/myrg_close.c +++ b/storage/myisammrg/myrg_close.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2001, 2005-2007 MySQL AB +/* 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 @@ -11,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* close a isam-database */ @@ -53,13 +53,13 @@ int myrg_close(MYRG_INFO *info) } } else - my_free((uchar*) info->rec_per_key_part, MYF(MY_ALLOW_ZERO_PTR)); + my_free(info->rec_per_key_part); delete_queue(&info->by_key); - pthread_mutex_lock(&THR_LOCK_open); + mysql_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)); + mysql_mutex_unlock(&THR_LOCK_open); + mysql_mutex_destroy(&info->mutex); + my_free(info); if (error) { DBUG_RETURN(my_errno=error); diff --git a/storage/myisammrg/myrg_create.c b/storage/myisammrg/myrg_create.c index 2720aeae894..53c104b4c4a 100644 --- a/storage/myisammrg/myrg_create.c +++ b/storage/myisammrg/myrg_create.c @@ -1,5 +1,4 @@ -/* - Copyright (c) 2000, 2001, 2005-2007 MySQL AB, 2009 Sun Microsystems, Inc. +/* 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 @@ -13,8 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Create a MYMERGE_-file */ @@ -35,9 +33,10 @@ int myrg_create(const char *name, const char **table_names, 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) + if ((file= mysql_file_create(rg_key_file_MRG, + 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) @@ -49,8 +48,8 @@ int myrg_create(const char *name, const char **table_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))) + if (mysql_file_write(file, (uchar*) buff, (uint) (end-buff+1), + MYF(MY_WME | MY_NABP))) goto err; } } @@ -58,10 +57,11 @@ int myrg_create(const char *name, const char **table_names, { 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))) + if (mysql_file_write(file, (uchar*) buff, (uint) (end-buff), + MYF(MY_WME | MY_NABP))) goto err; } - if (my_close(file,MYF(0))) + if (mysql_file_close(file, MYF(0))) goto err; DBUG_RETURN(0); @@ -69,7 +69,7 @@ err: save_errno=my_errno ? my_errno : -1; switch (errpos) { case 1: - VOID(my_close(file,MYF(0))); + (void) mysql_file_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 index 22f6264840c..e6fa869ff9b 100644 --- a/storage/myisammrg/myrg_def.h +++ b/storage/myisammrg/myrg_def.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2001, 2003, 2005-2007 MySQL AB +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* This file is included by all myisam-merge files */ @@ -23,9 +23,7 @@ extern LIST *myrg_open_list; -#ifdef THREAD -extern pthread_mutex_t THR_LOCK_open; -#endif +extern mysql_mutex_t THR_LOCK_open; 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); @@ -33,3 +31,14 @@ int _myrg_mi_read_record(MI_INFO *info, uchar *buf); extern "C" #endif void myrg_print_wrong_table(const char *table_name); + +#ifdef HAVE_PSI_INTERFACE +extern PSI_mutex_key rg_key_mutex_MYRG_INFO_mutex; + +extern PSI_file_key rg_key_file_MRG; + +C_MODE_START +void init_myisammrg_psi_keys(); +C_MODE_END +#endif /* HAVE_PSI_INTERFACE */ + diff --git a/storage/myisammrg/myrg_delete.c b/storage/myisammrg/myrg_delete.c index 858d1670df9..af9fb2877d2 100644 --- a/storage/myisammrg/myrg_delete.c +++ b/storage/myisammrg/myrg_delete.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000-2002, 2005-2007 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_extra.c b/storage/myisammrg/myrg_extra.c index 20367764d69..18cc66cf937 100644 --- a/storage/myisammrg/myrg_extra.c +++ b/storage/myisammrg/myrg_extra.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2000-2003, 2005-2007 MySQL AB +/* Copyright (c) 2000-2003, 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 @@ -11,7 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Extra functions we want to do with a database @@ -75,12 +76,17 @@ int myrg_reset(MYRG_INFO *info) 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; - + + /* + This is normally called with detached children. + Return OK as this is the normal case. + */ + if (!info->children_attached) + DBUG_RETURN(0); + for (file=info->open_tables ; file != info->end_table ; file++) { int error; diff --git a/storage/myisammrg/myrg_info.c b/storage/myisammrg/myrg_info.c index 67a403e7643..53484b430b3 100644 --- a/storage/myisammrg/myrg_info.c +++ b/storage/myisammrg/myrg_info.c @@ -1,5 +1,4 @@ -/* - Copyright (c) 2000-2002, 2005, 2006 MySQL AB, 2009 Sun Microsystems, Inc. +/* Copyright (c) 2000, 2001, 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 @@ -13,8 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "myrg_def.h" diff --git a/storage/myisammrg/myrg_locking.c b/storage/myisammrg/myrg_locking.c index d72c157dcc1..e6cd51e2ed3 100644 --- a/storage/myisammrg/myrg_locking.c +++ b/storage/myisammrg/myrg_locking.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000-2002, 2005-2007 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c index ac8b753b317..a2d4257e96f 100644 --- a/storage/myisammrg/myrg_open.c +++ b/storage/myisammrg/myrg_open.c @@ -1,5 +1,4 @@ -/* - Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -12,17 +11,13 @@ 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 -*/ + Foundation, Inc., 51 Franklin Street, 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 @@ -52,9 +47,10 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) 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) + if ((fd= mysql_file_open(rg_key_file_MRG, + 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, @@ -81,7 +77,7 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) { if (!strncmp(buff+1,"INSERT_METHOD=",14)) { /* Lookup insert method */ - int tmp=find_type(buff+15,&merge_insert_method,2); + int tmp= find_type(buff + 15, &merge_insert_method, FIND_TYPE_BASIC); found_merge_insert_method = (uint) (tmp >= 0 ? tmp : 0); } continue; /* Skip comments */ @@ -89,9 +85,9 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) if (!has_path(buff)) { - VOID(strmake(name_buff+dir_length,buff, - sizeof(name_buff)-1-dir_length)); - VOID(cleanup_dirname(buff,name_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); @@ -169,13 +165,14 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) m_info->last_used_table=m_info->open_tables; m_info->children_attached= TRUE; - VOID(my_close(fd,MYF(0))); + (void) mysql_file_close(fd, MYF(0)); end_io_cache(&file); - VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST)); + mysql_mutex_init(rg_key_mutex_MYRG_INFO_mutex, + &m_info->mutex, MY_MUTEX_INIT_FAST); m_info->open_list.data=(void*) m_info; - pthread_mutex_lock(&THR_LOCK_open); + mysql_mutex_lock(&THR_LOCK_open); myrg_open_list=list_add(myrg_open_list,&m_info->open_list); - pthread_mutex_unlock(&THR_LOCK_open); + mysql_mutex_unlock(&THR_LOCK_open); DBUG_RETURN(m_info); bad_children: @@ -186,13 +183,13 @@ err: case 3: while (files) (void) mi_close(m_info->open_tables[--files].table); - my_free((char*) m_info,MYF(0)); + my_free(m_info); /* Fall through */ case 2: end_io_cache(&file); /* Fall through */ case 1: - VOID(my_close(fd,MYF(0))); + (void) mysql_file_close(fd, MYF(0)); } my_errno=save_errno; DBUG_RETURN (NULL); @@ -241,9 +238,11 @@ MYRG_INFO *myrg_parent_open(const char *parent_name, 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) + if ((fd= mysql_file_open(rg_key_file_MRG, + 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; @@ -272,7 +271,7 @@ MYRG_INFO *myrg_parent_open(const char *parent_name, { /* Compare buffer with global methods list: merge_insert_method. */ insert_method= find_type(child_name_buff + 15, - &merge_insert_method, 2); + &merge_insert_method, FIND_TYPE_BASIC); } continue; } @@ -318,13 +317,14 @@ MYRG_INFO *myrg_parent_open(const char *parent_name, } end_io_cache(&file_cache); - VOID(my_close(fd, MYF(0))); - VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST)); + (void) mysql_file_close(fd, MYF(0)); + mysql_mutex_init(rg_key_mutex_MYRG_INFO_mutex, + &m_info->mutex, MY_MUTEX_INIT_FAST); m_info->open_list.data= (void*) m_info; - pthread_mutex_lock(&THR_LOCK_open); + mysql_mutex_lock(&THR_LOCK_open); myrg_open_list= list_add(myrg_open_list, &m_info->open_list); - pthread_mutex_unlock(&THR_LOCK_open); + mysql_mutex_unlock(&THR_LOCK_open); DBUG_RETURN(m_info); @@ -333,13 +333,13 @@ MYRG_INFO *myrg_parent_open(const char *parent_name, save_errno= my_errno; switch (errpos) { case 3: - my_free((char*) m_info, MYF(0)); + my_free(m_info); /* Fall through */ case 2: end_io_cache(&file_cache); /* Fall through */ case 1: - VOID(my_close(fd, MYF(0))); + (void) mysql_file_close(fd, MYF(0)); } my_errno= save_errno; DBUG_RETURN (NULL); @@ -385,6 +385,7 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, uint UNINIT_VAR(key_parts); uint min_keys; my_bool bad_children= FALSE; + my_bool first_child= TRUE; DBUG_ENTER("myrg_attach_children"); DBUG_PRINT("myrg", ("handle_locking: %d", handle_locking)); @@ -395,26 +396,36 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, '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); + mysql_mutex_lock(&m_info->mutex); errpos= 0; file_offset= 0; min_keys= 0; - child_nr= 0; - while ((myisam= (*callback)(callback_param))) + for (child_nr= 0; child_nr < m_info->tables; child_nr++) { + if (! (myisam= (*callback)(callback_param))) + { + if (handle_locking & HA_OPEN_FOR_REPAIR) + { + /* An appropriate error should've been already pushed by callback. */ + bad_children= TRUE; + continue; + } + goto bad_children; + } + 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) + if (first_child) { + first_child= FALSE; 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)); + my_free(m_info->rec_per_key_part); m_info->rec_per_key_part= NULL; } if (!m_info->rec_per_key_part) @@ -456,14 +467,11 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, 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; @@ -474,7 +482,7 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, 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); + mysql_mutex_unlock(&m_info->mutex); DBUG_RETURN(0); bad_children: @@ -483,10 +491,10 @@ err: save_errno= my_errno; switch (errpos) { case 1: - my_free((char*) m_info->rec_per_key_part, MYF(0)); + my_free(m_info->rec_per_key_part); m_info->rec_per_key_part= NULL; } - pthread_mutex_unlock(&m_info->mutex); + mysql_mutex_unlock(&m_info->mutex); my_errno= save_errno; DBUG_RETURN(1); } @@ -509,7 +517,7 @@ 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); + mysql_mutex_lock(&m_info->mutex); if (m_info->tables) { /* Do not attach/detach an empty child list. */ @@ -520,7 +528,7 @@ int myrg_detach_children(MYRG_INFO *m_info) m_info->del= 0; m_info->data_file_length= 0; m_info->options= 0; - pthread_mutex_unlock(&m_info->mutex); + mysql_mutex_unlock(&m_info->mutex); DBUG_RETURN(0); } diff --git a/storage/myisammrg/myrg_panic.c b/storage/myisammrg/myrg_panic.c index a69daba05ef..223322aff89 100644 --- a/storage/myisammrg/myrg_panic.c +++ b/storage/myisammrg/myrg_panic.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2001, 2005, 2006 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_queue.c b/storage/myisammrg/myrg_queue.c index 4ec42815355..5d993a8416f 100644 --- a/storage/myisammrg/myrg_queue.c +++ b/storage/myisammrg/myrg_queue.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000-2003, 2005-2007 MySQL AB +/* 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 @@ -11,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "myrg_def.h" @@ -65,7 +65,17 @@ int _myrg_init_queue(MYRG_INFO *info,int inx,enum ha_rkey_function search_flag) } } else - my_errno= error= HA_ERR_WRONG_INDEX; + { + /* + inx may be bigger than info->keys if there are no underlying tables + defined. In this case we should return empty result. As we check for + underlying tables conformance when we open a table, we may not enter + this branch with underlying table that has less keys than merge table + have. + */ + DBUG_ASSERT(!info->tables); + error= my_errno= HA_ERR_END_OF_FILE; + } return error; } diff --git a/storage/myisammrg/myrg_range.c b/storage/myisammrg/myrg_range.c index fa3df2c3c09..97292a05b36 100644 --- a/storage/myisammrg/myrg_range.c +++ b/storage/myisammrg/myrg_range.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002, 2004-2006 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_rfirst.c b/storage/myisammrg/myrg_rfirst.c index 12407ab9179..d47a52779eb 100644 --- a/storage/myisammrg/myrg_rfirst.c +++ b/storage/myisammrg/myrg_rfirst.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2001, 2003, 2005-2007 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_rkey.c b/storage/myisammrg/myrg_rkey.c index 3b14e8ce3b6..95056c09a49 100644 --- a/storage/myisammrg/myrg_rkey.c +++ b/storage/myisammrg/myrg_rkey.c @@ -1,5 +1,4 @@ -/* - Copyright (c) 2000-2003, 2005-2008 MySQL AB, 2009 Sun Microsystems, Inc. +/* 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 @@ -13,8 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Read record based on a key */ diff --git a/storage/myisammrg/myrg_rlast.c b/storage/myisammrg/myrg_rlast.c index 8becec23a44..06acaccfee3 100644 --- a/storage/myisammrg/myrg_rlast.c +++ b/storage/myisammrg/myrg_rlast.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2001, 2003, 2005-2007 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_rnext.c b/storage/myisammrg/myrg_rnext.c index 99eb8e67aae..d8b296aeccb 100644 --- a/storage/myisammrg/myrg_rnext.c +++ b/storage/myisammrg/myrg_rnext.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000-2003, 2005-2007 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_rnext_same.c b/storage/myisammrg/myrg_rnext_same.c index 33ddc4bc6f2..f4122b3018e 100644 --- a/storage/myisammrg/myrg_rnext_same.c +++ b/storage/myisammrg/myrg_rnext_same.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002, 2004-2007 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_rprev.c b/storage/myisammrg/myrg_rprev.c index 74168da3ead..c89853b293f 100644 --- a/storage/myisammrg/myrg_rprev.c +++ b/storage/myisammrg/myrg_rprev.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000-2003, 2005-2007 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_rrnd.c b/storage/myisammrg/myrg_rrnd.c index 4c03a5f9d06..6eb0107304b 100644 --- a/storage/myisammrg/myrg_rrnd.c +++ b/storage/myisammrg/myrg_rrnd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000-2002, 2005-2007 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_rsame.c b/storage/myisammrg/myrg_rsame.c index dcb3ad10085..7c49d9e4042 100644 --- a/storage/myisammrg/myrg_rsame.c +++ b/storage/myisammrg/myrg_rsame.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000-2002, 2005-2007 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_static.c b/storage/myisammrg/myrg_static.c index 7894bd1f9ec..b36b7741a64 100644 --- a/storage/myisammrg/myrg_static.c +++ b/storage/myisammrg/myrg_static.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2000, 2001, 2004-2006 MySQL AB +/* Copyright (c) 2000, 2001, 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 @@ -11,7 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Static variables for pisam library. All definied here for easy making of @@ -27,3 +28,35 @@ static const char *merge_insert_methods[] = { "FIRST", "LAST", NullS }; TYPELIB merge_insert_method= { array_elements(merge_insert_methods)-1,"", merge_insert_methods, 0}; + +#ifdef HAVE_PSI_INTERFACE +PSI_mutex_key rg_key_mutex_MYRG_INFO_mutex; + +static PSI_mutex_info all_myisammrg_mutexes[]= +{ + { &rg_key_mutex_MYRG_INFO_mutex, "MYRG_INFO::mutex", 0} +}; + +PSI_file_key rg_key_file_MRG; + +static PSI_file_info all_myisammrg_files[]= +{ + { &rg_key_file_MRG, "MRG", 0} +}; + +void init_myisammrg_psi_keys() +{ + const char* category= "myisammrg"; + int count; + + if (PSI_server == NULL) + return; + + count= array_elements(all_myisammrg_mutexes); + PSI_server->register_mutex(category, all_myisammrg_mutexes, count); + + count= array_elements(all_myisammrg_files); + PSI_server->register_file(category, all_myisammrg_files, count); +} +#endif /* HAVE_PSI_INTERFACE */ + diff --git a/storage/myisammrg/myrg_update.c b/storage/myisammrg/myrg_update.c index 2779c73cd09..69b7a1b5382 100644 --- a/storage/myisammrg/myrg_update.c +++ b/storage/myisammrg/myrg_update.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000-2002, 2005-2007 MySQL AB +/* 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 diff --git a/storage/myisammrg/myrg_write.c b/storage/myisammrg/myrg_write.c index befa28b2c14..e8ed9a13e9d 100644 --- a/storage/myisammrg/myrg_write.c +++ b/storage/myisammrg/myrg_write.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2001, 2002, 2004-2007 MySQL AB +/* 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 diff --git a/storage/myisammrg/plug.in b/storage/myisammrg/plug.in deleted file mode 100644 index 1f94e07d881..00000000000 --- a/storage/myisammrg/plug.in +++ /dev/null @@ -1,6 +0,0 @@ -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]) |