From 06eaf7232e9a920468c0f8d74dcf2fe8b555501c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 14:24:36 +0200 Subject: Adding upstream version 1:10.11.6. Signed-off-by: Daniel Baumann --- storage/myisammrg/CMakeLists.txt | 23 + storage/myisammrg/ha_myisammrg.cc | 1785 ++++++++++++++++++++ storage/myisammrg/ha_myisammrg.h | 162 ++ storage/myisammrg/myrg_close.c | 68 + storage/myisammrg/myrg_create.c | 73 + storage/myisammrg/myrg_def.h | 44 + storage/myisammrg/myrg_delete.c | 27 + storage/myisammrg/myrg_extra.c | 97 ++ storage/myisammrg/myrg_info.c | 86 + storage/myisammrg/myrg_locking.c | 45 + storage/myisammrg/myrg_open.c | 551 ++++++ storage/myisammrg/myrg_panic.c | 46 + storage/myisammrg/myrg_queue.c | 90 + storage/myisammrg/myrg_range.c | 43 + storage/myisammrg/myrg_records.c | 28 + storage/myisammrg/myrg_rfirst.c | 49 + storage/myisammrg/myrg_rkey.c | 95 ++ storage/myisammrg/myrg_rlast.c | 50 + storage/myisammrg/myrg_rnext.c | 53 + storage/myisammrg/myrg_rnext_same.c | 51 + storage/myisammrg/myrg_rprev.c | 53 + storage/myisammrg/myrg_rrnd.c | 117 ++ storage/myisammrg/myrg_rsame.c | 28 + storage/myisammrg/myrg_static.c | 71 + storage/myisammrg/myrg_update.c | 28 + storage/myisammrg/myrg_write.c | 30 + .../mysql-test/storage_engine/alter_table.inc | 116 ++ .../mysql-test/storage_engine/alter_table.rdiff | 151 ++ .../storage_engine/alter_table_online.rdiff | 82 + .../storage_engine/alter_tablespace.rdiff | 34 + .../mysql-test/storage_engine/analyze_table.rdiff | 34 + .../mysql-test/storage_engine/autoincrement.rdiff | 64 + .../mysql-test/storage_engine/cache_index.rdiff | 71 + .../storage_engine/checksum_table_live.rdiff | 13 + .../mysql-test/storage_engine/cleanup_engine.inc | 16 + .../mysql-test/storage_engine/create_table.inc | 208 +++ .../mysql-test/storage_engine/create_table.rdiff | 57 + .../mysql-test/storage_engine/define_engine.inc | 49 + .../mysql-test/storage_engine/disabled.def | 4 + .../mysql-test/storage_engine/foreign_keys.rdiff | 147 ++ .../storage_engine/fulltext_search.rdiff | 150 ++ .../mysql-test/storage_engine/handler.rdiff | 88 + .../mysql-test/storage_engine/index.rdiff | 11 + .../storage_engine/index_enable_disable.rdiff | 33 + .../storage_engine/index_type_btree.rdiff | 11 + .../storage_engine/index_type_hash.rdiff | 69 + .../mysql-test/storage_engine/insert_delayed.rdiff | 26 + .../myisammrg/mysql-test/storage_engine/lock.rdiff | 80 + .../myisammrg/mysql-test/storage_engine/misc.rdiff | 34 + .../mysql-test/storage_engine/optimize_table.rdiff | 35 + .../storage_engine/parts/alter_table.rdiff | 68 + .../storage_engine/parts/analyze_table.rdiff | 87 + .../storage_engine/parts/check_table.rdiff | 176 ++ .../storage_engine/parts/checksum_table.rdiff | 89 + .../storage_engine/parts/create_table.rdiff | 159 ++ .../storage_engine/parts/optimize_table.rdiff | 95 ++ .../storage_engine/parts/repair_table.rdiff | 299 ++++ .../storage_engine/parts/truncate_table.rdiff | 101 ++ .../mysql-test/storage_engine/repair_table.rdiff | 132 ++ .../mysql-test/storage_engine/show_engine.rdiff | 10 + .../mysql-test/storage_engine/tbl_opt_ai.rdiff | 16 + .../storage_engine/tbl_opt_avg_row_length.rdiff | 17 + .../storage_engine/tbl_opt_checksum.rdiff | 17 + .../storage_engine/tbl_opt_connection.rdiff | 19 + .../storage_engine/tbl_opt_data_dir.rdiff | 18 + .../storage_engine/tbl_opt_delay_key_write.rdiff | 17 + .../storage_engine/tbl_opt_index_dir.rdiff | 18 + .../storage_engine/tbl_opt_insert_method.rdiff | 17 + .../storage_engine/tbl_opt_key_block_size.rdiff | 17 + .../storage_engine/tbl_opt_max_rows.rdiff | 17 + .../storage_engine/tbl_opt_min_rows.rdiff | 17 + .../storage_engine/tbl_opt_pack_keys.rdiff | 17 + .../storage_engine/tbl_opt_password.rdiff | 17 + .../storage_engine/tbl_opt_row_format.rdiff | 33 + .../mysql-test/storage_engine/tbl_opt_union.rdiff | 16 + .../storage_engine/tbl_standard_opts.rdiff | 19 + .../mysql-test/storage_engine/tbl_temporary.rdiff | 10 + .../mysql-test/storage_engine/truncate_table.rdiff | 48 + .../trx/cons_snapshot_repeatable_read.rdiff | 20 + .../trx/cons_snapshot_serializable.rdiff | 20 + .../mysql-test/storage_engine/trx/delete.rdiff | 50 + .../mysql-test/storage_engine/trx/insert.rdiff | 65 + .../storage_engine/trx/level_read_committed.rdiff | 94 ++ .../trx/level_read_uncommitted.rdiff | 12 + .../storage_engine/trx/level_repeatable_read.rdiff | 96 ++ .../storage_engine/trx/level_serializable.rdiff | 103 ++ .../storage_engine/trx/select_for_update.rdiff | 50 + .../trx/select_lock_in_share_mode.rdiff | 37 + .../mysql-test/storage_engine/trx/update.rdiff | 58 + .../mysql-test/storage_engine/trx/xa.rdiff | 89 + .../storage_engine/trx/xa_recovery.rdiff | 32 + .../storage_engine/type_char_indexes.rdiff | 20 + .../storage_engine/type_float_indexes.rdiff | 11 + .../mysql-test/storage_engine/type_spatial.rdiff | 712 ++++++++ .../storage_engine/type_spatial_indexes.rdiff | 1422 ++++++++++++++++ .../myisammrg/mysql-test/storage_engine/vcol.rdiff | 82 + 96 files changed, 9915 insertions(+) create mode 100644 storage/myisammrg/CMakeLists.txt create mode 100644 storage/myisammrg/ha_myisammrg.cc create mode 100644 storage/myisammrg/ha_myisammrg.h create mode 100644 storage/myisammrg/myrg_close.c create mode 100644 storage/myisammrg/myrg_create.c create mode 100644 storage/myisammrg/myrg_def.h create mode 100644 storage/myisammrg/myrg_delete.c create mode 100644 storage/myisammrg/myrg_extra.c create mode 100644 storage/myisammrg/myrg_info.c create mode 100644 storage/myisammrg/myrg_locking.c create mode 100644 storage/myisammrg/myrg_open.c create mode 100644 storage/myisammrg/myrg_panic.c create mode 100644 storage/myisammrg/myrg_queue.c create mode 100644 storage/myisammrg/myrg_range.c create mode 100644 storage/myisammrg/myrg_records.c create mode 100644 storage/myisammrg/myrg_rfirst.c create mode 100644 storage/myisammrg/myrg_rkey.c create mode 100644 storage/myisammrg/myrg_rlast.c create mode 100644 storage/myisammrg/myrg_rnext.c create mode 100644 storage/myisammrg/myrg_rnext_same.c create mode 100644 storage/myisammrg/myrg_rprev.c create mode 100644 storage/myisammrg/myrg_rrnd.c create mode 100644 storage/myisammrg/myrg_rsame.c create mode 100644 storage/myisammrg/myrg_static.c create mode 100644 storage/myisammrg/myrg_update.c create mode 100644 storage/myisammrg/myrg_write.c create mode 100644 storage/myisammrg/mysql-test/storage_engine/alter_table.inc create mode 100644 storage/myisammrg/mysql-test/storage_engine/alter_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/alter_table_online.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/alter_tablespace.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/analyze_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/autoincrement.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/cache_index.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/checksum_table_live.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/cleanup_engine.inc create mode 100644 storage/myisammrg/mysql-test/storage_engine/create_table.inc create mode 100644 storage/myisammrg/mysql-test/storage_engine/create_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/define_engine.inc create mode 100644 storage/myisammrg/mysql-test/storage_engine/disabled.def create mode 100644 storage/myisammrg/mysql-test/storage_engine/foreign_keys.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/fulltext_search.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/handler.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/index.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/index_enable_disable.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/index_type_btree.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/index_type_hash.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/insert_delayed.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/lock.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/misc.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/optimize_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/parts/alter_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/parts/analyze_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/parts/check_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/parts/checksum_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/parts/create_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/parts/optimize_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/parts/repair_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/parts/truncate_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/repair_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/show_engine.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_ai.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_avg_row_length.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_checksum.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_connection.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_data_dir.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_delay_key_write.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_index_dir.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_insert_method.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_key_block_size.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_max_rows.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_min_rows.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_pack_keys.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_password.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_row_format.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_opt_union.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_standard_opts.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/tbl_temporary.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/truncate_table.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/cons_snapshot_repeatable_read.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/cons_snapshot_serializable.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/delete.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/insert.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/level_read_committed.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/level_read_uncommitted.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/level_repeatable_read.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/level_serializable.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/select_for_update.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/select_lock_in_share_mode.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/update.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/xa.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/trx/xa_recovery.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/type_char_indexes.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/type_float_indexes.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/type_spatial.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/type_spatial_indexes.rdiff create mode 100644 storage/myisammrg/mysql-test/storage_engine/vcol.rdiff (limited to 'storage/myisammrg') diff --git a/storage/myisammrg/CMakeLists.txt b/storage/myisammrg/CMakeLists.txt new file mode 100644 index 00000000..b4db348d --- /dev/null +++ b/storage/myisammrg/CMakeLists.txt @@ -0,0 +1,23 @@ +# 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 +# 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-1335 USA + +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_ADD_PLUGIN(myisammrg ${MYISAMMRG_SOURCES} STORAGE_ENGINE MANDATORY RECOMPILE_FOR_EMBEDDED) diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc new file mode 100644 index 00000000..d37636ab --- /dev/null +++ b/storage/myisammrg/ha_myisammrg.cc @@ -0,0 +1,1785 @@ +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates + Copyright (c) 2009, 2016, MariaDB + + 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-1335 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 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. + + 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. + + 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 +#pragma implementation // gcc: Class implementation +#endif + +#define MYSQL_SERVER 1 +#include +#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 +#include "../myisam/ha_myisam.h" +#include "ha_myisammrg.h" +#include "myrg_def.h" +#include "thr_malloc.h" // init_sql_alloc +#include "sql_class.h" // THD +#include "debug_sync.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) +{ + init_sql_alloc(rg_key_memory_children, &children_mem_root, + FN_REFLEN, 0, MYF(0)); +} + + +/** + @brief Destructor +*/ + +ha_myisammrg::~ha_myisammrg(void) +{ + free_root(&children_mem_root, MYF(0)); +} + + +static const char *ha_myisammrg_exts[] = { + MYRG_NAME_EXT, + 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 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); +} + + +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"); +} + + +/** + 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 -----------------------------------------+ +*/ + +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(); + Mrg_child_def *mrg_child_def; + char *db; + char *table_name; + size_t dirlen; + size_t db_length; + size_t table_name_length; + char dir_path[FN_REFLEN]; + char name_buf[NAME_LEN]; + DBUG_ENTER("myisammrg_parent_open_callback"); + + /* + 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. */ + 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) + { + 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 + { + table_name_length= strlen(filename); + table_name= strmake_root(&ha_myrg->children_mem_root, filename, + 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) + { + 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); + 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 + { + 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); + db_length= strlen(dir_path + dirlen); + db= strmake_root(&ha_myrg->children_mem_root, dir_path + dirlen, + db_length); + } + } + + 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 && 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 + + +/* + Set external_ref for the child MyISAM tables. They need this to be set in + order to check for killed status. +*/ +static void myrg_set_external_ref(MYRG_INFO *m_info, void *ext_ref_arg) +{ + int i; + for (i= 0; i < (int)m_info->tables; i++) + { + m_info->open_tables[i].table->external_ref= ext_ref_arg; + } +} + +/** + 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: %p", name, 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); + } + + file->children_attached= TRUE; + myrg_set_external_ref(file, (void*)table); + + info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); + } + else if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this))) + { + /* 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: %p child tables: %u", + 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 it(child_def_list); + Mrg_child_def *mrg_child_def; + DBUG_ENTER("ha_myisammrg::add_children_list"); + DBUG_PRINT("myrg", ("table: '%s'.'%s' %p", this->table->s->db.str, + this->table->s->table_name.str, 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.str); + DBUG_RETURN(1); + } + + while ((mrg_child_def= it++)) + { + TABLE_LIST *child_l; + LEX_CSTRING db; + LEX_CSTRING table_name; + + child_l= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST)); + db.str= (char*) thd->memdup(mrg_child_def->db.str, mrg_child_def->db.length+1); + db.length= mrg_child_def->db.length; + table_name.str= (char*) thd->memdup(mrg_child_def->name.str, + mrg_child_def->name.length+1); + table_name.length= mrg_child_def->name.length; + + if (child_l == NULL || db.str == NULL || table_name.str == NULL) + DBUG_RETURN(1); + + child_l->init_one_table(&db, &table_name, 0, 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()); + /* + Copy parent's prelocking attribute to allow opening of child + temporary residing in the prelocking list. + */ + child_l->prelocking_placeholder= parent_l->prelocking_placeholder; + /* + 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_UPGRADABLE) + 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; + } + + /* 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); +} + + +/** + A context of myrg_attach_children() callback. +*/ + +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 def_it; + Mrg_child_def *mrg_child_def; +public: + Mrg_attach_children_callback_param(TABLE_LIST *parent_l_arg, + TABLE_LIST *first_child, + List &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++; + } +}; + + +/** + 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, Error. + + @detail + This function retrieves the MyISAM table handle from the + next child table. It is called for each child table. +*/ + +CPP_UNNAMED_NS_START + +extern "C" MI_INFO *myisammrg_attach_children_callback(void *callback_param) +{ + 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= param->next_child_attach; + Mrg_child_def *mrg_child_def= param->mrg_child_def; + MI_INFO *myisam= NULL; + DBUG_ENTER("myisammrg_attach_children_callback"); + + /* + 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); + + child= child_l->table; + /* Prepare for next child. */ + param->next(); + + /* + 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) + { + DBUG_PRINT("error", ("failed to open underlying table '%s'.'%s'", + child_l->db.str, child_l->table_name.str)); + /* + This should only happen inside of CHECK/REPAIR TABLE or + for the tables added by the pre-locking code. + */ + DBUG_ASSERT(current_thd->open_options & HA_OPEN_FOR_REPAIR || + child_l->prelocking_placeholder); + goto end; + } + + /* + 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 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) mrg_child_def->get_child_def_version(), + (ulong) child->s->get_table_def_version())); + if (mrg_child_def->get_child_def_version() != child->s->get_table_def_version()) + param->need_compat_check= TRUE; + + /* + 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) + { + DBUG_PRINT("error", ("temporary table mismatch parent: %d child: %d", + parent->s->tmp_table, child->s->tmp_table)); + goto end; + } + + /* 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' %p", + child->s->db.str, child->s->table_name.str, + child)); + } + + DBUG_PRINT("myrg", ("MyISAM handle: %p", myisam)); + + end: + + 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.str, ".", + child_l->table_name.str, NULL); + /* + 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); + } + + DBUG_RETURN(myisam); +} + +CPP_UNNAMED_NS_END + +/** + Returns a cloned instance of the current handler. + + @return A cloned handler instance. + */ +handler *ha_myisammrg::clone(const char *name, 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, name, 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; + } + + +/** + 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 + 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. +*/ + +int ha_myisammrg::attach_children(void) +{ + MYRG_TABLE *u_table; + MI_COLUMNDEF *recinfo; + 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' %p", table->s->db.str, + table->s->table_name.str, table)); + DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked)); + + /* Must call this with open table. */ + DBUG_ASSERT(this->file); + + /* + A MERGE table with no children (empty union) is always seen as + attached internally. + */ + 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, ¶m, + (my_bool *) ¶m.need_compat_check)) + { + error= my_errno; + goto err; + } + 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", param.need_compat_check)); + if (param.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) + { + /* purecov: begin inspected */ + myrg_print_wrong_table(file->open_tables->table->filename); + /* purecov: end */ + } + 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(recinfo); + goto err; + } + /* purecov: begin inspected */ + myrg_print_wrong_table(u_table->table->filename); + /* purecov: end */ + } + } + my_free(recinfo); + if (error == HA_ERR_WRONG_MRG_TABLE_DEF) + goto err; /* purecov: inspected */ + + List_iterator_fast def_it(child_def_list); + DBUG_ASSERT(this->children_l); + for (child_l= this->children_l; ; child_l= child_l->next_global) + { + 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 == this->children_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 + + end: + DBUG_RETURN(0); + +err: + DBUG_PRINT("error", ("attaching MERGE children failed: %d", error)); + print_error(error, MYF(0)); + detach_children(); + DBUG_RETURN(my_errno= error); +} + + +/** + 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"); + + /* 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 */ + print_error(my_errno, MYF(0)); + DBUG_RETURN(my_errno ? my_errno : -1); + /* purecov: end */ + } + + end: + DBUG_RETURN(0); +} + + +/** + 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) +{ + int rc; + DBUG_ENTER("ha_myisammrg::close"); + /* + There are cases where children are not explicitly detached before + close. detach_children() protects itself against double detach. + */ + if (!is_cloned) + detach_children(); + + rc= myrg_close(file); + file= 0; + DBUG_RETURN(rc); +} + +int ha_myisammrg::write_row(const uchar * buf) +{ + DBUG_ENTER("ha_myisammrg::write_row"); + DBUG_ASSERT(this->file->children_attached); + + if (file->merge_insert_method == MERGE_INSERT_DISABLED || !file->tables) + DBUG_RETURN(HA_ERR_TABLE_READONLY); + + 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, const uchar * new_data) +{ + DBUG_ASSERT(this->file->children_attached); + return myrg_update(file,old_data,new_data); +} + +int ha_myisammrg::delete_row(const uchar * buf) +{ + DBUG_ASSERT(this->file->children_attached); + 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); + int error=myrg_rkey(file,buf,active_index, key, keypart_map, find_flag); + 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); + int error=myrg_rkey(file,buf,index, key, keypart_map, find_flag); + 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); + int error=myrg_rkey(file,buf,active_index, key, keypart_map, + HA_READ_PREFIX_LAST); + return error; +} + +int ha_myisammrg::index_next(uchar * buf) +{ + DBUG_ASSERT(this->file->children_attached); + int error=myrg_rnext(file,buf,active_index); + return error; +} + +int ha_myisammrg::index_prev(uchar * buf) +{ + DBUG_ASSERT(this->file->children_attached); + int error=myrg_rprev(file,buf, active_index); + return error; +} + +int ha_myisammrg::index_first(uchar * buf) +{ + DBUG_ASSERT(this->file->children_attached); + int error=myrg_rfirst(file, buf, active_index); + return error; +} + +int ha_myisammrg::index_last(uchar * buf) +{ + DBUG_ASSERT(this->file->children_attached); + int error=myrg_rlast(file, buf, active_index); + 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); + do + { + error= myrg_rnext_same(file,buf); + } while (error == HA_ERR_RECORD_DELETED); + 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); + int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR); + return error; +} + + +int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos) +{ + DBUG_ASSERT(this->file->children_attached); + int error=myrg_rrnd(file, buf, my_get_ptr(pos,ref_length)); + 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, + const key_range *min_key, + const key_range *max_key, + page_range *pages) +{ + DBUG_ASSERT(this->file->children_attached); + return (ha_rows) myrg_records_in_range(file, (int) inx, min_key, max_key, + pages); +} + + +int ha_myisammrg::delete_all_rows() +{ + int err= 0; + MYRG_TABLE *table; + DBUG_ENTER("ha_myisammrg::delete_all_rows"); + + 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; + 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_valgrind + /* + 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]) * + MY_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_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) + { + /* + 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 || + operation == HA_EXTRA_PREPARE_FOR_RENAME) + 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); +} + +/* 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); + return myrg_extra(file, operation, (void*) &cache_size); +} + +int ha_myisammrg::external_lock(THD *thd, int 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 0; +} + + +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. + */ + mysql_mutex_lock(&this->file->mutex); + + /* + When MERGE table is open, but not yet attached, other threads + could flush it, which means calling mysql_lock_abort_for_thread() + on this threads TABLE. 'children_attached' is FALSE in this + situation. 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++) + open_table->table->lock.priority|= THR_LOCK_MERGE_PRIV; + + end: + mysql_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_buf(buff, file_name); + 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)) + { + TABLE_LIST *child_table, *end= NULL; + THD *thd=ha_thd(); + + if (children_l != NULL) + { + for (child_table= children_l;; child_table= child_table->next_global) + { + TABLE_LIST *ptr; + + if (!(ptr= (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) + DBUG_VOID_RETURN; + + if (!(ptr->table_name.str= thd->strmake(child_table->table_name.str, + child_table->table_name.length))) + DBUG_VOID_RETURN; + ptr->table_name.length= child_table->table_name.length; + if (child_table->db.str && !(ptr->db.str= thd->strmake(child_table->db.str, + child_table->db.length))) + DBUG_VOID_RETURN; + ptr->db.length= child_table->db.length; + + if (create_info->merge_list) + end->next_local= ptr; + else + create_info->merge_list= ptr; + end= ptr; + + if (&child_table->next_global == children_last_l) + break; + } + } + } + if (!(create_info->used_fields & HA_CREATE_USED_INSERT_METHOD)) + { + create_info->merge_insert_method = file->merge_insert_method; + } + DBUG_VOID_RETURN; +} + + +int ha_myisammrg::create_mrg(const char *name, HA_CREATE_INFO *create_info) +{ + char buff[FN_REFLEN]; + const char **table_names, **pos; + TABLE_LIST *tables= create_info->merge_list; + THD *thd= ha_thd(); + size_t dirlgt= dirname_length(name); + uint ntables= 0; + DBUG_ENTER("ha_myisammrg::create_mrg"); + + for (tables= create_info->merge_list; tables; tables= tables->next_local) + ntables++; + + /* Allocate a table_names array in thread mem_root. */ + if (!(pos= table_names= (const char**) thd->alloc((ntables + 1) * sizeof(char*)))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */ + + /* Create child path names. */ + for (tables= create_info->merge_list; 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. + */ + size_t length= build_table_filename(buff, sizeof(buff), + tables->db.str, tables->table_name.str, "", 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. */ + int res= myrg_create(name, table_names, create_info->merge_insert_method, 0); + DBUG_RETURN(res); +} + + +int ha_myisammrg::create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info) +{ + char buff[FN_REFLEN]; + DBUG_ENTER("ha_myisammrg::create"); + fn_format(buff, name, "", MYRG_NAME_EXT, MY_UNPACK_FILENAME | MY_APPEND_EXT); + int res= create_mrg(buff, create_info); + DBUG_RETURN(res); +} + + +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) + { + const char *type; + packet->append(STRING_WITH_LEN(" INSERT_METHOD=")); + type= get_type(&merge_insert_method,file->merge_insert_method-1); + packet->append(type, strlen(type)); + } + /* + 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= children_l;; + open_table= open_table->next_global) + { + LEX_CSTRING db= open_table->db; + + 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); + if (&open_table->next_global == children_last_l) + break; + } + packet->append(')'); +} + + +enum_alter_inplace_result +ha_myisammrg::check_if_supported_inplace_alter(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) +{ + /* + We always support inplace ALTER in the new API, because old + HA_NO_COPY_ON_ALTER table_flags() hack prevents non-inplace ALTER anyway. + */ + return HA_ALTER_INPLACE_EXCLUSIVE_LOCK; +} + + +bool ha_myisammrg::inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) +{ + char tmp_path[FN_REFLEN]; + const char *name= table->s->normalized_path.str; + DBUG_ENTER("ha_myisammrg::inplace_alter_table"); + fn_format(tmp_path, name, "", MYRG_NAME_TMPEXT, MY_UNPACK_FILENAME | MY_APPEND_EXT); + int res= create_mrg(tmp_path, ha_alter_info->create_info); + if (res) + mysql_file_delete(rg_key_file_MRG, tmp_path, MYF(0)); + else + { + char path[FN_REFLEN]; + fn_format(path, name, "", MYRG_NAME_EXT, MY_UNPACK_FILENAME | MY_APPEND_EXT); + if (mysql_file_rename(rg_key_file_MRG, tmp_path, path, MYF(0))) + { + res= my_errno; + mysql_file_delete(rg_key_file_MRG, tmp_path, MYF(0)); + } + } + DBUG_RETURN(res); +} + +int ha_myisammrg::check(THD* thd, HA_CHECK_OPT* check_opt) +{ + return this->file->children_attached ? HA_ADMIN_OK : HA_ADMIN_CORRUPT; +} + + +ha_rows ha_myisammrg::records() +{ + return myrg_records(file); +} + +uint ha_myisammrg::count_query_cache_dependant_tables(uint8 *tables_type) +{ + MYRG_INFO *file = myrg_info(); + /* + Here should be following statement + (*tables_type)|= HA_CACHE_TBL_NONTRANSACT; + but it has no effect because HA_CACHE_TBL_NONTRANSACT is 0 + */ + return (uint)(file->end_table - file->open_tables); +} + + +my_bool ha_myisammrg::register_query_cache_dependant_tables(THD *thd + __attribute__((unused)), + Query_cache *cache, + Query_cache_block_table **block_table, + uint *n) +{ + MYRG_INFO *file =myrg_info(); + DBUG_ENTER("ha_myisammrg::register_query_cache_dependant_tables"); + + for (MYRG_TABLE *table =file->open_tables; + table != file->end_table ; + table++) + { + char key[MAX_DBKEY_LENGTH]; + uint32 db_length; + uint key_length= cache->filename_2_table_key(key, table->table->filename, + &db_length); + (++(*block_table))->n= ++(*n); + /* + There are not callback function for for MyISAM, and engine data + */ + if (!cache->insert_table(thd, key_length, key, (*block_table), + db_length, 0, + table_cache_type(), + 0, 0, TRUE)) + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + + +void ha_myisammrg::set_lock_type(enum thr_lock_type lock) +{ + handler::set_lock_type(lock); + if (children_l != NULL) + { + for (TABLE_LIST *child_table= children_l;; + child_table= child_table->next_global) + { + child_table->lock_type= + child_table->table->reginfo.lock_type= lock; + + if (&child_table->next_global == children_last_l) + break; + } + } +} + +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; + +#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; + myisammrg_hton->flags= HTON_NO_PARTITION; + myisammrg_hton->tablefile_extensions= ha_myisammrg_exts; + + return 0; +} + +struct st_mysql_storage_engine myisammrg_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION }; + +maria_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 */ + "1.0", /* string version */ + MariaDB_PLUGIN_MATURITY_STABLE /* maturity */ +} +maria_declare_plugin_end; diff --git a/storage/myisammrg/ha_myisammrg.h b/storage/myisammrg/ha_myisammrg.h new file mode 100644 index 00000000..6da327ec --- /dev/null +++ b/storage/myisammrg/ha_myisammrg.h @@ -0,0 +1,162 @@ +/* + Copyright (c) 2000, 2011, Oracle and/or its affiliates + + 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-1335 USA */ + + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +/* class for the the myisam merge handler */ + +#include + +/** + 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 final : public handler +{ + MYRG_INFO *file; + my_bool is_cloned; /* This instance has been cloned */ + +public: + MEM_ROOT children_mem_root; /* mem root for children list */ + List child_def_list; + TABLE_LIST *children_l; /* children list */ + TABLE_LIST **children_last_l; /* children list end */ + uint test_if_locked; /* flags from ::open() */ + + ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg); + ~ha_myisammrg(); + 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_CAN_EXPORT | + HA_NO_COPY_ON_ALTER | + HA_DUPLICATE_POS | HA_CAN_MULTISTEP_MERGE); + } + 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 HA_MAX_KEY_LENGTH; } + uint max_supported_key_part_length() const { return HA_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 add_children_list(void); + int attach_children(void); + int detach_children(void); + virtual handler *clone(const char *name, MEM_ROOT *mem_root); + int close(void); + int write_row(const uchar * buf); + int update_row(const uchar * old_data, const 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, const key_range *start_key, + const key_range *end_key, page_range *pages); + int delete_all_rows(); + 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_mrg(const char *name, HA_CREATE_INFO *create_info); + 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; } + enum_alter_inplace_result check_if_supported_inplace_alter(TABLE *, + Alter_inplace_info *); + bool inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info); + int check(THD* thd, HA_CHECK_OPT* check_opt); + ha_rows records(); + virtual uint count_query_cache_dependant_tables(uint8 *tables_type); + virtual my_bool + register_query_cache_dependant_tables(THD *thd, + Query_cache *cache, + Query_cache_block_table **block, + uint *n); + virtual void set_lock_type(enum thr_lock_type lock); +}; diff --git a/storage/myisammrg/myrg_close.c b/storage/myisammrg/myrg_close.c new file mode 100644 index 00000000..636382f9 --- /dev/null +++ b/storage/myisammrg/myrg_close.c @@ -0,0 +1,68 @@ +/* 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-1335 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(info->rec_per_key_part); + delete_queue(&info->by_key); + mysql_mutex_lock(&THR_LOCK_open); + myrg_open_list=list_delete(myrg_open_list,&info->open_list); + mysql_mutex_unlock(&THR_LOCK_open); + mysql_mutex_destroy(&info->mutex); + my_free(info); + 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 00000000..67e94f1d --- /dev/null +++ b/storage/myisammrg/myrg_create.c @@ -0,0 +1,73 @@ +/* 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-1335 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= mysql_file_create(rg_key_file_MRG, name, 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 (mysql_file_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 (mysql_file_write(file, (uchar*) buff, (uint) (end-buff), + MYF(MY_WME | MY_NABP))) + goto err; + } + if (mysql_file_close(file, MYF(0))) + goto err; + DBUG_RETURN(0); + +err: + save_errno=my_errno ? my_errno : -1; + switch (errpos) { + case 1: + (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 new file mode 100644 index 00000000..8bb79a73 --- /dev/null +++ b/storage/myisammrg/myrg_def.h @@ -0,0 +1,44 @@ +/* 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 + 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-1335 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; + +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); +#ifdef __cplusplus +extern "C" +#endif +void myrg_print_wrong_table(const char *table_name); + +/* Always defined */ +extern PSI_memory_key rg_key_memory_MYRG_INFO; + +C_MODE_START +extern PSI_mutex_key rg_key_mutex_MYRG_INFO_mutex; +extern PSI_memory_key rg_key_memory_children; +extern PSI_file_key rg_key_file_MRG; +void init_myisammrg_psi_keys(); +C_MODE_END + diff --git a/storage/myisammrg/myrg_delete.c b/storage/myisammrg/myrg_delete.c new file mode 100644 index 00000000..e13b9b4e --- /dev/null +++ b/storage/myisammrg/myrg_delete.c @@ -0,0 +1,27 @@ +/* Copyright (c) 2000-2002, 2005-2007 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 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 00000000..2b3861b9 --- /dev/null +++ b/storage/myisammrg/myrg_extra.c @@ -0,0 +1,97 @@ +/* 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 + 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-1335 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(0); + 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"); + + 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; + 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 00000000..1d78c650 --- /dev/null +++ b/storage/myisammrg/myrg_info.c @@ -0,0 +1,86 @@ +/* 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 + 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-1335 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 00000000..a79e35d2 --- /dev/null +++ b/storage/myisammrg/myrg_locking.c @@ -0,0 +1,45 @@ +/* Copyright (c) 2000-2002, 2005-2007 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 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++) + { + DBUG_ASSERT(file->table->open_flag & HA_OPEN_MERGE_TABLE); + + 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 00000000..4a983684 --- /dev/null +++ b/storage/myisammrg/myrg_open.c @@ -0,0 +1,551 @@ +/* + Copyright (c) 2000, 2011, Oracle and/or its affiliates + Copyright (c) 2010, 2020, MariaDB Corporation. + + 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-1335 USA */ + +/* open a MyISAM MERGE table */ + +#include "myrg_def.h" +#include +#include + +/* + 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 only used in the MySQL server when a + table is cloned. It is also used for usage of MERGE + 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, UNINIT_VAR(key_parts), min_keys= 0; + size_t length, dir_length; + ulonglong file_offset=0; + char name_buff[FN_REFLEN*2],buff[FN_REFLEN]; + 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= 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, + 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))) + { + char *end= &buff[length - 1]; + if (*end == '\n') + *end= '\0'; + if (buff[0] && buff[0] != '#') + files++; + } + + my_b_seek(&file, 0); + while ((length=my_b_gets(&file,buff,FN_REFLEN-1))) + { + char *end= &buff[length - 1]; + if (*end == '\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, FIND_TYPE_BASIC); + 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) | + HA_OPEN_MERGE_TABLE))) + { + 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(rg_key_memory_MYRG_INFO, + 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(rg_key_memory_MYRG_INFO, + 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) mysql_file_close(fd, MYF(0)); + end_io_cache(&file); + mysql_mutex_init(rg_key_mutex_MYRG_INFO_mutex, + &m_info->mutex, MY_MUTEX_INIT_FAST); + m_info->open_list.data=(void*) m_info; + mysql_mutex_lock(&THR_LOCK_open); + myrg_open_list=list_add(myrg_open_list,&m_info->open_list); + mysql_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(m_info); + /* Fall through */ + case 2: + end_io_cache(&file); + /* Fall through */ + case 1: + (void) mysql_file_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; + size_t 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= 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; + + 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, FIND_TYPE_BASIC); + } + continue; + } + + /* Count the child. */ + child_count++; + } + + /* Allocate MERGE parent table structure. */ + if (!(m_info= (MYRG_INFO*) my_malloc(rg_key_memory_MYRG_INFO, + 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) 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; + mysql_mutex_lock(&THR_LOCK_open); + myrg_open_list= list_add(myrg_open_list, &m_info->open_list); + mysql_mutex_unlock(&THR_LOCK_open); + + DBUG_RETURN(m_info); + + /* purecov: begin inspected */ + err: + save_errno= my_errno; + switch (errpos) { + case 3: + my_free(m_info); + /* Fall through */ + case 2: + end_io_cache(&file_cache); + /* Fall through */ + case 1: + (void) mysql_file_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; + my_bool first_child= TRUE; + 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. + */ + mysql_mutex_lock(&m_info->mutex); + errpos= 0; + file_offset= 0; + min_keys= 0; + 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)); + + /* Special handling when the first child is attached. */ + if (first_child) + { + first_child= FALSE; + m_info->reclength= myisam->s->base.reclength; + min_keys= myisam->s->base.keys; + key_parts= myisam->s->base.base_key_parts; + if (*need_compat_check && m_info->rec_per_key_part) + { + my_free(m_info->rec_per_key_part); + m_info->rec_per_key_part= NULL; + } + if (!m_info->rec_per_key_part || m_info->key_parts != key_parts) + { + m_info->key_parts= key_parts; + /* The +1 is because by my_realloc() don't allow zero length */ + if (!(m_info->rec_per_key_part= (ulong*) + my_realloc(rg_key_memory_MYRG_INFO, m_info->rec_per_key_part, + key_parts * sizeof(long) +1, + MYF(MY_WME | MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR)))) + 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; + /* Mark as MERGE table */ + myisam->open_flag|= HA_OPEN_MERGE_TABLE; + + /* Check table definition match. */ + if (m_info->reclength != myisam->s->base.reclength || + key_parts != myisam->s->base.base_key_parts) + { + 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); + } + + if (bad_children) + goto bad_children; + + 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; + mysql_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(m_info->rec_per_key_part); + m_info->rec_per_key_part= NULL; + } + mysql_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. */ + mysql_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; + mysql_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 00000000..3721b403 --- /dev/null +++ b/storage/myisammrg/myrg_panic.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2000, 2001, 2005, 2006 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 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 00000000..08d02bd5 --- /dev/null +++ b/storage/myisammrg/myrg_queue.c @@ -0,0 +1,90 @@ +/* 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-1335 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, 0, 0)) + 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, 0, 0)) + error=my_errno; + } + } + else + { + /* + 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; +} + +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 00000000..da5e2c38 --- /dev/null +++ b/storage/myisammrg/myrg_range.c @@ -0,0 +1,43 @@ +/* Copyright (c) 2002, 2004-2006 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "myrg_def.h" + +ha_rows myrg_records_in_range(MYRG_INFO *info, int inx, + const key_range *min_key, + const key_range *max_key, + page_range *pages) +{ + ha_rows records=0, res; + MYRG_TABLE *table; + page_range ignore_pages; + + /* Don't calculate pages of more than one active partition */ + if (info->open_tables +1 != info->end_table) + pages= &ignore_pages; + + for (table=info->open_tables ; table != info->end_table ; table++) + { + res= mi_records_in_range(table->table, inx, min_key, max_key, pages); + 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 00000000..4aa33a08 --- /dev/null +++ b/storage/myisammrg/myrg_records.c @@ -0,0 +1,28 @@ +/* Copyright (C) 2008 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 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 00000000..c8400c0a --- /dev/null +++ b/storage/myisammrg/myrg_rfirst.c @@ -0,0 +1,49 @@ +/* Copyright (c) 2000, 2001, 2003, 2005-2007 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 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 00000000..84e8297f --- /dev/null +++ b/storage/myisammrg/myrg_rkey.c @@ -0,0 +1,95 @@ +/* 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-1335 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 00000000..d9402568 --- /dev/null +++ b/storage/myisammrg/myrg_rlast.c @@ -0,0 +1,50 @@ +/* Copyright (c) 2000, 2001, 2003, 2005-2007 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 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 00000000..8b35e40f --- /dev/null +++ b/storage/myisammrg/myrg_rnext.c @@ -0,0 +1,53 @@ +/* Copyright (c) 2000-2003, 2005-2007 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 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_top(&(info->by_key)); + 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_replace_top(&(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 00000000..f9a114a7 --- /dev/null +++ b/storage/myisammrg/myrg_rnext_same.c @@ -0,0 +1,51 @@ +/* Copyright (c) 2002, 2004-2007 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 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_top(&(info->by_key)); + 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_replace_top(&(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 00000000..72765f5d --- /dev/null +++ b/storage/myisammrg/myrg_rprev.c @@ -0,0 +1,53 @@ +/* Copyright (c) 2000-2003, 2005-2007 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 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_top(&(info->by_key)); + 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_replace_top(&(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 00000000..a97f6d30 --- /dev/null +++ b/storage/myisammrg/myrg_rrnd.c @@ -0,0 +1,117 @@ +/* Copyright (c) 2000-2002, 2005-2007 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 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 00000000..f76fe79f --- /dev/null +++ b/storage/myisammrg/myrg_rsame.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2000-2002, 2005-2007 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 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 00000000..36ec25cb --- /dev/null +++ b/storage/myisammrg/myrg_static.c @@ -0,0 +1,71 @@ +/* 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 + 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-1335 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}; + +PSI_memory_key rg_key_memory_MYRG_INFO; +PSI_memory_key rg_key_memory_children; + +#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} +}; + +static PSI_memory_info all_myisammrg_memory[]= +{ + { &rg_key_memory_MYRG_INFO, "MYRG_INFO", 0}, + { &rg_key_memory_children, "children", 0} +}; + +void init_myisammrg_psi_keys() +{ + const char* category= "myisammrg"; + int count; + + count= array_elements(all_myisammrg_mutexes); + mysql_mutex_register(category, all_myisammrg_mutexes, count); + + count= array_elements(all_myisammrg_files); + mysql_file_register(category, all_myisammrg_files, count); + + count= array_elements(all_myisammrg_memory); + mysql_memory_register(category, all_myisammrg_memory, count); +} +#endif /* HAVE_PSI_INTERFACE */ + diff --git a/storage/myisammrg/myrg_update.c b/storage/myisammrg/myrg_update.c new file mode 100644 index 00000000..1620b712 --- /dev/null +++ b/storage/myisammrg/myrg_update.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2000-2002, 2005-2007 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/* Update last read record */ + +#include "myrg_def.h" + +int myrg_update(register MYRG_INFO *info,const uchar *oldrec, + const 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 00000000..e511d60d --- /dev/null +++ b/storage/myisammrg/myrg_write.c @@ -0,0 +1,30 @@ +/* Copyright (c) 2001, 2002, 2004-2007 MySQL AB + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/* Write a row to a MyISAM MERGE table */ + +#include "myrg_def.h" + +int myrg_write(register MYRG_INFO *info, const 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/mysql-test/storage_engine/alter_table.inc b/storage/myisammrg/mysql-test/storage_engine/alter_table.inc new file mode 100644 index 00000000..a978ade4 --- /dev/null +++ b/storage/myisammrg/mysql-test/storage_engine/alter_table.inc @@ -0,0 +1,116 @@ +################################## +# +# This include file will be used for all ALTER TABLE statements in the suite. +# If you need to add additional steps or change the logic, copy the file +# to storage//mysql-test/storage_engine/ folder and modify it there. +# +################## +# +# Parameters: +# +# --let $alter_definition = # mandatory, everything that goes after the table name in ALTER statement +# --let $table_name = # optional, default t1 +# --let $error_codes = # optional, default 0 +# --let $online = [0|1] # optional, default 0 (1 adds ONLINE) +# --let $rename_to = # optional, default empty. +# # If set, means we are running RENAME TO, then alter definition is ignored +# +# Usage examples: +# +# --let $alter_definition = ADD COLUMN b $char_col DEFAULT '' +# + +--let $child_alter_definition = $alter_definition + +if ($rename_to) +{ + --let $alter_definition = RENAME TO $rename_to + --let $child_alter_definition = RENAME TO mrg.$rename_to +} + +if (!$alter_definition) +{ + --die # The ALTER statement is empty +} + +--let $alter_statement = ALTER + +if ($online) +{ + --let $alter_statement = $alter_statement ONLINE +} + +if (!$table_name) +{ + --let $table_name = t1 +} + +--let $alter_statement = $alter_statement TABLE $table_name $alter_definition +# We don't want to do ONLINE on underlying tables, we are not testing MyISAM +--let $child_statement = ALTER TABLE mrg.$table_name $child_alter_definition + + + +# We now have the complete ALTER statement in $alter_statement. +# If your ALTER statement should be composed differently, +# modify the logic above. + +##################### +# Here you can add logic needed BEFORE the main statement +# (e.g. base tables need to be altered, etc.). +# Surround it by --disable_query_log/--enable_query_log +# if you don't want it to appear in the result output. +##################### + +--source obfuscate.inc + +eval $alter_statement; +--source check_errors.inc + +# Make sure you don't add any statements between the main ALTER (above) +# and saving mysql_errno and mysql_errname (below) +# They are saved in case you want to add more logic after the main ALTER, +# because we need the result code of the statement. +# Also, do not change $alter_statement after it is executed! + +--let $my_errno = $mysql_errno +--let $my_errname = $mysql_errname + +##################### +# Here you can add logic needed AFTER the main statement. +# Surround it by --disable_query_log/--enable_query_log +# if you don't want it to appear in the result output. +##################### +--disable_query_log +--disable_warnings +--disable_result_log +# We will only try to alter the underlying table if the main alter was successful +if (!$my_errno) +{ + if ($rename_to) + { + eval ALTER TABLE $rename_to UNION(mrg.$rename_to); + } + # In the same section, the manual says that FLUSH TABLES should be performed before altering + # the underlying table, and later also says that it should be done after. We'll do both + FLUSH TABLES; + eval $child_statement; + FLUSH TABLES; +} +--enable_result_log +--enable_warnings +--enable_query_log + +# Unset the parameters, we don't want them to be accidentally reused later +--let $alter_definition = +--let $table_name = +--let $error_codes = +--let $online = 0 +--let $rename_to = + +# Restore the error codes of the main statement +--let $mysql_errno = $my_errno +--let $mysql_errname = $my_errname +# Make sure you don't add any SQL statements after restoring +# mysql_errno and mysql_errname (above) + diff --git a/storage/myisammrg/mysql-test/storage_engine/alter_table.rdiff b/storage/myisammrg/mysql-test/storage_engine/alter_table.rdiff new file mode 100644 index 00000000..b2ec2129 --- /dev/null +++ b/storage/myisammrg/mysql-test/storage_engine/alter_table.rdiff @@ -0,0 +1,151 @@ +--- alter_table.result 2013-01-22 22:05:05.246633000 +0400 ++++ alter_table.reject 2013-01-23 02:50:10.652118538 +0400 +@@ -8,7 +8,7 @@ + `a` int(11) DEFAULT NULL, + `c` char(8) DEFAULT NULL, + `b` int(11) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 ALTER COLUMN a SET DEFAULT '0'; + SHOW CREATE TABLE t1; + Table Create Table +@@ -16,7 +16,7 @@ + `a` int(11) DEFAULT '0', + `c` char(8) DEFAULT NULL, + `b` int(11) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 ALTER a DROP DEFAULT; + SHOW CREATE TABLE t1; + Table Create Table +@@ -24,7 +24,7 @@ + `a` int(11), + `c` char(8) DEFAULT NULL, + `b` int(11) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 CHANGE COLUMN b b1 FIRST; + SHOW CREATE TABLE t1; + Table Create Table +@@ -32,7 +32,7 @@ + `b1` char(8) DEFAULT NULL, + `a` int(11), + `c` char(8) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 CHANGE b1 b AFTER c; + SHOW CREATE TABLE t1; + Table Create Table +@@ -40,7 +40,7 @@ + `a` int(11), + `c` char(8) DEFAULT NULL, + `b` int(11) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 CHANGE b b ; + SHOW CREATE TABLE t1; + Table Create Table +@@ -48,7 +48,7 @@ + `a` int(11), + `c` char(8) DEFAULT NULL, + `b` char(8) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 MODIFY COLUMN b ; + SHOW CREATE TABLE t1; + Table Create Table +@@ -56,7 +56,7 @@ + `a` int(11), + `c` char(8) DEFAULT NULL, + `b` int(11) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 MODIFY COLUMN b FIRST; + SHOW CREATE TABLE t1; + Table Create Table +@@ -64,7 +64,7 @@ + `b` char(8) DEFAULT NULL, + `a` int(11), + `c` char(8) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 MODIFY COLUMN b AFTER a; + SHOW CREATE TABLE t1; + Table Create Table +@@ -72,14 +72,14 @@ + `a` int(11), + `b` int(11) DEFAULT NULL, + `c` char(8) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 DROP COLUMN b; + SHOW CREATE TABLE t1; + Table Create Table + t1 CREATE TABLE `t1` ( + `a` int(11), + `c` char(8) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 RENAME TO t2; + SHOW CREATE TABLE t1; + ERROR 42S02: Table 'test.t1' doesn't exist +@@ -88,7 +88,7 @@ + t2 CREATE TABLE `t2` ( + `a` int(11), + `c` char(8) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t2`) + DROP TABLE t2; + CREATE TABLE t1 (a , b ) ENGINE= ; + INSERT INTO t1 (a,b) VALUES (1,5),(2,2),(4,3); +@@ -97,14 +97,14 @@ + t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 ORDER BY b ASC, a DESC; + SHOW CREATE TABLE t1; + Table Create Table + t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + SELECT a,b FROM t1; + a b + 2 2 +@@ -119,7 +119,7 @@ + `a` int(11) DEFAULT NULL, + `b` char(8) COLLATE latin1_general_cs DEFAULT NULL, + `c` char(8) COLLATE latin1_general_cs DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 COLLATE=latin1_general_cs ++) ENGINE= DEFAULT CHARSET=latin1 COLLATE=latin1_general_cs INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 CONVERT TO CHARACTER SET utf8; + SHOW CREATE TABLE t1; + Table Create Table +@@ -127,7 +127,7 @@ + `a` int(11) DEFAULT NULL, + `b` char(8) DEFAULT NULL, + `c` char(8) DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=utf8 ++) ENGINE= DEFAULT CHARSET=utf8 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 DEFAULT CHARACTER SET = latin1 COLLATE latin1_general_ci; + SHOW CREATE TABLE t1; + Table Create Table +@@ -135,7 +135,7 @@ + `a` int(11) DEFAULT NULL, + `b` char(8) CHARACTER SET utf8 DEFAULT NULL, + `c` char(8) CHARACTER SET utf8 DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci ++) ENGINE= DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + ALTER TABLE t1 FORCE; + SHOW CREATE TABLE t1; + Table Create Table +@@ -143,5 +143,5 @@ + `a` int(11) DEFAULT NULL, + `b` char(8) CHARACTER SET utf8 DEFAULT NULL, + `c` char(8) CHARACTER SET utf8 DEFAULT NULL +-) ENGINE= DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci ++) ENGINE= DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + DROP TABLE t1; diff --git a/storage/myisammrg/mysql-test/storage_engine/alter_table_online.rdiff b/storage/myisammrg/mysql-test/storage_engine/alter_table_online.rdiff new file mode 100644 index 00000000..857854a6 --- /dev/null +++ b/storage/myisammrg/mysql-test/storage_engine/alter_table_online.rdiff @@ -0,0 +1,82 @@ +--- suite/storage_engine/alter_table_online.result 2014-11-12 05:27:00.000000000 +0400 ++++ suite/storage_engine/alter_table_online.reject 2014-12-05 20:42:25.000000000 +0400 +@@ -2,8 +2,35 @@ + CREATE TABLE t1 (a , b , c ) ENGINE= ; + INSERT INTO t1 (a,b,c) VALUES (1,100,'a'),(2,200,'b'),(3,300,'c'); + ALTER ONLINE TABLE t1 MODIFY b DEFAULT 5; ++ERROR 0A000: LOCK=NONE/SHARED is not supported for this operation. Try LOCK=EXCLUSIVE. ++# ERROR: Statement ended with errno 1845, errname ER_ALTER_OPERATION_NOT_SUPPORTED (expected to succeed) ++# ------------ UNEXPECTED RESULT ------------ ++# The statement|command finished with ER_ALTER_OPERATION_NOT_SUPPORTED. ++# Functionality or the mix could be unsupported|malfunctioning, or the problem was caused by previous errors. ++# You can change the engine code, or create an rdiff, or disable the test by adding it to disabled.def. ++# Further in this test, the message might sometimes be suppressed; a part of the test might be skipped. ++# Also, this problem may cause a chain effect (more errors of different kinds in the test). ++# ------------------------------------------- + ALTER ONLINE TABLE t1 CHANGE b new_name ; ++ERROR 0A000: LOCK=NONE/SHARED is not supported for this operation. Try LOCK=EXCLUSIVE. ++# ERROR: Statement ended with errno 1845, errname ER_ALTER_OPERATION_NOT_SUPPORTED (expected to succeed) ++# ------------ UNEXPECTED RESULT ------------ ++# The statement|command finished with ER_ALTER_OPERATION_NOT_SUPPORTED. ++# Functionality or the mix could be unsupported|malfunctioning, or the problem was caused by previous errors. ++# You can change the engine code, or create an rdiff, or disable the test by adding it to disabled.def. ++# Further in this test, the message might sometimes be suppressed; a part of the test might be skipped. ++# Also, this problem may cause a chain effect (more errors of different kinds in the test). ++# ------------------------------------------- + ALTER ONLINE TABLE t1 COMMENT 'new comment'; ++ERROR 0A000: LOCK=NONE/SHARED is not supported for this operation. Try LOCK=EXCLUSIVE. ++# ERROR: Statement ended with errno 1845, errname ER_ALTER_OPERATION_NOT_SUPPORTED (expected to succeed) ++# ------------ UNEXPECTED RESULT ------------ ++# The statement|command finished with ER_ALTER_OPERATION_NOT_SUPPORTED. ++# Functionality or the mix could be unsupported|malfunctioning, or the problem was caused by previous errors. ++# You can change the engine code, or create an rdiff, or disable the test by adding it to disabled.def. ++# Further in this test, the message might sometimes be suppressed; a part of the test might be skipped. ++# Also, this problem may cause a chain effect (more errors of different kinds in the test). ++# ------------------------------------------- + ALTER ONLINE TABLE t1 RENAME TO t2; + ERROR 0A000: LOCK=NONE/SHARED is not supported for this operation. Try LOCK=EXCLUSIVE. + DROP TABLE IF EXISTS t2; +@@ -12,10 +39,6 @@ + CREATE TEMPORARY TABLE t1 (a , b ) ENGINE= ; + INSERT INTO t1 (a,b) VALUES (1,'a'),(2,'b'),(3,'c'); + ALTER ONLINE TABLE t1 MODIFY b DEFAULT 5; +-Warnings: +-Warning 1366 Incorrect integer value: 'a' for column 'b' at row 1 +-Warning 1366 Incorrect integer value: 'b' for column 'b' at row 2 +-Warning 1366 Incorrect integer value: 'c' for column 'b' at row 3 + ALTER ONLINE TABLE t1 CHANGE b new_name ; + ALTER ONLINE TABLE t1 COMMENT 'new comment'; + ALTER ONLINE TABLE t1 RENAME TO t2; +@@ -23,12 +46,30 @@ + CREATE TABLE t1 (a , b , c ) ENGINE= ; + INSERT INTO t1 (a,b,c) VALUES (1,100,'a'),(2,200,'b'),(3,300,'c'); + ALTER ONLINE TABLE t1 DROP COLUMN b, ADD b ; ++ERROR 0A000: LOCK=NONE/SHARED is not supported for this operation. Try LOCK=EXCLUSIVE. ++# ERROR: Statement ended with errno 1845, errname ER_ALTER_OPERATION_NOT_SUPPORTED (expected to succeed) ++# ------------ UNEXPECTED RESULT ------------ ++# The statement|command finished with ER_ALTER_OPERATION_NOT_SUPPORTED. ++# Functionality or the mix could be unsupported|malfunctioning, or the problem was caused by previous errors. ++# You can change the engine code, or create an rdiff, or disable the test by adding it to disabled.def. ++# Further in this test, the message might sometimes be suppressed; a part of the test might be skipped. ++# Also, this problem may cause a chain effect (more errors of different kinds in the test). ++# ------------------------------------------- + ALTER ONLINE TABLE t1 MODIFY b BIGINT ; +-ERROR 0A000: LOCK=NONE is not supported. Reason: Cannot change column type. Try LOCK=SHARED. ++ERROR 0A000: LOCK=NONE/SHARED is not supported for this operation. Try LOCK=EXCLUSIVE. ++# ERROR: Statement ended with errno 1845, errname ER_ALTER_OPERATION_NOT_SUPPORTED (expected results: ER_ALTER_OPERATION_NOT_SUPPORTED_REASON) + ALTER ONLINE TABLE t1 ENGINE=MEMORY; + ERROR 0A000: LOCK=NONE is not supported. Reason: COPY algorithm requires a lock. Try LOCK=SHARED. + DROP TABLE t1; + CREATE TABLE t1 (a , b , c ) ENGINE= ; + ALTER ONLINE TABLE t1 ADD INDEX (b); +-ALTER ONLINE TABLE t1 DROP INDEX b; ++ERROR 0A000: LOCK=NONE/SHARED is not supported for this operation. Try LOCK=EXCLUSIVE. ++# ERROR: Statement ended with errno 1845, errname ER_ALTER_OPERATION_NOT_SUPPORTED (expected to succeed) ++# ------------ UNEXPECTED RESULT ------------ ++# The statement|command finished with ER_ALTER_OPERATION_NOT_SUPPORTED. ++# Adding an index or ALTER ONLINE or the mix could be unsupported|malfunctioning, or the problem was caused by previous errors. ++# You can change the engine code, or create an rdiff, or disable the test by adding it to disabled.def. ++# Further in this test, the message might sometimes be suppressed; a part of the test might be skipped. ++# Also, this problem may cause a chain effect (more errors of different kinds in the test). ++# ------------------------------------------- + DROP TABLE t1; diff --git a/storage/myisammrg/mysql-test/storage_engine/alter_tablespace.rdiff b/storage/myisammrg/mysql-test/storage_engine/alter_tablespace.rdiff new file mode 100644 index 00000000..e5462f8c --- /dev/null +++ b/storage/myisammrg/mysql-test/storage_engine/alter_tablespace.rdiff @@ -0,0 +1,34 @@ +--- alter_tablespace.result 2013-01-22 22:05:05.246633000 +0400 ++++ alter_tablespace.reject 2013-01-23 02:50:11.288110543 +0400 +@@ -1,21 +1,14 @@ + DROP TABLE IF EXISTS t1, t2; + CREATE TABLE t1 (a ) ENGINE= ; + ALTER TABLE t1 DISCARD TABLESPACE; +-DROP TABLE t1; +-CREATE TABLE t1 (a ) ENGINE= ; +-INSERT INTO t1 (a) VALUES (1),(2); +-SELECT a FROM t1; +-a +-1 +-2 +-ALTER TABLE t1 DISCARD TABLESPACE; +-SELECT a FROM t1; +-ERROR HY000: Tablespace has been discarded for table `t1` +-ALTER TABLE t1 IMPORT TABLESPACE; +-Warnings: +-Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t1.cfg', will attempt to import without schema verification +-SELECT a FROM t1; +-a +-1 +-2 ++ERROR HY000: Storage engine MRG_MyISAM of the table `test`.`t1` doesn't have this option ++# ERROR: Statement ended with errno 1031, errname ER_ILLEGAL_HA (expected to succeed) ++# ------------ UNEXPECTED RESULT ------------ ++# [ ALTER TABLE t1 DISCARD TABLESPACE ] ++# The statement|command finished with ER_ILLEGAL_HA. ++# Tablespace operations or the syntax or the mix could be unsupported. ++# You can change the engine code, or create an rdiff, or disable the test by adding it to disabled.def. ++# Further in this test, the message might sometimes be suppressed; a part of the test might be skipped. ++# Also, this problem may cause a chain effect (more errors of different kinds in the test). ++# ------------------------------------------- + DROP TABLE t1; diff --git a/storage/myisammrg/mysql-test/storage_engine/analyze_table.rdiff b/storage/myisammrg/mysql-test/storage_engine/analyze_table.rdiff new file mode 100644 index 00000000..9854a986 --- /dev/null +++ b/storage/myisammrg/mysql-test/storage_engine/analyze_table.rdiff @@ -0,0 +1,34 @@ +--- analyze_table.result 2013-01-22 22:05:05.246633000 +0400 ++++ analyze_table.reject 2013-01-23 02:50:11.912102699 +0400 +@@ -5,25 +5,25 @@ + INSERT INTO t1 (a,b) VALUES (3,'c'); + ANALYZE TABLE t1; + Table Op Msg_type Msg_text +-test.t1 analyze status OK ++test.t1 analyze note The storage engine for the table doesn't support analyze + INSERT INTO t2 (a,b) VALUES (4,'d'); + ANALYZE NO_WRITE_TO_BINLOG TABLE t2; + Table Op Msg_type Msg_text +-test.t2 analyze status OK ++test.t2 analyze note The storage engine for the table doesn't support analyze + INSERT INTO t1 (a,b) VALUES (5,'e'); + INSERT INTO t2 (a,b) VALUES (6,'f'); + ANALYZE LOCAL TABLE t1, t2; + Table Op Msg_type Msg_text +-test.t1 analyze status OK +-test.t2 analyze status OK ++test.t1 analyze note The storage engine for the table doesn't support analyze ++test.t2 analyze note The storage engine for the table doesn't support analyze + DROP TABLE t1, t2; + CREATE TABLE t1 (a , (a)) ENGINE= ; + INSERT INTO t1 (a) VALUES (1),(2),(4),(7); + ANALYZE TABLE t1; + Table Op Msg_type Msg_text +-test.t1 analyze status OK ++test.t1 analyze note The storage engine for the table doesn't support analyze + INSERT INTO t1 (a) VALUES (8),(10),(11),(12); + ANALYZE TABLE t1; + Table Op Msg_type Msg_text +-test.t1 analyze status OK ++test.t1 analyze note The storage engine for the table doesn't support analyze + DROP TABLE t1; diff --git a/storage/myisammrg/mysql-test/storage_engine/autoincrement.rdiff b/storage/myisammrg/mysql-test/storage_engine/autoincrement.rdiff new file mode 100644 index 00000000..cc04b800 --- /dev/null +++ b/storage/myisammrg/mysql-test/storage_engine/autoincrement.rdiff @@ -0,0 +1,64 @@ +--- autoincrement.result 2013-01-22 22:05:05.246633000 +0400 ++++ autoincrement.reject 2013-01-23 02:50:12.848090932 +0400 +@@ -6,7 +6,7 @@ + `a` int(11) NOT NULL AUTO_INCREMENT, + `b` char(8) DEFAULT NULL, + KEY `a` (`a`) +-) ENGINE= DEFAULT CHARSET=latin1 ++) ENGINE= DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`mrg`.`t1`) + INSERT INTO t1 (b) VALUES ('a'),('b'); + SELECT a,b FROM t1 ORDER BY a; + a b +@@ -52,14 +52,14 @@ + SET sql_mode = ''; + SHOW TABLE STATUS FROM test LIKE 't1'; + Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Max_index_length Temporary +-t1 # # # # # # # # 6 # # # # # # # # N ++t1 # # # # # # # # 0 # # # # # # # # N + INSERT INTO t1 (a,b) VALUES (6,'g'),(7,'h'); + SELECT LAST_INSERT_ID(); + LAST_INSERT_ID() + 5 + SHOW TABLE STATUS FROM test LIKE 't1'; + Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Max_index_length Temporary +-t1 # # # # # # # # # 8 # # # # # # # # N ++t1 # # # # # # # # # 0 # # # # # # # # N + INSERT INTO t1 (a,b) VALUES (NULL,'i'),(9,'j'); + SELECT a,b FROM t1 ORDER BY a; + a b +@@ -78,11 +78,11 @@ + 8 + SHOW TABLE STATUS FROM test LIKE 't1'; + Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Max_index_length Temporary +-t1 # # # # # # # # # 10 # # # # # # # # N ++t1 # # # # # # # # # 0 # # # # # # # # N + INSERT INTO t1 (a,b) VALUES (20,'k'); + SHOW TABLE STATUS FROM test LIKE 't1'; + Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Max_index_length Temporary +-t1 # # # # # # # # # 21 # # # # # # # # N ++t1 # # # # # # # # # 0 # # # # # # # # N + INSERT INTO t1 (a,b) VALUES (NULL,'l'); + SELECT a,b FROM t1 ORDER BY a; + a b +@@ -103,7 +103,7 @@ + 21 + SHOW TABLE STATUS FROM test LIKE 't1'; + Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Max_index_length Temporary +-t1 # # # # # # # # # 22 # # # # # # # # N ++t1 # # # # # # # # # 0 # # # # # # # # N + INSERT INTO t1 (a,b) VALUES (-5,'m'); + SELECT a,b FROM t1 ORDER BY a; + a b +@@ -125,9 +125,9 @@ + INSERT INTO t1 (a,b) VALUES (NULL,'a'),(NULL,'b'); + SELECT a,b FROM t1; + a b +-100 a +-101 b ++1 a ++2 b + SELECT LAST_INSERT_ID(); + LAST_INSERT_ID() +-100 ++1 + DROP TABLE t1; diff --git a/storage/myisammrg/mysql-test/storage_engine/cache_index.rdiff b/storage/myisammrg/mysql-test/storage_engine/cache_index.rdiff new file mode 100644 index 00000000..612c8d38 --- /dev/null +++ b/storage/myisammrg/mysql-test/storage_engine/cache_index.rdiff @@ -0,0 +1,71 @@ +--- cache_index.result 2013-01-22 22:05:05.246633000 +0400 ++++ cache_index.reject 2013-01-23 02:50:13.468083137 +0400 +@@ -12,31 +12,31 @@ + SET GLOBAL .key_buffer_size=128*1024; + CACHE INDEX t1 INDEX (a), t2 IN ; + Table Op Msg_type Msg_text +-test.t1 assign_to_keycache status OK +-test.t2 assign_to_keycache status OK ++test.t1 assign_to_keycache note The storage engine for the table doesn't support assign_to_keycache ++test.t2 assign_to_keycache note The storage engine for the table doesn't support assign_to_keycache + LOAD INDEX INTO CACHE t1, t2; + Table Op Msg_type Msg_text +-test.t1 preload_keys status OK +-test.t2 preload_keys status OK ++test.t1 preload_keys note The storage engine for the table doesn't support preload_keys ++test.t2 preload_keys note The storage engine for the table doesn't support preload_keys + INSERT INTO t1 (a,b) VALUES (3,'c'),(4,'d'); + SET GLOBAL .key_buffer_size=8*1024; + LOAD INDEX INTO CACHE t1, t2 IGNORE LEAVES; + Table Op Msg_type Msg_text +-test.t1 preload_keys status OK +-test.t2 preload_keys status OK ++test.t1 preload_keys note The storage engine for the table doesn't support preload_keys ++test.t2 preload_keys note The storage engine for the table doesn't support preload_keys + SET GLOBAL .key_cache_age_threshold = 100, .key_cache_block_size = 512, .key_cache_division_limit = 1, .key_cache_segments=2; + INSERT INTO t1 (a,b) VALUES (5,'e'),(6,'f'); + LOAD INDEX INTO CACHE t1; + Table Op Msg_type Msg_text +-test.t1 preload_keys status OK ++test.t1 preload_keys note The storage engine for the table doesn't support preload_keys + SET GLOBAL new_.key_buffer_size=128*1024; + CACHE INDEX t1 IN new_; + Table Op Msg_type Msg_text +-test.t1 assign_to_keycache status OK ++test.t1 assign_to_keycache note The storage engine for the table doesn't support assign_to_keycache + INSERT INTO t1 (a,b) VALUES (7,'g'),(8,'h'); + LOAD INDEX INTO CACHE t1 IGNORE LEAVES; + Table Op Msg_type Msg_text +-test.t1 preload_keys status OK ++test.t1 preload_keys note The storage engine for the table doesn't support preload_keys + INSERT INTO t1 (a,b) VALUES (9,'i'); + DROP TABLE t2; + DROP TABLE t1; +@@ -47,11 +47,11 @@ + ) ENGINE= ; + CACHE INDEX t1 IN ; + Table Op Msg_type Msg_text +-test.t1 assign_to_keycache status OK ++test.t1 assign_to_keycache note The storage engine for the table doesn't support assign_to_keycache + INSERT INTO t1 (a,b) VALUES (1,'a'),(2,'b'); + LOAD INDEX INTO CACHE t1; + Table Op Msg_type Msg_text +-test.t1 preload_keys status OK ++test.t1 preload_keys note The storage engine for the table doesn't support preload_keys + DROP TABLE t1; + CREATE TABLE t1 (a , + b , +@@ -59,11 +59,11 @@ + ) ENGINE= ; + CACHE INDEX t1 IN ; + Table Op Msg_type Msg_text +-test.t1 assign_to_keycache status OK ++test.t1 assign_to_keycache note The storage engine for the table doesn't support assign_to_keycache + INSERT INTO t1 (a,b) VALUES (1,'a'),(2,'b'); + LOAD INDEX INTO CACHE t1; + Table Op Msg_type Msg_text +-test.t1 preload_keys status OK ++test.t1 preload_keys note The storage engine for the table doesn't support preload_keys + DROP TABLE t1; + SET GLOBAL .key_buffer_size=0; + SET GLOBAL new_.key_buffer_size=0; diff --git a/storage/myisammrg/mysql-test/storage_engine/checksum_table_live.rdiff b/storage/myisammrg/mysql-test/storage_engine/checksum_table_live.rdiff new file mode 100644 index 00000000..f09aec97 --- /dev/null +++ b/storage/myisammrg/mysql-test/storage_engine/checksum_table_live.rdiff @@ -0,0 +1,13 @@ +--- checksum_table_live.result 2013-01-22 22:05:05.246633000 +0400 ++++ checksum_table_live.reject 2013-01-23 02:50:14.440070917 +0400 +@@ -11,8 +11,8 @@ + test.t1 4272806499 + CHECKSUM TABLE t1, t2 QUICK; + Table Checksum +-test.t1 4272806499 +-test.t2 0 ++test.t1 NULL ++test.t2 NULL + CHECKSUM TABLE t1, t2 EXTENDED; + Table Checksum + test.t1 4272806499 diff --git a/storage/myisammrg/mysql-test/storage_engine/cleanup_engine.inc b/storage/myisammrg/mysql-test/storage_engine/cleanup_engine.inc new file mode 100644 index 00000000..b8f84110 --- /dev/null +++ b/storage/myisammrg/mysql-test/storage_engine/cleanup_engine.inc @@ -0,0 +1,16 @@ +########################################### +# +# This is a stub of the include file cleanup_engine.inc which +# should be placed in storage//mysql-test/storage_engine folder. +# +################################ +# +# Here you can add whatever is needed to cleanup +# in case your define_engine.inc created any artefacts, +# e.g. an additional schema and/or tables. +--disable_query_log +--disable_warnings +DROP DATABASE IF EXISTS mrg; +--enable_warnings +--enable_query_log + diff --git a/storage/myisammrg/mysql-test/storage_engine/create_table.inc b/storage/myisammrg/mysql-test/storage_engine/create_table.inc new file mode 100644 index 00000000..c74460d4 --- /dev/null +++ b/storage/myisammrg/mysql-test/storage_engine/create_table.inc @@ -0,0 +1,208 @@ +################################## +# +# This include file will be used for all CREATE TABLE statements in the suite. +# If you need to add additional steps or change the logic, copy the file +# to storage//mysql-test/storage_engine/ folder and modify it there. +# +################## +# +# Parameters: +# +# --let $create_definition = # optional, default t1 +# --let $table_options =
# optional, default based on define_engine.inc +# --let $partition_options = # optional, default none +# --let $as_select =