summaryrefslogtreecommitdiffstats
path: root/storage/myisammrg/myrg_open.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/myisammrg/myrg_open.c')
-rw-r--r--storage/myisammrg/myrg_open.c551
1 files changed, 551 insertions, 0 deletions
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 <stddef.h>
+#include <errno.h>
+
+/*
+ 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);
+}
+