mirror of
https://github.com/MariaDB/server.git
synced 2025-01-31 02:51:44 +01:00
20acfbf30d
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.
1397 lines
44 KiB
C
1397 lines
44 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 isam-database */
|
|
|
|
#include "fulltext.h"
|
|
#include "sp_defs.h"
|
|
#include "rt_index.h"
|
|
#include <m_ctype.h>
|
|
#include <mysql_version.h>
|
|
|
|
#if defined(MSDOS) || defined(__WIN__)
|
|
#ifdef __WIN__
|
|
#include <fcntl.h>
|
|
#else
|
|
#include <process.h> /* Prototype for getpid */
|
|
#endif
|
|
#endif
|
|
#ifdef VMS
|
|
#include "static.c"
|
|
#endif
|
|
|
|
static void setup_key_functions(MI_KEYDEF *keyinfo);
|
|
#define get_next_element(to,pos,size) { memcpy((char*) to,pos,(size_t) size); \
|
|
pos+=size;}
|
|
|
|
|
|
#define disk_pos_assert(pos, end_pos) \
|
|
if (pos > end_pos) \
|
|
{ \
|
|
my_errno=HA_ERR_CRASHED; \
|
|
goto err; \
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
** Return the shared struct if the table is already open.
|
|
** In MySQL the server will handle version issues.
|
|
******************************************************************************/
|
|
|
|
MI_INFO *test_if_reopen(char *filename)
|
|
{
|
|
LIST *pos;
|
|
|
|
for (pos=myisam_open_list ; pos ; pos=pos->next)
|
|
{
|
|
MI_INFO *info=(MI_INFO*) pos->data;
|
|
MYISAM_SHARE *share=info->s;
|
|
DBUG_ASSERT(strcmp(share->unique_file_name,filename) ||
|
|
share->last_version);
|
|
if (!strcmp(share->unique_file_name,filename) && share->last_version)
|
|
return info;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
open a MyISAM database.
|
|
See my_base.h for the handle_locking argument
|
|
if handle_locking and HA_OPEN_ABORT_IF_CRASHED then abort if the table
|
|
is marked crashed or if we are not using locking and the table doesn't
|
|
have an open count of 0.
|
|
******************************************************************************/
|
|
|
|
MI_INFO *mi_open(const char *name, int mode, uint open_flags)
|
|
{
|
|
int lock_error,kfile,open_mode,save_errno,have_rtree=0, realpath_err;
|
|
uint i,j,len,errpos,head_length,base_pos,offset,info_length,keys,
|
|
key_parts,unique_key_parts,fulltext_keys,uniques;
|
|
char name_buff[FN_REFLEN], org_name[FN_REFLEN], index_name[FN_REFLEN],
|
|
data_name[FN_REFLEN];
|
|
uchar *disk_cache, *disk_pos, *end_pos;
|
|
MI_INFO info,*m_info,*old_info;
|
|
MYISAM_SHARE share_buff,*share;
|
|
ulong rec_per_key_part[HA_MAX_POSSIBLE_KEY*HA_MAX_KEY_SEG];
|
|
my_off_t key_root[HA_MAX_POSSIBLE_KEY],key_del[MI_MAX_KEY_BLOCK_SIZE];
|
|
ulonglong max_key_file_length, max_data_file_length;
|
|
DBUG_ENTER("mi_open");
|
|
|
|
LINT_INIT(m_info);
|
|
kfile= -1;
|
|
lock_error=1;
|
|
errpos=0;
|
|
head_length=sizeof(share_buff.state.header);
|
|
bzero((uchar*) &info,sizeof(info));
|
|
|
|
realpath_err= my_realpath(name_buff,
|
|
fn_format(org_name,name,"",MI_NAME_IEXT,4),MYF(0));
|
|
if (my_is_symlink(org_name) &&
|
|
(realpath_err || (*myisam_test_invalid_symlink)(name_buff)))
|
|
{
|
|
my_errno= HA_WRONG_CREATE_OPTION;
|
|
DBUG_RETURN (NULL);
|
|
}
|
|
|
|
pthread_mutex_lock(&THR_LOCK_myisam);
|
|
if (!(old_info=test_if_reopen(name_buff)))
|
|
{
|
|
share= &share_buff;
|
|
bzero((uchar*) &share_buff,sizeof(share_buff));
|
|
share_buff.state.rec_per_key_part=rec_per_key_part;
|
|
share_buff.state.key_root=key_root;
|
|
share_buff.state.key_del=key_del;
|
|
share_buff.key_cache= multi_key_cache_search((uchar*) name_buff,
|
|
strlen(name_buff),
|
|
dflt_key_cache);
|
|
|
|
DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_open",
|
|
if (strstr(name, "/crashed"))
|
|
{
|
|
my_errno= HA_ERR_CRASHED;
|
|
goto err;
|
|
});
|
|
if ((kfile=my_open(name_buff,(open_mode=O_RDWR) | O_SHARE,MYF(0))) < 0)
|
|
{
|
|
if ((errno != EROFS && errno != EACCES) ||
|
|
mode != O_RDONLY ||
|
|
(kfile=my_open(name_buff,(open_mode=O_RDONLY) | O_SHARE,MYF(0))) < 0)
|
|
goto err;
|
|
}
|
|
share->mode=open_mode;
|
|
errpos=1;
|
|
if (my_read(kfile, share->state.header.file_version, head_length,
|
|
MYF(MY_NABP)))
|
|
{
|
|
my_errno= HA_ERR_NOT_A_TABLE;
|
|
goto err;
|
|
}
|
|
if (bcmp(share->state.header.file_version, myisam_file_magic, 4))
|
|
{
|
|
DBUG_PRINT("error",("Wrong header in %s",name_buff));
|
|
DBUG_DUMP("error_dump", share->state.header.file_version,
|
|
(size_t) head_length);
|
|
my_errno=HA_ERR_NOT_A_TABLE;
|
|
goto err;
|
|
}
|
|
share->options= mi_uint2korr(share->state.header.options);
|
|
if (share->options &
|
|
~(HA_OPTION_PACK_RECORD | HA_OPTION_PACK_KEYS |
|
|
HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA |
|
|
HA_OPTION_TEMP_COMPRESS_RECORD | HA_OPTION_CHECKSUM |
|
|
HA_OPTION_TMP_TABLE | HA_OPTION_DELAY_KEY_WRITE |
|
|
HA_OPTION_RELIES_ON_SQL_LAYER | HA_OPTION_NULL_FIELDS))
|
|
{
|
|
DBUG_PRINT("error",("wrong options: 0x%lx", share->options));
|
|
my_errno=HA_ERR_OLD_FILE;
|
|
goto err;
|
|
}
|
|
if ((share->options & HA_OPTION_RELIES_ON_SQL_LAYER) &&
|
|
! (open_flags & HA_OPEN_FROM_SQL_LAYER))
|
|
{
|
|
DBUG_PRINT("error", ("table cannot be openned from non-sql layer"));
|
|
my_errno= HA_ERR_UNSUPPORTED;
|
|
goto err;
|
|
}
|
|
/* Don't call realpath() if the name can't be a link */
|
|
if (!strcmp(name_buff, org_name) ||
|
|
my_readlink(index_name, org_name, MYF(0)) == -1)
|
|
(void) strmov(index_name, org_name);
|
|
*strrchr(org_name, '.')= '\0';
|
|
(void) fn_format(data_name,org_name,"",MI_NAME_DEXT,
|
|
MY_APPEND_EXT|MY_UNPACK_FILENAME|MY_RESOLVE_SYMLINKS);
|
|
|
|
info_length=mi_uint2korr(share->state.header.header_length);
|
|
base_pos=mi_uint2korr(share->state.header.base_pos);
|
|
if (!(disk_cache= (uchar*) my_alloca(info_length+128)))
|
|
{
|
|
my_errno=ENOMEM;
|
|
goto err;
|
|
}
|
|
end_pos=disk_cache+info_length;
|
|
errpos=2;
|
|
|
|
VOID(my_seek(kfile,0L,MY_SEEK_SET,MYF(0)));
|
|
if (!(open_flags & HA_OPEN_TMP_TABLE))
|
|
{
|
|
if ((lock_error=my_lock(kfile,F_RDLCK,0L,F_TO_EOF,
|
|
MYF(open_flags & HA_OPEN_WAIT_IF_LOCKED ?
|
|
0 : MY_SHORT_WAIT))) &&
|
|
!(open_flags & HA_OPEN_IGNORE_IF_LOCKED))
|
|
goto err;
|
|
}
|
|
errpos=3;
|
|
if (my_read(kfile,disk_cache,info_length,MYF(MY_NABP)))
|
|
{
|
|
my_errno=HA_ERR_CRASHED;
|
|
goto err;
|
|
}
|
|
len=mi_uint2korr(share->state.header.state_info_length);
|
|
keys= (uint) share->state.header.keys;
|
|
uniques= (uint) share->state.header.uniques;
|
|
fulltext_keys= (uint) share->state.header.fulltext_keys;
|
|
key_parts= mi_uint2korr(share->state.header.key_parts);
|
|
unique_key_parts= mi_uint2korr(share->state.header.unique_key_parts);
|
|
if (len != MI_STATE_INFO_SIZE)
|
|
{
|
|
DBUG_PRINT("warning",
|
|
("saved_state_info_length: %d state_info_length: %d",
|
|
len,MI_STATE_INFO_SIZE));
|
|
}
|
|
share->state_diff_length=len-MI_STATE_INFO_SIZE;
|
|
|
|
mi_state_info_read(disk_cache, &share->state);
|
|
len= mi_uint2korr(share->state.header.base_info_length);
|
|
if (len != MI_BASE_INFO_SIZE)
|
|
{
|
|
DBUG_PRINT("warning",("saved_base_info_length: %d base_info_length: %d",
|
|
len,MI_BASE_INFO_SIZE));
|
|
}
|
|
disk_pos= mi_n_base_info_read(disk_cache + base_pos, &share->base);
|
|
share->state.state_length=base_pos;
|
|
|
|
if (!(open_flags & HA_OPEN_FOR_REPAIR) &&
|
|
((share->state.changed & STATE_CRASHED) ||
|
|
((open_flags & HA_OPEN_ABORT_IF_CRASHED) &&
|
|
(my_disable_locking && share->state.open_count))))
|
|
{
|
|
DBUG_PRINT("error",("Table is marked as crashed. open_flags: %u "
|
|
"changed: %u open_count: %u !locking: %d",
|
|
open_flags, share->state.changed,
|
|
share->state.open_count, my_disable_locking));
|
|
my_errno=((share->state.changed & STATE_CRASHED_ON_REPAIR) ?
|
|
HA_ERR_CRASHED_ON_REPAIR : HA_ERR_CRASHED_ON_USAGE);
|
|
goto err;
|
|
}
|
|
|
|
/* sanity check */
|
|
if (share->base.keystart > 65535 || share->base.rec_reflength > 8)
|
|
{
|
|
my_errno=HA_ERR_CRASHED;
|
|
goto err;
|
|
}
|
|
|
|
key_parts+=fulltext_keys*FT_SEGS;
|
|
if (share->base.max_key_length > HA_MAX_KEY_BUFF || keys > MI_MAX_KEY ||
|
|
key_parts > MI_MAX_KEY * HA_MAX_KEY_SEG)
|
|
{
|
|
DBUG_PRINT("error",("Wrong key info: Max_key_length: %d keys: %d key_parts: %d", share->base.max_key_length, keys, key_parts));
|
|
my_errno=HA_ERR_UNSUPPORTED;
|
|
goto err;
|
|
}
|
|
|
|
/* Correct max_file_length based on length of sizeof(off_t) */
|
|
max_data_file_length=
|
|
(share->options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ?
|
|
(((ulonglong) 1 << (share->base.rec_reflength*8))-1) :
|
|
(mi_safe_mul(share->base.pack_reclength,
|
|
(ulonglong) 1 << (share->base.rec_reflength*8))-1);
|
|
max_key_file_length=
|
|
mi_safe_mul(MI_MIN_KEY_BLOCK_LENGTH,
|
|
((ulonglong) 1 << (share->base.key_reflength*8))-1);
|
|
#if SIZEOF_OFF_T == 4
|
|
set_if_smaller(max_data_file_length, INT_MAX32);
|
|
set_if_smaller(max_key_file_length, INT_MAX32);
|
|
#endif
|
|
#if USE_RAID && SYSTEM_SIZEOF_OFF_T == 4
|
|
set_if_smaller(max_key_file_length, INT_MAX32);
|
|
if (!share->base.raid_type)
|
|
{
|
|
set_if_smaller(max_data_file_length, INT_MAX32);
|
|
}
|
|
else
|
|
{
|
|
set_if_smaller(max_data_file_length,
|
|
(ulonglong) share->base.raid_chunks << 31);
|
|
}
|
|
#elif !defined(USE_RAID)
|
|
if (share->base.raid_type)
|
|
{
|
|
DBUG_PRINT("error",("Table uses RAID but we don't have RAID support"));
|
|
my_errno=HA_ERR_UNSUPPORTED;
|
|
goto err;
|
|
}
|
|
#endif
|
|
share->base.max_data_file_length=(my_off_t) max_data_file_length;
|
|
share->base.max_key_file_length=(my_off_t) max_key_file_length;
|
|
|
|
if (share->options & HA_OPTION_COMPRESS_RECORD)
|
|
share->base.max_key_length+=2; /* For safety */
|
|
|
|
/* Add space for node pointer */
|
|
share->base.max_key_length+= share->base.key_reflength;
|
|
|
|
if (!my_multi_malloc(MY_WME,
|
|
&share,sizeof(*share),
|
|
&share->state.rec_per_key_part,sizeof(long)*key_parts,
|
|
&share->keyinfo,keys*sizeof(MI_KEYDEF),
|
|
&share->uniqueinfo,uniques*sizeof(MI_UNIQUEDEF),
|
|
&share->keyparts,
|
|
(key_parts+unique_key_parts+keys+uniques) *
|
|
sizeof(HA_KEYSEG),
|
|
&share->rec,
|
|
(share->base.fields+1)*sizeof(MI_COLUMNDEF),
|
|
&share->blobs,sizeof(MI_BLOB)*share->base.blobs,
|
|
&share->unique_file_name,strlen(name_buff)+1,
|
|
&share->index_file_name,strlen(index_name)+1,
|
|
&share->data_file_name,strlen(data_name)+1,
|
|
&share->state.key_root,keys*sizeof(my_off_t),
|
|
&share->state.key_del,
|
|
(share->state.header.max_block_size_index*sizeof(my_off_t)),
|
|
#ifdef THREAD
|
|
&share->key_root_lock,sizeof(rw_lock_t)*keys,
|
|
#endif
|
|
&share->mmap_lock,sizeof(rw_lock_t),
|
|
NullS))
|
|
goto err;
|
|
errpos=4;
|
|
*share=share_buff;
|
|
memcpy((char*) share->state.rec_per_key_part,
|
|
(char*) rec_per_key_part, sizeof(long)*key_parts);
|
|
memcpy((char*) share->state.key_root,
|
|
(char*) key_root, sizeof(my_off_t)*keys);
|
|
memcpy((char*) share->state.key_del,
|
|
(char*) key_del, (sizeof(my_off_t) *
|
|
share->state.header.max_block_size_index));
|
|
strmov(share->unique_file_name, name_buff);
|
|
share->unique_name_length= strlen(name_buff);
|
|
strmov(share->index_file_name, index_name);
|
|
strmov(share->data_file_name, data_name);
|
|
|
|
share->blocksize=min(IO_SIZE,myisam_block_size);
|
|
{
|
|
HA_KEYSEG *pos=share->keyparts;
|
|
uint32 ftkey_nr= 1;
|
|
for (i=0 ; i < keys ; i++)
|
|
{
|
|
share->keyinfo[i].share= share;
|
|
disk_pos=mi_keydef_read(disk_pos, &share->keyinfo[i]);
|
|
disk_pos_assert(disk_pos + share->keyinfo[i].keysegs * HA_KEYSEG_SIZE,
|
|
end_pos);
|
|
if (share->keyinfo[i].key_alg == HA_KEY_ALG_RTREE)
|
|
have_rtree=1;
|
|
set_if_smaller(share->blocksize,share->keyinfo[i].block_length);
|
|
share->keyinfo[i].seg=pos;
|
|
for (j=0 ; j < share->keyinfo[i].keysegs; j++,pos++)
|
|
{
|
|
disk_pos=mi_keyseg_read(disk_pos, pos);
|
|
if (pos->flag & HA_BLOB_PART &&
|
|
! (share->options & (HA_OPTION_COMPRESS_RECORD |
|
|
HA_OPTION_PACK_RECORD)))
|
|
{
|
|
my_errno= HA_ERR_CRASHED;
|
|
goto err;
|
|
}
|
|
if (pos->type == HA_KEYTYPE_TEXT ||
|
|
pos->type == HA_KEYTYPE_VARTEXT1 ||
|
|
pos->type == HA_KEYTYPE_VARTEXT2)
|
|
{
|
|
if (!pos->language)
|
|
pos->charset=default_charset_info;
|
|
else if (!(pos->charset= get_charset(pos->language, MYF(MY_WME))))
|
|
{
|
|
my_errno=HA_ERR_UNKNOWN_CHARSET;
|
|
goto err;
|
|
}
|
|
}
|
|
else if (pos->type == HA_KEYTYPE_BINARY)
|
|
pos->charset= &my_charset_bin;
|
|
}
|
|
if (share->keyinfo[i].flag & HA_SPATIAL)
|
|
{
|
|
#ifdef HAVE_SPATIAL
|
|
uint sp_segs=SPDIMS*2;
|
|
share->keyinfo[i].seg=pos-sp_segs;
|
|
share->keyinfo[i].keysegs--;
|
|
#else
|
|
my_errno=HA_ERR_UNSUPPORTED;
|
|
goto err;
|
|
#endif
|
|
}
|
|
else if (share->keyinfo[i].flag & HA_FULLTEXT)
|
|
{
|
|
if (!fulltext_keys)
|
|
{ /* 4.0 compatibility code, to be removed in 5.0 */
|
|
share->keyinfo[i].seg=pos-FT_SEGS;
|
|
share->keyinfo[i].keysegs-=FT_SEGS;
|
|
}
|
|
else
|
|
{
|
|
uint k;
|
|
share->keyinfo[i].seg=pos;
|
|
for (k=0; k < FT_SEGS; k++)
|
|
{
|
|
*pos= ft_keysegs[k];
|
|
pos[0].language= pos[-1].language;
|
|
if (!(pos[0].charset= pos[-1].charset))
|
|
{
|
|
my_errno=HA_ERR_CRASHED;
|
|
goto err;
|
|
}
|
|
pos++;
|
|
}
|
|
}
|
|
if (!share->ft2_keyinfo.seg)
|
|
{
|
|
memcpy(& share->ft2_keyinfo, & share->keyinfo[i], sizeof(MI_KEYDEF));
|
|
share->ft2_keyinfo.keysegs=1;
|
|
share->ft2_keyinfo.flag=0;
|
|
share->ft2_keyinfo.keylength=
|
|
share->ft2_keyinfo.minlength=
|
|
share->ft2_keyinfo.maxlength=HA_FT_WLEN+share->base.rec_reflength;
|
|
share->ft2_keyinfo.seg=pos-1;
|
|
share->ft2_keyinfo.end=pos;
|
|
setup_key_functions(& share->ft2_keyinfo);
|
|
}
|
|
share->keyinfo[i].ftkey_nr= ftkey_nr++;
|
|
}
|
|
setup_key_functions(share->keyinfo+i);
|
|
share->keyinfo[i].end=pos;
|
|
pos->type=HA_KEYTYPE_END; /* End */
|
|
pos->length=share->base.rec_reflength;
|
|
pos->null_bit=0;
|
|
pos->flag=0; /* For purify */
|
|
pos++;
|
|
}
|
|
|
|
for (i=0 ; i < uniques ; i++)
|
|
{
|
|
disk_pos=mi_uniquedef_read(disk_pos, &share->uniqueinfo[i]);
|
|
disk_pos_assert(disk_pos + share->uniqueinfo[i].keysegs *
|
|
HA_KEYSEG_SIZE, end_pos);
|
|
share->uniqueinfo[i].seg=pos;
|
|
for (j=0 ; j < share->uniqueinfo[i].keysegs; j++,pos++)
|
|
{
|
|
disk_pos=mi_keyseg_read(disk_pos, pos);
|
|
if (pos->type == HA_KEYTYPE_TEXT ||
|
|
pos->type == HA_KEYTYPE_VARTEXT1 ||
|
|
pos->type == HA_KEYTYPE_VARTEXT2)
|
|
{
|
|
if (!pos->language)
|
|
pos->charset=default_charset_info;
|
|
else if (!(pos->charset= get_charset(pos->language, MYF(MY_WME))))
|
|
{
|
|
my_errno=HA_ERR_UNKNOWN_CHARSET;
|
|
goto err;
|
|
}
|
|
}
|
|
}
|
|
share->uniqueinfo[i].end=pos;
|
|
pos->type=HA_KEYTYPE_END; /* End */
|
|
pos->null_bit=0;
|
|
pos->flag=0;
|
|
pos++;
|
|
}
|
|
share->ftkeys= ftkey_nr;
|
|
}
|
|
|
|
disk_pos_assert(disk_pos + share->base.fields *MI_COLUMNDEF_SIZE, end_pos);
|
|
for (i=j=offset=0 ; i < share->base.fields ; i++)
|
|
{
|
|
disk_pos=mi_recinfo_read(disk_pos,&share->rec[i]);
|
|
share->rec[i].pack_type=0;
|
|
share->rec[i].huff_tree=0;
|
|
share->rec[i].offset=offset;
|
|
if (share->rec[i].type == FIELD_BLOB)
|
|
{
|
|
share->blobs[j].pack_length=
|
|
share->rec[i].length - portable_sizeof_char_ptr;
|
|
share->blobs[j].offset=offset;
|
|
j++;
|
|
}
|
|
#if MYSQL_VERSION_ID <= 60100
|
|
/* This is to detect old checksum option */
|
|
if (share->rec[i].null_bit)
|
|
share->has_null_fields= 1;
|
|
if (share->rec[i].type == FIELD_VARCHAR)
|
|
share->has_varchar_fields= 1;
|
|
#endif
|
|
offset+=share->rec[i].length;
|
|
}
|
|
share->rec[i].type=(int) FIELD_LAST; /* End marker */
|
|
if (offset > share->base.reclength)
|
|
{
|
|
/* purecov: begin inspected */
|
|
my_errno= HA_ERR_CRASHED;
|
|
goto err;
|
|
/* purecov: end */
|
|
}
|
|
|
|
if (! lock_error)
|
|
{
|
|
VOID(my_lock(kfile,F_UNLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE)));
|
|
lock_error=1; /* Database unlocked */
|
|
}
|
|
|
|
if (mi_open_datafile(&info, share, name, -1))
|
|
goto err;
|
|
errpos=5;
|
|
|
|
share->kfile=kfile;
|
|
share->this_process=(ulong) getpid();
|
|
share->last_process= share->state.process;
|
|
share->base.key_parts=key_parts;
|
|
share->base.all_key_parts=key_parts+unique_key_parts;
|
|
if (!(share->last_version=share->state.version))
|
|
share->last_version=1; /* Safety */
|
|
share->rec_reflength=share->base.rec_reflength; /* May be changed */
|
|
share->base.margin_key_file_length=(share->base.max_key_file_length -
|
|
(keys ? MI_INDEX_BLOCK_MARGIN *
|
|
share->blocksize * keys : 0));
|
|
share->blocksize=min(IO_SIZE,myisam_block_size);
|
|
share->data_file_type=STATIC_RECORD;
|
|
if (share->options & HA_OPTION_COMPRESS_RECORD)
|
|
{
|
|
share->data_file_type = COMPRESSED_RECORD;
|
|
share->options|= HA_OPTION_READ_ONLY_DATA;
|
|
info.s=share;
|
|
if (_mi_read_pack_info(&info,
|
|
(pbool)
|
|
test(!(share->options &
|
|
(HA_OPTION_PACK_RECORD |
|
|
HA_OPTION_TEMP_COMPRESS_RECORD)))))
|
|
goto err;
|
|
}
|
|
else if (share->options & HA_OPTION_PACK_RECORD)
|
|
share->data_file_type = DYNAMIC_RECORD;
|
|
my_afree(disk_cache);
|
|
mi_setup_functions(share);
|
|
share->is_log_table= FALSE;
|
|
#ifdef THREAD
|
|
thr_lock_init(&share->lock);
|
|
VOID(pthread_mutex_init(&share->intern_lock,MY_MUTEX_INIT_FAST));
|
|
for (i=0; i<keys; i++)
|
|
VOID(my_rwlock_init(&share->key_root_lock[i], NULL));
|
|
VOID(my_rwlock_init(&share->mmap_lock, NULL));
|
|
if (!thr_lock_inited)
|
|
{
|
|
/* Probably a single threaded program; Don't use concurrent inserts */
|
|
myisam_concurrent_insert=0;
|
|
}
|
|
else if (myisam_concurrent_insert)
|
|
{
|
|
share->concurrent_insert=
|
|
((share->options & (HA_OPTION_READ_ONLY_DATA | HA_OPTION_TMP_TABLE |
|
|
HA_OPTION_COMPRESS_RECORD |
|
|
HA_OPTION_TEMP_COMPRESS_RECORD)) ||
|
|
(open_flags & HA_OPEN_TMP_TABLE) ||
|
|
have_rtree) ? 0 : 1;
|
|
if (share->concurrent_insert)
|
|
{
|
|
share->lock.get_status=mi_get_status;
|
|
share->lock.copy_status=mi_copy_status;
|
|
share->lock.update_status=mi_update_status;
|
|
share->lock.restore_status= mi_restore_status;
|
|
share->lock.check_status=mi_check_status;
|
|
share->lock.fix_status= (void (*)(void *, void *)) mi_fix_status;
|
|
}
|
|
}
|
|
#endif
|
|
/*
|
|
Memory mapping can only be requested after initializing intern_lock.
|
|
*/
|
|
if (open_flags & HA_OPEN_MMAP)
|
|
{
|
|
info.s= share;
|
|
mi_extra(&info, HA_EXTRA_MMAP, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
share= old_info->s;
|
|
if (mode == O_RDWR && share->mode == O_RDONLY)
|
|
{
|
|
my_errno=EACCES; /* Can't open in write mode */
|
|
goto err;
|
|
}
|
|
if (mi_open_datafile(&info, share, name, old_info->dfile))
|
|
goto err;
|
|
errpos=5;
|
|
have_rtree= old_info->rtree_recursion_state != NULL;
|
|
}
|
|
|
|
/* alloc and set up private structure parts */
|
|
if (!my_multi_malloc(MY_WME,
|
|
&m_info,sizeof(MI_INFO),
|
|
&info.blobs,sizeof(MI_BLOB)*share->base.blobs,
|
|
&info.buff,(share->base.max_key_block_length*2+
|
|
share->base.max_key_length),
|
|
&info.lastkey,share->base.max_key_length*3+1,
|
|
&info.first_mbr_key, share->base.max_key_length,
|
|
&info.filename,strlen(name)+1,
|
|
&info.rtree_recursion_state,have_rtree ? 1024 : 0,
|
|
NullS))
|
|
goto err;
|
|
errpos=6;
|
|
|
|
if (!have_rtree)
|
|
info.rtree_recursion_state= NULL;
|
|
|
|
strmov(info.filename,name);
|
|
memcpy(info.blobs,share->blobs,sizeof(MI_BLOB)*share->base.blobs);
|
|
info.lastkey2=info.lastkey+share->base.max_key_length;
|
|
|
|
info.s=share;
|
|
info.lastpos= HA_OFFSET_ERROR;
|
|
info.update= (short) (HA_STATE_NEXT_FOUND+HA_STATE_PREV_FOUND);
|
|
info.open_flag= open_flags;
|
|
info.opt_flag=READ_CHECK_USED;
|
|
info.this_unique= (ulong) info.dfile; /* Uniq number in process */
|
|
if (share->data_file_type == COMPRESSED_RECORD)
|
|
info.this_unique= share->state.unique;
|
|
info.this_loop=0; /* Update counter */
|
|
info.last_unique= share->state.unique;
|
|
info.last_loop= share->state.update_count;
|
|
if (mode == O_RDONLY)
|
|
share->options|=HA_OPTION_READ_ONLY_DATA;
|
|
info.lock_type=F_UNLCK;
|
|
info.quick_mode=0;
|
|
info.bulk_insert=0;
|
|
info.ft1_to_ft2=0;
|
|
info.errkey= -1;
|
|
info.page_changed=1;
|
|
pthread_mutex_lock(&share->intern_lock);
|
|
info.read_record=share->read_record;
|
|
share->reopen++;
|
|
share->write_flag=MYF(MY_NABP | MY_WAIT_IF_FULL);
|
|
if (share->options & HA_OPTION_READ_ONLY_DATA)
|
|
{
|
|
info.lock_type=F_RDLCK;
|
|
share->r_locks++;
|
|
share->tot_locks++;
|
|
}
|
|
if ((open_flags & HA_OPEN_TMP_TABLE) ||
|
|
(share->options & HA_OPTION_TMP_TABLE))
|
|
{
|
|
share->temporary=share->delay_key_write=1;
|
|
share->write_flag=MYF(MY_NABP);
|
|
share->w_locks++; /* We don't have to update status */
|
|
share->tot_locks++;
|
|
info.lock_type=F_WRLCK;
|
|
}
|
|
if (((open_flags & HA_OPEN_DELAY_KEY_WRITE) ||
|
|
(share->options & HA_OPTION_DELAY_KEY_WRITE)) &&
|
|
myisam_delay_key_write)
|
|
share->delay_key_write=1;
|
|
info.state= &share->state.state; /* Change global values by default */
|
|
pthread_mutex_unlock(&share->intern_lock);
|
|
|
|
/* Allocate buffer for one record */
|
|
|
|
/* prerequisites: bzero(info) && info->s=share; are met. */
|
|
if (!mi_alloc_rec_buff(&info, -1, &info.rec_buff))
|
|
goto err;
|
|
bzero(info.rec_buff, mi_get_rec_buff_len(&info, info.rec_buff));
|
|
|
|
*m_info=info;
|
|
#ifdef THREAD
|
|
thr_lock_data_init(&share->lock,&m_info->lock,(void*) m_info);
|
|
#endif
|
|
m_info->open_list.data=(void*) m_info;
|
|
myisam_open_list=list_add(myisam_open_list,&m_info->open_list);
|
|
|
|
pthread_mutex_unlock(&THR_LOCK_myisam);
|
|
|
|
bzero(info.buff, share->base.max_key_block_length * 2);
|
|
|
|
if (myisam_log_file >= 0)
|
|
{
|
|
intern_filename(name_buff,share->index_file_name);
|
|
_myisam_log(MI_LOG_OPEN, m_info, (uchar*) name_buff, strlen(name_buff));
|
|
}
|
|
DBUG_RETURN(m_info);
|
|
|
|
err:
|
|
save_errno=my_errno ? my_errno : HA_ERR_END_OF_FILE;
|
|
if ((save_errno == HA_ERR_CRASHED) ||
|
|
(save_errno == HA_ERR_CRASHED_ON_USAGE) ||
|
|
(save_errno == HA_ERR_CRASHED_ON_REPAIR))
|
|
mi_report_error(save_errno, name);
|
|
switch (errpos) {
|
|
case 6:
|
|
my_free((uchar*) m_info,MYF(0));
|
|
/* fall through */
|
|
case 5:
|
|
VOID(my_close(info.dfile,MYF(0)));
|
|
if (old_info)
|
|
break; /* Don't remove open table */
|
|
/* fall through */
|
|
case 4:
|
|
my_free((uchar*) share,MYF(0));
|
|
/* fall through */
|
|
case 3:
|
|
if (! lock_error)
|
|
VOID(my_lock(kfile, F_UNLCK, 0L, F_TO_EOF, MYF(MY_SEEK_NOT_DONE)));
|
|
/* fall through */
|
|
case 2:
|
|
my_afree(disk_cache);
|
|
/* fall through */
|
|
case 1:
|
|
VOID(my_close(kfile,MYF(0)));
|
|
/* fall through */
|
|
case 0:
|
|
default:
|
|
break;
|
|
}
|
|
pthread_mutex_unlock(&THR_LOCK_myisam);
|
|
my_errno=save_errno;
|
|
DBUG_RETURN (NULL);
|
|
} /* mi_open */
|
|
|
|
|
|
uchar *mi_alloc_rec_buff(MI_INFO *info, ulong length, uchar **buf)
|
|
{
|
|
uint extra;
|
|
uint32 UNINIT_VAR(old_length);
|
|
LINT_INIT(old_length);
|
|
|
|
if (! *buf || length > (old_length=mi_get_rec_buff_len(info, *buf)))
|
|
{
|
|
uchar *newptr = *buf;
|
|
|
|
/* to simplify initial init of info->rec_buf in mi_open and mi_extra */
|
|
if (length == (ulong) -1)
|
|
{
|
|
if (info->s->options & HA_OPTION_COMPRESS_RECORD)
|
|
length= max(info->s->base.pack_reclength, info->s->max_pack_length);
|
|
else
|
|
length= info->s->base.pack_reclength;
|
|
length= max(length, info->s->base.max_key_length);
|
|
/* Avoid unnecessary realloc */
|
|
if (newptr && length == old_length)
|
|
return newptr;
|
|
}
|
|
|
|
extra= ((info->s->options & HA_OPTION_PACK_RECORD) ?
|
|
ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
|
|
MI_REC_BUFF_OFFSET : 0);
|
|
if (extra && newptr)
|
|
newptr-= MI_REC_BUFF_OFFSET;
|
|
if (!(newptr=(uchar*) my_realloc((uchar*)newptr, length+extra+8,
|
|
MYF(MY_ALLOW_ZERO_PTR))))
|
|
return newptr;
|
|
*((uint32 *) newptr)= (uint32) length;
|
|
*buf= newptr+(extra ? MI_REC_BUFF_OFFSET : 0);
|
|
}
|
|
return *buf;
|
|
}
|
|
|
|
|
|
ulonglong mi_safe_mul(ulonglong a, ulonglong b)
|
|
{
|
|
ulonglong max_val= ~ (ulonglong) 0; /* my_off_t is unsigned */
|
|
|
|
if (!a || max_val / a < b)
|
|
return max_val;
|
|
return a*b;
|
|
}
|
|
|
|
/* Set up functions in structs */
|
|
|
|
void mi_setup_functions(register MYISAM_SHARE *share)
|
|
{
|
|
if (share->options & HA_OPTION_COMPRESS_RECORD)
|
|
{
|
|
share->read_record=_mi_read_pack_record;
|
|
share->read_rnd=_mi_read_rnd_pack_record;
|
|
if ((share->options &
|
|
(HA_OPTION_PACK_RECORD | HA_OPTION_NULL_FIELDS)))
|
|
share->calc_checksum= mi_checksum;
|
|
else
|
|
share->calc_checksum= mi_static_checksum;
|
|
share->calc_check_checksum= share->calc_checksum;
|
|
if (!(share->options & HA_OPTION_TEMP_COMPRESS_RECORD))
|
|
share->calc_checksum=0; /* No checksum */
|
|
}
|
|
else if (share->options & HA_OPTION_PACK_RECORD)
|
|
{
|
|
share->read_record=_mi_read_dynamic_record;
|
|
share->read_rnd=_mi_read_rnd_dynamic_record;
|
|
share->delete_record=_mi_delete_dynamic_record;
|
|
share->compare_record=_mi_cmp_dynamic_record;
|
|
share->compare_unique=_mi_cmp_dynamic_unique;
|
|
share->calc_checksum= mi_checksum;
|
|
share->calc_check_checksum= share->calc_checksum;
|
|
|
|
/* add bits used to pack data to pack_reclength for faster allocation */
|
|
share->base.pack_reclength+= share->base.pack_bits;
|
|
if (share->base.blobs)
|
|
{
|
|
share->update_record=_mi_update_blob_record;
|
|
share->write_record=_mi_write_blob_record;
|
|
}
|
|
else
|
|
{
|
|
share->write_record=_mi_write_dynamic_record;
|
|
share->update_record=_mi_update_dynamic_record;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
share->read_record=_mi_read_static_record;
|
|
share->read_rnd=_mi_read_rnd_static_record;
|
|
share->delete_record=_mi_delete_static_record;
|
|
share->compare_record=_mi_cmp_static_record;
|
|
share->update_record=_mi_update_static_record;
|
|
share->write_record=_mi_write_static_record;
|
|
share->compare_unique=_mi_cmp_static_unique;
|
|
if (share->options & HA_OPTION_NULL_FIELDS)
|
|
share->calc_checksum= mi_checksum;
|
|
else
|
|
share->calc_checksum= mi_static_checksum;
|
|
share->calc_check_checksum= share->calc_checksum;
|
|
}
|
|
share->file_read= mi_nommap_pread;
|
|
share->file_write= mi_nommap_pwrite;
|
|
if (!(share->options & HA_OPTION_CHECKSUM))
|
|
share->calc_checksum=0;
|
|
return;
|
|
}
|
|
|
|
|
|
static void setup_key_functions(register MI_KEYDEF *keyinfo)
|
|
{
|
|
if (keyinfo->key_alg == HA_KEY_ALG_RTREE)
|
|
{
|
|
#ifdef HAVE_RTREE_KEYS
|
|
keyinfo->ck_insert = rtree_insert;
|
|
keyinfo->ck_delete = rtree_delete;
|
|
#else
|
|
DBUG_ASSERT(0); /* mi_open should check it never happens */
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
keyinfo->ck_insert = _mi_ck_write;
|
|
keyinfo->ck_delete = _mi_ck_delete;
|
|
}
|
|
if (keyinfo->flag & HA_BINARY_PACK_KEY)
|
|
{ /* Simple prefix compression */
|
|
keyinfo->bin_search=_mi_seq_search;
|
|
keyinfo->get_key=_mi_get_binary_pack_key;
|
|
keyinfo->pack_key=_mi_calc_bin_pack_key_length;
|
|
keyinfo->store_key=_mi_store_bin_pack_key;
|
|
}
|
|
else if (keyinfo->flag & HA_VAR_LENGTH_KEY)
|
|
{
|
|
keyinfo->get_key= _mi_get_pack_key;
|
|
if (keyinfo->seg[0].flag & HA_PACK_KEY)
|
|
{ /* Prefix compression */
|
|
/*
|
|
_mi_prefix_search() compares end-space against ASCII blank (' ').
|
|
It cannot be used for character sets, that do not encode the
|
|
blank character like ASCII does. UCS2 is an example. All
|
|
character sets with a fixed width > 1 or a mimimum width > 1
|
|
cannot represent blank like ASCII does. In these cases we have
|
|
to use _mi_seq_search() for the search.
|
|
*/
|
|
if (!keyinfo->seg->charset || use_strnxfrm(keyinfo->seg->charset) ||
|
|
(keyinfo->seg->flag & HA_NULL_PART) ||
|
|
(keyinfo->seg->charset->mbminlen > 1))
|
|
keyinfo->bin_search=_mi_seq_search;
|
|
else
|
|
keyinfo->bin_search=_mi_prefix_search;
|
|
keyinfo->pack_key=_mi_calc_var_pack_key_length;
|
|
keyinfo->store_key=_mi_store_var_pack_key;
|
|
}
|
|
else
|
|
{
|
|
keyinfo->bin_search=_mi_seq_search;
|
|
keyinfo->pack_key=_mi_calc_var_key_length; /* Variable length key */
|
|
keyinfo->store_key=_mi_store_static_key;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
keyinfo->bin_search=_mi_bin_search;
|
|
keyinfo->get_key=_mi_get_static_key;
|
|
keyinfo->pack_key=_mi_calc_static_key_length;
|
|
keyinfo->store_key=_mi_store_static_key;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
Function to save and store the header in the index file (.MYI)
|
|
*/
|
|
|
|
uint mi_state_info_write(File file, MI_STATE_INFO *state, uint pWrite)
|
|
{
|
|
uchar buff[MI_STATE_INFO_SIZE + MI_STATE_EXTRA_SIZE];
|
|
uchar *ptr=buff;
|
|
uint i, keys= (uint) state->header.keys,
|
|
key_blocks=state->header.max_block_size_index;
|
|
DBUG_ENTER("mi_state_info_write");
|
|
|
|
memcpy_fixed(ptr,&state->header,sizeof(state->header));
|
|
ptr+=sizeof(state->header);
|
|
|
|
/* open_count must be first because of _mi_mark_file_changed ! */
|
|
mi_int2store(ptr,state->open_count); ptr +=2;
|
|
*ptr++= (uchar)state->changed; *ptr++= state->sortkey;
|
|
mi_rowstore(ptr,state->state.records); ptr +=8;
|
|
mi_rowstore(ptr,state->state.del); ptr +=8;
|
|
mi_rowstore(ptr,state->split); ptr +=8;
|
|
mi_sizestore(ptr,state->dellink); ptr +=8;
|
|
mi_sizestore(ptr,state->state.key_file_length); ptr +=8;
|
|
mi_sizestore(ptr,state->state.data_file_length); ptr +=8;
|
|
mi_sizestore(ptr,state->state.empty); ptr +=8;
|
|
mi_sizestore(ptr,state->state.key_empty); ptr +=8;
|
|
mi_int8store(ptr,state->auto_increment); ptr +=8;
|
|
mi_int8store(ptr,(ulonglong) state->state.checksum);ptr +=8;
|
|
mi_int4store(ptr,state->process); ptr +=4;
|
|
mi_int4store(ptr,state->unique); ptr +=4;
|
|
mi_int4store(ptr,state->status); ptr +=4;
|
|
mi_int4store(ptr,state->update_count); ptr +=4;
|
|
|
|
ptr+=state->state_diff_length;
|
|
|
|
for (i=0; i < keys; i++)
|
|
{
|
|
mi_sizestore(ptr,state->key_root[i]); ptr +=8;
|
|
}
|
|
for (i=0; i < key_blocks; i++)
|
|
{
|
|
mi_sizestore(ptr,state->key_del[i]); ptr +=8;
|
|
}
|
|
if (pWrite & 2) /* From isamchk */
|
|
{
|
|
uint key_parts= mi_uint2korr(state->header.key_parts);
|
|
mi_int4store(ptr,state->sec_index_changed); ptr +=4;
|
|
mi_int4store(ptr,state->sec_index_used); ptr +=4;
|
|
mi_int4store(ptr,state->version); ptr +=4;
|
|
mi_int8store(ptr,state->key_map); ptr +=8;
|
|
mi_int8store(ptr,(ulonglong) state->create_time); ptr +=8;
|
|
mi_int8store(ptr,(ulonglong) state->recover_time); ptr +=8;
|
|
mi_int8store(ptr,(ulonglong) state->check_time); ptr +=8;
|
|
mi_sizestore(ptr,state->rec_per_key_rows); ptr+=8;
|
|
for (i=0 ; i < key_parts ; i++)
|
|
{
|
|
mi_int4store(ptr,state->rec_per_key_part[i]); ptr+=4;
|
|
}
|
|
}
|
|
|
|
if (pWrite & 1)
|
|
DBUG_RETURN(my_pwrite(file, buff, (size_t) (ptr-buff), 0L,
|
|
MYF(MY_NABP | MY_THREADSAFE)) != 0);
|
|
DBUG_RETURN(my_write(file, buff, (size_t) (ptr-buff),
|
|
MYF(MY_NABP)) != 0);
|
|
}
|
|
|
|
|
|
uchar *mi_state_info_read(uchar *ptr, MI_STATE_INFO *state)
|
|
{
|
|
uint i,keys,key_parts,key_blocks;
|
|
memcpy_fixed(&state->header,ptr, sizeof(state->header));
|
|
ptr +=sizeof(state->header);
|
|
keys=(uint) state->header.keys;
|
|
key_parts=mi_uint2korr(state->header.key_parts);
|
|
key_blocks=state->header.max_block_size_index;
|
|
|
|
state->open_count = mi_uint2korr(ptr); ptr +=2;
|
|
state->changed= *ptr++;
|
|
state->sortkey = (uint) *ptr++;
|
|
state->state.records= mi_rowkorr(ptr); ptr +=8;
|
|
state->state.del = mi_rowkorr(ptr); ptr +=8;
|
|
state->split = mi_rowkorr(ptr); ptr +=8;
|
|
state->dellink= mi_sizekorr(ptr); ptr +=8;
|
|
state->state.key_file_length = mi_sizekorr(ptr); ptr +=8;
|
|
state->state.data_file_length= mi_sizekorr(ptr); ptr +=8;
|
|
state->state.empty = mi_sizekorr(ptr); ptr +=8;
|
|
state->state.key_empty= mi_sizekorr(ptr); ptr +=8;
|
|
state->auto_increment=mi_uint8korr(ptr); ptr +=8;
|
|
state->state.checksum=(ha_checksum) mi_uint8korr(ptr); ptr +=8;
|
|
state->process= mi_uint4korr(ptr); ptr +=4;
|
|
state->unique = mi_uint4korr(ptr); ptr +=4;
|
|
state->status = mi_uint4korr(ptr); ptr +=4;
|
|
state->update_count=mi_uint4korr(ptr); ptr +=4;
|
|
|
|
ptr+= state->state_diff_length;
|
|
|
|
for (i=0; i < keys; i++)
|
|
{
|
|
state->key_root[i]= mi_sizekorr(ptr); ptr +=8;
|
|
}
|
|
for (i=0; i < key_blocks; i++)
|
|
{
|
|
state->key_del[i] = mi_sizekorr(ptr); ptr +=8;
|
|
}
|
|
state->sec_index_changed = mi_uint4korr(ptr); ptr +=4;
|
|
state->sec_index_used = mi_uint4korr(ptr); ptr +=4;
|
|
state->version = mi_uint4korr(ptr); ptr +=4;
|
|
state->key_map = mi_uint8korr(ptr); ptr +=8;
|
|
state->create_time = (time_t) mi_sizekorr(ptr); ptr +=8;
|
|
state->recover_time =(time_t) mi_sizekorr(ptr); ptr +=8;
|
|
state->check_time = (time_t) mi_sizekorr(ptr); ptr +=8;
|
|
state->rec_per_key_rows=mi_sizekorr(ptr); ptr +=8;
|
|
for (i=0 ; i < key_parts ; i++)
|
|
{
|
|
state->rec_per_key_part[i]= mi_uint4korr(ptr); ptr+=4;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
|
|
uint mi_state_info_read_dsk(File file, MI_STATE_INFO *state, my_bool pRead)
|
|
{
|
|
uchar buff[MI_STATE_INFO_SIZE + MI_STATE_EXTRA_SIZE];
|
|
|
|
if (!myisam_single_user)
|
|
{
|
|
if (pRead)
|
|
{
|
|
if (my_pread(file, buff, state->state_length,0L, MYF(MY_NABP)))
|
|
return 1;
|
|
}
|
|
else if (my_read(file, buff, state->state_length,MYF(MY_NABP)))
|
|
return 1;
|
|
mi_state_info_read(buff, state);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** store and read of MI_BASE_INFO
|
|
****************************************************************************/
|
|
|
|
uint mi_base_info_write(File file, MI_BASE_INFO *base)
|
|
{
|
|
uchar buff[MI_BASE_INFO_SIZE], *ptr=buff;
|
|
|
|
mi_sizestore(ptr,base->keystart); ptr +=8;
|
|
mi_sizestore(ptr,base->max_data_file_length); ptr +=8;
|
|
mi_sizestore(ptr,base->max_key_file_length); ptr +=8;
|
|
mi_rowstore(ptr,base->records); ptr +=8;
|
|
mi_rowstore(ptr,base->reloc); ptr +=8;
|
|
mi_int4store(ptr,base->mean_row_length); ptr +=4;
|
|
mi_int4store(ptr,base->reclength); ptr +=4;
|
|
mi_int4store(ptr,base->pack_reclength); ptr +=4;
|
|
mi_int4store(ptr,base->min_pack_length); ptr +=4;
|
|
mi_int4store(ptr,base->max_pack_length); ptr +=4;
|
|
mi_int4store(ptr,base->min_block_length); ptr +=4;
|
|
mi_int4store(ptr,base->fields); ptr +=4;
|
|
mi_int4store(ptr,base->pack_fields); ptr +=4;
|
|
*ptr++=base->rec_reflength;
|
|
*ptr++=base->key_reflength;
|
|
*ptr++=base->keys;
|
|
*ptr++=base->auto_key;
|
|
mi_int2store(ptr,base->pack_bits); ptr +=2;
|
|
mi_int2store(ptr,base->blobs); ptr +=2;
|
|
mi_int2store(ptr,base->max_key_block_length); ptr +=2;
|
|
mi_int2store(ptr,base->max_key_length); ptr +=2;
|
|
mi_int2store(ptr,base->extra_alloc_bytes); ptr +=2;
|
|
*ptr++= base->extra_alloc_procent;
|
|
*ptr++= base->raid_type;
|
|
mi_int2store(ptr,base->raid_chunks); ptr +=2;
|
|
mi_int4store(ptr,base->raid_chunksize); ptr +=4;
|
|
bzero(ptr,6); ptr +=6; /* extra */
|
|
return my_write(file, buff, (size_t) (ptr-buff), MYF(MY_NABP)) != 0;
|
|
}
|
|
|
|
|
|
uchar *mi_n_base_info_read(uchar *ptr, MI_BASE_INFO *base)
|
|
{
|
|
base->keystart = mi_sizekorr(ptr); ptr +=8;
|
|
base->max_data_file_length = mi_sizekorr(ptr); ptr +=8;
|
|
base->max_key_file_length = mi_sizekorr(ptr); ptr +=8;
|
|
base->records = (ha_rows) mi_sizekorr(ptr); ptr +=8;
|
|
base->reloc = (ha_rows) mi_sizekorr(ptr); ptr +=8;
|
|
base->mean_row_length = mi_uint4korr(ptr); ptr +=4;
|
|
base->reclength = mi_uint4korr(ptr); ptr +=4;
|
|
base->pack_reclength = mi_uint4korr(ptr); ptr +=4;
|
|
base->min_pack_length = mi_uint4korr(ptr); ptr +=4;
|
|
base->max_pack_length = mi_uint4korr(ptr); ptr +=4;
|
|
base->min_block_length = mi_uint4korr(ptr); ptr +=4;
|
|
base->fields = mi_uint4korr(ptr); ptr +=4;
|
|
base->pack_fields = mi_uint4korr(ptr); ptr +=4;
|
|
|
|
base->rec_reflength = *ptr++;
|
|
base->key_reflength = *ptr++;
|
|
base->keys= *ptr++;
|
|
base->auto_key= *ptr++;
|
|
base->pack_bits = mi_uint2korr(ptr); ptr +=2;
|
|
base->blobs = mi_uint2korr(ptr); ptr +=2;
|
|
base->max_key_block_length= mi_uint2korr(ptr); ptr +=2;
|
|
base->max_key_length = mi_uint2korr(ptr); ptr +=2;
|
|
base->extra_alloc_bytes = mi_uint2korr(ptr); ptr +=2;
|
|
base->extra_alloc_procent = *ptr++;
|
|
base->raid_type= *ptr++;
|
|
base->raid_chunks= mi_uint2korr(ptr); ptr +=2;
|
|
base->raid_chunksize= mi_uint4korr(ptr); ptr +=4;
|
|
/* TO BE REMOVED: Fix for old RAID files */
|
|
if (base->raid_type == 0)
|
|
{
|
|
base->raid_chunks=0;
|
|
base->raid_chunksize=0;
|
|
}
|
|
|
|
ptr+=6;
|
|
return ptr;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
mi_keydef
|
|
---------------------------------------------------------------------------*/
|
|
|
|
uint mi_keydef_write(File file, MI_KEYDEF *keydef)
|
|
{
|
|
uchar buff[MI_KEYDEF_SIZE];
|
|
uchar *ptr=buff;
|
|
|
|
*ptr++ = (uchar) keydef->keysegs;
|
|
*ptr++ = keydef->key_alg; /* Rtree or Btree */
|
|
mi_int2store(ptr,keydef->flag); ptr +=2;
|
|
mi_int2store(ptr,keydef->block_length); ptr +=2;
|
|
mi_int2store(ptr,keydef->keylength); ptr +=2;
|
|
mi_int2store(ptr,keydef->minlength); ptr +=2;
|
|
mi_int2store(ptr,keydef->maxlength); ptr +=2;
|
|
return my_write(file, buff, (size_t) (ptr-buff), MYF(MY_NABP)) != 0;
|
|
}
|
|
|
|
uchar *mi_keydef_read(uchar *ptr, MI_KEYDEF *keydef)
|
|
{
|
|
keydef->keysegs = (uint) *ptr++;
|
|
keydef->key_alg = *ptr++; /* Rtree or Btree */
|
|
|
|
keydef->flag = mi_uint2korr(ptr); ptr +=2;
|
|
keydef->block_length = mi_uint2korr(ptr); ptr +=2;
|
|
keydef->keylength = mi_uint2korr(ptr); ptr +=2;
|
|
keydef->minlength = mi_uint2korr(ptr); ptr +=2;
|
|
keydef->maxlength = mi_uint2korr(ptr); ptr +=2;
|
|
keydef->block_size_index= keydef->block_length/MI_MIN_KEY_BLOCK_LENGTH-1;
|
|
keydef->underflow_block_length=keydef->block_length/3;
|
|
keydef->version = 0; /* Not saved */
|
|
keydef->parser = &ft_default_parser;
|
|
keydef->ftkey_nr = 0;
|
|
return ptr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
** mi_keyseg
|
|
***************************************************************************/
|
|
|
|
int mi_keyseg_write(File file, const HA_KEYSEG *keyseg)
|
|
{
|
|
uchar buff[HA_KEYSEG_SIZE];
|
|
uchar *ptr=buff;
|
|
ulong pos;
|
|
|
|
*ptr++= keyseg->type;
|
|
*ptr++= keyseg->language;
|
|
*ptr++= keyseg->null_bit;
|
|
*ptr++= keyseg->bit_start;
|
|
*ptr++= keyseg->bit_end;
|
|
*ptr++= keyseg->bit_length;
|
|
mi_int2store(ptr,keyseg->flag); ptr+=2;
|
|
mi_int2store(ptr,keyseg->length); ptr+=2;
|
|
mi_int4store(ptr,keyseg->start); ptr+=4;
|
|
pos= keyseg->null_bit ? keyseg->null_pos : keyseg->bit_pos;
|
|
mi_int4store(ptr, pos);
|
|
ptr+=4;
|
|
|
|
return my_write(file, buff, (size_t) (ptr-buff), MYF(MY_NABP)) != 0;
|
|
}
|
|
|
|
|
|
uchar *mi_keyseg_read(uchar *ptr, HA_KEYSEG *keyseg)
|
|
{
|
|
keyseg->type = *ptr++;
|
|
keyseg->language = *ptr++;
|
|
keyseg->null_bit = *ptr++;
|
|
keyseg->bit_start = *ptr++;
|
|
keyseg->bit_end = *ptr++;
|
|
keyseg->bit_length = *ptr++;
|
|
keyseg->flag = mi_uint2korr(ptr); ptr +=2;
|
|
keyseg->length = mi_uint2korr(ptr); ptr +=2;
|
|
keyseg->start = mi_uint4korr(ptr); ptr +=4;
|
|
keyseg->null_pos = mi_uint4korr(ptr); ptr +=4;
|
|
keyseg->charset=0; /* Will be filled in later */
|
|
if (keyseg->null_bit)
|
|
/* We adjust bit_pos if null_bit is last in the byte */
|
|
keyseg->bit_pos= (uint16)(keyseg->null_pos + (keyseg->null_bit == (1 << 7)));
|
|
else
|
|
{
|
|
keyseg->bit_pos= (uint16)keyseg->null_pos;
|
|
keyseg->null_pos= 0;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
mi_uniquedef
|
|
---------------------------------------------------------------------------*/
|
|
|
|
uint mi_uniquedef_write(File file, MI_UNIQUEDEF *def)
|
|
{
|
|
uchar buff[MI_UNIQUEDEF_SIZE];
|
|
uchar *ptr=buff;
|
|
|
|
mi_int2store(ptr,def->keysegs); ptr+=2;
|
|
*ptr++= (uchar) def->key;
|
|
*ptr++ = (uchar) def->null_are_equal;
|
|
|
|
return my_write(file, buff, (size_t) (ptr-buff), MYF(MY_NABP)) != 0;
|
|
}
|
|
|
|
uchar *mi_uniquedef_read(uchar *ptr, MI_UNIQUEDEF *def)
|
|
{
|
|
def->keysegs = mi_uint2korr(ptr);
|
|
def->key = ptr[2];
|
|
def->null_are_equal=ptr[3];
|
|
return ptr+4; /* 1 extra byte */
|
|
}
|
|
|
|
/***************************************************************************
|
|
** MI_COLUMNDEF
|
|
***************************************************************************/
|
|
|
|
uint mi_recinfo_write(File file, MI_COLUMNDEF *recinfo)
|
|
{
|
|
uchar buff[MI_COLUMNDEF_SIZE];
|
|
uchar *ptr=buff;
|
|
|
|
mi_int2store(ptr,recinfo->type); ptr +=2;
|
|
mi_int2store(ptr,recinfo->length); ptr +=2;
|
|
*ptr++ = recinfo->null_bit;
|
|
mi_int2store(ptr,recinfo->null_pos); ptr+= 2;
|
|
return my_write(file, buff, (size_t) (ptr-buff), MYF(MY_NABP)) != 0;
|
|
}
|
|
|
|
uchar *mi_recinfo_read(uchar *ptr, MI_COLUMNDEF *recinfo)
|
|
{
|
|
recinfo->type= mi_sint2korr(ptr); ptr +=2;
|
|
recinfo->length=mi_uint2korr(ptr); ptr +=2;
|
|
recinfo->null_bit= (uint8) *ptr++;
|
|
recinfo->null_pos=mi_uint2korr(ptr); ptr +=2;
|
|
return ptr;
|
|
}
|
|
|
|
/**************************************************************************
|
|
Open data file with or without RAID
|
|
We can't use dup() here as the data file descriptors need to have different
|
|
active seek-positions.
|
|
|
|
The argument file_to_dup is here for the future if there would on some OS
|
|
exist a dup()-like call that would give us two different file descriptors.
|
|
*************************************************************************/
|
|
|
|
int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, const char *org_name,
|
|
File file_to_dup __attribute__((unused)))
|
|
{
|
|
char *data_name= share->data_file_name;
|
|
char real_data_name[FN_REFLEN];
|
|
|
|
if (org_name)
|
|
{
|
|
fn_format(real_data_name,org_name,"",MI_NAME_DEXT,4);
|
|
if (my_is_symlink(real_data_name))
|
|
{
|
|
if (my_realpath(real_data_name, real_data_name, MYF(0)) ||
|
|
(*myisam_test_invalid_symlink)(real_data_name))
|
|
{
|
|
my_errno= HA_WRONG_CREATE_OPTION;
|
|
return 1;
|
|
}
|
|
data_name= real_data_name;
|
|
}
|
|
}
|
|
#ifdef USE_RAID
|
|
if (share->base.raid_type)
|
|
{
|
|
info->dfile=my_raid_open(data_name,
|
|
share->mode | O_SHARE,
|
|
share->base.raid_type,
|
|
share->base.raid_chunks,
|
|
share->base.raid_chunksize,
|
|
MYF(MY_WME | MY_RAID));
|
|
}
|
|
else
|
|
#endif
|
|
info->dfile=my_open(data_name, share->mode | O_SHARE, MYF(MY_WME));
|
|
return info->dfile >= 0 ? 0 : 1;
|
|
}
|
|
|
|
|
|
int mi_open_keyfile(MYISAM_SHARE *share)
|
|
{
|
|
if ((share->kfile=my_open(share->unique_file_name, share->mode | O_SHARE,
|
|
MYF(MY_WME))) < 0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Disable all indexes.
|
|
|
|
SYNOPSIS
|
|
mi_disable_indexes()
|
|
info A pointer to the MyISAM storage engine MI_INFO struct.
|
|
|
|
DESCRIPTION
|
|
Disable all indexes.
|
|
|
|
RETURN
|
|
0 ok
|
|
*/
|
|
|
|
int mi_disable_indexes(MI_INFO *info)
|
|
{
|
|
MYISAM_SHARE *share= info->s;
|
|
|
|
mi_clear_all_keys_active(share->state.key_map);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Enable all indexes
|
|
|
|
SYNOPSIS
|
|
mi_enable_indexes()
|
|
info A pointer to the MyISAM storage engine MI_INFO struct.
|
|
|
|
DESCRIPTION
|
|
Enable all indexes. The indexes might have been disabled
|
|
by mi_disable_index() before.
|
|
The function works only if both data and indexes are empty,
|
|
otherwise a repair is required.
|
|
To be sure, call handler::delete_all_rows() before.
|
|
|
|
RETURN
|
|
0 ok
|
|
HA_ERR_CRASHED data or index is non-empty.
|
|
*/
|
|
|
|
int mi_enable_indexes(MI_INFO *info)
|
|
{
|
|
int error= 0;
|
|
MYISAM_SHARE *share= info->s;
|
|
|
|
if (share->state.state.data_file_length ||
|
|
(share->state.state.key_file_length != share->base.keystart))
|
|
{
|
|
mi_print_error(info->s, HA_ERR_CRASHED);
|
|
error= HA_ERR_CRASHED;
|
|
}
|
|
else
|
|
mi_set_all_keys_active(share->state.key_map, share->base.keys);
|
|
return error;
|
|
}
|
|
|
|
|
|
/*
|
|
Test if indexes are disabled.
|
|
|
|
SYNOPSIS
|
|
mi_indexes_are_disabled()
|
|
info A pointer to the MyISAM storage engine MI_INFO struct.
|
|
|
|
DESCRIPTION
|
|
Test if indexes are disabled.
|
|
|
|
RETURN
|
|
0 indexes are not disabled
|
|
1 all indexes are disabled
|
|
2 non-unique indexes are disabled
|
|
*/
|
|
|
|
int mi_indexes_are_disabled(MI_INFO *info)
|
|
{
|
|
MYISAM_SHARE *share= info->s;
|
|
|
|
/*
|
|
No keys or all are enabled. keys is the number of keys. Left shifted
|
|
gives us only one bit set. When decreased by one, gives us all all bits
|
|
up to this one set and it gets unset.
|
|
*/
|
|
if (!share->base.keys ||
|
|
(mi_is_all_keys_active(share->state.key_map, share->base.keys)))
|
|
return 0;
|
|
|
|
/* All are disabled */
|
|
if (mi_is_any_key_active(share->state.key_map))
|
|
return 1;
|
|
|
|
/*
|
|
We have keys. Some enabled, some disabled.
|
|
Don't check for any non-unique disabled but return directly 2
|
|
*/
|
|
return 2;
|
|
}
|
|
|