mirror of
https://github.com/MariaDB/server.git
synced 2025-02-02 03:51:50 +01:00
0605274155
corrupts a MERGE table Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Bug 26377 - Deadlock with MERGE and FLUSH TABLE Bug 25038 - Waiting TRUNCATE Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Bug 19627 - temporary merge table locking Bug 27660 - Falcon: merge table possible Bug 30273 - merge tables: Can't lock file (errno: 155) The problems were: Bug 26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table 1. A thread trying to lock a MERGE table performs busy waiting while REPAIR TABLE or a similar table administration task is ongoing on one or more of its MyISAM tables. 2. A thread trying to lock a MERGE table performs busy waiting until all threads that did REPAIR TABLE or similar table administration tasks on one or more of its MyISAM tables in LOCK TABLES segments do UNLOCK TABLES. The difference against problem #1 is that the busy waiting takes place *after* the administration task. It is terminated by UNLOCK TABLES only. 3. Two FLUSH TABLES within a LOCK TABLES segment can invalidate the lock. This does *not* require a MERGE table. The first FLUSH TABLES can be replaced by any statement that requires other threads to reopen the table. In 5.0 and 5.1 a single FLUSH TABLES can provoke the problem. Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Trying DML on a MERGE table, which has a child locked and repaired by another thread, made an infinite loop in the server. Bug 26377 - Deadlock with MERGE and FLUSH TABLE Locking a MERGE table and its children in parent-child order and flushing the child deadlocked the server. Bug 25038 - Waiting TRUNCATE Truncating a MERGE child, while the MERGE table was in use, let the truncate fail instead of waiting for the table to become free. Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Repairing a child of an open MERGE table corrupted the child. It was necessary to FLUSH the child first. Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Flushing and optimizing locked MERGE children crashed the server. Bug 19627 - temporary merge table locking Use of a temporary MERGE table with non-temporary children could corrupt the children. Temporary tables are never locked. So we do now prohibit non-temporary chidlren of a temporary MERGE table. Bug 27660 - Falcon: merge table possible It was possible to create a MERGE table with non-MyISAM children. Bug 30273 - merge tables: Can't lock file (errno: 155) This was a Windows-only bug. Table administration statements sometimes failed with "Can't lock file (errno: 155)". These bugs are fixed by a new implementation of MERGE table open. When opening a MERGE table in open_tables() we do now add the child tables to the list of tables to be opened by open_tables() (the "query_list"). The children are not opened in the handler at this stage. After opening the parent, open_tables() opens each child from the now extended query_list. When the last child is opened, we remove the children from the query_list again and attach the children to the parent. This behaves similar to the old open. However it does not open the MyISAM tables directly, but grabs them from the already open children. When closing a MERGE table in close_thread_table() we detach the children only. Closing of the children is done implicitly because they are in thd->open_tables. For more detail see the comment at the top of ha_myisammrg.cc. Changed from open_ltable() to open_and_lock_tables() in all places that can be relevant for MERGE tables. The latter can handle tables added to the list on the fly. When open_ltable() was used in a loop over a list of tables, the list must be temporarily terminated after every table for open_and_lock_tables(). table_list->required_type is set to FRMTYPE_TABLE to avoid open of special tables. Handling of derived tables is suppressed. These details are handled by the new function open_n_lock_single_table(), which has nearly the same signature as open_ltable() and can replace it in most cases. In reopen_tables() some of the tables open by a thread can be closed and reopened. When a MERGE child is affected, the parent must be closed and reopened too. Closing of the parent is forced before the first child is closed. Reopen happens in the order of thd->open_tables. MERGE parents do not attach their children automatically at open. This is done after all tables are reopened. So all children are open when attaching them. Special lock handling like mysql_lock_abort() or mysql_lock_remove() needs to be suppressed for MERGE children or forwarded to the parent. This depends on the situation. In loops over all open tables one suppresses child lock handling. When a single table is touched, forwarding is done. Behavioral changes: =================== This patch changes the behavior of temporary MERGE tables. Temporary MERGE must have temporary children. The old behavior was wrong. A temporary table is not locked. Hence even non-temporary children were not locked. See Bug 19627 - temporary merge table locking. You cannot change the union list of a non-temporary MERGE table when LOCK TABLES is in effect. The following does *not* work: CREATE TABLE m1 ... ENGINE=MRG_MYISAM ...; LOCK TABLES t1 WRITE, t2 WRITE, m1 WRITE; ALTER TABLE m1 ... UNION=(t1,t2) ...; However, you can do this with a temporary MERGE table. You cannot create a MERGE table with CREATE ... SELECT, neither as a temporary MERGE table, nor as a non-temporary MERGE table. CREATE TABLE m1 ... ENGINE=MRG_MYISAM ... SELECT ...; Gives error message: table is not BASE TABLE.
509 lines
16 KiB
C
509 lines
16 KiB
C
/* Copyright (C) 2000-2006 MySQL AB
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
/* open a MyISAM MERGE table */
|
|
|
|
#include "myrg_def.h"
|
|
#include <stddef.h>
|
|
#include <errno.h>
|
|
#ifdef VMS
|
|
#include "mrg_static.c"
|
|
#endif
|
|
|
|
/*
|
|
open a MyISAM MERGE table
|
|
if handle_locking is 0 then exit with error if some table is locked
|
|
if handle_locking is 1 then wait if table is locked
|
|
|
|
NOTE: This function is not used in the MySQL server. It is for
|
|
MERGE use independent from MySQL. Currently there is some code
|
|
duplication between myrg_open() and myrg_parent_open() +
|
|
myrg_attach_children(). Please duplicate changes in these
|
|
functions or make common sub-functions.
|
|
*/
|
|
/* purecov: begin unused */
|
|
|
|
MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking)
|
|
{
|
|
int save_errno,errpos=0;
|
|
uint files= 0, i, dir_length, length, key_parts, min_keys= 0;
|
|
ulonglong file_offset=0;
|
|
char name_buff[FN_REFLEN*2],buff[FN_REFLEN],*end;
|
|
MYRG_INFO *m_info=0;
|
|
File fd;
|
|
IO_CACHE file;
|
|
MI_INFO *isam=0;
|
|
uint found_merge_insert_method= 0;
|
|
size_t name_buff_length;
|
|
DBUG_ENTER("myrg_open");
|
|
|
|
LINT_INIT(key_parts);
|
|
|
|
bzero((char*) &file,sizeof(file));
|
|
if ((fd=my_open(fn_format(name_buff,name,"",MYRG_NAME_EXT,
|
|
MY_UNPACK_FILENAME|MY_APPEND_EXT),
|
|
O_RDONLY | O_SHARE,MYF(0))) < 0)
|
|
goto err;
|
|
errpos=1;
|
|
if (init_io_cache(&file, fd, 4*IO_SIZE, READ_CACHE, 0, 0,
|
|
MYF(MY_WME | MY_NABP)))
|
|
goto err;
|
|
errpos=2;
|
|
dir_length=dirname_part(name_buff, name, &name_buff_length);
|
|
while ((length=my_b_gets(&file,buff,FN_REFLEN-1)))
|
|
{
|
|
if ((end=buff+length)[-1] == '\n')
|
|
end[-1]='\0';
|
|
if (buff[0] && buff[0] != '#')
|
|
files++;
|
|
}
|
|
|
|
my_b_seek(&file, 0);
|
|
while ((length=my_b_gets(&file,buff,FN_REFLEN-1)))
|
|
{
|
|
if ((end=buff+length)[-1] == '\n')
|
|
*--end='\0';
|
|
if (!buff[0])
|
|
continue; /* Skip empty lines */
|
|
if (buff[0] == '#')
|
|
{
|
|
if (!strncmp(buff+1,"INSERT_METHOD=",14))
|
|
{ /* Lookup insert method */
|
|
int tmp=find_type(buff+15,&merge_insert_method,2);
|
|
found_merge_insert_method = (uint) (tmp >= 0 ? tmp : 0);
|
|
}
|
|
continue; /* Skip comments */
|
|
}
|
|
|
|
if (!has_path(buff))
|
|
{
|
|
VOID(strmake(name_buff+dir_length,buff,
|
|
sizeof(name_buff)-1-dir_length));
|
|
VOID(cleanup_dirname(buff,name_buff));
|
|
}
|
|
else
|
|
fn_format(buff, buff, "", "", 0);
|
|
if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0))))
|
|
{
|
|
my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
|
|
if (handle_locking & HA_OPEN_FOR_REPAIR)
|
|
{
|
|
myrg_print_wrong_table(buff);
|
|
continue;
|
|
}
|
|
goto err;
|
|
}
|
|
if (!m_info) /* First file */
|
|
{
|
|
key_parts=isam->s->base.key_parts;
|
|
if (!(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO) +
|
|
files*sizeof(MYRG_TABLE) +
|
|
key_parts*sizeof(long),
|
|
MYF(MY_WME|MY_ZEROFILL))))
|
|
goto err;
|
|
DBUG_ASSERT(files);
|
|
m_info->open_tables=(MYRG_TABLE *) (m_info+1);
|
|
m_info->rec_per_key_part=(ulong *) (m_info->open_tables+files);
|
|
m_info->tables= files;
|
|
files= 0;
|
|
m_info->reclength=isam->s->base.reclength;
|
|
min_keys= isam->s->base.keys;
|
|
errpos=3;
|
|
}
|
|
m_info->open_tables[files].table= isam;
|
|
m_info->open_tables[files].file_offset=(my_off_t) file_offset;
|
|
file_offset+=isam->state->data_file_length;
|
|
files++;
|
|
if (m_info->reclength != isam->s->base.reclength)
|
|
{
|
|
my_errno=HA_ERR_WRONG_MRG_TABLE_DEF;
|
|
if (handle_locking & HA_OPEN_FOR_REPAIR)
|
|
{
|
|
myrg_print_wrong_table(buff);
|
|
continue;
|
|
}
|
|
goto err;
|
|
}
|
|
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 (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF)
|
|
goto err;
|
|
if (!m_info && !(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO),
|
|
MYF(MY_WME | MY_ZEROFILL))))
|
|
goto err;
|
|
/* Don't mark table readonly, for ALTER TABLE ... UNION=(...) to work */
|
|
m_info->options&= ~(HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA);
|
|
m_info->merge_insert_method= found_merge_insert_method;
|
|
|
|
if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L)
|
|
{
|
|
my_errno=HA_ERR_RECORD_FILE_FULL;
|
|
goto err;
|
|
}
|
|
m_info->keys= min_keys;
|
|
bzero((char*) &m_info->by_key,sizeof(m_info->by_key));
|
|
|
|
/* this works ok if the table list is empty */
|
|
m_info->end_table=m_info->open_tables+files;
|
|
m_info->last_used_table=m_info->open_tables;
|
|
m_info->children_attached= TRUE;
|
|
|
|
VOID(my_close(fd,MYF(0)));
|
|
end_io_cache(&file);
|
|
m_info->open_list.data=(void*) m_info;
|
|
pthread_mutex_lock(&THR_LOCK_open);
|
|
myrg_open_list=list_add(myrg_open_list,&m_info->open_list);
|
|
pthread_mutex_unlock(&THR_LOCK_open);
|
|
DBUG_RETURN(m_info);
|
|
|
|
err:
|
|
save_errno=my_errno;
|
|
switch (errpos) {
|
|
case 3:
|
|
while (files)
|
|
mi_close(m_info->open_tables[--files].table);
|
|
my_free((char*) m_info,MYF(0));
|
|
/* Fall through */
|
|
case 2:
|
|
end_io_cache(&file);
|
|
/* Fall through */
|
|
case 1:
|
|
VOID(my_close(fd,MYF(0)));
|
|
}
|
|
my_errno=save_errno;
|
|
DBUG_RETURN (NULL);
|
|
}
|
|
/* purecov: end */
|
|
|
|
|
|
/**
|
|
@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 *m_info;
|
|
int rc;
|
|
int errpos;
|
|
int save_errno;
|
|
int insert_method;
|
|
uint length;
|
|
uint dir_length;
|
|
uint child_count;
|
|
size_t name_buff_length;
|
|
File fd;
|
|
IO_CACHE file_cache;
|
|
char parent_name_buff[FN_REFLEN * 2];
|
|
char child_name_buff[FN_REFLEN];
|
|
DBUG_ENTER("myrg_parent_open");
|
|
|
|
rc= 1;
|
|
errpos= 0;
|
|
bzero((char*) &file_cache, sizeof(file_cache));
|
|
|
|
/* Open MERGE meta file. */
|
|
if ((fd= my_open(fn_format(parent_name_buff, parent_name, "", MYRG_NAME_EXT,
|
|
MY_UNPACK_FILENAME|MY_APPEND_EXT),
|
|
O_RDONLY | O_SHARE, MYF(0))) < 0)
|
|
goto err; /* purecov: inspected */
|
|
errpos= 1;
|
|
|
|
if (init_io_cache(&file_cache, fd, 4 * IO_SIZE, READ_CACHE, 0, 0,
|
|
MYF(MY_WME | MY_NABP)))
|
|
goto err; /* purecov: inspected */
|
|
errpos= 2;
|
|
|
|
/* Count children. Determine insert method. */
|
|
child_count= 0;
|
|
insert_method= 0;
|
|
while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1)))
|
|
{
|
|
/* Remove line terminator. */
|
|
if (child_name_buff[length - 1] == '\n')
|
|
child_name_buff[--length]= '\0';
|
|
|
|
/* Skip empty lines. */
|
|
if (!child_name_buff[0])
|
|
continue; /* purecov: inspected */
|
|
|
|
/* Skip comments, but evaluate insert method. */
|
|
if (child_name_buff[0] == '#')
|
|
{
|
|
if (!strncmp(child_name_buff + 1, "INSERT_METHOD=", 14))
|
|
{
|
|
/* Compare buffer with global methods list: merge_insert_method. */
|
|
insert_method= find_type(child_name_buff + 15,
|
|
&merge_insert_method, 2);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Count the child. */
|
|
child_count++;
|
|
}
|
|
|
|
/* Allocate MERGE parent table structure. */
|
|
if (!(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO) +
|
|
child_count * sizeof(MYRG_TABLE),
|
|
MYF(MY_WME | MY_ZEROFILL))))
|
|
goto err; /* purecov: inspected */
|
|
errpos= 3;
|
|
m_info->open_tables= (MYRG_TABLE*) (m_info + 1);
|
|
m_info->tables= child_count;
|
|
m_info->merge_insert_method= insert_method > 0 ? insert_method : 0;
|
|
/* This works even if the table list is empty. */
|
|
m_info->end_table= m_info->open_tables + child_count;
|
|
if (!child_count)
|
|
{
|
|
/* Do not attach/detach an empty child list. */
|
|
m_info->children_attached= TRUE;
|
|
}
|
|
|
|
/* Call callback for each child. */
|
|
dir_length= dirname_part(parent_name_buff, parent_name, &name_buff_length);
|
|
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;
|
|
|
|
if (!has_path(child_name_buff))
|
|
{
|
|
VOID(strmake(parent_name_buff + dir_length, child_name_buff,
|
|
sizeof(parent_name_buff) - 1 - dir_length));
|
|
VOID(cleanup_dirname(child_name_buff, parent_name_buff));
|
|
}
|
|
else
|
|
fn_format(child_name_buff, child_name_buff, "", "", 0);
|
|
DBUG_PRINT("info", ("child: '%s'", child_name_buff));
|
|
|
|
/* Callback registers child with handler table. */
|
|
if ((rc= (*callback)(callback_param, child_name_buff)))
|
|
goto err; /* purecov: inspected */
|
|
}
|
|
|
|
end_io_cache(&file_cache);
|
|
VOID(my_close(fd, MYF(0)));
|
|
|
|
m_info->open_list.data= (void*) m_info;
|
|
pthread_mutex_lock(&THR_LOCK_open);
|
|
myrg_open_list= list_add(myrg_open_list, &m_info->open_list);
|
|
pthread_mutex_unlock(&THR_LOCK_open);
|
|
|
|
DBUG_RETURN(m_info);
|
|
|
|
/* purecov: begin inspected */
|
|
err:
|
|
save_errno= my_errno;
|
|
switch (errpos) {
|
|
case 3:
|
|
my_free((char*) m_info, MYF(0));
|
|
/* Fall through */
|
|
case 2:
|
|
end_io_cache(&file_cache);
|
|
/* Fall through */
|
|
case 1:
|
|
VOID(my_close(fd, MYF(0)));
|
|
}
|
|
my_errno= save_errno;
|
|
DBUG_RETURN (NULL);
|
|
/* purecov: end */
|
|
}
|
|
|
|
|
|
/**
|
|
@brief Attach children to a MyISAM MERGE parent table.
|
|
|
|
@detail Call a callback function for each child table.
|
|
The callback returns the MyISAM table handle of the child table.
|
|
Check table definition match.
|
|
|
|
@param[in] m_info MERGE parent table structure
|
|
@param[in] handle_locking if contains HA_OPEN_FOR_REPAIR, warn about
|
|
incompatible child tables, but continue
|
|
@param[in] callback function to call for each child table
|
|
@param[in] callback_param data pointer to give to the callback
|
|
|
|
@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)
|
|
{
|
|
ulonglong file_offset;
|
|
MI_INFO *myisam;
|
|
int rc;
|
|
int errpos;
|
|
int save_errno;
|
|
uint idx;
|
|
uint child_nr;
|
|
uint key_parts;
|
|
uint min_keys;
|
|
DBUG_ENTER("myrg_attach_children");
|
|
DBUG_PRINT("myrg", ("handle_locking: %d", handle_locking));
|
|
|
|
rc= 1;
|
|
errpos= 0;
|
|
file_offset= 0;
|
|
LINT_INIT(key_parts);
|
|
min_keys= 0;
|
|
child_nr= 0;
|
|
while ((myisam= (*callback)(callback_param)))
|
|
{
|
|
DBUG_PRINT("myrg", ("child_nr: %u table: '%s'",
|
|
child_nr, myisam->filename));
|
|
DBUG_ASSERT(child_nr < m_info->tables);
|
|
|
|
/* Special handling when the first child is attached. */
|
|
if (!child_nr)
|
|
{
|
|
m_info->reclength= myisam->s->base.reclength;
|
|
min_keys= myisam->s->base.keys;
|
|
key_parts= myisam->s->base.key_parts;
|
|
if (!m_info->rec_per_key_part)
|
|
{
|
|
if(!(m_info->rec_per_key_part= (ulong*)
|
|
my_malloc(key_parts * sizeof(long), MYF(MY_WME|MY_ZEROFILL))))
|
|
goto err; /* purecov: inspected */
|
|
errpos= 1;
|
|
}
|
|
}
|
|
|
|
/* Add MyISAM table info. */
|
|
m_info->open_tables[child_nr].table= myisam;
|
|
m_info->open_tables[child_nr].file_offset= (my_off_t) file_offset;
|
|
file_offset+= myisam->state->data_file_length;
|
|
|
|
/* Check table definition match. */
|
|
if (m_info->reclength != myisam->s->base.reclength)
|
|
{
|
|
DBUG_PRINT("error", ("definition mismatch table: '%s' repair: %d",
|
|
myisam->filename,
|
|
(handle_locking & HA_OPEN_FOR_REPAIR)));
|
|
my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
|
|
if (handle_locking & HA_OPEN_FOR_REPAIR)
|
|
{
|
|
myrg_print_wrong_table(myisam->filename);
|
|
continue;
|
|
}
|
|
goto err;
|
|
}
|
|
|
|
m_info->options|= myisam->s->options;
|
|
m_info->records+= myisam->state->records;
|
|
m_info->del+= myisam->state->del;
|
|
m_info->data_file_length+= myisam->state->data_file_length;
|
|
if (min_keys > myisam->s->base.keys)
|
|
min_keys= myisam->s->base.keys; /* purecov: inspected */
|
|
for (idx= 0; idx < key_parts; idx++)
|
|
m_info->rec_per_key_part[idx]+= (myisam->s->state.rec_per_key_part[idx] /
|
|
m_info->tables);
|
|
child_nr++;
|
|
}
|
|
|
|
if (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF)
|
|
goto err;
|
|
if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L)
|
|
{
|
|
my_errno= HA_ERR_RECORD_FILE_FULL;
|
|
goto err;
|
|
}
|
|
/* Don't mark table readonly, for ALTER TABLE ... UNION=(...) to work */
|
|
m_info->options&= ~(HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA);
|
|
m_info->keys= min_keys;
|
|
m_info->last_used_table= m_info->open_tables;
|
|
m_info->children_attached= TRUE;
|
|
DBUG_RETURN(0);
|
|
|
|
err:
|
|
save_errno= my_errno;
|
|
switch (errpos) {
|
|
case 1:
|
|
my_free((char*) m_info->rec_per_key_part, MYF(0));
|
|
m_info->rec_per_key_part= NULL;
|
|
}
|
|
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");
|
|
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;
|
|
DBUG_RETURN(0);
|
|
}
|
|
|