mirror of
https://github.com/MariaDB/server.git
synced 2025-09-03 20:43:11 +03:00
Added locking of lock mutex when updating status in external_unlock() for Aria and MyISAM tables. Fixed that 'source' command doesn't cause mysql command line tool to exit on error. DEBUG_EXECUTE() and DEBUG_EVALUATE_IF() should not execute things based on wildcards. (Allows one to run --debug with mysql-test-run scripts that uses @debug) Fixed several core dump, deadlock and crashed table bugs in handling of LOCK TABLE with MERGE tables: - Added priority of locks to avoid crashes with MERGE tables. - Added thr_lock_merge() to allow one to merge two results of thr_lock(). Fixed 'not found row' bug in REPLACE with Aria tables. Mark MyISAM tables that are part of MERGE with HA_OPEN_MERGE_TABLE and set the locks to have priority THR_LOCK_MERGE_PRIV. - By sorting MERGE tables last in thr_multi_unlock() it's safer to release and relock them many times (can happen when TRIGGERS are created) Avoid printing (null) in debug file (to easier find out wrong NULL pointer usage with %s). client/mysql.cc: Fixed that 'source' command doesn't cause mysql command line tool to exit on error. client/mysqltest.cc: Don't send NULL to fn_format(). (Can cause crash on Solaris when using --debug) dbug/dbug.c: DEBUG_EXECUTE() and DEBUG_EVALUATE_IF() should not execute things based on wildcards. include/my_base.h: Added flag to signal if one opens a MERGE table. Added extra() command to signal that one is not part of a MERGE table anymore. include/thr_lock.h: Added priority for locks (needed to fix bug in thr_lock when using MERGE tables) Added option to thr_unlock() if get_status() should be called. Added prototype for thr_merge_locks(). mysql-test/mysql-test-run.pl: Ignore crashed table warnings for tables named 'crashed'. mysql-test/r/merge.result: Renamed triggers to make debugging easier. Added some CHECK TABLES to catch errors earlier. Additional tests. mysql-test/r/merge_debug.result: Test of error handling when reopening MERGE tables. mysql-test/r/udf_query_cache.result: Added missing flush status mysql-test/suite/parts/r/partition_repair_myisam.result: Update results mysql-test/t/merge.test: Renamed triggers to make debugging easier. Added some CHECK TABLES to catch errors earlier. Additional tests. mysql-test/t/merge_debug.test: Test of error handling when reopening MERGE tables. mysql-test/t/udf_query_cache.test: Added missing flush status mysys/my_getopt.c: Removed not used variable mysys/my_symlink2.c: Changed (null) to (NULL) to make it easier to find NULL arguments to DBUG_PRINT() functions. (On linux, NULL to sprintf is printed 'null') mysys/thr_lock.c: Added priority of locks to avoid crashes with MERGE tables. Added thr_lock_merge() to allow one to merge two results of thr_lock(). - This is needed for MyISAM as all locked table must share the same status. If not, you will not see newly inserted rows in other instances of the table. If calling thr_unlock() with THR_UNLOCK_UPDATE_STATUS, call update_status() and restore_status() for the locks. This is needed in some rare cases where we call thr_unlock() followed by thr_lock() without calling external_unlock/external_lock in between. Simplify loop in thr_multi_lock(). Added 'start_trans', which is called at end of thr_multi_lock() when all locks are taken. - This was needed by Aria to ensure that transaction is started when we got all locks, not at get_status(). Without this, some rows could not be visible when we lock two tables at the same time, causing REPLACE using two tables to fail unexpectedly. sql/handler.cc: Add an assert() in handler::print_error() for "impossible errors" (like table is crashed) when --debug-assert-if-crashed-table is used. sql/lock.cc: Simplify mysql_lock_tables() code if get_lock_data() returns 0 locks. Added new parameter to thr_multi_unlock() In mysql_unlock_read_tables(), call first externa_unlock(), then thr_multi_unlock(); This is same order as we do in mysql_unlock_tables(). Don't abort locks in mysql_lock_abort() for merged tables when a MERGE table is deleted; Would cause a spin lock. Added call to thr_merge_locks() in mysql_lock_merge() to ensure consistency in thr_locks(). - New locks of same type and table is stored after the old lock to ensure that we get the status from the original lock. sql/mysql_priv.h: Added debug_assert_if_crashed_table sql/mysqld.cc: Added --debug-assert-if-crashed-table sql/parse_file.cc: Don't print '(null)' in DBUG_PRINT of no dir given sql/set_var.cc: Increase default size of buffer for @debug variable. sql/sql_base.cc: In case of error from reopen_table() in reopen_tables(), call unlock_open_table() and restart loop. - This fixed bug when we twice deleted same table from open_cache. Don't take name lock for already name locked table in open_unireg_entry(). - Fixed bug when doing repair in reopen_table(). - In detach_merge_children(), always detach if 'clear_refs' is given. We can't trust parent->children_attached as this function can be called twice, first time with clear_refs set to 0. sql/sql_class.cc: Changed printing of (null) to "" in set_thd_proc_info() sql/sql_parse.cc: Added DBUG sql/sql_trigger.cc: Don't call unlink_open_table() if reopen_table() fails as the table may already be freed. storage/maria/ma_bitmap.c: Fixed DBUG_ASSERT() in allocate_tail() storage/maria/ma_blockrec.c: Fixed wrong calculation of row length for very small rows in undo_row_update(). - Fixes ASSERT() when doing undo. storage/maria/ma_blockrec.h: Added _ma_block_start_trans() and _ma_block_start_trans_no_versioning() storage/maria/ma_locking.c: Call _ma_update_status_with_lock() when releasing write locks. - Fixes potential problem with updating status without the proper lock. storage/maria/ma_open.c: Changed to use start_trans() instead of get_status() to ensure that we see all rows in all locked tables when we got the locks. - Fixed 'not found row' bug in REPLACE with Aria tables. storage/maria/ma_state.c: Added _ma_update_status_with_lock() and _ma_block_start_trans(). This is to ensure that we see all rows in all locked tables when we got the locks. storage/maria/ma_state.h: Added _ma_update_status_with_lock() storage/maria/ma_write.c: More DBUG_PRINT storage/myisam/mi_check.c: Fixed error message storage/myisam/mi_extra.c: Added HA_EXTRA_DETACH_CHILD: - Detach MyISAM table to not be part of MERGE table (remove flag & lock priority). storage/myisam/mi_locking.c: Call mi_update_status_with_lock() when releasing write locks. - Fixes potential problem with updating status without the proper lock. Change to use new HA_OPEN_MERGE_TABLE flag to test if MERGE table. Added mi_fix_status(), called by thr_merge(). storage/myisam/mi_open.c: Added marker if part of MERGE table. Call mi_fix_status() in thr_lock() for transactional tables. storage/myisam/myisamdef.h: Change my_once_flag to uint, as it stored different values than just 0/1 Added 'open_flag' to store state given to mi_open() storage/myisammrg/ha_myisammrg.cc: Add THR_LOCK_MERGE_PRIV to THR_LOCK_DATA to get MERGE locks sorted after other types of locks. storage/myisammrg/myrg_locking.c: Remove windows specific code. storage/myisammrg/myrg_open.c: Use HA_OPEN_MERGE_TABLE to mi_open(). Set HA_OPEN_MERGE_TABLE for linked MyISAM tables. storage/xtradb/buf/buf0buf.c: Fixed compiler warning storage/xtradb/buf/buf0lru.c: Initialize variable that could be used not initialized.
530 lines
17 KiB
C
530 lines
17 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 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, dir_length, length, UNINIT_VAR(key_parts), min_keys= 0;
|
|
ulonglong file_offset=0;
|
|
char name_buff[FN_REFLEN*2],buff[FN_REFLEN],*end;
|
|
MYRG_INFO *m_info=0;
|
|
File fd;
|
|
IO_CACHE file;
|
|
MI_INFO *isam=0;
|
|
uint found_merge_insert_method= 0;
|
|
size_t name_buff_length;
|
|
my_bool bad_children= FALSE;
|
|
DBUG_ENTER("myrg_open");
|
|
|
|
bzero((char*) &file,sizeof(file));
|
|
if ((fd=my_open(fn_format(name_buff,name,"",MYRG_NAME_EXT,
|
|
MY_UNPACK_FILENAME|MY_APPEND_EXT),
|
|
O_RDONLY | O_SHARE,MYF(0))) < 0)
|
|
goto err;
|
|
errpos=1;
|
|
if (init_io_cache(&file, fd, 4*IO_SIZE, READ_CACHE, 0, 0,
|
|
MYF(MY_WME | MY_NABP)))
|
|
goto err;
|
|
errpos=2;
|
|
dir_length=dirname_part(name_buff, name, &name_buff_length);
|
|
while ((length=my_b_gets(&file,buff,FN_REFLEN-1)))
|
|
{
|
|
if ((end=buff+length)[-1] == '\n')
|
|
end[-1]='\0';
|
|
if (buff[0] && buff[0] != '#')
|
|
files++;
|
|
}
|
|
|
|
my_b_seek(&file, 0);
|
|
while ((length=my_b_gets(&file,buff,FN_REFLEN-1)))
|
|
{
|
|
if ((end=buff+length)[-1] == '\n')
|
|
*--end='\0';
|
|
if (!buff[0])
|
|
continue; /* Skip empty lines */
|
|
if (buff[0] == '#')
|
|
{
|
|
if (!strncmp(buff+1,"INSERT_METHOD=",14))
|
|
{ /* Lookup insert method */
|
|
int tmp=find_type(buff+15,&merge_insert_method,2);
|
|
found_merge_insert_method = (uint) (tmp >= 0 ? tmp : 0);
|
|
}
|
|
continue; /* Skip comments */
|
|
}
|
|
|
|
if (!has_path(buff))
|
|
{
|
|
VOID(strmake(name_buff+dir_length,buff,
|
|
sizeof(name_buff)-1-dir_length));
|
|
VOID(cleanup_dirname(buff,name_buff));
|
|
}
|
|
else
|
|
fn_format(buff, buff, "", "", 0);
|
|
if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0) |
|
|
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(sizeof(MYRG_INFO) +
|
|
files*sizeof(MYRG_TABLE) +
|
|
key_parts*sizeof(long),
|
|
MYF(MY_WME|MY_ZEROFILL))))
|
|
goto err;
|
|
DBUG_ASSERT(files);
|
|
m_info->open_tables=(MYRG_TABLE *) (m_info+1);
|
|
m_info->rec_per_key_part=(ulong *) (m_info->open_tables+files);
|
|
m_info->tables= files;
|
|
files= 0;
|
|
m_info->reclength=isam->s->base.reclength;
|
|
min_keys= isam->s->base.keys;
|
|
errpos=3;
|
|
}
|
|
m_info->open_tables[files].table= isam;
|
|
m_info->open_tables[files].file_offset=(my_off_t) file_offset;
|
|
file_offset+=isam->state->data_file_length;
|
|
files++;
|
|
if (m_info->reclength != isam->s->base.reclength)
|
|
{
|
|
if (handle_locking & HA_OPEN_FOR_REPAIR)
|
|
{
|
|
myrg_print_wrong_table(buff);
|
|
bad_children= TRUE;
|
|
continue;
|
|
}
|
|
goto bad_children;
|
|
}
|
|
m_info->options|= isam->s->options;
|
|
m_info->records+= isam->state->records;
|
|
m_info->del+= isam->state->del;
|
|
m_info->data_file_length+= isam->state->data_file_length;
|
|
if (min_keys > isam->s->base.keys)
|
|
min_keys= isam->s->base.keys;
|
|
for (i=0; i < key_parts; i++)
|
|
m_info->rec_per_key_part[i]+= (isam->s->state.rec_per_key_part[i] /
|
|
m_info->tables);
|
|
}
|
|
|
|
if (bad_children)
|
|
goto bad_children;
|
|
if (!m_info && !(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO),
|
|
MYF(MY_WME | MY_ZEROFILL))))
|
|
goto err;
|
|
/* Don't mark table readonly, for ALTER TABLE ... UNION=(...) to work */
|
|
m_info->options&= ~(HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA);
|
|
m_info->merge_insert_method= found_merge_insert_method;
|
|
|
|
if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L)
|
|
{
|
|
my_errno=HA_ERR_RECORD_FILE_FULL;
|
|
goto err;
|
|
}
|
|
m_info->keys= min_keys;
|
|
bzero((char*) &m_info->by_key,sizeof(m_info->by_key));
|
|
|
|
/* this works ok if the table list is empty */
|
|
m_info->end_table=m_info->open_tables+files;
|
|
m_info->last_used_table=m_info->open_tables;
|
|
m_info->children_attached= TRUE;
|
|
|
|
VOID(my_close(fd,MYF(0)));
|
|
end_io_cache(&file);
|
|
VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST));
|
|
m_info->open_list.data=(void*) m_info;
|
|
pthread_mutex_lock(&THR_LOCK_open);
|
|
myrg_open_list=list_add(myrg_open_list,&m_info->open_list);
|
|
pthread_mutex_unlock(&THR_LOCK_open);
|
|
DBUG_RETURN(m_info);
|
|
|
|
bad_children:
|
|
my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
|
|
err:
|
|
save_errno=my_errno;
|
|
switch (errpos) {
|
|
case 3:
|
|
while (files)
|
|
(void) mi_close(m_info->open_tables[--files].table);
|
|
my_free((char*) m_info,MYF(0));
|
|
/* Fall through */
|
|
case 2:
|
|
end_io_cache(&file);
|
|
/* Fall through */
|
|
case 1:
|
|
VOID(my_close(fd,MYF(0)));
|
|
}
|
|
my_errno=save_errno;
|
|
DBUG_RETURN (NULL);
|
|
}
|
|
|
|
|
|
/**
|
|
@brief Open parent table of a MyISAM MERGE table.
|
|
|
|
@detail Open MERGE meta file to get the table name paths for the child
|
|
tables. Count the children. Allocate and initialize MYRG_INFO
|
|
structure. Call a callback function for each child table.
|
|
|
|
@param[in] parent_name merge table name path as "database/table"
|
|
@param[in] callback function to call for each child table
|
|
@param[in] callback_param data pointer to give to the callback
|
|
|
|
@return MYRG_INFO pointer
|
|
@retval != NULL OK
|
|
@retval NULL Error
|
|
|
|
@note: Currently there is some code duplication between myrg_open()
|
|
and myrg_parent_open() + myrg_attach_children(). Please duplicate
|
|
changes in these functions or make common sub-functions.
|
|
*/
|
|
|
|
MYRG_INFO *myrg_parent_open(const char *parent_name,
|
|
int (*callback)(void*, const char*),
|
|
void *callback_param)
|
|
{
|
|
MYRG_INFO *m_info;
|
|
int rc;
|
|
int errpos;
|
|
int save_errno;
|
|
int insert_method;
|
|
uint length;
|
|
uint child_count;
|
|
File fd;
|
|
IO_CACHE file_cache;
|
|
char parent_name_buff[FN_REFLEN * 2];
|
|
char child_name_buff[FN_REFLEN];
|
|
DBUG_ENTER("myrg_parent_open");
|
|
|
|
rc= 1;
|
|
errpos= 0;
|
|
bzero((char*) &file_cache, sizeof(file_cache));
|
|
LINT_INIT(m_info);
|
|
|
|
/* Open MERGE meta file. */
|
|
if ((fd= my_open(fn_format(parent_name_buff, parent_name, "", MYRG_NAME_EXT,
|
|
MY_UNPACK_FILENAME|MY_APPEND_EXT),
|
|
O_RDONLY | O_SHARE, MYF(0))) < 0)
|
|
goto err; /* purecov: inspected */
|
|
errpos= 1;
|
|
|
|
if (init_io_cache(&file_cache, fd, 4 * IO_SIZE, READ_CACHE, 0, 0,
|
|
MYF(MY_WME | MY_NABP)))
|
|
goto err; /* purecov: inspected */
|
|
errpos= 2;
|
|
|
|
/* Count children. Determine insert method. */
|
|
child_count= 0;
|
|
insert_method= 0;
|
|
while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1)))
|
|
{
|
|
/* Remove line terminator. */
|
|
if (child_name_buff[length - 1] == '\n')
|
|
child_name_buff[--length]= '\0';
|
|
|
|
/* Skip empty lines. */
|
|
if (!child_name_buff[0])
|
|
continue; /* purecov: inspected */
|
|
|
|
/* Skip comments, but evaluate insert method. */
|
|
if (child_name_buff[0] == '#')
|
|
{
|
|
if (!strncmp(child_name_buff + 1, "INSERT_METHOD=", 14))
|
|
{
|
|
/* Compare buffer with global methods list: merge_insert_method. */
|
|
insert_method= find_type(child_name_buff + 15,
|
|
&merge_insert_method, 2);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Count the child. */
|
|
child_count++;
|
|
}
|
|
|
|
/* Allocate MERGE parent table structure. */
|
|
if (!(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO) +
|
|
child_count * sizeof(MYRG_TABLE),
|
|
MYF(MY_WME | MY_ZEROFILL))))
|
|
goto err; /* purecov: inspected */
|
|
errpos= 3;
|
|
m_info->open_tables= (MYRG_TABLE*) (m_info + 1);
|
|
m_info->tables= child_count;
|
|
m_info->merge_insert_method= insert_method > 0 ? insert_method : 0;
|
|
/* This works even if the table list is empty. */
|
|
m_info->end_table= m_info->open_tables + child_count;
|
|
if (!child_count)
|
|
{
|
|
/* Do not attach/detach an empty child list. */
|
|
m_info->children_attached= TRUE;
|
|
}
|
|
|
|
/* Call callback for each child. */
|
|
my_b_seek(&file_cache, 0);
|
|
while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1)))
|
|
{
|
|
/* Remove line terminator. */
|
|
if (child_name_buff[length - 1] == '\n')
|
|
child_name_buff[--length]= '\0';
|
|
|
|
/* Skip empty lines and comments. */
|
|
if (!child_name_buff[0] || (child_name_buff[0] == '#'))
|
|
continue;
|
|
|
|
DBUG_PRINT("info", ("child: '%s'", child_name_buff));
|
|
|
|
/* Callback registers child with handler table. */
|
|
if ((rc= (*callback)(callback_param, child_name_buff)))
|
|
goto err; /* purecov: inspected */
|
|
}
|
|
|
|
end_io_cache(&file_cache);
|
|
VOID(my_close(fd, MYF(0)));
|
|
VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST));
|
|
|
|
m_info->open_list.data= (void*) m_info;
|
|
pthread_mutex_lock(&THR_LOCK_open);
|
|
myrg_open_list= list_add(myrg_open_list, &m_info->open_list);
|
|
pthread_mutex_unlock(&THR_LOCK_open);
|
|
|
|
DBUG_RETURN(m_info);
|
|
|
|
/* purecov: begin inspected */
|
|
err:
|
|
save_errno= my_errno;
|
|
switch (errpos) {
|
|
case 3:
|
|
my_free((char*) m_info, MYF(0));
|
|
/* Fall through */
|
|
case 2:
|
|
end_io_cache(&file_cache);
|
|
/* Fall through */
|
|
case 1:
|
|
VOID(my_close(fd, MYF(0)));
|
|
}
|
|
my_errno= save_errno;
|
|
DBUG_RETURN (NULL);
|
|
/* purecov: end */
|
|
}
|
|
|
|
|
|
/**
|
|
@brief Attach children to a MyISAM MERGE parent table.
|
|
|
|
@detail Call a callback function for each child table.
|
|
The callback returns the MyISAM table handle of the child table.
|
|
Check table definition match.
|
|
|
|
@param[in] m_info MERGE parent table structure
|
|
@param[in] handle_locking if contains HA_OPEN_FOR_REPAIR, warn about
|
|
incompatible child tables, but continue
|
|
@param[in] callback function to call for each child table
|
|
@param[in] callback_param data pointer to give to the callback
|
|
@param[in] need_compat_check pointer to ha_myisammrg::need_compat_check
|
|
(we need this one to decide if previously
|
|
allocated buffers can be reused).
|
|
|
|
@return status
|
|
@retval 0 OK
|
|
@retval != 0 Error
|
|
|
|
@note: Currently there is some code duplication between myrg_open()
|
|
and myrg_parent_open() + myrg_attach_children(). Please duplicate
|
|
changes in these functions or make common sub-functions.
|
|
*/
|
|
|
|
int myrg_attach_children(MYRG_INFO *m_info, int handle_locking,
|
|
MI_INFO *(*callback)(void*),
|
|
void *callback_param, my_bool *need_compat_check)
|
|
{
|
|
ulonglong file_offset;
|
|
MI_INFO *myisam;
|
|
int errpos;
|
|
int save_errno;
|
|
uint idx;
|
|
uint child_nr;
|
|
uint UNINIT_VAR(key_parts);
|
|
uint min_keys;
|
|
my_bool bad_children= FALSE;
|
|
DBUG_ENTER("myrg_attach_children");
|
|
DBUG_PRINT("myrg", ("handle_locking: %d", handle_locking));
|
|
|
|
/*
|
|
This function can be called while another thread is trying to abort
|
|
locks of this MERGE table. If the processor reorders instructions or
|
|
write to memory, 'children_attached' could be set before
|
|
'open_tables' has all the pointers to the children. Use of a mutex
|
|
here and in ha_myisammrg::store_lock() forces consistent data.
|
|
*/
|
|
pthread_mutex_lock(&m_info->mutex);
|
|
errpos= 0;
|
|
file_offset= 0;
|
|
min_keys= 0;
|
|
child_nr= 0;
|
|
while ((myisam= (*callback)(callback_param)))
|
|
{
|
|
DBUG_PRINT("myrg", ("child_nr: %u table: '%s'",
|
|
child_nr, myisam->filename));
|
|
DBUG_ASSERT(child_nr < m_info->tables);
|
|
|
|
/* Special handling when the first child is attached. */
|
|
if (!child_nr)
|
|
{
|
|
m_info->reclength= myisam->s->base.reclength;
|
|
min_keys= myisam->s->base.keys;
|
|
key_parts= myisam->s->base.key_parts;
|
|
if (*need_compat_check && m_info->rec_per_key_part)
|
|
{
|
|
my_free((char *) m_info->rec_per_key_part, MYF(0));
|
|
m_info->rec_per_key_part= NULL;
|
|
}
|
|
if (!m_info->rec_per_key_part)
|
|
{
|
|
if(!(m_info->rec_per_key_part= (ulong*)
|
|
my_malloc(key_parts * sizeof(long), MYF(MY_WME))))
|
|
goto err; /* purecov: inspected */
|
|
errpos= 1;
|
|
}
|
|
bzero((char*) m_info->rec_per_key_part, key_parts * sizeof(long));
|
|
}
|
|
|
|
/* Add MyISAM table info. */
|
|
m_info->open_tables[child_nr].table= myisam;
|
|
m_info->open_tables[child_nr].file_offset= (my_off_t) file_offset;
|
|
file_offset+= myisam->state->data_file_length;
|
|
/* Mark as MERGE table */
|
|
myisam->open_flag|= HA_OPEN_MERGE_TABLE;
|
|
|
|
/* Check table definition match. */
|
|
if (m_info->reclength != myisam->s->base.reclength)
|
|
{
|
|
DBUG_PRINT("error", ("definition mismatch table: '%s' repair: %d",
|
|
myisam->filename,
|
|
(handle_locking & HA_OPEN_FOR_REPAIR)));
|
|
if (handle_locking & HA_OPEN_FOR_REPAIR)
|
|
{
|
|
myrg_print_wrong_table(myisam->filename);
|
|
bad_children= TRUE;
|
|
continue;
|
|
}
|
|
goto bad_children;
|
|
}
|
|
|
|
m_info->options|= myisam->s->options;
|
|
m_info->records+= myisam->state->records;
|
|
m_info->del+= myisam->state->del;
|
|
m_info->data_file_length+= myisam->state->data_file_length;
|
|
if (min_keys > myisam->s->base.keys)
|
|
min_keys= myisam->s->base.keys; /* purecov: inspected */
|
|
for (idx= 0; idx < key_parts; idx++)
|
|
m_info->rec_per_key_part[idx]+= (myisam->s->state.rec_per_key_part[idx] /
|
|
m_info->tables);
|
|
child_nr++;
|
|
}
|
|
|
|
if (bad_children)
|
|
goto bad_children;
|
|
/* Note: callback() resets my_errno, so it is safe to check it here */
|
|
if (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF)
|
|
goto err;
|
|
if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L)
|
|
{
|
|
my_errno= HA_ERR_RECORD_FILE_FULL;
|
|
goto err;
|
|
}
|
|
/* Don't mark table readonly, for ALTER TABLE ... UNION=(...) to work */
|
|
m_info->options&= ~(HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA);
|
|
m_info->keys= min_keys;
|
|
m_info->last_used_table= m_info->open_tables;
|
|
m_info->children_attached= TRUE;
|
|
pthread_mutex_unlock(&m_info->mutex);
|
|
DBUG_RETURN(0);
|
|
|
|
bad_children:
|
|
my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
|
|
err:
|
|
save_errno= my_errno;
|
|
switch (errpos) {
|
|
case 1:
|
|
my_free((char*) m_info->rec_per_key_part, MYF(0));
|
|
m_info->rec_per_key_part= NULL;
|
|
}
|
|
pthread_mutex_unlock(&m_info->mutex);
|
|
my_errno= save_errno;
|
|
DBUG_RETURN(1);
|
|
}
|
|
|
|
|
|
/**
|
|
@brief Detach children from a MyISAM MERGE parent table.
|
|
|
|
@param[in] m_info MERGE parent table structure
|
|
|
|
@note Detach must not touch the children in any way.
|
|
They may have been closed at ths point already.
|
|
All references to the children should be removed.
|
|
|
|
@return status
|
|
@retval 0 OK
|
|
*/
|
|
|
|
int myrg_detach_children(MYRG_INFO *m_info)
|
|
{
|
|
DBUG_ENTER("myrg_detach_children");
|
|
/* For symmetry with myrg_attach_children() we use the mutex here. */
|
|
pthread_mutex_lock(&m_info->mutex);
|
|
if (m_info->tables)
|
|
{
|
|
/* Do not attach/detach an empty child list. */
|
|
m_info->children_attached= FALSE;
|
|
bzero((char*) m_info->open_tables, m_info->tables * sizeof(MYRG_TABLE));
|
|
}
|
|
m_info->records= 0;
|
|
m_info->del= 0;
|
|
m_info->data_file_length= 0;
|
|
m_info->options= 0;
|
|
pthread_mutex_unlock(&m_info->mutex);
|
|
DBUG_RETURN(0);
|
|
}
|
|
|