2000-07-31 21:29:14 +02:00
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
2001-12-06 13:10:51 +01:00
2000-07-31 21:29:14 +02:00
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 ; either version 2 of the License , or
( at your option ) any later version .
2001-12-06 13:10:51 +01:00
2000-07-31 21:29:14 +02:00
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 .
2001-12-06 13:10:51 +01:00
2000-07-31 21:29:14 +02:00
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 */
/* Descript, check and repair of ISAM tables */
# include "isamdef.h"
# include <m_ctype.h>
# include <stdarg.h>
2002-05-21 23:05:05 +02:00
# include <my_getopt.h>
2000-07-31 21:29:14 +02:00
# ifdef HAVE_SYS_VADVICE_H
# include <sys/vadvise.h>
# endif
# ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
# endif
SET_STACK_SIZE ( 9000 ) /* Minimum stack size for program */
# define T_VERBOSE 1
# define T_SILENT 2
# define T_DESCRIPT 4
# define T_EXTEND 8
# define T_INFO 16
# define T_REP 32
# define T_OPT 64 /* Not currently used */
# define T_FORCE_CREATE 128
# define T_WRITE_LOOP 256
# define T_UNPACK 512
# define T_STATISTICS 1024
# define T_VERY_SILENT 2048
# define T_SORT_RECORDS 4096
# define T_SORT_INDEX 8192
# define T_WAIT_FOREVER 16384
2001-08-22 00:45:07 +02:00
# define T_REP_BY_SORT 32768L
2000-07-31 21:29:14 +02:00
# define O_NEW_INDEX 1 /* Bits set in out_flag */
# define O_NEW_DATA 2
# if defined(_MSC_VER) && !defined(__WIN__)
# define USE_BUFFER_INIT 250L*1024L
# define READ_BUFFER_INIT ((uint) 32768-MALLOC_OVERHEAD)
# define SORT_BUFFER_INIT (uint) (65536L-MALLOC_OVERHEAD)
# define MIN_SORT_BUFFER (1024*16-MALLOC_OVERHEAD)
# else
# define USE_BUFFER_INIT (((1024L*512L-MALLOC_OVERHEAD) / IO_SIZE)*IO_SIZE)
# define READ_BUFFER_INIT (1024L*256L-MALLOC_OVERHEAD)
# define SORT_BUFFER_INIT (2048L*1024L-MALLOC_OVERHEAD)
# define MIN_SORT_BUFFER (4096-MALLOC_OVERHEAD)
# endif
2001-09-15 04:03:16 +02:00
# define NEED_MEM ((uint) 10*4*(IO_SIZE+32)+32) /* Nead for recursion */
2000-07-31 21:29:14 +02:00
# define MAXERR 20
# define BUFFERS_WHEN_SORTING 16 /* Alloc for sort-key-tree */
# define WRITE_COUNT MY_HOW_OFTEN_TO_WRITE
# define INDEX_TMP_EXT ".TMM"
# define DATA_TMP_EXT ".TMD"
# define MYF_RW MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL)
# define UPDATE_TIME 1
# define UPDATE_STAT 2
# define UPDATE_SORT 4
2001-08-22 00:45:07 +02:00
typedef struct st_isam_sort_key_blocks { /* Used when sorting */
2000-07-31 21:29:14 +02:00
uchar * buff , * end_pos ;
uchar lastkey [ N_MAX_POSSIBLE_KEY_BUFF ] ;
uint last_length ;
int inited ;
2001-08-22 00:45:07 +02:00
} ISAM_SORT_KEY_BLOCKS ;
2000-07-31 21:29:14 +02:00
2001-08-22 00:45:07 +02:00
typedef struct st_isam_sort_info {
2000-07-31 21:29:14 +02:00
N_INFO * info ;
enum data_file_type new_data_file_type ;
2001-08-22 00:45:07 +02:00
ISAM_SORT_KEY_BLOCKS * key_block , * key_block_end ;
2000-07-31 21:29:14 +02:00
uint key , find_length ;
ulong pos , max_pos , filepos , start_recpos , filelength , dupp , max_records , unique ,
buff_length ;
my_bool fix_datafile ;
char * record , * buff ;
N_KEYDEF * keyinfo ;
N_KEYSEG * keyseg ;
2001-08-22 00:45:07 +02:00
} ISAM_SORT_INFO ;
2000-07-31 21:29:14 +02:00
2002-05-21 23:05:05 +02:00
enum ic_options { OPT_CHARSETS_DIR_IC = 256 , OPT_KEY_BUFFER_SIZE ,
OPT_READ_BUFFER_SIZE , OPT_WRITE_BUFFER_SIZE ,
OPT_SORT_BUFFER_SIZE , OPT_SORT_KEY_BLOCKS ,
OPT_DECODE_BITS } ;
2000-07-31 21:29:14 +02:00
static ulong use_buffers = 0 , read_buffer_length = 0 , write_buffer_length = 0 ,
sort_buffer_length = 0 , sort_key_blocks = 0 , crc = 0 , unique_count = 0 ;
static uint testflag = 0 , out_flag = 0 , warning_printed = 0 , error_printed = 0 ,
2002-05-21 23:05:05 +02:00
verbose = 0 , opt_follow_links = 1 ;
static my_bool rep_quick = 0 ;
2000-07-31 21:29:14 +02:00
static uint opt_sort_key = 0 , total_files = 0 , max_level = 0 , max_key = N_MAXKEY ;
static ulong keydata = 0 , totaldata = 0 , key_blocks = 0 ;
static ulong new_file_pos = 0 , record_checksum = 0 , key_file_blocks = 0 , decode_bits ;
static ulong total_records = 0 , total_deleted = 0 ;
static ulong search_after_block = NI_POS_ERROR ;
static byte * record_buff ;
static char * * defaults_alloc ;
static const char * type_names [ ] =
{ " ? " , " text " , " binary " , " short " , " long " , " float " ,
" double " , " number " , " unsigned short " ,
" unsigned long " , " longlong " , " ulonglong " , " int24 " ,
" uint24 " , " int8 " , " ? " , } ,
* packed_txt = " packed " ,
* diff_txt = " stripped " ,
* field_pack [ ] = { " " , " no endspace " , " no prespace " ,
" no zeros " , " blob " , " constant " , " table-lookup " ,
" always zero " , " ? " , " ? " , } ;
static char temp_filename [ FN_REFLEN ] , * isam_file_name , * default_charset ;
static IO_CACHE read_cache ;
2001-08-22 00:45:07 +02:00
static ISAM_SORT_INFO sort_info ;
2000-07-31 21:29:14 +02:00
static int tmpfile_createflag = O_RDWR | O_TRUNC | O_EXCL ;
static const char * load_default_groups [ ] = { " isamchk " , 0 } ;
/* Functions defined in this file */
extern int main ( int argc , char * * argv ) ;
extern void print_error _VARARGS ( ( const char * fmt , . . . ) ) ;
static void print_warning _VARARGS ( ( const char * fmt , . . . ) ) ;
static void print_info _VARARGS ( ( const char * fmt , . . . ) ) ;
static int nisamchk ( char * filename ) ;
static void get_options ( int * argc , char * * * argv ) ;
static int chk_del ( N_INFO * info , uint testflag ) ;
static int check_k_link ( N_INFO * info , uint nr ) ;
static int chk_size ( N_INFO * info ) ;
static int chk_key ( N_INFO * info ) ;
2001-08-22 00:45:07 +02:00
static int chk_index ( N_INFO * info , N_KEYDEF * keyinfo , ulong page , uchar * buff ,
ulong * keys , uint level ) ;
2000-07-31 21:29:14 +02:00
static uint isam_key_length ( N_INFO * info , N_KEYDEF * keyinfo ) ;
static unsigned long calc_checksum ( ulong count ) ;
static int chk_data_link ( N_INFO * info , int extend ) ;
static int rep ( N_INFO * info , char * name ) ;
static int writekeys ( N_INFO * info , byte * buff , ulong filepos ) ;
static void descript ( N_INFO * info , char * name ) ;
static int movepoint ( N_INFO * info , byte * record , ulong oldpos , ulong newpos ,
uint prot_key ) ;
static void lock_memory ( void ) ;
static int flush_blocks ( File file ) ;
static int sort_records ( N_INFO * , my_string , uint , int ) ;
static int sort_index ( N_INFO * info , my_string name ) ;
static int sort_record_index ( N_INFO * info , N_KEYDEF * keyinfo , ulong page ,
uchar * buff , uint sortkey , File new_file ) ;
static int sort_one_index ( N_INFO * info , N_KEYDEF * keyinfo , uchar * buff ,
File new_file ) ;
static int change_to_newfile ( const char * filename , const char * old_ext ,
const char * new_ext ) ;
static int lock_file ( File file , ulong start , int lock_type , const char * filetype ,
const char * filename ) ;
static int filecopy ( File to , File from , ulong start , ulong length ,
const char * type ) ;
static void print_version ( void ) ;
static int rep_by_sort ( N_INFO * info , my_string name ) ;
static int sort_key_read ( void * key ) ;
static int sort_get_next_record ( void ) ;
static int sort_write_record ( void ) ;
static int sort_key_cmp ( const void * not_used , const void * a , const void * b ) ;
static int sort_key_write ( const void * a ) ;
static ulong get_record_for_key ( N_INFO * info , N_KEYDEF * keyinfo ,
uchar * key ) ;
2001-08-22 00:45:07 +02:00
static int sort_insert_key ( reg1 ISAM_SORT_KEY_BLOCKS * key_block , uchar * key ,
2000-07-31 21:29:14 +02:00
ulong prev_block ) ;
static int sort_delete_record ( void ) ;
static void usage ( void ) ;
static int flush_pending_blocks ( void ) ;
2001-08-22 00:45:07 +02:00
static ISAM_SORT_KEY_BLOCKS * alloc_key_blocks ( uint blocks , uint buffer_length ) ;
2000-07-31 21:29:14 +02:00
static int test_if_almost_full ( N_INFO * info ) ;
static int recreate_database ( N_INFO * * info , char * filename ) ;
static void save_integer ( byte * pos , uint pack_length , ulong value ) ;
static int write_data_suffix ( N_INFO * info ) ;
static int update_state_info ( N_INFO * info , uint update ) ;
/* Main program */
2001-08-22 00:45:07 +02:00
int main ( int argc , char * * argv )
2000-07-31 21:29:14 +02:00
{
int error ;
MY_INIT ( argv [ 0 ] ) ;
2000-11-28 03:47:47 +01:00
# ifdef __EMX__
_wildcard ( & argc , & argv ) ;
# endif
2000-07-31 21:29:14 +02:00
get_options ( & argc , ( char * * * ) & argv ) ;
nisam_quick_table_bits = ( uint ) decode_bits ;
error = 0 ;
while ( - - argc > = 0 )
{
error | = nisamchk ( * ( argv + + ) ) ;
VOID ( fflush ( stdout ) ) ;
VOID ( fflush ( stderr ) ) ;
if ( ( error_printed | warning_printed ) & & ( testflag & T_FORCE_CREATE ) & &
( ! ( testflag & ( T_REP | T_REP_BY_SORT | T_SORT_RECORDS |
T_SORT_INDEX ) ) ) )
{
testflag | = T_REP ;
error | = nisamchk ( argv [ - 1 ] ) ;
testflag & = ~ T_REP ;
VOID ( fflush ( stdout ) ) ;
VOID ( fflush ( stderr ) ) ;
}
if ( argc & & ( ! ( testflag & T_SILENT ) | | testflag & T_INFO ) )
{
puts ( " \n --------- \n " ) ;
VOID ( fflush ( stdout ) ) ;
}
}
if ( total_files > 1 )
{ /* Only if descript */
if ( ! ( testflag & T_SILENT ) | | testflag & T_INFO )
puts ( " \n --------- \n " ) ;
printf ( " \n Total of all %d ISAM-files: \n Data records: %8lu Deleted blocks: %8lu \n " , total_files , total_records , total_deleted ) ;
}
free_defaults ( defaults_alloc ) ;
my_end ( testflag & T_INFO ? MY_CHECK_ERROR | MY_GIVE_INFO : MY_CHECK_ERROR ) ;
exit ( error ) ;
# ifndef _lint
return 0 ; /* No compiler warning */
# endif
} /* main */
2002-05-21 23:05:05 +02:00
static struct my_option my_long_options [ ] =
2000-07-31 21:29:14 +02:00
{
2002-05-21 23:05:05 +02:00
{ " analyze " , ' a ' ,
" Analyze distribution of keys. Will make some joins in MySQL faster. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " character-sets-dir " , OPT_CHARSETS_DIR_IC ,
" Directory where character sets are " , ( gptr * ) & charsets_dir ,
( gptr * ) & charsets_dir , 0 , GET_STR , REQUIRED_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
2000-07-31 21:29:14 +02:00
# ifndef DBUG_OFF
2002-05-21 23:05:05 +02:00
{ " debug " , ' # ' , " Output debug log. Often this is 'd:t:o,filename' " ,
0 , 0 , 0 , GET_STR , REQUIRED_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
2000-07-31 21:29:14 +02:00
# endif
2002-05-21 23:05:05 +02:00
{ " default-character-set " , ' C ' , " Set the default character set " ,
( gptr * ) & default_charset , ( gptr * ) & default_charset , 0 , GET_STR ,
REQUIRED_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " description " , ' d ' , " Prints some information about table. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " extend-check " , ' e ' ,
" Check the table VERY thoroughly. One need to use this only in extreme cases, because isamchk should normally find all errors even without this switch. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " information " , ' i ' , " Print statistics information about the table " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " force " , ' f ' ,
" Overwrite old temporary files. If one uses -f when checking tables (running isamchk without -r), isamchk will automatically restart with -r on any wrong table. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " help " , ' ? ' , " Display this help and exit. " , 0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 ,
0 , 0 , 0 , 0 , 0 } ,
{ " keys-used " , ' k ' ,
" Used with '-r'. Tell ISAM to update only the first # keys. This can be used to get faster inserts! " ,
( gptr * ) & max_key , ( gptr * ) & max_key , 0 , GET_UINT , REQUIRED_ARG , N_MAXKEY , 0 ,
0 , 0 , 0 , 0 } ,
{ " no-symlinks " , ' l ' ,
" Do not follow symbolic links when repairing. Normally isamchk repairs the table a symlink points at. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " quick " , ' q ' ,
" Used with -r to get a faster repair. (The data file isn't touched.) One can give a second '-q' to force isamchk to modify the original datafile. " ,
( gptr * ) & rep_quick , ( gptr * ) & rep_quick , 0 , GET_BOOL , NO_ARG , 0 , 0 , 0 , 0 , 0 ,
0 } ,
{ " recover " , ' r ' ,
" Can fix almost anything except unique keys that aren't unique. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " safe-recover " , ' o ' ,
" Uses old recovery method; slower than '-r' but can handle a couple of cases that '-r' cannot handle. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " set-variable " , ' O ' ,
" Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value. " ,
0 , 0 , 0 , GET_STR , REQUIRED_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " block-search " , ' b ' , " For debugging. " , ( gptr * ) & search_after_block ,
( gptr * ) & search_after_block , 0 , GET_ULONG , REQUIRED_ARG , NI_POS_ERROR , 0 ,
0 , 0 , 0 , 0 } ,
{ " silent " , ' s ' ,
" Only print errors. One can use two -s to make isamchk very silent. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " sort-index " , ' S ' ,
" Sort index blocks. This speeds up 'read-next' in applications. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " sort-records " , ' R ' ,
" Sort records according to an index. This makes your data much more localized and may speed up things (It may be VERY slow to do a sort the first time!) " ,
( gptr * ) & opt_sort_key , ( gptr * ) & opt_sort_key , 0 , GET_UINT , REQUIRED_ARG ,
0 , 0 , ( N_MAXKEY - 1 ) , 1 , 0 , 0 } ,
{ " unpack " , ' u ' , " Unpack file packed with pack_isam. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " verbose " , ' v ' ,
" Print more information. This can be used with -d and -e. Use many -v for more verbosity! " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " version " , ' V ' , " Print version and exit. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " wait " , ' w ' , " Wait if table is locked. " ,
0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ " key_buffer_size " , OPT_KEY_BUFFER_SIZE , " " , ( gptr * ) & use_buffers ,
( gptr * ) & use_buffers , 0 , GET_ULONG , REQUIRED_ARG , ( long ) USE_BUFFER_INIT ,
( long ) MALLOC_OVERHEAD , ( long ) ~ 0L , ( long ) MALLOC_OVERHEAD , ( long ) IO_SIZE ,
0 } ,
{ " read_buffer_size " , OPT_READ_BUFFER_SIZE , " " ,
( gptr * ) & read_buffer_length , ( gptr * ) & read_buffer_length , 0 , GET_ULONG ,
REQUIRED_ARG , ( long ) READ_BUFFER_INIT , ( long ) MALLOC_OVERHEAD ,
( long ) ~ 0L , ( long ) MALLOC_OVERHEAD , ( long ) 1L , 0 } ,
{ " write_buffer_size " , OPT_WRITE_BUFFER_SIZE , " " ,
( gptr * ) & write_buffer_length , ( gptr * ) & write_buffer_length , 0 , GET_ULONG ,
REQUIRED_ARG , ( long ) READ_BUFFER_INIT , ( long ) MALLOC_OVERHEAD , ( long ) ~ 0L ,
( long ) MALLOC_OVERHEAD , ( long ) 1L , 0 } ,
{ " sort_buffer_size " , OPT_SORT_BUFFER_SIZE , " " ,
( gptr * ) & sort_buffer_length , ( gptr * ) & sort_buffer_length , 0 , GET_ULONG ,
REQUIRED_ARG , ( long ) SORT_BUFFER_INIT ,
( long ) ( MIN_SORT_BUFFER + MALLOC_OVERHEAD ) , ( long ) ~ 0L ,
( long ) MALLOC_OVERHEAD , ( long ) 1L , 0 } ,
{ " sort_key_blocks " , OPT_SORT_KEY_BLOCKS , " " ,
( gptr * ) & sort_key_blocks , ( gptr * ) & sort_key_blocks , 0 , GET_ULONG ,
REQUIRED_ARG , BUFFERS_WHEN_SORTING , 4L , 100L , 0L , 1L , 0 } ,
{ " decode_bits " , OPT_DECODE_BITS , " " ,
( gptr * ) & decode_bits , ( gptr * ) & decode_bits , 0 , GET_ULONG , REQUIRED_ARG ,
9L , 4L , 17L , 0L , 1L , 0 } ,
{ 0 , 0 , 0 , 0 , 0 , 0 , GET_NO_ARG , NO_ARG , 0 , 0 , 0 , 0 , 0 , 0 }
2000-07-31 21:29:14 +02:00
} ;
static void print_version ( void )
{
2002-05-29 14:07:30 +02:00
printf ( " %s Ver 6.01 for %s at %s \n " , my_progname , SYSTEM_TYPE ,
2000-07-31 21:29:14 +02:00
MACHINE_TYPE ) ;
}
static void usage ( void )
{
print_version ( ) ;
2002-05-21 23:05:05 +02:00
puts ( " MySQL AB, by Monty, for your professional use " ) ;
2000-07-31 21:29:14 +02:00
puts ( " This software comes with NO WARRANTY: see the PUBLIC for details. \n " ) ;
puts ( " Description, check and repair of ISAM tables. " ) ;
puts ( " Used without options all tables on the command will be checked for errors " ) ;
printf ( " Usage: %s [OPTIONS] tables[.ISM] \n " , my_progname ) ;
2002-05-21 23:05:05 +02:00
my_print_help ( my_long_options ) ;
print_defaults ( " my " , load_default_groups ) ;
my_print_variables ( my_long_options ) ;
2000-07-31 21:29:14 +02:00
}
/* Check table */
static int nisamchk ( my_string filename )
{
int error , lock_type , recreate ;
N_INFO * info ;
File datafile ;
char fixed_name [ FN_REFLEN ] ;
ISAM_SHARE * share ;
DBUG_ENTER ( " nisamchk " ) ;
out_flag = error = warning_printed = error_printed = recreate = 0 ;
datafile = 0 ;
isam_file_name = filename ; /* For error messages */
if ( ! ( info = nisam_open ( filename ,
( testflag & T_DESCRIPT ) ? O_RDONLY : O_RDWR ,
( testflag & T_WAIT_FOREVER ) ? HA_OPEN_WAIT_IF_LOCKED :
( testflag & T_DESCRIPT ) ? HA_OPEN_IGNORE_IF_LOCKED :
HA_OPEN_ABORT_IF_LOCKED ) ) )
{
/* Avoid twice printing of isam file name */
error_printed = 1 ;
switch ( my_errno ) {
case HA_ERR_CRASHED :
print_error ( " '%s' is not a ISAM-table " , filename ) ;
break ;
case HA_ERR_OLD_FILE :
print_error ( " '%s' is a old type of ISAM-table " , filename ) ;
break ;
case HA_ERR_END_OF_FILE :
print_error ( " Couldn't read compleat header from '%s' " , filename ) ;
break ;
case EAGAIN :
print_error ( " '%s' is locked. Use -w to wait until unlocked " , filename ) ;
break ;
case ENOENT :
print_error ( " File '%s' doesn't exist " , filename ) ;
break ;
case EACCES :
print_error ( " You don't have permission to use '%s' " , filename ) ;
break ;
default :
print_error ( " %d when opening ISAM-table '%s' " ,
my_errno , filename ) ;
break ;
}
DBUG_RETURN ( 1 ) ;
}
share = info - > s ;
share - > base . options & = ~ HA_OPTION_READ_ONLY_DATA ; /* We are modifing it */
share - > r_locks = 0 ;
if ( ( testflag & ( T_REP_BY_SORT | T_REP | T_STATISTICS |
T_SORT_RECORDS | T_SORT_INDEX ) ) & &
( ( testflag & T_UNPACK & & share - > data_file_type = = COMPRESSED_RECORD ) | |
share - > state_length ! = sizeof ( share - > state ) | |
uint2korr ( share - > state . header . base_info_length ) ! =
sizeof ( share - > base ) | |
( max_key & & ! share - > state . keys & & share - > base . keys ) | |
test_if_almost_full ( info ) | |
info - > s - > state . header . file_version [ 3 ] ! = nisam_file_magic [ 3 ] ) )
{
if ( recreate_database ( & info , filename ) )
{
VOID ( fprintf ( stderr ,
" ISAM-table '%s' is not fixed because of errors \n " ,
filename ) ) ;
return ( - 1 ) ;
}
recreate = 1 ;
if ( ! ( testflag & ( T_REP | T_REP_BY_SORT ) ) )
{
testflag | = T_REP_BY_SORT ; /* if only STATISTICS */
if ( ! ( testflag & T_SILENT ) )
printf ( " - '%s' has old table-format. Recreating index \n " , filename ) ;
if ( ! rep_quick )
rep_quick = 1 ;
}
share = info - > s ;
share - > r_locks = 0 ;
}
if ( testflag & T_DESCRIPT )
{
total_files + + ;
total_records + = share - > state . records ; total_deleted + = share - > state . del ;
descript ( info , filename ) ;
}
else
{
if ( testflag & ( T_REP + T_REP_BY_SORT + T_OPT + T_SORT_RECORDS + T_SORT_INDEX ) )
lock_type = F_WRLCK ; /* table is changed */
else
lock_type = F_RDLCK ;
if ( info - > lock_type = = F_RDLCK )
info - > lock_type = F_UNLCK ; /* Read only table */
if ( _nisam_readinfo ( info , lock_type , 0 ) )
{
print_error ( " Can't lock indexfile of '%s', error: %d " ,
filename , my_errno ) ;
error_printed = 0 ;
goto end2 ;
}
share - > w_locks + + ; /* Mark (for writeinfo) */
if ( lock_file ( info - > dfile , 0L , lock_type , " datafile of " , filename ) )
goto end ;
info - > lock_type = F_EXTRA_LCK ; /* Simulate as locked */
info - > tmp_lock_type = lock_type ;
datafile = info - > dfile ;
if ( testflag & ( T_REP + T_REP_BY_SORT + T_SORT_RECORDS + T_SORT_INDEX ) )
{
if ( testflag & ( T_REP + T_REP_BY_SORT ) )
share - > state . keys = min ( share - > base . keys , max_key ) ;
VOID ( fn_format ( fixed_name , filename , " " , N_NAME_IEXT ,
4 + ( opt_follow_links ? 16 : 0 ) ) ) ;
if ( rep_quick & & ( error = chk_del ( info , testflag & ~ T_VERBOSE ) ) )
print_error ( " Quick-recover aborted; Run recovery without switch 'q' " ) ;
else
{
if ( testflag & T_REP_BY_SORT & &
( share - > state . keys | | ( rep_quick & & ! max_key & & ! recreate ) ) )
error = rep_by_sort ( info , fixed_name ) ;
else if ( testflag & ( T_REP | T_REP_BY_SORT ) )
error = rep ( info , fixed_name ) ;
}
if ( ! error & & testflag & T_SORT_RECORDS )
{
if ( out_flag & O_NEW_DATA )
{ /* Change temp file to org file */
VOID ( lock_file ( datafile , 0L , F_UNLCK , " datafile of " , filename ) ) ;
VOID ( my_close ( datafile , MYF ( MY_WME ) ) ) ; /* Close old file */
VOID ( my_close ( info - > dfile , MYF ( MY_WME ) ) ) ; /* Close new file */
error | = change_to_newfile ( fixed_name , N_NAME_DEXT , DATA_TMP_EXT ) ;
if ( ( info - > dfile = my_open ( fn_format ( temp_filename , fixed_name , " " ,
N_NAME_DEXT , 2 + 4 ) ,
O_RDWR | O_SHARE ,
MYF ( MY_WME ) ) ) < = 0 | |
lock_file ( info - > dfile , 0L , F_WRLCK , " datafile " , temp_filename ) )
error = 1 ;
out_flag & = ~ O_NEW_DATA ; /* We are using new datafile */
read_cache . file = info - > dfile ;
}
if ( ! error )
error = sort_records ( info , fixed_name , opt_sort_key ,
test ( ! ( testflag & T_REP ) ) ) ;
datafile = info - > dfile ; /* This is now locked */
}
if ( ! error & & testflag & T_SORT_INDEX )
error = sort_index ( info , fixed_name ) ;
}
else
{
if ( ! ( testflag & T_SILENT ) | | testflag & T_INFO )
printf ( " Checking ISAM file: %s \n " , filename ) ;
if ( ! ( testflag & T_SILENT ) )
printf ( " Data records: %7ld Deleted blocks: %7ld \n " ,
share - > state . records , share - > state . del ) ;
share - > state . keys = min ( share - > state . keys , max_key ) ;
error = chk_size ( info ) ;
error | = chk_del ( info , testflag ) ;
error | = chk_key ( info ) ;
if ( ! rep_quick )
{
if ( testflag & T_EXTEND )
2002-07-23 17:31:22 +02:00
VOID ( init_key_cache ( use_buffers ) ) ;
2000-07-31 21:29:14 +02:00
VOID ( init_io_cache ( & read_cache , datafile , ( uint ) read_buffer_length ,
READ_CACHE , share - > pack . header_length , 1 ,
MYF ( MY_WME ) ) ) ;
lock_memory ( ) ;
error | = chk_data_link ( info , testflag & T_EXTEND ) ;
error | = flush_blocks ( share - > kfile ) ;
VOID ( end_io_cache ( & read_cache ) ) ;
}
}
}
end :
if ( ! ( testflag & T_DESCRIPT ) )
{
if ( info - > update & ( HA_STATE_CHANGED | HA_STATE_ROW_CHANGED ) )
error | = update_state_info ( info ,
( ( testflag & ( T_REP | T_REP_BY_SORT ) ) ?
UPDATE_TIME | UPDATE_STAT : 0 ) |
( ( testflag & T_SORT_RECORDS ) ?
UPDATE_SORT : 0 ) ) ;
VOID ( lock_file ( share - > kfile , 0L , F_UNLCK , " indexfile " , filename ) ) ;
if ( datafile > 0 )
VOID ( lock_file ( datafile , 0L , F_UNLCK , " datafile of " , filename ) ) ;
info - > update & = ~ ( HA_STATE_CHANGED | HA_STATE_ROW_CHANGED ) ;
}
share - > w_locks - - ;
end2 :
if ( datafile & & datafile ! = info - > dfile )
VOID ( my_close ( datafile , MYF ( MY_WME ) ) ) ;
if ( nisam_close ( info ) )
{
print_error ( " %d when closing ISAM-table '%s' " , my_errno , filename ) ;
DBUG_RETURN ( 1 ) ;
}
if ( error = = 0 )
{
if ( out_flag & O_NEW_DATA )
error | = change_to_newfile ( fixed_name , N_NAME_DEXT , DATA_TMP_EXT ) ;
if ( out_flag & O_NEW_INDEX )
error | = change_to_newfile ( fixed_name , N_NAME_IEXT , INDEX_TMP_EXT ) ;
}
VOID ( fflush ( stdout ) ) ; VOID ( fflush ( stderr ) ) ;
if ( error_printed )
{
if ( testflag & ( T_REP + T_REP_BY_SORT + T_SORT_RECORDS + T_SORT_INDEX ) )
VOID ( fprintf ( stderr ,
" ISAM-table '%s' is not fixed because of errors \n " ,
filename ) ) ;
else if ( ! ( error_printed & 2 ) & & ! ( testflag & T_FORCE_CREATE ) )
VOID ( fprintf ( stderr ,
" ISAM-table '%s' is corrupted \n Fix it using switch \" -r \" or \" -o \" \n " ,
filename ) ) ;
}
else if ( warning_printed & &
! ( testflag & ( T_REP + T_REP_BY_SORT + T_SORT_RECORDS + T_SORT_INDEX +
T_FORCE_CREATE ) ) )
VOID ( fprintf ( stderr , " ISAM-table '%s' is useable but should be fixed \n " ,
filename ) ) ;
VOID ( fflush ( stderr ) ) ;
DBUG_RETURN ( error ) ;
} /* nisamchk */
2002-05-21 23:05:05 +02:00
static my_bool
get_one_option ( int optid , const struct my_option * opt __attribute__ ( ( unused ) ) ,
char * argument )
{
switch ( optid ) {
case ' a ' :
testflag | = T_STATISTICS ;
break ;
case ' s ' : /* silent */
if ( testflag & T_SILENT )
testflag | = T_VERY_SILENT ;
testflag | = T_SILENT ;
testflag & = ~ T_WRITE_LOOP ;
break ;
case ' w ' :
testflag | = T_WAIT_FOREVER ;
break ;
case ' d ' : /* description if isam-file */
testflag | = T_DESCRIPT ;
break ;
case ' e ' : /* extend check */
testflag | = T_EXTEND ;
break ;
case ' i ' :
testflag | = T_INFO ;
break ;
case ' f ' :
tmpfile_createflag = O_RDWR | O_TRUNC ;
testflag | = T_FORCE_CREATE ;
break ;
case ' l ' :
opt_follow_links = 0 ;
break ;
case ' r ' : /* Repair table */
testflag = ( testflag & ~ T_REP ) | T_REP_BY_SORT ;
break ;
case ' o ' :
testflag = ( testflag & ~ T_REP_BY_SORT ) | T_REP ;
my_disable_async_io = 1 ; /* More safety */
break ;
case ' u ' :
testflag | = T_UNPACK | T_REP_BY_SORT ;
break ;
case ' v ' : /* Verbose */
testflag | = T_VERBOSE ;
verbose + + ;
break ;
case ' R ' : /* Sort records */
testflag | = T_SORT_RECORDS ;
if ( opt_sort_key > = N_MAXKEY )
{
fprintf ( stderr ,
" The value of the sort key is bigger than max key: %d. \n " ,
N_MAXKEY ) ;
exit ( 1 ) ;
}
break ;
case ' S ' : /* Sort index */
testflag | = T_SORT_INDEX ;
break ;
case ' # ' :
DBUG_PUSH ( argument ? argument : " d:t:o,/tmp/isamchk.trace " ) ;
break ;
case ' V ' :
print_version ( ) ;
exit ( 0 ) ;
case ' ? ' :
usage ( ) ;
exit ( 0 ) ;
}
return 0 ;
}
2000-07-31 21:29:14 +02:00
/* Read options */
2002-05-21 23:05:05 +02:00
static void get_options ( register int * argc , register char * * * argv )
2000-07-31 21:29:14 +02:00
{
2002-05-21 23:05:05 +02:00
int ho_error ;
2000-07-31 21:29:14 +02:00
load_defaults ( " my " , load_default_groups , argc , argv ) ;
defaults_alloc = * argv ;
if ( isatty ( fileno ( stdout ) ) )
testflag | = T_WRITE_LOOP ;
2002-05-21 23:05:05 +02:00
if ( ( ho_error = handle_options ( argc , argv , my_long_options , get_one_option ) ) )
2002-05-29 14:07:30 +02:00
exit ( ho_error ) ;
2002-05-21 23:05:05 +02:00
2000-07-31 21:29:14 +02:00
if ( * argc = = 0 )
{
usage ( ) ;
exit ( - 1 ) ;
}
if ( ( testflag & T_UNPACK ) & & ( rep_quick | | ( testflag & T_SORT_RECORDS ) ) )
{
VOID ( fprintf ( stderr ,
" %s: --unpack can't be used with --quick or --sort-records \n " ,
my_progname ) ) ;
exit ( 1 ) ;
}
if ( default_charset )
{
2003-03-16 07:56:14 +01:00
if ( ! ( default_charset_info = get_charset_by_name ( default_charset , MYF ( MY_WME ) ) ) )
2000-07-31 21:29:14 +02:00
exit ( 1 ) ;
}
return ;
} /* get options */
/* Check delete links */
2001-08-22 00:45:07 +02:00
static int chk_del ( reg1 N_INFO * info , uint test_flag )
2000-07-31 21:29:14 +02:00
{
reg2 ulong i ;
uint j , delete_link_length ;
ulong empty , next_link ;
uchar buff [ 8 ] ;
DBUG_ENTER ( " chk_del " ) ;
if ( ! ( test_flag & T_SILENT ) ) puts ( " - check delete-chain " ) ;
record_checksum = 0L ;
key_file_blocks = info - > s - > base . keystart ;
for ( j = 0 ; j < info - > s - > state . keys ; j + + )
if ( check_k_link ( info , j ) )
goto wrong ;
delete_link_length = ( info - > s - > base . options & HA_OPTION_PACK_RECORD ) ? 8 : 5 ;
next_link = info - > s - > state . dellink ;
if ( info - > s - > state . del = = 0 )
{
if ( test_flag & T_VERBOSE )
{
puts ( " No recordlinks " ) ;
}
}
else
{
if ( test_flag & T_VERBOSE )
printf ( " Recordlinks: " ) ;
empty = 0 ;
for ( i = info - > s - > state . del ; i > 0L & & next_link ! = NI_POS_ERROR ; i - - )
{
if ( test_flag & T_VERBOSE ) printf ( " %10lu " , next_link ) ;
if ( next_link > = info - > s - > state . data_file_length )
goto wrong ;
if ( my_pread ( info - > dfile , ( char * ) buff , delete_link_length ,
next_link , MYF ( MY_NABP ) ) )
{
if ( test_flag & T_VERBOSE ) puts ( " " ) ;
print_error ( " Can't read delete-link at filepos: %lu " ,
( ulong ) next_link ) ;
DBUG_RETURN ( 1 ) ;
}
if ( * buff ! = ' \0 ' )
{
if ( test_flag & T_VERBOSE ) puts ( " " ) ;
print_error ( " Record at pos: %lu is not remove-marked " ,
( ulong ) next_link ) ;
goto wrong ;
}
if ( info - > s - > base . options & HA_OPTION_PACK_RECORD )
{
next_link = uint4korr ( buff + 4 ) ;
empty + = uint3korr ( buff + 1 ) ;
}
else
{
record_checksum + = next_link ;
next_link = uint4korr ( buff + 1 ) ;
empty + = info - > s - > base . reclength ;
}
if ( next_link = = ( uint32 ) ~ 0 ) /* Fix for 64 bit long */
next_link = NI_POS_ERROR ;
}
if ( empty ! = info - > s - > state . empty )
{
if ( test_flag & T_VERBOSE ) puts ( " " ) ;
print_warning ( " Not used space is supposed to be: %lu but is: %lu " ,
( ulong ) info - > s - > state . empty , ( ulong ) empty ) ;
info - > s - > state . empty = empty ;
}
if ( i ! = 0 | | next_link ! = NI_POS_ERROR )
goto wrong ;
if ( test_flag & T_VERBOSE ) puts ( " \n " ) ;
}
DBUG_RETURN ( 0 ) ;
wrong :
if ( test_flag & T_VERBOSE ) puts ( " " ) ;
print_error ( " delete-link-chain corrupted " ) ;
DBUG_RETURN ( 1 ) ;
} /* chk_del */
/* Kontrollerar l{nkarna i nyckelfilen */
2001-08-22 00:45:07 +02:00
static int check_k_link ( register N_INFO * info , uint nr )
2000-07-31 21:29:14 +02:00
{
ulong next_link , records ;
DBUG_ENTER ( " check_k_link " ) ;
if ( testflag & T_VERBOSE )
printf ( " index %2d: " , nr + 1 ) ;
next_link = info - > s - > state . key_del [ nr ] ;
records = ( info - > s - > state . key_file_length /
info - > s - > keyinfo [ nr ] . base . block_length ) ;
while ( next_link ! = NI_POS_ERROR & & records > 0 )
{
if ( testflag & T_VERBOSE ) printf ( " %10lu " , next_link ) ;
if ( next_link > info - > s - > state . key_file_length | |
next_link & ( info - > s - > blocksize - 1 ) )
DBUG_RETURN ( 1 ) ;
if ( my_pread ( info - > s - > kfile , ( char * ) & next_link , sizeof ( long ) , next_link ,
MYF ( MY_NABP ) ) )
DBUG_RETURN ( 1 ) ;
records - - ;
key_file_blocks + = info - > s - > keyinfo [ nr ] . base . block_length ;
}
if ( testflag & T_VERBOSE )
{
if ( next_link ! = NI_POS_ERROR )
printf ( " %10lu \n " , next_link ) ;
else
puts ( " " ) ;
}
DBUG_RETURN ( next_link ! = NI_POS_ERROR ) ;
} /* check_k_link */
/* Kontrollerar storleken p} filerna */
static int chk_size ( register N_INFO * info )
{
int error = 0 ;
register my_off_t skr , size ;
DBUG_ENTER ( " chk_size " ) ;
if ( ! ( testflag & T_SILENT ) ) puts ( " - check file-size " ) ;
size = my_seek ( info - > s - > kfile , 0L , MY_SEEK_END , MYF ( 0 ) ) ;
if ( ( skr = ( my_off_t ) info - > s - > state . key_file_length ) ! = size )
{
if ( skr > size )
{
error = 1 ;
print_error ( " Size of indexfile is: %-8lu Should be: %lu " ,
( ulong ) size , ( ulong ) skr ) ;
}
else
print_warning ( " Size of indexfile is: %-8lu Should be: %lu " ,
( ulong ) size , ( ulong ) skr ) ;
}
if ( ! ( testflag & T_VERY_SILENT ) & &
! ( info - > s - > base . options & HA_OPTION_COMPRESS_RECORD ) & &
info - > s - > state . key_file_length >
( ulong ) ( ulong_to_double ( info - > s - > base . max_key_file_length ) * 0.9 ) )
print_warning ( " Keyfile is almost full, %10lu of %10lu used " ,
info - > s - > state . key_file_length ,
info - > s - > base . max_key_file_length - 1 ) ;
size = my_seek ( info - > dfile , 0L , MY_SEEK_END , MYF ( 0 ) ) ;
skr = ( my_off_t ) info - > s - > state . data_file_length ;
if ( info - > s - > base . options & HA_OPTION_COMPRESS_RECORD )
skr + = MEMMAP_EXTRA_MARGIN ;
# ifdef USE_RELOC
if ( info - > data_file_type = = STATIC_RECORD & &
skr < ( my_off_t ) info - > s - > base . reloc * info - > s - > base . min_pack_length )
skr = ( my_off_t ) info - > s - > base . reloc * info - > s - > base . min_pack_length ;
# endif
if ( skr ! = size )
{
info - > s - > state . data_file_length = ( ulong ) size ; /* Skipp other errors */
if ( skr > size & & skr ! = size + MEMMAP_EXTRA_MARGIN )
{
error = 1 ;
print_error ( " Size of datafile is: %-8lu Should be: %lu " ,
( ulong ) size , ( ulong ) skr ) ;
}
else
{
print_warning ( " Size of datafile is: %-8lu Should be: %lu " ,
( ulong ) size , ( ulong ) skr ) ;
}
}
if ( ! ( testflag & T_VERY_SILENT ) & &
! ( info - > s - > base . options & HA_OPTION_COMPRESS_RECORD ) & &
info - > s - > state . data_file_length >
( ulong ) ( ulong_to_double ( info - > s - > base . max_data_file_length ) * 0.9 ) )
print_warning ( " Datafile is almost full, %10lu of %10lu used " ,
info - > s - > state . data_file_length ,
info - > s - > base . max_data_file_length - 1 ) ;
DBUG_RETURN ( error ) ;
} /* chk_size */
/* Kontrollerar nycklarna */
2001-08-22 00:45:07 +02:00
static int chk_key ( register N_INFO * info )
2000-07-31 21:29:14 +02:00
{
uint key ;
ulong keys , all_keydata , all_totaldata , key_totlength , length ,
init_checksum , old_record_checksum ;
ISAM_SHARE * share = info - > s ;
N_KEYDEF * keyinfo ;
DBUG_ENTER ( " chk_key " ) ;
if ( ! ( testflag & T_SILENT ) ) puts ( " - check index reference " ) ;
all_keydata = all_totaldata = key_totlength = old_record_checksum = 0 ;
init_checksum = record_checksum ;
if ( ! ( share - > base . options &
( HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD ) ) )
old_record_checksum = calc_checksum ( share - > state . records + share - > state . del - 1 ) *
share - > base . reclength ;
for ( key = 0 , keyinfo = & share - > keyinfo [ 0 ] ; key < share - > state . keys ;
key + + , keyinfo + + )
{
record_checksum = init_checksum ;
unique_count = 0L ;
if ( ( ! ( testflag & T_SILENT ) ) & & share - > state . keys > 1 )
printf ( " - check data record references index: %d \n " , key + 1 ) ;
if ( share - > state . key_root [ key ] = = NI_POS_ERROR & &
share - > state . records = = 0 )
continue ;
if ( ! _nisam_fetch_keypage ( info , keyinfo , share - > state . key_root [ key ] , info - > buff ,
0 ) )
{
print_error ( " Can't read indexpage from filepos: %lu " ,
( ulong ) share - > state . key_root [ key ] ) ;
DBUG_RETURN ( - 1 ) ;
}
key_file_blocks + = keyinfo - > base . block_length ;
keys = keydata = totaldata = key_blocks = 0 ; max_level = 0 ;
if ( chk_index ( info , keyinfo , share - > state . key_root [ key ] , info - > buff , & keys , 1 ) )
DBUG_RETURN ( - 1 ) ;
if ( keys ! = share - > state . records )
{
print_error ( " Found %lu keys of %lu " , ( ulong ) keys ,
( ulong ) share - > state . records ) ;
DBUG_RETURN ( - 1 ) ;
}
if ( ! key & & ( share - > base . options &
( HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD ) ) )
old_record_checksum = record_checksum ;
else if ( old_record_checksum ! = record_checksum )
{
if ( key )
print_error ( " Key %u doesn't point at same records that key 1 " ,
key + 1 ) ;
else
print_error ( " Key 1 doesn't point at all records " ) ;
DBUG_RETURN ( - 1 ) ;
}
length = ( ulong ) isam_key_length ( info , keyinfo ) * keys + key_blocks * 2 ;
if ( testflag & T_INFO & & totaldata ! = 0L & & keys ! = 0L )
printf ( " Key: %2d: Keyblocks used: %3d%% Packed: %4d%% Max levels: %2d \n " ,
key + 1 ,
( int ) ( keydata * 100.0 / totaldata ) ,
( int ) ( ( long ) ( length - keydata ) * 100.0 / ( double ) length ) ,
max_level ) ;
all_keydata + = keydata ; all_totaldata + = totaldata ; key_totlength + = length ;
share - > base . rec_per_key [ key ] =
unique_count ? ( ( share - > state . records + unique_count / 2 ) /
unique_count ) : 1L ;
}
if ( testflag & T_INFO )
{
if ( all_totaldata ! = 0L & & share - > state . keys ! = 1 )
printf ( " Total: Keyblocks used: %3d%% Packed: %4d%% \n \n " ,
( int ) ( all_keydata * 100.0 / all_totaldata ) ,
( int ) ( ( long ) ( key_totlength - all_keydata ) * 100.0 /
( double ) key_totlength ) ) ;
else if ( all_totaldata ! = 0L & & share - > state . keys )
puts ( " " ) ;
}
if ( key_file_blocks ! = share - > state . key_file_length )
print_warning ( " Some data are unreferenced in keyfile " ) ;
record_checksum - = init_checksum ; /* Remove delete links */
if ( testflag & T_STATISTICS )
DBUG_RETURN ( update_state_info ( info , UPDATE_STAT ) ) ;
DBUG_RETURN ( 0 ) ;
} /* chk_key */
/* Check if index is ok */
2001-08-22 00:45:07 +02:00
static int chk_index ( N_INFO * info , N_KEYDEF * keyinfo , ulong page , uchar * buff ,
ulong * keys , uint level )
2000-07-31 21:29:14 +02:00
{
int flag ;
uint used_length , comp_flag , nod_flag ;
uchar key [ N_MAX_POSSIBLE_KEY_BUFF ] , * temp_buff , * keypos , * endpos ;
ulong next_page , record ;
DBUG_ENTER ( " chk_index " ) ;
DBUG_DUMP ( " buff " , ( byte * ) buff , getint ( buff ) ) ;
if ( ! ( temp_buff = ( uchar * ) my_alloca ( ( uint ) keyinfo - > base . block_length ) ) )
{
print_error ( " Not Enough memory " ) ;
DBUG_RETURN ( - 1 ) ;
}
if ( keyinfo - > base . flag & HA_NOSAME )
comp_flag = SEARCH_FIND ; /* Not dupplicates */
else
comp_flag = SEARCH_SAME ; /* Keys in positionorder */
nod_flag = test_if_nod ( buff ) ;
used_length = getint ( buff ) ;
keypos = buff + 2 + nod_flag ;
endpos = buff + used_length ;
keydata + = used_length ; totaldata + = keyinfo - > base . block_length ; /* INFO */
key_blocks + + ;
if ( level > max_level )
max_level = level ;
if ( used_length > keyinfo - > base . block_length )
{
print_error ( " Wrong pageinfo at page: %lu " , ( ulong ) page ) ;
goto err ;
}
for ( ; ; )
{
if ( nod_flag )
{
next_page = _nisam_kpos ( nod_flag , keypos ) ;
if ( next_page > info - > s - > state . key_file_length | |
( nod_flag & & ( next_page & ( info - > s - > blocksize - 1 ) ) ) )
{
my_off_t max_length = my_seek ( info - > s - > kfile , 0L , MY_SEEK_END , MYF ( 0 ) ) ;
print_error ( " Wrong pagepointer: %lu at page: %lu " ,
( ulong ) next_page , ( ulong ) page ) ;
if ( next_page + info - > s - > blocksize > max_length )
goto err ;
info - > s - > state . key_file_length = ( ulong ) ( max_length &
~ ( my_off_t )
( info - > s - > blocksize - 1 ) ) ;
}
if ( ! _nisam_fetch_keypage ( info , keyinfo , next_page , temp_buff , 0 ) )
{
print_error ( " Can't read key from filepos: %lu " , ( ulong ) next_page ) ;
goto err ;
}
key_file_blocks + = keyinfo - > base . block_length ;
if ( chk_index ( info , keyinfo , next_page , temp_buff , keys , level + 1 ) )
goto err ;
}
if ( keypos > = endpos | |
( * keyinfo - > get_key ) ( keyinfo , nod_flag , & keypos , key ) = = 0 )
break ;
if ( ( * keys ) + + & &
( flag = _nisam_key_cmp ( keyinfo - > seg , info - > lastkey , key , 0 , comp_flag ) ) > = 0 )
{
DBUG_DUMP ( " old " , ( byte * ) info - > lastkey ,
_nisam_keylength ( keyinfo , info - > lastkey ) ) ;
DBUG_DUMP ( " new " , ( byte * ) key , _nisam_keylength ( keyinfo , key ) ) ;
if ( comp_flag = = SEARCH_FIND & & flag = = 0 )
print_error ( " Found dupplicated key at page %lu " , ( ulong ) page ) ;
else
print_error ( " Key in wrong position at page %lu " , ( ulong ) page ) ;
goto err ;
}
if ( testflag & T_STATISTICS )
{
if ( * keys = = 1L | |
_nisam_key_cmp ( keyinfo - > seg , info - > lastkey , key , 0 , SEARCH_FIND ) )
unique_count + + ;
}
VOID ( _nisam_move_key ( keyinfo , ( uchar * ) info - > lastkey , key ) ) ;
record = _nisam_dpos ( info , nod_flag , keypos ) ;
if ( record > = info - > s - > state . data_file_length )
{
print_error ( " Found key at page %lu that points to record outside datafile " , page ) ;
DBUG_PRINT ( " test " , ( " page: %lu record: %lu filelength: %lu " ,
( ulong ) page , ( ulong ) record ,
( ulong ) info - > s - > state . data_file_length ) ) ;
DBUG_DUMP ( " key " , ( byte * ) info - > lastkey , info - > s - > base . max_key_length ) ;
goto err ;
}
record_checksum + = record ;
}
if ( keypos ! = endpos )
{
print_error ( " Keyblock size at page %lu is not correct. Block length: %d key length: %d " , ( ulong ) page , used_length , ( keypos - buff ) ) ;
goto err ;
}
my_afree ( ( byte * ) temp_buff ) ;
DBUG_RETURN ( 0 ) ;
err :
my_afree ( ( byte * ) temp_buff ) ;
DBUG_RETURN ( 1 ) ;
} /* chk_index */
/* Calculate a checksum of 1+2+3+4...N = N*(N+1)/2 without overflow */
static ulong calc_checksum ( count )
ulong count ;
{
ulong sum , a , b ;
DBUG_ENTER ( " calc_checksum " ) ;
sum = 0 ;
a = count ; b = count + 1 ;
if ( a & 1 )
b > > = 1 ;
else
a > > = 1 ;
while ( b )
{
if ( b & 1 )
sum + = a ;
a < < = 1 ; b > > = 1 ;
}
DBUG_PRINT ( " exit " , ( " sum: %lx " , sum ) ) ;
DBUG_RETURN ( sum ) ;
} /* calc_checksum */
/* Calc length of key in normal isam */
2001-08-22 00:45:07 +02:00
static uint isam_key_length ( N_INFO * info , reg1 N_KEYDEF * keyinfo )
2000-07-31 21:29:14 +02:00
{
uint length ;
N_KEYSEG * keyseg ;
DBUG_ENTER ( " isam_key_length " ) ;
length = info - > s - > rec_reflength ;
for ( keyseg = keyinfo - > seg ; keyseg - > base . type ; keyseg + + )
length + = keyseg - > base . length ;
DBUG_PRINT ( " exit " , ( " length: %d " , length ) ) ;
DBUG_RETURN ( length ) ;
} /* key_length */
/* Check that record-link is ok */
static int chk_data_link ( info , extend )
reg1 N_INFO * info ;
int extend ;
{
int error , got_error , flag ;
uint key , left_length , b_type ;
ulong records , del_blocks , used , empty , pos , splitts , start_recpos ,
del_length , link_used , intern_record_checksum , start_block ;
byte * record , * to ;
N_KEYDEF * keyinfo ;
BLOCK_INFO block_info ;
DBUG_ENTER ( " chk_data_link " ) ;
if ( ! ( info - > s - > base . options & ( HA_OPTION_PACK_RECORD |
HA_OPTION_COMPRESS_RECORD ) ) & &
! extend )
DBUG_RETURN ( 0 ) ;
if ( ! ( testflag & T_SILENT ) )
{
if ( extend )
puts ( " - check records and index references " ) ;
else
puts ( " - check record links " ) ;
}
if ( ! ( record = ( byte * ) my_alloca ( info - > s - > base . reclength ) ) )
{
print_error ( " Not Enough memory " ) ;
DBUG_RETURN ( - 1 ) ;
}
records = used = link_used = splitts = del_blocks = del_length =
intern_record_checksum = crc = 0L ;
LINT_INIT ( left_length ) ; LINT_INIT ( start_recpos ) ; LINT_INIT ( to ) ;
got_error = error = 0 ;
empty = pos = info - > s - > pack . header_length ;
while ( pos < info - > s - > state . data_file_length )
{
switch ( info - > s - > data_file_type ) {
case STATIC_RECORD :
if ( my_b_read ( & read_cache , ( byte * ) record , info - > s - > base . reclength ) )
goto err ;
start_recpos = pos ;
pos + = info - > s - > base . reclength ;
splitts + + ;
if ( * record = = ' \0 ' )
{
del_blocks + + ;
del_length + = info - > s - > base . reclength ;
continue ; /* Record removed */
}
used + = info - > s - > base . reclength ;
break ;
case DYNAMIC_RECORD :
flag = block_info . second_read = 0 ;
block_info . next_filepos = pos ;
do
{
if ( _nisam_read_cache ( & read_cache , ( byte * ) block_info . header ,
( start_block = block_info . next_filepos ) ,
sizeof ( block_info . header ) , test ( ! flag ) | 2 ) )
goto err ;
b_type = _nisam_get_block_info ( & block_info , - 1 , start_block ) ;
if ( b_type & ( BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
BLOCK_FATAL_ERROR ) )
{
if ( b_type & BLOCK_SYNC_ERROR )
{
if ( flag )
{
print_error ( " Unexpected byte: %d at link: %lu " ,
( int ) block_info . header [ 0 ] , ( ulong ) start_block ) ;
goto err2 ;
}
pos = block_info . filepos + block_info . block_len ;
goto next ;
}
if ( b_type & BLOCK_DELETED )
{
if ( block_info . block_len < info - > s - > base . min_block_length | |
block_info . block_len - 4 > ( uint ) info - > s - > base . max_pack_length )
{
print_error ( " Deleted block with impossible length %u at %lu " ,
block_info . block_len , ( ulong ) pos ) ;
goto err2 ;
}
del_blocks + + ;
del_length + = block_info . block_len ;
pos = block_info . filepos + block_info . block_len ;
splitts + + ;
goto next ;
}
print_error ( " Wrong bytesec: %d-%d-%d at linkstart: %lu " ,
block_info . header [ 0 ] , block_info . header [ 1 ] ,
block_info . header [ 2 ] , ( ulong ) start_block ) ;
goto err2 ;
}
if ( info - > s - > state . data_file_length < block_info . filepos +
block_info . block_len )
{
print_error ( " Recordlink that points outside datafile at %lu " ,
( ulong ) pos ) ;
got_error = 1 ;
break ;
}
splitts + + ;
if ( ! flag + + ) /* First block */
{
start_recpos = pos ;
pos = block_info . filepos + block_info . block_len ;
if ( block_info . rec_len > ( uint ) info - > s - > base . max_pack_length )
{
print_error ( " Found too long record at %lu " , ( ulong ) start_recpos ) ;
got_error = 1 ;
break ;
}
if ( info - > s - > base . blobs )
{
if ( ! ( to = fix_rec_buff_for_blob ( info , block_info . rec_len ) ) )
{
print_error ( " Not enough memory for blob at %lu " ,
( ulong ) start_recpos ) ;
got_error = 1 ;
break ;
}
}
else
to = info - > rec_buff ;
left_length = block_info . rec_len ;
}
if ( left_length < block_info . data_len )
{
print_error ( " Found too long record at %lu " , ( ulong ) start_recpos ) ;
got_error = 1 ; break ;
}
if ( _nisam_read_cache ( & read_cache , ( byte * ) to , block_info . filepos ,
( uint ) block_info . data_len , test ( flag = = 1 ) ) )
goto err ;
to + = block_info . data_len ;
link_used + = block_info . filepos - start_block ;
used + = block_info . filepos - start_block + block_info . data_len ;
empty + = block_info . block_len - block_info . data_len ;
left_length - = block_info . data_len ;
if ( left_length )
{
if ( b_type & BLOCK_LAST )
{
print_error ( " Record link to short for record at %lu " ,
( ulong ) start_recpos ) ;
got_error = 1 ;
break ;
}
if ( info - > s - > state . data_file_length < block_info . next_filepos )
{
print_error ( " Found next-recordlink that points outside datafile at %lu " ,
( ulong ) block_info . filepos ) ;
got_error = 1 ;
break ;
}
}
} while ( left_length ) ;
if ( ! got_error )
{
if ( _nisam_rec_unpack ( info , record , info - > rec_buff , block_info . rec_len ) = =
MY_FILE_ERROR )
{
print_error ( " Found wrong record at %lu " , ( ulong ) start_recpos ) ;
got_error = 1 ;
}
if ( testflag & ( T_EXTEND | T_VERBOSE ) )
{
if ( _nisam_rec_check ( info , record ) )
{
print_error ( " Found wrong packed record at %lu " ,
( ulong ) start_recpos ) ;
got_error = 1 ;
}
}
}
else if ( ! flag )
pos = block_info . filepos + block_info . block_len ;
break ;
case COMPRESSED_RECORD :
if ( _nisam_read_cache ( & read_cache , ( byte * ) block_info . header , pos , 3 , 1 ) )
goto err ;
start_recpos = pos ;
splitts + + ;
VOID ( _nisam_pack_get_block_info ( & block_info , info - > s - > pack . ref_length , - 1 ,
start_recpos ) ) ;
pos = start_recpos + info - > s - > pack . ref_length + block_info . rec_len ;
if ( block_info . rec_len < ( uint ) info - > s - > min_pack_length | |
block_info . rec_len > ( uint ) info - > s - > max_pack_length )
{
print_error ( " Found block with wrong recordlength: %d at %lu " ,
block_info . rec_len , ( ulong ) start_recpos ) ;
got_error = 1 ;
break ;
}
if ( _nisam_read_cache ( & read_cache , ( byte * ) info - > rec_buff ,
block_info . filepos , block_info . rec_len , 1 ) )
goto err ;
if ( _nisam_pack_rec_unpack ( info , record , info - > rec_buff , block_info . rec_len ) )
{
print_error ( " Found wrong record at %lu " , ( ulong ) start_recpos ) ;
got_error = 1 ;
}
crc ^ = checksum ( record , info - > s - > base . reclength ) ;
link_used + = info - > s - > pack . ref_length ;
used + = block_info . rec_len + info - > s - > pack . ref_length ;
}
if ( ! got_error )
{
intern_record_checksum + = start_recpos ;
records + + ;
if ( testflag & T_WRITE_LOOP & & records % WRITE_COUNT = = 0 )
{
printf ( " %lu \r " , ( ulong ) records ) ; VOID ( fflush ( stdout ) ) ;
}
if ( extend )
{
for ( key = 0 , keyinfo = info - > s - > keyinfo ; key < info - > s - > state . keys ;
key + + , keyinfo + + )
{
VOID ( _nisam_make_key ( info , key , info - > lastkey , record , start_recpos ) ) ;
if ( _nisam_search ( info , keyinfo , info - > lastkey , 0 , SEARCH_SAME ,
info - > s - > state . key_root [ key ] ) )
{
print_error ( " Record at: %10lu Can't find key for index: %2d " ,
start_recpos , key + 1 ) ;
if ( error + + > MAXERR | | ! ( testflag & T_VERBOSE ) )
goto err2 ;
}
}
}
}
else
{
got_error = 0 ;
if ( error + + > MAXERR | | ! ( testflag & T_VERBOSE ) )
goto err2 ;
}
next : ; /* Next record */
}
if ( testflag & T_WRITE_LOOP )
{
VOID ( fputs ( " \r " , stdout ) ) ; VOID ( fflush ( stdout ) ) ;
}
if ( records ! = info - > s - > state . records )
{
print_error ( " Record-count is not ok; is %-10lu Should be: %lu " ,
( ulong ) records , ( ulong ) info - > s - > state . records ) ;
error = 1 ;
}
else if ( record_checksum ! = intern_record_checksum & & info - > s - > state . keys )
{
2000-08-16 11:17:40 +02:00
print_error ( " Keypointers and records don't match " ) ;
2000-07-31 21:29:14 +02:00
error = 1 ;
}
if ( used + empty + del_length ! = info - > s - > state . data_file_length )
{
print_warning ( " Found %lu record-data and %lu unused data and %lu deleted-data \n Total %lu, Should be: %lu " ,
( ulong ) used , ( ulong ) empty , ( ulong ) del_length ,
( ulong ) ( used + empty + del_length ) ,
( ulong ) info - > s - > state . data_file_length ) ;
}
if ( del_blocks ! = info - > s - > state . del )
{
print_warning ( " Found %10lu deleted blocks Should be: %lu " ,
( ulong ) del_blocks , ( ulong ) info - > s - > state . del ) ;
}
if ( splitts ! = info - > s - > state . splitt )
{
print_warning ( " Found %10lu parts Should be: %lu parts " ,
( ulong ) splitts , ( ulong ) info - > s - > state . splitt ) ;
}
if ( ( info - > s - > base . options & HA_OPTION_COMPRESS_RECORD ) & &
crc ! = info - > s - > state . uniq )
print_warning ( " Wrong checksum for records; Restore uncompressed table " ) ;
if ( testflag & T_INFO )
{
if ( warning_printed | | error_printed )
puts ( " " ) ;
if ( used ! = 0 & & ! error_printed )
{
printf ( " Records:%17lu M.recordlength:%8lu Packed:%14.0f%% \n " ,
records , ( used - link_used ) / records ,
( info - > s - > base . blobs ? 0 :
( ulong_to_double ( info - > s - > base . reclength * records ) - used ) /
ulong_to_double ( info - > s - > base . reclength * records ) * 100.0 ) ) ;
printf ( " Recordspace used:%8.0f%% Empty space:%11d%% Blocks/Record: %6.2f \n " ,
( ulong_to_double ( used - link_used ) / ulong_to_double ( used - link_used + empty ) * 100.0 ) ,
( ! records ? 100 : ( int ) ( ulong_to_double ( del_length + empty ) / used * 100.0 ) ) ,
ulong_to_double ( splitts - del_blocks ) / records ) ;
}
printf ( " Record blocks:%12lu Delete blocks:%10lu \n " ,
splitts - del_blocks , del_blocks ) ;
printf ( " Record data: %12lu Deleted data :%10lu \n " ,
used - link_used , del_length ) ;
printf ( " Lost space: %12lu Linkdata: %10lu \n " ,
empty , link_used ) ;
}
my_afree ( ( gptr ) record ) ;
DBUG_RETURN ( error ) ;
err :
print_error ( " got error: %d when reading datafile " , my_errno ) ;
err2 :
my_afree ( ( gptr ) record ) ;
DBUG_RETURN ( 1 ) ;
} /* chk_data_link */
/* Recover old table by reading each record and writing all keys */
/* Save new datafile-name in temp_filename */
static int rep ( info , name )
reg1 N_INFO * info ;
my_string name ;
{
int error , got_error ;
uint i ;
ulong start_records , new_header_length , del ;
File new_file ;
ISAM_SHARE * share = info - > s ;
DBUG_ENTER ( " rep " ) ;
start_records = share - > state . records ;
new_header_length = ( testflag & T_UNPACK ) ? 0L : share - > pack . header_length ;
got_error = 1 ;
new_file = - 1 ;
if ( ! ( testflag & T_SILENT ) )
{
printf ( " - recovering ISAM-table '%s' \n " , name ) ;
printf ( " Data records: %lu \n " , ( ulong ) share - > state . records ) ;
}
2002-07-23 17:31:22 +02:00
VOID ( init_key_cache ( use_buffers ) ) ;
2000-07-31 21:29:14 +02:00
if ( init_io_cache ( & read_cache , info - > dfile , ( uint ) read_buffer_length ,
READ_CACHE , share - > pack . header_length , 1 , MYF ( MY_WME ) ) )
goto err ;
if ( ! rep_quick )
if ( init_io_cache ( & info - > rec_cache , - 1 , ( uint ) write_buffer_length ,
WRITE_CACHE , new_header_length , 1 ,
MYF ( MY_WME | MY_WAIT_IF_FULL ) ) )
goto err ;
info - > opt_flag | = WRITE_CACHE_USED ;
sort_info . start_recpos = 0 ;
sort_info . buff = 0 ; sort_info . buff_length = 0 ;
if ( ! ( sort_info . record = ( byte * ) my_alloca ( ( uint ) share - > base . reclength ) ) )
{
print_error ( " Not Enough memory " ) ;
goto err ;
}
if ( ! rep_quick )
{
if ( ( new_file = my_create ( fn_format ( temp_filename , name , " " , DATA_TMP_EXT ,
2 + 4 ) ,
0 , tmpfile_createflag , MYF ( 0 ) ) ) < 0 )
{
print_error ( " Can't create new tempfile: '%s' " , temp_filename ) ;
goto err ;
}
if ( filecopy ( new_file , info - > dfile , 0L , new_header_length , " datafile-header " ) )
goto err ;
share - > state . dellink = NI_POS_ERROR ;
info - > rec_cache . file = new_file ;
if ( testflag & T_UNPACK )
share - > base . options & = ~ HA_OPTION_COMPRESS_RECORD ;
}
sort_info . info = info ;
sort_info . pos = sort_info . max_pos = share - > pack . header_length ;
sort_info . filepos = new_header_length ;
read_cache . end_of_file = sort_info . filelength = ( ulong )
my_seek ( info - > dfile , 0L , MY_SEEK_END , MYF ( 0 ) ) ;
sort_info . dupp = 0 ;
sort_info . fix_datafile = ( my_bool ) ( ! rep_quick ) ;
sort_info . max_records = LONG_MAX ;
if ( ( sort_info . new_data_file_type = share - > data_file_type ) = =
COMPRESSED_RECORD & & testflag & T_UNPACK )
{
if ( share - > base . options & HA_OPTION_PACK_RECORD )
sort_info . new_data_file_type = DYNAMIC_RECORD ;
else
sort_info . new_data_file_type = STATIC_RECORD ;
}
del = share - > state . del ;
share - > state . records = share - > state . del = share - > state . empty =
share - > state . splitt = 0 ;
info - > update = ( short ) ( HA_STATE_CHANGED | HA_STATE_ROW_CHANGED ) ;
for ( i = 0 ; i < N_MAXKEY ; i + + )
share - > state . key_del [ i ] = share - > state . key_root [ i ] = NI_POS_ERROR ;
share - > state . key_file_length = share - > base . keystart ;
lock_memory ( ) ; /* Everything is alloced */
while ( ! ( error = sort_get_next_record ( ) ) )
{
if ( writekeys ( info , ( byte * ) sort_info . record , sort_info . filepos ) )
{
if ( my_errno ! = HA_ERR_FOUND_DUPP_KEY ) goto err ;
DBUG_DUMP ( " record " , ( byte * ) sort_info . record , share - > base . pack_reclength ) ;
print_info ( " Dupplicate key %2d for record at %10lu against new record at %10lu " , info - > errkey + 1 , sort_info . start_recpos , info - > int_pos ) ;
if ( testflag & T_VERBOSE )
{
VOID ( _nisam_make_key ( info , ( uint ) info - > errkey , info - > lastkey ,
sort_info . record , 0L ) ) ;
_nisam_print_key ( stdout , share - > keyinfo [ info - > errkey ] . seg , info - > lastkey ) ;
}
sort_info . dupp + + ;
if ( rep_quick = = 1 )
{
error_printed = 1 ;
goto err ;
}
continue ;
}
if ( sort_write_record ( ) )
goto err ;
}
if ( error > 0 | | write_data_suffix ( info ) | |
flush_io_cache ( & info - > rec_cache ) | | read_cache . error < 0 )
goto err ;
if ( testflag & T_WRITE_LOOP )
{
VOID ( fputs ( " \r " , stdout ) ) ; VOID ( fflush ( stdout ) ) ;
}
2002-08-08 02:12:02 +02:00
if ( my_chsize ( share - > kfile , share - > state . key_file_length , 0 , MYF ( 0 ) ) )
2000-07-31 21:29:14 +02:00
{
print_warning ( " Can't change size of indexfile, error: %d " , my_errno ) ;
goto err ;
}
if ( rep_quick & & del + sort_info . dupp ! = share - > state . del )
{
print_error ( " Couldn't fix table with quick recovery: Found wrong number of deleted records " ) ;
print_error ( " Run recovery again without -q " ) ;
got_error = 1 ;
goto err ;
}
if ( ! rep_quick )
{
info - > dfile = new_file ;
share - > state . data_file_length = sort_info . filepos ;
share - > state . splitt = share - > state . records ; /* Only hole records */
out_flag | = O_NEW_DATA ; /* Data in new file */
share - > state . version = ( ulong ) time ( ( time_t * ) 0 ) ; /* Force reopen */
}
else
share - > state . data_file_length = sort_info . max_pos ;
if ( ! ( testflag & T_SILENT ) )
{
if ( start_records ! = share - > state . records )
printf ( " Data records: %lu \n " , ( ulong ) share - > state . records ) ;
if ( sort_info . dupp )
print_warning ( " %lu records have been removed " , ( ulong ) sort_info . dupp ) ;
}
got_error = 0 ;
err :
if ( got_error )
{
if ( ! error_printed )
print_error ( " %d for record at pos %lu " , my_errno ,
( ulong ) sort_info . start_recpos ) ;
if ( new_file > = 0 )
{
VOID ( my_close ( new_file , MYF ( 0 ) ) ) ;
VOID ( my_delete ( temp_filename , MYF ( MY_WME ) ) ) ;
}
}
if ( sort_info . record )
{
my_afree ( sort_info . record ) ;
}
my_free ( sort_info . buff , MYF ( MY_ALLOW_ZERO_PTR ) ) ;
VOID ( end_io_cache ( & read_cache ) ) ;
info - > opt_flag & = ~ ( READ_CACHE_USED | WRITE_CACHE_USED ) ;
VOID ( end_io_cache ( & info - > rec_cache ) ) ;
got_error | = flush_blocks ( share - > kfile ) ;
if ( ! got_error & & testflag & T_UNPACK )
{
share - > state . header . options [ 0 ] & = ( uchar ) ~ HA_OPTION_COMPRESS_RECORD ;
share - > pack . header_length = 0 ;
share - > data_file_type = sort_info . new_data_file_type ;
}
DBUG_RETURN ( got_error ) ;
} /* rep */
/* Uppdaterar nyckelfilen i samband med reparation */
static int writekeys ( register N_INFO * info , byte * buff , ulong filepos )
{
register uint i ;
uchar * key ;
DBUG_ENTER ( " writekeys " ) ;
key = info - > lastkey + info - > s - > base . max_key_length ;
for ( i = 0 ; i < info - > s - > state . keys ; i + + )
{
VOID ( _nisam_make_key ( info , i , key , buff , filepos ) ) ;
if ( _nisam_ck_write ( info , i , key ) ) goto err ;
}
DBUG_RETURN ( 0 ) ;
err :
if ( my_errno = = HA_ERR_FOUND_DUPP_KEY )
{
info - > errkey = ( int ) i ; /* This key was found */
while ( i - - > 0 )
{
VOID ( _nisam_make_key ( info , i , key , buff , filepos ) ) ;
if ( _nisam_ck_delete ( info , i , key ) ) break ;
}
}
DBUG_PRINT ( " error " , ( " errno: %d " , my_errno ) ) ;
DBUG_RETURN ( - 1 ) ;
} /* writekeys */
/* Write info about table */
static void descript ( info , name )
reg1 N_INFO * info ;
my_string name ;
{
uint key , field , start , len ;
reg3 N_KEYDEF * keyinfo ;
reg2 N_KEYSEG * keyseg ;
reg4 const char * text ;
char buff [ 40 ] , length [ 10 ] , * pos , * end ;
enum en_fieldtype type ;
ISAM_SHARE * share = info - > s ;
DBUG_ENTER ( " describe " ) ;
printf ( " \n ISAM file: %s \n " , name ) ;
if ( testflag & T_VERBOSE )
{
printf ( " Isam-version: %d \n " , ( int ) share - > state . header . file_version [ 3 ] ) ;
if ( share - > base . create_time )
{
get_date ( buff , 1 , share - > base . create_time ) ;
printf ( " Creation time: %s \n " , buff ) ;
}
if ( share - > base . isamchk_time )
{
get_date ( buff , 1 , share - > base . isamchk_time ) ;
printf ( " Recover time: %s \n " , buff ) ;
}
}
printf ( " Data records: %10lu Deleted blocks: %10lu \n " ,
share - > state . records , share - > state . del ) ;
if ( testflag & T_SILENT )
DBUG_VOID_RETURN ; /* This is enough */
if ( testflag & T_VERBOSE )
{
# ifdef USE_RELOC
printf ( " Init-relocation: %10lu \n " , share - > base . reloc ) ;
# endif
printf ( " Datafile Parts: %10lu Deleted data: %10lu \n " ,
share - > state . splitt , share - > state . empty ) ;
printf ( " Datafile pointer (bytes):%6d Keyfile pointer (bytes):%6d \n " ,
share - > rec_reflength , share - > base . key_reflength ) ;
if ( info - > s - > base . reloc = = 1L & & info - > s - > base . records = = 1L )
puts ( " This is a one-record table " ) ;
else
{
if ( share - > base . max_data_file_length ! = NI_POS_ERROR | |
share - > base . max_key_file_length ! = NI_POS_ERROR )
printf ( " Max datafile length: %10lu Max keyfile length: %10lu \n " ,
share - > base . max_data_file_length - 1 ,
share - > base . max_key_file_length - 1 ) ;
}
}
printf ( " Recordlength: %10d \n " , ( int ) share - > base . reclength ) ;
VOID ( fputs ( " Record format: " , stdout ) ) ;
if ( share - > base . options & HA_OPTION_COMPRESS_RECORD )
puts ( " Compressed " ) ;
else if ( share - > base . options & HA_OPTION_PACK_RECORD )
puts ( " Packed " ) ;
else
puts ( " Fixed length " ) ;
if ( share - > state . keys ! = share - > base . keys )
printf ( " Using only %d keys of %d possibly keys \n " , share - > state . keys ,
share - > base . keys ) ;
puts ( " \n table description: " ) ;
printf ( " Key Start Len Index Type " ) ;
if ( testflag & T_VERBOSE )
printf ( " Root Blocksize Rec/key " ) ;
VOID ( putchar ( ' \n ' ) ) ;
for ( key = 0 , keyinfo = & share - > keyinfo [ 0 ] ; key < share - > base . keys ;
key + + , keyinfo + + )
{
keyseg = keyinfo - > seg ;
if ( keyinfo - > base . flag & HA_NOSAME ) text = " unique " ;
else text = " multip. " ;
pos = buff ;
if ( keyseg - > base . flag & HA_REVERSE_SORT )
* pos + + = ' - ' ;
pos = strmov ( pos , type_names [ keyseg - > base . type ] ) ;
* pos + + = ' ' ;
* pos = 0 ;
if ( keyinfo - > base . flag & HA_PACK_KEY )
pos = strmov ( pos , packed_txt ) ;
if ( keyseg - > base . flag & HA_SPACE_PACK )
pos = strmov ( pos , diff_txt ) ;
printf ( " %-4d%-6d%-3d %-8s%-21s " ,
key + 1 , keyseg - > base . start + 1 , keyseg - > base . length , text , buff ) ;
if ( testflag & T_VERBOSE )
printf ( " %9ld %9d %9ld " ,
share - > state . key_root [ key ] , keyinfo - > base . block_length ,
share - > base . rec_per_key [ key ] ) ;
VOID ( putchar ( ' \n ' ) ) ;
while ( ( + + keyseg ) - > base . type )
{
pos = buff ;
if ( keyseg - > base . flag & HA_REVERSE_SORT )
* pos + + = ' - ' ;
pos = strmov ( pos , type_names [ keyseg - > base . type ] ) ;
* pos + + = ' ' ;
if ( keyseg - > base . flag & HA_SPACE_PACK )
pos = strmov ( pos , diff_txt ) ;
* pos = 0 ;
printf ( " %-6d%-3d %-24s \n " ,
keyseg - > base . start + 1 , keyseg - > base . length , buff ) ;
}
}
if ( verbose > 1 )
{
printf ( " \n Field Start Length Type " ) ;
if ( share - > base . options & HA_OPTION_COMPRESS_RECORD )
printf ( " Huff tree Bits " ) ;
VOID ( putchar ( ' \n ' ) ) ;
2001-03-21 00:02:22 +01:00
if ( verbose > 2 & & share - > base . pack_bits )
printf ( " - %-7d%-35s \n " , share - > base . pack_bits , " bit field " ) ;
2000-07-31 21:29:14 +02:00
start = 1 ;
for ( field = 0 ; field < share - > base . fields ; field + + )
{
if ( share - > base . options & HA_OPTION_COMPRESS_RECORD )
type = share - > rec [ field ] . base_type ;
else
type = ( enum en_fieldtype ) share - > rec [ field ] . base . type ;
end = strmov ( buff , field_pack [ type ] ) ;
# ifndef NOT_PACKED_DATABASES
if ( share - > base . options & HA_OPTION_COMPRESS_RECORD )
{
if ( share - > rec [ field ] . pack_type & PACK_TYPE_SELECTED )
end = strmov ( end , " , not_always " ) ;
if ( share - > rec [ field ] . pack_type & PACK_TYPE_SPACE_FIELDS )
end = strmov ( end , " , no empty " ) ;
if ( share - > rec [ field ] . pack_type & PACK_TYPE_ZERO_FILL )
{
sprintf ( end , " , zerofill(%d) " , share - > rec [ field ] . space_length_bits ) ;
end = strend ( end ) ;
}
}
if ( buff [ 0 ] = = ' , ' )
strmov ( buff , buff + 2 ) ;
# endif
len = ( uint ) ( int2str ( ( long ) share - > rec [ field ] . base . length , length , 10 ) -
length ) ;
if ( type = = FIELD_BLOB )
{
length [ len ] = ' + ' ;
VOID ( int2str ( ( long ) sizeof ( char * ) , length + len + 1 , 10 ) ) ;
}
printf ( " %-6d%-6d%-7s%-35s " , field + 1 , start , length , buff ) ;
# ifndef NOT_PACKED_DATABASES
if ( share - > base . options & HA_OPTION_COMPRESS_RECORD )
{
if ( share - > rec [ field ] . huff_tree )
printf ( " %3d %2d " ,
( uint ) ( share - > rec [ field ] . huff_tree - share - > decode_trees ) + 1 ,
share - > rec [ field ] . huff_tree - > quick_table_bits ) ;
}
# endif
VOID ( putchar ( ' \n ' ) ) ;
start + = share - > rec [ field ] . base . length ;
if ( type = = FIELD_BLOB )
start + = sizeof ( char * ) ;
}
}
DBUG_VOID_RETURN ;
} /* describe */
/* Change all key-pointers that points to a records */
static int movepoint ( info , record , oldpos , newpos , prot_key )
register N_INFO * info ;
byte * record ;
ulong oldpos , newpos ;
uint prot_key ;
{
register uint i ;
uchar * key ;
DBUG_ENTER ( " movepoint " ) ;
key = info - > lastkey + info - > s - > base . max_key_length ;
for ( i = 0 ; i < info - > s - > state . keys ; i + + )
{
if ( i ! = prot_key )
{
VOID ( _nisam_make_key ( info , i , key , record , oldpos ) ) ;
if ( info - > s - > keyinfo [ i ] . base . flag & HA_NOSAME )
{ /* Change pointer direct */
uint nod_flag ;
N_KEYDEF * keyinfo ;
keyinfo = info - > s - > keyinfo + i ;
if ( _nisam_search ( info , keyinfo , key , USE_HOLE_KEY ,
( uint ) ( SEARCH_SAME | SEARCH_SAVE_BUFF ) ,
info - > s - > state . key_root [ i ] ) )
DBUG_RETURN ( - 1 ) ;
nod_flag = test_if_nod ( info - > buff ) ;
_nisam_dpointer ( info , info - > int_keypos - nod_flag -
info - > s - > rec_reflength , newpos ) ;
if ( _nisam_write_keypage ( info , keyinfo , info - > int_pos , info - > buff ) )
DBUG_RETURN ( - 1 ) ;
}
else
{ /* Change old key to new */
if ( _nisam_ck_delete ( info , i , key ) )
DBUG_RETURN ( - 1 ) ;
VOID ( _nisam_make_key ( info , i , key , record , newpos ) ) ;
if ( _nisam_ck_write ( info , i , key ) )
DBUG_RETURN ( - 1 ) ;
}
}
}
DBUG_RETURN ( 0 ) ;
} /* movepoint */
/* Tell system that we want all memory for our cache */
static void lock_memory ( void )
{
# ifdef SUN_OS /* Key-cacheing thrases on sun 4.1 */
int success ;
success = mlockall ( MCL_CURRENT ) ; /* or plock(DATLOCK); */
if ( geteuid ( ) = = 0 & & success ! = 0 )
print_warning ( " Failed to lock memory. errno %d " , my_errno ) ;
# endif
} /* lock_memory */
/* Flush all changed blocks to disk */
static int flush_blocks ( file )
File file ;
{
if ( flush_key_blocks ( file , FLUSH_RELEASE ) )
{
print_error ( " %d when trying to write bufferts " , my_errno ) ;
return ( 1 ) ;
}
end_key_cache ( ) ;
return 0 ;
} /* flush_blocks */
/* Sort records according to one key */
static int sort_records ( info , name , sort_key , write_info )
register N_INFO * info ;
my_string name ;
uint sort_key ;
int write_info ;
{
int got_error ;
uint key ;
N_KEYDEF * keyinfo ;
File new_file ;
uchar * temp_buff ;
ulong old_record_count ;
ISAM_SHARE * share = info - > s ;
DBUG_ENTER ( " sort_records " ) ;
keyinfo = & share - > keyinfo [ sort_key ] ;
got_error = 1 ;
temp_buff = 0 ; record_buff = 0 ;
new_file = - 1 ;
if ( sort_key > = share - > state . keys )
{
print_error ( " Can't sort table '%s' on key %d. It has only %d keys " ,
name , sort_key + 1 , share - > state . keys ) ;
error_printed = 0 ;
DBUG_RETURN ( - 1 ) ;
}
if ( ! ( testflag & T_SILENT ) )
{
printf ( " - Sorting records in ISAM-table '%s' \n " , name ) ;
if ( write_info )
printf ( " Data records: %7lu Deleted: %7lu \n " ,
share - > state . records , share - > state . del ) ;
}
if ( share - > state . key_root [ sort_key ] = = NI_POS_ERROR )
DBUG_RETURN ( 0 ) ; /* Nothing to do */
2002-07-23 17:31:22 +02:00
init_key_cache ( use_buffers ) ;
2000-07-31 21:29:14 +02:00
if ( init_io_cache ( & info - > rec_cache , - 1 , ( uint ) write_buffer_length ,
WRITE_CACHE , share - > pack . header_length , 1 ,
MYF ( MY_WME | MY_WAIT_IF_FULL ) ) )
goto err ;
info - > opt_flag | = WRITE_CACHE_USED ;
if ( ! ( temp_buff = ( uchar * ) my_alloca ( ( uint ) keyinfo - > base . block_length ) ) )
{
print_error ( " Not Enough memory " ) ;
goto err ;
}
if ( ! ( record_buff = ( byte * ) my_alloca ( ( uint ) share - > base . reclength ) ) )
{
print_error ( " Not Enough memory " ) ;
goto err ;
}
if ( ( new_file = my_create ( fn_format ( temp_filename , name , " " , DATA_TMP_EXT , 2 + 4 ) ,
0 , tmpfile_createflag , MYF ( 0 ) ) ) < = 0 )
{
print_error ( " Can't create new tempfile: '%s' " , temp_filename ) ;
goto err ;
}
if ( filecopy ( new_file , info - > dfile , 0L , share - > pack . header_length ,
" datafile-header " ) )
goto err ;
info - > rec_cache . file = new_file ; /* Use this file for cacheing*/
lock_memory ( ) ;
for ( key = 0 ; key < share - > state . keys ; key + + )
share - > keyinfo [ key ] . base . flag | = HA_SORT_ALLOWS_SAME ;
if ( my_pread ( share - > kfile , ( byte * ) temp_buff ,
( uint ) keyinfo - > base . block_length ,
share - > state . key_root [ sort_key ] ,
MYF ( MY_NABP + MY_WME ) ) )
{
print_error ( " Can't read indexpage from filepos: %lu " ,
( ulong ) share - > state . key_root [ sort_key ] ) ;
goto err ;
}
/* Setup param for sort_write_record */
bzero ( ( char * ) & sort_info , sizeof ( sort_info ) ) ;
sort_info . info = info ;
sort_info . new_data_file_type = share - > data_file_type ;
sort_info . fix_datafile = 1 ;
sort_info . filepos = share - > pack . header_length ;
sort_info . record = record_buff ;
old_record_count = share - > state . records ;
share - > state . records = 0 ;
if ( sort_record_index ( info , keyinfo , share - > state . key_root [ sort_key ] , temp_buff ,
sort_key , new_file ) | |
write_data_suffix ( info ) | |
flush_io_cache ( & info - > rec_cache ) )
goto err ;
if ( share - > state . records ! = old_record_count )
{
print_error ( " found %lu of %lu records " ,
( ulong ) share - > state . records , ( ulong ) old_record_count ) ;
goto err ;
}
/* Put same locks as old file */
if ( lock_file ( new_file , 0L , F_WRLCK , " tempfile " , temp_filename ) )
goto err ;
VOID ( lock_file ( info - > dfile , 0L , F_UNLCK , " datafile of " , name ) ) ;
VOID ( my_close ( info - > dfile , MYF ( MY_WME ) ) ) ;
out_flag | = O_NEW_DATA ; /* Data in new file */
info - > dfile = new_file ; /* Use new indexfile */
share - > state . del = share - > state . empty = 0 ;
share - > state . dellink = NI_POS_ERROR ;
share - > state . data_file_length = sort_info . filepos ;
share - > state . splitt = share - > state . records ; /* Only hole records */
share - > state . version = ( ulong ) time ( ( time_t * ) 0 ) ;
info - > update = ( short ) ( HA_STATE_CHANGED | HA_STATE_ROW_CHANGED ) ;
if ( testflag & T_WRITE_LOOP )
{
VOID ( fputs ( " \r " , stdout ) ) ; VOID ( fflush ( stdout ) ) ;
}
got_error = 0 ;
err :
if ( got_error & & new_file > = 0 )
{
VOID ( my_close ( new_file , MYF ( MY_WME ) ) ) ;
VOID ( my_delete ( temp_filename , MYF ( MY_WME ) ) ) ;
}
if ( temp_buff )
{
my_afree ( ( gptr ) temp_buff ) ;
}
if ( record_buff )
{
my_afree ( record_buff ) ;
}
info - > opt_flag & = ~ ( READ_CACHE_USED | WRITE_CACHE_USED ) ;
VOID ( end_io_cache ( & info - > rec_cache ) ) ;
share - > base . sortkey = sort_key ;
DBUG_RETURN ( flush_blocks ( share - > kfile ) | got_error ) ;
} /* sort_records */
/* Sort records recursive using one index */
static int sort_record_index ( info , keyinfo , page , buff , sort_key , new_file )
N_INFO * info ;
N_KEYDEF * keyinfo ;
ulong page ;
uchar * buff ;
uint sort_key ;
File new_file ;
{
uint nod_flag , used_length ;
uchar * temp_buff , * keypos , * endpos ;
ulong next_page , rec_pos ;
uchar lastkey [ N_MAX_KEY_BUFF ] ;
DBUG_ENTER ( " sort_record_index " ) ;
nod_flag = test_if_nod ( buff ) ;
temp_buff = 0 ;
if ( nod_flag )
{
if ( ! ( temp_buff = ( uchar * ) my_alloca ( ( uint ) keyinfo - > base . block_length ) ) )
{
print_error ( " Not Enough memory " ) ;
DBUG_RETURN ( - 1 ) ;
}
}
used_length = getint ( buff ) ;
keypos = buff + 2 + nod_flag ;
endpos = buff + used_length ;
for ( ; ; )
{
_sanity ( __FILE__ , __LINE__ ) ;
if ( nod_flag )
{
next_page = _nisam_kpos ( nod_flag , keypos ) ;
if ( my_pread ( info - > s - > kfile , ( byte * ) temp_buff ,
( uint ) keyinfo - > base . block_length , next_page ,
MYF ( MY_NABP + MY_WME ) ) )
{
print_error ( " Can't read keys from filepos: %lu " , ( ulong ) next_page ) ;
goto err ;
}
if ( sort_record_index ( info , keyinfo , next_page , temp_buff , sort_key ,
new_file ) )
goto err ;
}
_sanity ( __FILE__ , __LINE__ ) ;
if ( keypos > = endpos | |
( * keyinfo - > get_key ) ( keyinfo , nod_flag , & keypos , lastkey ) = = 0 )
break ;
rec_pos = _nisam_dpos ( info , nod_flag , keypos ) ;
if ( ( * info - > s - > read_rnd ) ( info , record_buff , rec_pos , 0 ) )
{
print_error ( " %d when reading datafile " , my_errno ) ;
goto err ;
}
if ( rec_pos ! = sort_info . filepos )
{
_nisam_dpointer ( info , keypos - nod_flag - info - > s - > rec_reflength ,
sort_info . filepos ) ;
if ( movepoint ( info , record_buff , rec_pos , sort_info . filepos , sort_key ) )
{
print_error ( " %d when updating key-pointers " , my_errno ) ;
goto err ;
}
}
if ( sort_write_record ( ) )
goto err ;
}
bzero ( ( byte * ) buff + used_length , keyinfo - > base . block_length - used_length ) ;
if ( my_pwrite ( info - > s - > kfile , ( byte * ) buff , ( uint ) keyinfo - > base . block_length ,
page , MYF_RW ) )
{
print_error ( " %d when updating keyblock " , my_errno ) ;
goto err ;
}
if ( temp_buff )
my_afree ( ( gptr ) temp_buff ) ;
DBUG_RETURN ( 0 ) ;
err :
if ( temp_buff )
my_afree ( ( gptr ) temp_buff ) ;
DBUG_RETURN ( 1 ) ;
} /* sort_record_index */
/* Sort index for more efficent reads */
static int sort_index ( info , name )
register N_INFO * info ;
my_string name ;
{
reg2 uint key ;
reg1 N_KEYDEF * keyinfo ;
File new_file ;
ulong index_pos [ N_MAXKEY ] ;
DBUG_ENTER ( " sort_index " ) ;
if ( ! ( testflag & T_SILENT ) )
printf ( " - Sorting index for ISAM-table '%s' \n " , name ) ;
if ( ( new_file = my_create ( fn_format ( temp_filename , name , " " , INDEX_TMP_EXT , 2 + 4 ) ,
0 , tmpfile_createflag , MYF ( 0 ) ) ) < = 0 )
{
print_error ( " Can't create new tempfile: '%s' " , temp_filename ) ;
DBUG_RETURN ( - 1 ) ;
}
if ( filecopy ( new_file , info - > s - > kfile , 0L , ( ulong ) info - > s - > base . keystart ,
" headerblock " ) )
goto err ;
new_file_pos = info - > s - > base . keystart ;
for ( key = 0 , keyinfo = & info - > s - > keyinfo [ 0 ] ; key < info - > s - > state . keys ;
key + + , keyinfo + + )
{
if ( info - > s - > state . key_root [ key ] ! = NI_POS_ERROR )
{
index_pos [ key ] = new_file_pos ; /* Write first block here */
if ( ! _nisam_fetch_keypage ( info , keyinfo , info - > s - > state . key_root [ key ] ,
info - > buff , 0 ) )
{
print_error ( " Can't read indexpage from filepos: %lu " ,
( ulong ) info - > s - > state . key_root [ key ] ) ;
goto err ;
}
if ( sort_one_index ( info , keyinfo , info - > buff , new_file ) )
goto err ;
}
else
index_pos [ key ] = NI_POS_ERROR ; /* No blocks */
}
/* Put same locks as old file */
if ( lock_file ( new_file , 0L , F_WRLCK , " tempfile " , temp_filename ) )
goto err ;
info - > s - > state . version = ( ulong ) time ( ( time_t * ) 0 ) ;
VOID ( _nisam_writeinfo ( info , 1 ) ) ; /* This unlocks table */
VOID ( my_close ( info - > s - > kfile , MYF ( MY_WME ) ) ) ;
out_flag | = O_NEW_INDEX ; /* Data in new file */
info - > s - > kfile = new_file ;
info - > s - > state . key_file_length = new_file_pos ;
info - > update = ( short ) ( HA_STATE_CHANGED | HA_STATE_ROW_CHANGED ) ;
for ( key = 0 ; key < info - > s - > state . keys ; key + + )
{
info - > s - > state . key_root [ key ] = index_pos [ key ] ;
info - > s - > state . key_del [ key ] = NI_POS_ERROR ;
}
DBUG_RETURN ( 0 ) ;
err :
VOID ( my_close ( new_file , MYF ( MY_WME ) ) ) ;
VOID ( my_delete ( temp_filename , MYF ( MY_WME ) ) ) ;
DBUG_RETURN ( - 1 ) ;
} /* sort_index */
/* Sort records recursive using one index */
static int sort_one_index ( info , keyinfo , buff , new_file )
N_INFO * info ;
N_KEYDEF * keyinfo ;
uchar * buff ;
File new_file ;
{
uint length , nod_flag , used_length ;
uchar * temp_buff , * keypos , * endpos ;
ulong new_page_pos , next_page ;
DBUG_ENTER ( " sort_one_index " ) ;
temp_buff = 0 ;
new_page_pos = new_file_pos ;
new_file_pos + = keyinfo - > base . block_length ;
if ( ( nod_flag = test_if_nod ( buff ) ) )
{
if ( ! ( temp_buff = ( uchar * ) my_alloca ( ( uint ) keyinfo - > base . block_length ) ) )
{
print_error ( " Not Enough memory " ) ;
DBUG_RETURN ( - 1 ) ;
}
used_length = getint ( buff ) ;
keypos = buff + 2 + nod_flag ;
endpos = buff + used_length ;
for ( ; ; )
{
if ( nod_flag )
{
next_page = _nisam_kpos ( nod_flag , keypos ) ;
_nisam_kpointer ( info , keypos - nod_flag , new_file_pos ) ; /* Save new pos */
if ( ! _nisam_fetch_keypage ( info , keyinfo , next_page , temp_buff , 0 ) )
{
print_error ( " Can't read keys from filepos: %lu " ,
( ulong ) next_page ) ;
goto err ;
}
if ( sort_one_index ( info , keyinfo , temp_buff , new_file ) )
goto err ;
}
if ( keypos > = endpos | |
( ( * keyinfo - > get_key ) ( keyinfo , nod_flag , & keypos , info - > lastkey ) ) = = 0 )
break ;
}
my_afree ( ( gptr ) temp_buff ) ;
}
/* Fill block with zero and write it to new file */
length = getint ( buff ) ;
bzero ( ( byte * ) buff + length , keyinfo - > base . block_length - length ) ;
if ( my_pwrite ( new_file , ( byte * ) buff , ( uint ) keyinfo - > base . block_length ,
new_page_pos , MYF ( MY_NABP | MY_WAIT_IF_FULL ) ) )
{
print_error ( " Can't write indexblock, error: %d " , my_errno ) ;
goto err ;
}
DBUG_RETURN ( 0 ) ;
err :
if ( temp_buff )
my_afree ( ( gptr ) temp_buff ) ;
DBUG_RETURN ( 1 ) ;
} /* sort_one_index */
/* Change to use new file */
/* Copy stats from old file to new file, deletes orginal and */
/* changes new file name to old file name */
static int change_to_newfile ( filename , old_ext , new_ext )
const char * filename , * old_ext , * new_ext ;
{
char old_filename [ FN_REFLEN ] , new_filename [ FN_REFLEN ] ;
return my_redel ( fn_format ( old_filename , filename , " " , old_ext , 2 + 4 ) ,
fn_format ( new_filename , filename , " " , new_ext , 2 + 4 ) ,
MYF ( MY_WME + MY_LINK_WARNING ) ) ;
} /* change_to_newfile */
/* Locks a hole file */
/* Gives error-message if file can't be locked */
static int lock_file ( file , start , lock_type , filetype , filename )
File file ;
ulong start ;
int lock_type ;
const char * filetype , * filename ;
{
# ifndef NO_LOCKING
if ( my_lock ( file , lock_type , start , F_TO_EOF ,
testflag & T_WAIT_FOREVER ? MYF ( MY_SEEK_NOT_DONE ) :
MYF ( MY_SEEK_NOT_DONE | MY_DONT_WAIT ) ) )
{
print_error ( " %d when %s %s '%s' " , my_errno ,
lock_type = = F_UNLCK ? " unlocking " : " locking " ,
filetype , filename ) ;
error_printed = 2 ; /* Don't give that data is crashed */
return 1 ;
}
# endif
return 0 ;
} /* lock_file */
/* Copy a block between two files */
static int filecopy ( File to , File from , ulong start , ulong length ,
const char * type )
{
char tmp_buff [ IO_SIZE ] , * buff ;
ulong buff_length ;
DBUG_ENTER ( " filecopy " ) ;
buff_length = min ( write_buffer_length , length ) ;
if ( ! ( buff = my_malloc ( buff_length , MYF ( 0 ) ) ) )
{
buff = tmp_buff ; buff_length = IO_SIZE ;
}
VOID ( my_seek ( from , start , MY_SEEK_SET , MYF ( 0 ) ) ) ;
while ( length > buff_length )
{
if ( my_read ( from , ( byte * ) buff , buff_length , MYF ( MY_NABP ) ) | |
my_write ( to , ( byte * ) buff , buff_length , MYF ( MY_NABP | MY_WAIT_IF_FULL ) ) )
goto err ;
length - = buff_length ;
}
if ( my_read ( from , ( byte * ) buff , ( uint ) length , MYF ( MY_NABP ) ) | |
my_write ( to , ( byte * ) buff , ( uint ) length , MYF ( MY_NABP | MY_WAIT_IF_FULL ) ) )
goto err ;
if ( buff ! = tmp_buff )
my_free ( buff , MYF ( 0 ) ) ;
DBUG_RETURN ( 0 ) ;
err :
if ( buff ! = tmp_buff )
my_free ( buff , MYF ( 0 ) ) ;
print_error ( " Can't copy %s to tempfile, error %d " , type , my_errno ) ;
DBUG_RETURN ( 1 ) ;
}
/* Fix table using sorting */
/* saves new table in temp_filename */
static int rep_by_sort ( info , name )
reg1 N_INFO * info ;
my_string name ;
{
int got_error ;
uint i , length ;
ulong start_records , new_header_length , del ;
File new_file ;
SORT_PARAM sort_param ;
ISAM_SHARE * share = info - > s ;
DBUG_ENTER ( " rep_by_sort " ) ;
start_records = share - > state . records ;
got_error = 1 ;
new_file = - 1 ;
new_header_length = ( testflag & T_UNPACK ) ? 0 : share - > pack . header_length ;
if ( ! ( testflag & T_SILENT ) )
{
printf ( " - recovering ISAM-table '%s' \n " , name ) ;
printf ( " Data records: %lu \n " , ( ulong ) start_records ) ;
}
bzero ( ( char * ) & sort_info , sizeof ( sort_info ) ) ;
if ( ! ( sort_info . key_block = alloc_key_blocks ( ( uint ) sort_key_blocks ,
share - > base . max_block ) )
| | init_io_cache ( & read_cache , info - > dfile , ( uint ) read_buffer_length ,
READ_CACHE , share - > pack . header_length , 1 , MYF ( MY_WME ) ) | |
( ! rep_quick & &
init_io_cache ( & info - > rec_cache , info - > dfile , ( uint ) write_buffer_length ,
WRITE_CACHE , new_header_length , 1 ,
MYF ( MY_WME | MY_WAIT_IF_FULL ) ) ) )
goto err ;
sort_info . key_block_end = sort_info . key_block + sort_key_blocks ;
info - > opt_flag | = WRITE_CACHE_USED ;
info - > rec_cache . file = info - > dfile ; /* for sort_delete_record */
if ( ! ( sort_info . record = ( byte * ) my_alloca ( ( uint ) share - > base . reclength ) ) )
{
print_error ( " Not enough memory for extra record " ) ;
goto err ;
}
if ( ! rep_quick )
{
if ( ( new_file = my_create ( fn_format ( temp_filename , name , " " , DATA_TMP_EXT ,
2 + 4 ) ,
0 , tmpfile_createflag , MYF ( 0 ) ) ) < 0 )
{
print_error ( " Can't create new tempfile: '%s' " , temp_filename ) ;
goto err ;
}
if ( filecopy ( new_file , info - > dfile , 0L , new_header_length , " datafile-header " ) )
goto err ;
if ( testflag & T_UNPACK )
share - > base . options & = ~ HA_OPTION_COMPRESS_RECORD ;
share - > state . dellink = NI_POS_ERROR ;
info - > rec_cache . file = new_file ;
}
info - > update = ( short ) ( HA_STATE_CHANGED | HA_STATE_ROW_CHANGED ) ;
for ( i = 0 ; i < N_MAXKEY ; i + + )
share - > state . key_del [ i ] = share - > state . key_root [ i ] = NI_POS_ERROR ;
share - > state . key_file_length = share - > base . keystart ;
sort_info . info = info ;
if ( ( sort_info . new_data_file_type = share - > data_file_type ) = =
COMPRESSED_RECORD & & testflag & T_UNPACK )
{
if ( share - > base . options & HA_OPTION_PACK_RECORD )
sort_info . new_data_file_type = DYNAMIC_RECORD ;
else
sort_info . new_data_file_type = STATIC_RECORD ;
}
sort_info . filepos = new_header_length ;
sort_info . dupp = 0 ;
read_cache . end_of_file = sort_info . filelength =
( ulong ) my_seek ( read_cache . file , 0L , MY_SEEK_END , MYF ( 0 ) ) ;
if ( share - > data_file_type = = DYNAMIC_RECORD )
length = max ( share - > base . min_pack_length + 1 , share - > base . min_block_length ) ;
else if ( share - > data_file_type = = COMPRESSED_RECORD )
length = share - > base . min_block_length ;
else
length = share - > base . reclength ;
sort_param . max_records = sort_info . max_records = sort_info . filelength / length + 1 ;
sort_param . key_cmp = sort_key_cmp ;
sort_param . key_write = sort_key_write ;
sort_param . key_read = sort_key_read ;
sort_param . lock_in_memory = lock_memory ;
del = share - > state . del ;
for ( sort_info . key = 0 ; sort_info . key < share - > state . keys ; sort_info . key + + )
{
if ( ( ! ( testflag & T_SILENT ) ) )
printf ( " - Fixing index %d \n " , sort_info . key + 1 ) ;
sort_info . max_pos = sort_info . pos = share - > pack . header_length ;
sort_info . keyinfo = share - > keyinfo + sort_info . key ;
sort_info . keyseg = sort_info . keyinfo - > seg ;
sort_info . fix_datafile = ( my_bool ) ( sort_info . key = = 0 & & ! rep_quick ) ;
sort_info . unique = 0 ;
sort_param . key_length = share - > rec_reflength ;
for ( i = 0 ; sort_info . keyseg [ i ] . base . type ; i + + )
sort_param . key_length + = sort_info . keyseg [ i ] . base . length +
( sort_info . keyseg [ i ] . base . flag & HA_SPACE_PACK ? 1 : 0 ) ;
share - > state . records = share - > state . del = share - > state . empty = share - > state . splitt = 0 ;
if ( _create_index_by_sort ( & sort_param ,
( pbool ) ( ! ( testflag & T_VERBOSE ) ) ,
( uint ) sort_buffer_length ) )
goto err ;
/* Set for next loop */
sort_param . max_records = sort_info . max_records = share - > state . records ;
share - > base . rec_per_key [ sort_info . key ] =
sort_info . unique ? ( ( sort_info . max_records + sort_info . unique / 2 ) /
sort_info . unique )
: 1L ;
if ( sort_info . fix_datafile )
{
info - > dfile = new_file ;
share - > state . data_file_length = sort_info . filepos ;
share - > state . splitt = share - > state . records ; /* Only hole records */
share - > state . version = ( ulong ) time ( ( time_t * ) 0 ) ;
out_flag | = O_NEW_DATA ; /* Data in new file */
read_cache . end_of_file = sort_info . filepos ;
if ( write_data_suffix ( info ) | | end_io_cache ( & info - > rec_cache ) )
goto err ;
share - > data_file_type = sort_info . new_data_file_type ;
share - > pack . header_length = new_header_length ;
}
else
share - > state . data_file_length = sort_info . max_pos ;
if ( flush_pending_blocks ( ) )
goto err ;
read_cache . file = info - > dfile ; /* re-init read cache */
reinit_io_cache ( & read_cache , READ_CACHE , share - > pack . header_length , 1 , 1 ) ;
}
if ( testflag & T_WRITE_LOOP )
{
VOID ( fputs ( " \r " , stdout ) ) ; VOID ( fflush ( stdout ) ) ;
}
if ( rep_quick & & del + sort_info . dupp ! = share - > state . del )
{
print_error ( " Couldn't fix table with quick recovery: Found wrong number of deleted records " ) ;
print_error ( " Run recovery again without -q " ) ;
got_error = 1 ;
goto err ;
}
if ( rep_quick ! = 1 )
{
ulong skr = share - > state . data_file_length +
( share - > base . options & HA_OPTION_COMPRESS_RECORD ?
MEMMAP_EXTRA_MARGIN : 0 ) ;
# ifdef USE_RELOC
if ( share - > data_file_type = = STATIC_RECORD & &
skr < share - > base . reloc * share - > base . min_pack_length )
skr = share - > base . reloc * share - > base . min_pack_length ;
# endif
if ( skr ! = sort_info . filelength )
2002-08-08 02:12:02 +02:00
if ( my_chsize ( info - > dfile , skr , 0 , MYF ( 0 ) ) )
2000-07-31 21:29:14 +02:00
print_warning ( " Can't change size of datafile, error: %d " , my_errno ) ;
}
2002-08-08 02:12:02 +02:00
if ( my_chsize ( share - > kfile , share - > state . key_file_length , 0 , MYF ( 0 ) ) )
2000-07-31 21:29:14 +02:00
print_warning ( " Can't change size of indexfile, error: %d " , my_errno ) ;
if ( ! ( testflag & T_SILENT ) )
{
if ( start_records ! = share - > state . records )
printf ( " Data records: %lu \n " , ( ulong ) share - > state . records ) ;
if ( sort_info . dupp )
print_warning ( " %lu records have been removed " , ( ulong ) sort_info . dupp ) ;
}
got_error = 0 ;
err :
if ( got_error )
{
if ( ! error_printed )
print_error ( " %d when fixing table " , my_errno ) ;
if ( new_file > = 0 )
{
VOID ( end_io_cache ( & info - > rec_cache ) ) ;
VOID ( my_close ( new_file , MYF ( 0 ) ) ) ;
VOID ( my_delete ( temp_filename , MYF ( MY_WME ) ) ) ;
}
}
if ( sort_info . key_block )
my_free ( ( gptr ) sort_info . key_block , MYF ( 0 ) ) ;
if ( sort_info . record )
{
my_afree ( sort_info . record ) ;
}
VOID ( end_io_cache ( & read_cache ) ) ;
VOID ( end_io_cache ( & info - > rec_cache ) ) ;
info - > opt_flag & = ~ ( READ_CACHE_USED | WRITE_CACHE_USED ) ;
if ( ! got_error & & testflag & T_UNPACK )
{
share - > state . header . options [ 0 ] & = ( uchar ) ~ HA_OPTION_COMPRESS_RECORD ;
share - > pack . header_length = 0 ;
}
DBUG_RETURN ( got_error ) ;
} /* rep_by_sort */
/* Read next record and return next key */
static int sort_key_read ( key )
void * key ;
{
int error ;
N_INFO * info ;
DBUG_ENTER ( " sort_key_read " ) ;
info = sort_info . info ;
if ( ( error = sort_get_next_record ( ) ) )
DBUG_RETURN ( error ) ;
if ( info - > s - > state . records = = sort_info . max_records )
{
print_error ( " Found too many records; Can`t continue " ) ;
DBUG_RETURN ( 1 ) ;
}
VOID ( _nisam_make_key ( info , sort_info . key , key , sort_info . record ,
sort_info . filepos ) ) ;
DBUG_RETURN ( sort_write_record ( ) ) ;
} /* sort_key_read */
/* Read next record from file using parameters in sort_info */
/* Return -1 if end of file, 0 if ok and > 0 if error */
static int sort_get_next_record ( )
{
int searching ;
uint found_record , b_type , left_length ;
ulong pos ;
byte * to ;
BLOCK_INFO block_info ;
N_INFO * info ;
ISAM_SHARE * share ;
DBUG_ENTER ( " sort_get_next_record " ) ;
info = sort_info . info ;
share = info - > s ;
switch ( share - > data_file_type ) {
case STATIC_RECORD :
for ( ; ; )
{
if ( my_b_read ( & read_cache , sort_info . record , share - > base . reclength ) )
DBUG_RETURN ( - 1 ) ;
sort_info . start_recpos = sort_info . pos ;
if ( ! sort_info . fix_datafile )
sort_info . filepos = sort_info . pos ;
sort_info . max_pos = ( sort_info . pos + = share - > base . reclength ) ;
share - > state . splitt + + ;
if ( * sort_info . record )
DBUG_RETURN ( 0 ) ;
if ( ! sort_info . fix_datafile )
{
share - > state . del + + ;
share - > state . empty + = share - > base . reclength ;
}
}
case DYNAMIC_RECORD :
LINT_INIT ( to ) ;
pos = sort_info . pos ;
searching = ( sort_info . fix_datafile & & ( testflag & T_EXTEND ) ) ;
for ( ; ; )
{
found_record = block_info . second_read = 0 ;
left_length = 1 ;
do
{
if ( pos > sort_info . max_pos )
sort_info . max_pos = pos ;
if ( found_record & & pos = = search_after_block )
print_info ( " Block: %lu used by record at %lu " ,
search_after_block ,
sort_info . start_recpos ) ;
if ( _nisam_read_cache ( & read_cache , ( byte * ) block_info . header , pos ,
BLOCK_INFO_HEADER_LENGTH , test ( ! found_record ) | 2 ) )
{
if ( found_record )
{
print_info ( " Can't read whole record at %lu (errno: %d) " ,
( ulong ) sort_info . start_recpos , errno ) ;
goto try_next ;
}
DBUG_RETURN ( - 1 ) ;
}
if ( searching & & ! sort_info . fix_datafile )
{
error_printed = 1 ;
DBUG_RETURN ( 1 ) ; /* Something wrong with data */
}
if ( ( ( b_type = _nisam_get_block_info ( & block_info , - 1 , pos ) ) &
( BLOCK_ERROR | BLOCK_FATAL_ERROR ) ) | |
( ( b_type & BLOCK_FIRST ) & &
( block_info . rec_len < ( uint ) share - > base . min_pack_length | |
block_info . rec_len > ( uint ) share - > base . max_pack_length ) ) )
{
uint i ;
if ( testflag & T_VERBOSE | | searching = = 0 )
print_info ( " Wrong bytesec: %3d-%3d-%3d at %10lu; Skipped " ,
block_info . header [ 0 ] , block_info . header [ 1 ] ,
block_info . header [ 2 ] , pos ) ;
if ( found_record )
goto try_next ;
block_info . second_read = 0 ;
searching = 1 ;
for ( i = 1 ; i < 11 ; i + + ) /* Skipp from read string */
if ( block_info . header [ i ] > = 1 & & block_info . header [ i ] < = 16 )
break ;
pos + = ( ulong ) i ;
continue ;
}
if ( block_info . block_len + ( uint ) ( block_info . filepos - pos ) <
share - > base . min_block_length | |
block_info . block_len - 4 > ( uint ) share - > base . max_pack_length )
{
if ( ! searching )
print_info ( " Found block with impossible length %u at %lu; Skipped " ,
block_info . block_len + ( uint ) ( block_info . filepos - pos ) ,
( ulong ) pos ) ;
if ( found_record )
goto try_next ;
searching = 1 ;
pos + + ;
block_info . second_read = 0 ;
continue ;
}
if ( b_type & ( BLOCK_DELETED | BLOCK_SYNC_ERROR ) )
{
if ( ! sort_info . fix_datafile & & ( b_type & BLOCK_DELETED ) )
{
share - > state . empty + = block_info . block_len ;
share - > state . del + + ;
share - > state . splitt + + ;
}
if ( found_record )
goto try_next ;
/* Check if impossible big deleted block */
if ( block_info . block_len > share - > base . max_pack_length + 4 )
searching = 1 ;
if ( searching )
pos + + ;
else
pos = block_info . filepos + block_info . block_len ;
block_info . second_read = 0 ;
continue ;
}
share - > state . splitt + + ;
if ( ! found_record + + )
{
sort_info . find_length = left_length = block_info . rec_len ;
sort_info . start_recpos = pos ;
if ( ! sort_info . fix_datafile )
sort_info . filepos = sort_info . start_recpos ;
if ( sort_info . fix_datafile & & ( testflag & T_EXTEND ) )
sort_info . pos = block_info . filepos + 1 ;
else
sort_info . pos = block_info . filepos + block_info . block_len ;
if ( share - > base . blobs )
{
if ( ! ( to = fix_rec_buff_for_blob ( info , block_info . rec_len ) ) )
{
print_error ( " Not enough memory for blob at %lu " ,
( ulong ) sort_info . start_recpos ) ;
DBUG_RETURN ( - 1 ) ;
}
}
else
to = info - > rec_buff ;
}
if ( left_length < block_info . data_len | | ! block_info . data_len )
{
print_info ( " Found block with too small length at %lu; Skipped " ,
( ulong ) sort_info . start_recpos ) ;
goto try_next ;
}
if ( block_info . filepos + block_info . data_len > read_cache . end_of_file )
{
print_info ( " Found block that points outside data file at %lu " ,
( ulong ) sort_info . start_recpos ) ;
goto try_next ;
}
if ( _nisam_read_cache ( & read_cache , to , block_info . filepos ,
block_info . data_len , test ( found_record = = 1 ) ) )
{
print_info ( " Read error for block at: %lu (error: %d); Skipped " ,
( ulong ) block_info . filepos , my_errno ) ;
goto try_next ;
}
left_length - = block_info . data_len ;
to + = block_info . data_len ;
pos = block_info . next_filepos ;
if ( pos = = NI_POS_ERROR & & left_length )
{
print_info ( " Wrong block with wrong total length starting at %lu " ,
( ulong ) sort_info . start_recpos ) ;
goto try_next ;
}
if ( pos + BLOCK_INFO_HEADER_LENGTH > read_cache . end_of_file )
{
print_info ( " Found link that points at %lu (outside data file) at %lu " ,
( ulong ) pos , ( ulong ) sort_info . start_recpos ) ;
goto try_next ;
}
} while ( left_length ) ;
if ( _nisam_rec_unpack ( info , sort_info . record , info - > rec_buff ,
sort_info . find_length ) ! = MY_FILE_ERROR )
{
if ( read_cache . error < 0 )
DBUG_RETURN ( 1 ) ;
if ( ( testflag & ( T_EXTEND | T_REP ) ) | | searching )
{
if ( _nisam_rec_check ( info , sort_info . record ) )
{
print_info ( " Found wrong packed record at %lu " ,
( ulong ) sort_info . start_recpos ) ;
goto try_next ;
}
}
DBUG_RETURN ( 0 ) ;
}
2001-03-21 00:02:22 +01:00
if ( ! searching )
{
print_info ( " Found wrong packed record at %lu " ,
( ulong ) sort_info . start_recpos ) ;
}
2000-07-31 21:29:14 +02:00
try_next :
pos = sort_info . start_recpos + 1 ;
searching = 1 ;
}
case COMPRESSED_RECORD :
for ( searching = 0 ; ; searching = 1 , sort_info . pos + + )
{
if ( _nisam_read_cache ( & read_cache , ( byte * ) block_info . header , sort_info . pos ,
share - > pack . ref_length , 1 ) )
DBUG_RETURN ( - 1 ) ;
if ( searching & & ! sort_info . fix_datafile )
{
error_printed = 1 ;
DBUG_RETURN ( 1 ) ; /* Something wrong with data */
}
sort_info . start_recpos = sort_info . pos ;
VOID ( _nisam_pack_get_block_info ( & block_info , share - > pack . ref_length , - 1 ,
sort_info . pos ) ) ;
if ( ! block_info . rec_len & &
sort_info . pos + MEMMAP_EXTRA_MARGIN = = read_cache . end_of_file )
DBUG_RETURN ( - 1 ) ;
if ( block_info . rec_len < ( uint ) share - > min_pack_length | |
block_info . rec_len > ( uint ) share - > max_pack_length )
{
if ( ! searching )
print_info ( " Found block with wrong recordlength: %d at %lu \n " ,
block_info . rec_len , ( ulong ) sort_info . pos ) ;
continue ;
}
if ( _nisam_read_cache ( & read_cache , ( byte * ) info - > rec_buff ,
block_info . filepos , block_info . rec_len , 1 ) )
{
if ( ! searching )
print_info ( " Couldn't read hole record from %lu " ,
( ulong ) sort_info . pos ) ;
continue ;
}
if ( _nisam_pack_rec_unpack ( info , sort_info . record , info - > rec_buff ,
block_info . rec_len ) )
{
if ( ! searching )
print_info ( " Found wrong record at %lu " , ( ulong ) sort_info . pos ) ;
continue ;
}
if ( ! sort_info . fix_datafile )
sort_info . filepos = sort_info . pos ;
sort_info . max_pos = ( sort_info . pos + = share - > pack . ref_length +
block_info . rec_len ) ;
share - > state . splitt + + ;
info - > packed_length = block_info . rec_len ;
DBUG_RETURN ( 0 ) ;
}
}
DBUG_RETURN ( 1 ) ; /* Impossible */
}
/* Write record to new file */
static int sort_write_record ( )
{
int flag ;
uint block_length , reclength ;
byte * from ;
uchar * block_buff [ 3 ] ;
N_INFO * info ;
ISAM_SHARE * share ;
DBUG_ENTER ( " sort_write_record " ) ;
info = sort_info . info ;
share = info - > s ;
if ( sort_info . fix_datafile )
{
switch ( sort_info . new_data_file_type ) {
case STATIC_RECORD :
if ( my_b_write ( & info - > rec_cache , sort_info . record , share - > base . reclength ) )
{
print_error ( " %d when writing to datafile " , my_errno ) ;
DBUG_RETURN ( 1 ) ;
}
sort_info . filepos + = share - > base . reclength ;
break ;
case DYNAMIC_RECORD :
if ( ! info - > blobs )
from = info - > rec_buff ;
else
{
/* must be sure that local buffer is big enough */
reclength = info - > s - > base . pack_reclength +
_calc_total_blob_length ( info , sort_info . record ) +
ALIGN_SIZE ( MAX_DYN_BLOCK_HEADER ) + N_SPLITT_LENGTH +
DYN_DELETE_BLOCK_HEADER ;
if ( sort_info . buff_length < reclength )
{
if ( ! ( sort_info . buff = my_realloc ( sort_info . buff , ( uint ) reclength ,
MYF ( MY_FREE_ON_ERROR |
MY_ALLOW_ZERO_PTR ) ) ) )
DBUG_RETURN ( 1 ) ;
sort_info . buff_length = reclength ;
}
from = sort_info . buff + ALIGN_SIZE ( MAX_DYN_BLOCK_HEADER ) ;
}
reclength = _nisam_rec_pack ( info , from , sort_info . record ) ;
block_length = reclength + 3 + test ( reclength > 65532L ) ;
if ( block_length < share - > base . min_block_length )
block_length = share - > base . min_block_length ;
flag = 0 ;
info - > update | = HA_STATE_WRITE_AT_END ;
if ( _nisam_write_part_record ( info , 0L , block_length , NI_POS_ERROR ,
& from , & reclength , & flag ) )
{
print_error ( " %d when writing to datafile " , my_errno ) ;
DBUG_RETURN ( 1 ) ;
}
sort_info . filepos + = block_length ;
break ;
case COMPRESSED_RECORD :
reclength = info - > packed_length ;
save_integer ( ( byte * ) block_buff , share - > pack . ref_length , reclength ) ;
if ( my_b_write ( & info - > rec_cache , ( byte * ) block_buff , share - > pack . ref_length )
| | my_b_write ( & info - > rec_cache , ( byte * ) info - > rec_buff , reclength ) )
{
print_error ( " %d when writing to datafile " , my_errno ) ;
DBUG_RETURN ( 1 ) ;
}
sort_info . filepos + = reclength + share - > pack . ref_length ;
break ;
}
}
share - > state . records + + ;
if ( testflag & T_WRITE_LOOP & & share - > state . records % WRITE_COUNT = = 0 )
{
printf ( " %lu \r " , ( ulong ) share - > state . records ) ; VOID ( fflush ( stdout ) ) ;
}
DBUG_RETURN ( 0 ) ;
} /* sort_write_record */
/* Compare two keys from _create_index_by_sort */
static int sort_key_cmp ( const void * not_used __attribute__ ( ( unused ) ) ,
const void * a , const void * b )
{
return ( _nisam_key_cmp ( sort_info . keyseg , * ( ( uchar * * ) a ) , * ( ( uchar * * ) b ) , 0 ,
SEARCH_SAME ) ) ;
} /* sort_key_cmp */
2001-08-22 00:45:07 +02:00
static int sort_key_write ( const void * a )
2000-07-31 21:29:14 +02:00
{
int cmp = sort_info . key_block - > inited ?
_nisam_key_cmp ( sort_info . keyseg , sort_info . key_block - > lastkey , ( uchar * ) a ,
0 , SEARCH_FIND ) : - 1L ;
if ( ( sort_info . keyinfo - > base . flag & HA_NOSAME ) & &
cmp = = 0 )
{
sort_info . dupp + + ;
print_warning ( " Dupplicate key for record at %10lu against record at %10lu " ,
sort_info . info - > lastpos = get_record_for_key ( sort_info . info ,
sort_info . keyinfo ,
( uchar * ) a ) ,
get_record_for_key ( sort_info . info , sort_info . keyinfo ,
sort_info . key_block - > lastkey ) ) ;
if ( testflag & T_VERBOSE )
_nisam_print_key ( stdout , sort_info . keyseg , ( uchar * ) a ) ;
return ( sort_delete_record ( ) ) ;
}
if ( cmp )
sort_info . unique + + ;
# ifndef DBUG_OFF
if ( cmp > 0 )
{
print_error ( " Fatal intern error: Keys are not in order from sort " ) ;
return ( 1 ) ;
}
# endif
return ( sort_insert_key ( sort_info . key_block , ( uchar * ) a , NI_POS_ERROR ) ) ;
} /* sort_key_write */
/* get pointer to record from a key */
2001-08-22 00:45:07 +02:00
static ulong get_record_for_key ( N_INFO * info , N_KEYDEF * keyinfo , uchar * key )
2000-07-31 21:29:14 +02:00
{
return _nisam_dpos ( info , 0 , key + _nisam_keylength ( keyinfo , key ) ) ;
} /* get_record_for_key */
/* Insert a key in sort-key-blocks */
2001-08-22 00:45:07 +02:00
static int sort_insert_key ( reg1 ISAM_SORT_KEY_BLOCKS * key_block ,
uchar * key , ulong prev_block )
2000-07-31 21:29:14 +02:00
{
uint a_length , t_length , nod_flag ;
ulong filepos ;
uchar * anc_buff , * lastkey ;
S_PARAM s_temp ;
N_INFO * info ;
DBUG_ENTER ( " sort_insert_key " ) ;
anc_buff = key_block - > buff ;
info = sort_info . info ;
lastkey = key_block - > lastkey ;
nod_flag = ( key_block = = sort_info . key_block ? 0 :
sort_info . info - > s - > base . key_reflength ) ;
if ( ! key_block - > inited )
{
key_block - > inited = 1 ;
if ( key_block = = sort_info . key_block_end )
{
print_error ( " To many keyblocklevels; Try increasing sort_key_blocks " ) ;
DBUG_RETURN ( 1 ) ;
}
a_length = 2 + nod_flag ;
key_block - > end_pos = anc_buff + 2 ;
lastkey = 0 ; /* No previous key in block */
}
else
a_length = getint ( anc_buff ) ;
/* Save pointer to previous block */
if ( nod_flag )
_nisam_kpointer ( info , key_block - > end_pos , prev_block ) ;
t_length = _nisam_get_pack_key_length ( sort_info . keyinfo , nod_flag ,
( uchar * ) 0 , lastkey , key , & s_temp ) ;
_nisam_store_key ( sort_info . keyinfo , key_block - > end_pos + nod_flag , & s_temp ) ;
a_length + = t_length ;
putint ( anc_buff , a_length , nod_flag ) ;
key_block - > end_pos + = t_length ;
if ( a_length < = sort_info . keyinfo - > base . block_length )
{
VOID ( _nisam_move_key ( sort_info . keyinfo , key_block - > lastkey , key ) ) ;
key_block - > last_length = a_length - t_length ;
DBUG_RETURN ( 0 ) ;
}
/* Fill block with end-zero and write filled block */
putint ( anc_buff , key_block - > last_length , nod_flag ) ;
bzero ( ( byte * ) anc_buff + key_block - > last_length ,
sort_info . keyinfo - > base . block_length - key_block - > last_length ) ;
if ( ( filepos = _nisam_new ( info , sort_info . keyinfo ) ) = = NI_POS_ERROR )
return 1 ;
if ( my_pwrite ( info - > s - > kfile , ( byte * ) anc_buff ,
( uint ) sort_info . keyinfo - > base . block_length , filepos , MYF_RW ) )
DBUG_RETURN ( 1 ) ;
DBUG_DUMP ( " buff " , ( byte * ) anc_buff , getint ( anc_buff ) ) ;
/* Write separator-key to block in next level */
if ( sort_insert_key ( key_block + 1 , key_block - > lastkey , filepos ) )
DBUG_RETURN ( 1 ) ;
/* clear old block and write new key in it */
key_block - > inited = 0 ;
DBUG_RETURN ( sort_insert_key ( key_block , key , prev_block ) ) ;
} /* sort_insert_key */
/* Delete record when we found a dupplicated key */
static int sort_delete_record ( )
{
uint i ;
int old_file , error ;
uchar * key ;
N_INFO * info ;
DBUG_ENTER ( " sort_delete_record " ) ;
if ( rep_quick = = 1 )
{
VOID ( fputs ( " Quick-recover aborted; Run recovery without switch 'q' or with switch -qq \n " , stderr ) ) ;
error_printed = 1 ;
DBUG_RETURN ( 1 ) ;
}
info = sort_info . info ;
if ( info - > s - > base . options & HA_OPTION_COMPRESS_RECORD )
{
VOID ( fputs ( " Recover aborted; Can't run standard recovery on compressed tables \n with errors in data-file \n Use switch '--safe-recover' to fix it \n " , stderr ) ) ;
error_printed = 1 ;
DBUG_RETURN ( 1 ) ;
}
old_file = info - > dfile ;
info - > dfile = info - > rec_cache . file ;
if ( sort_info . key )
{
key = info - > lastkey + info - > s - > base . max_key_length ;
if ( ( * info - > s - > read_rnd ) ( info , sort_info . record , info - > lastpos , 0 ) < 0 )
{
print_error ( " Can't read record to be removed " ) ;
info - > dfile = old_file ;
DBUG_RETURN ( 1 ) ;
}
for ( i = 0 ; i < sort_info . key ; i + + )
{
VOID ( _nisam_make_key ( info , i , key , sort_info . record , info - > lastpos ) ) ;
if ( _nisam_ck_delete ( info , i , key ) )
{
print_error ( " Can't delete key %d from record to be removed " , i + 1 ) ;
info - > dfile = old_file ;
DBUG_RETURN ( 1 ) ;
}
}
}
error = flush_io_cache ( & info - > rec_cache ) | | ( * info - > s - > delete_record ) ( info ) ;
info - > dfile = old_file ; /* restore actual value */
info - > s - > state . records - - ;
DBUG_RETURN ( error ) ;
} /* sort_delete_record */
/* Fix all pending blocks and flush everything to disk */
static int flush_pending_blocks ( )
{
uint nod_flag , length ;
ulong filepos ;
N_INFO * info ;
2001-08-22 00:45:07 +02:00
ISAM_SORT_KEY_BLOCKS * key_block ;
2000-07-31 21:29:14 +02:00
DBUG_ENTER ( " flush_pending_blocks " ) ;
filepos = NI_POS_ERROR ; /* if empty file */
info = sort_info . info ;
nod_flag = 0 ;
for ( key_block = sort_info . key_block ; key_block - > inited ; key_block + + )
{
key_block - > inited = 0 ;
length = getint ( key_block - > buff ) ;
if ( nod_flag )
_nisam_kpointer ( info , key_block - > end_pos , filepos ) ;
if ( ( filepos = _nisam_new ( info , sort_info . keyinfo ) ) = = NI_POS_ERROR )
DBUG_RETURN ( 1 ) ;
bzero ( ( byte * ) key_block - > buff + length ,
sort_info . keyinfo - > base . block_length - length ) ;
if ( my_pwrite ( info - > s - > kfile , ( byte * ) key_block - > buff ,
( uint ) sort_info . keyinfo - > base . block_length , filepos , MYF_RW ) )
DBUG_RETURN ( 1 ) ;
DBUG_DUMP ( " buff " , ( byte * ) key_block - > buff , length ) ;
nod_flag = 1 ;
}
info - > s - > state . key_root [ sort_info . key ] = filepos ; /* Last is root for tree */
DBUG_RETURN ( 0 ) ;
} /* flush_pending_blocks */
/* alloc space and pointers for key_blocks */
2001-08-22 00:45:07 +02:00
static ISAM_SORT_KEY_BLOCKS * alloc_key_blocks ( uint blocks , uint buffer_length )
2000-07-31 21:29:14 +02:00
{
reg1 uint i ;
2001-08-22 00:45:07 +02:00
ISAM_SORT_KEY_BLOCKS * block ;
2000-07-31 21:29:14 +02:00
DBUG_ENTER ( " alloc_key_blocks " ) ;
2001-08-22 00:45:07 +02:00
if ( ! ( block = ( ISAM_SORT_KEY_BLOCKS * ) my_malloc ( ( sizeof ( ISAM_SORT_KEY_BLOCKS ) +
buffer_length + IO_SIZE ) * blocks ,
MYF ( 0 ) ) ) )
2000-07-31 21:29:14 +02:00
{
print_error ( " Not Enough memory for sort-key-blocks " ) ;
return ( 0 ) ;
}
for ( i = 0 ; i < blocks ; i + + )
{
block [ i ] . inited = 0 ;
block [ i ] . buff = ( uchar * ) ( block + blocks ) + ( buffer_length + IO_SIZE ) * i ;
}
DBUG_RETURN ( block ) ;
} /* alloc_key_blocks */
/* print warnings and errors */
/* VARARGS */
static void print_info ( const char * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
VOID ( vfprintf ( stdout , fmt , args ) ) ;
VOID ( fputc ( ' \n ' , stdout ) ) ;
va_end ( args ) ;
return ;
}
/* VARARGS */
static void print_warning ( const char * fmt , . . . )
{
va_list args ;
DBUG_ENTER ( " print_warning " ) ;
if ( ! warning_printed & & ! error_printed )
{
fflush ( stdout ) ;
if ( testflag & T_SILENT )
fprintf ( stderr , " %s: ISAM file %s \n " , my_progname , isam_file_name ) ;
}
warning_printed = 1 ;
va_start ( args , fmt ) ;
fprintf ( stderr , " %s: warning: " , my_progname ) ;
VOID ( vfprintf ( stderr , fmt , args ) ) ;
VOID ( fputc ( ' \n ' , stderr ) ) ;
va_end ( args ) ;
DBUG_VOID_RETURN ;
}
/* VARARGS */
void print_error ( const char * fmt , . . . )
{
va_list args ;
DBUG_ENTER ( " print_error " ) ;
DBUG_PRINT ( " enter " , ( " format: %s " , fmt ) ) ;
if ( ! warning_printed & & ! error_printed )
{
fflush ( stdout ) ;
if ( testflag & T_SILENT )
fprintf ( stderr , " %s: ISAM file %s \n " , my_progname , isam_file_name ) ;
}
error_printed | = 1 ;
va_start ( args , fmt ) ;
fprintf ( stderr , " %s: error: " , my_progname ) ;
VOID ( vfprintf ( stderr , fmt , args ) ) ;
VOID ( fputc ( ' \n ' , stderr ) ) ;
va_end ( args ) ;
DBUG_VOID_RETURN ;
}
/* Check if file is almost full */
2001-08-22 00:45:07 +02:00
static int test_if_almost_full ( N_INFO * info )
2000-07-31 21:29:14 +02:00
{
double diff = 0.9 ;
if ( info - > s - > base . options & HA_OPTION_COMPRESS_RECORD )
{ /* Fix problem with pack_isam */
diff = 1.0 ;
if ( info - > s - > base . rec_reflength = = 4 )
info - > s - > base . max_data_file_length = ( uint32 ) ~ 0L ;
else
info - > s - > base . max_data_file_length =
1L < < ( info - > s - > base . rec_reflength ) ;
}
return ( my_seek ( info - > s - > kfile , 0L , MY_SEEK_END , MYF ( 0 ) ) >
( ulong ) ( info - > s - > base . max_key_file_length * diff ) | |
my_seek ( info - > dfile , 0L , MY_SEEK_END , MYF ( 0 ) ) >
( ulong ) ( info - > s - > base . max_data_file_length * diff ) ) ;
}
/* Recreate table with bigger more alloced record-data */
2001-08-22 00:45:07 +02:00
static int recreate_database ( N_INFO * * org_info , char * filename )
2000-07-31 21:29:14 +02:00
{
int error ;
N_INFO info ;
ISAM_SHARE share ;
N_KEYDEF * keyinfo ;
N_RECINFO * recinfo , * rec , * end ;
uint unpack ;
ulong max_records ;
char name [ FN_REFLEN ] ;
error = 1 ; /* Default error */
info = * * org_info ;
share = * ( * org_info ) - > s ;
unpack = ( share . base . options & HA_OPTION_COMPRESS_RECORD ) & &
( testflag & T_UNPACK ) ;
if ( ! ( keyinfo = ( N_KEYDEF * ) my_alloca ( sizeof ( N_KEYDEF ) * share . base . keys ) ) )
return 0 ;
memcpy ( ( byte * ) keyinfo , ( byte * ) share . keyinfo ,
( size_t ) ( sizeof ( N_KEYDEF ) * share . base . keys ) ) ;
if ( ! ( recinfo = ( N_RECINFO * )
my_alloca ( sizeof ( N_RECINFO ) * ( share . base . fields + 1 ) ) ) )
{
my_afree ( ( gptr ) keyinfo ) ;
return 1 ;
}
memcpy ( ( byte * ) recinfo , ( byte * ) share . rec ,
( size_t ) ( sizeof ( N_RECINFO ) * ( share . base . fields + 1 ) ) ) ;
for ( rec = recinfo , end = recinfo + share . base . fields ; rec ! = end ; rec + + )
{
if ( rec - > base . type = = ( int ) FIELD_BLOB )
rec - > base . length + = sizeof ( char * ) ;
else if ( unpack & & ! ( share . base . options & HA_OPTION_PACK_RECORD ) )
rec - > base . type = ( int ) FIELD_NORMAL ;
}
if ( share . base . options & HA_OPTION_COMPRESS_RECORD )
share . base . records = max_records = share . state . records ;
else if ( share . base . min_pack_length )
max_records = ( ulong ) ( my_seek ( info . dfile , 0L , MY_SEEK_END , MYF ( 0 ) ) /
( ulong ) share . base . min_pack_length ) ;
else
max_records = 0 ;
unpack = ( share . base . options & HA_OPTION_COMPRESS_RECORD ) & &
( testflag & T_UNPACK ) ;
share . base . options & = ~ HA_OPTION_TEMP_COMPRESS_RECORD ;
VOID ( nisam_close ( * org_info ) ) ;
if ( nisam_create ( fn_format ( name , filename , " " , N_NAME_IEXT ,
4 + ( opt_follow_links ? 16 : 0 ) ) ,
share . base . keys , keyinfo , recinfo ,
max ( max_records , share . base . records ) , share . base . reloc ,
HA_DONT_TOUCH_DATA ,
share . base . options |
( unpack ? HA_OPTION_TEMP_COMPRESS_RECORD
: 0 ) ,
( ulong ) my_seek ( info . dfile , 0L , MY_SEEK_END , MYF ( 0 ) ) ) )
{
print_error ( " Got error %d when trying to recreate indexfile " , my_errno ) ;
goto end ;
}
* org_info = nisam_open ( name , O_RDWR ,
( testflag & T_WAIT_FOREVER ) ? HA_OPEN_WAIT_IF_LOCKED :
( testflag & T_DESCRIPT ) ? HA_OPEN_IGNORE_IF_LOCKED :
HA_OPEN_ABORT_IF_LOCKED ) ;
if ( ! * org_info )
{
print_error ( " Got error %d when trying to open re-created indexfile " ,
my_errno ) ;
goto end ;
}
/* We are modifing */
( * org_info ) - > s - > base . options & = ~ HA_OPTION_READ_ONLY_DATA ;
VOID ( _nisam_readinfo ( * org_info , F_WRLCK , 0 ) ) ;
( * org_info ) - > s - > state . records = share . state . records ;
if ( share . base . create_time )
( * org_info ) - > s - > base . create_time = share . base . create_time ;
( * org_info ) - > s - > state . uniq = ( * org_info ) - > this_uniq =
share . state . uniq ;
( * org_info ) - > s - > state . del = share . state . del ;
( * org_info ) - > s - > state . dellink = share . state . dellink ;
( * org_info ) - > s - > state . empty = share . state . empty ;
( * org_info ) - > s - > state . data_file_length = share . state . data_file_length ;
if ( update_state_info ( * org_info , UPDATE_TIME | UPDATE_STAT ) )
goto end ;
error = 0 ;
end :
my_afree ( ( gptr ) keyinfo ) ;
my_afree ( ( gptr ) recinfo ) ;
return error ;
}
/* Store long in 1,2,3 or 4 bytes */
2001-08-22 00:45:07 +02:00
static void save_integer ( byte * pos , uint pack_length , ulong value )
2000-07-31 21:29:14 +02:00
{
switch ( pack_length ) {
case 4 : int4store ( pos , value ) ; break ;
case 3 : int3store ( pos , value ) ; break ;
case 2 : int2store ( pos , ( uint ) value ) ; break ;
case 1 : pos [ 0 ] = ( char ) ( uchar ) value ; break ;
default : break ;
}
return ;
}
/* write suffix to data file if neaded */
2001-08-22 00:45:07 +02:00
static int write_data_suffix ( N_INFO * info )
2000-07-31 21:29:14 +02:00
{
if ( info - > s - > base . options & HA_OPTION_COMPRESS_RECORD & &
sort_info . fix_datafile )
{
char buff [ MEMMAP_EXTRA_MARGIN ] ;
bzero ( buff , sizeof ( buff ) ) ;
if ( my_b_write ( & info - > rec_cache , buff , sizeof ( buff ) ) )
{
print_error ( " %d when writing to datafile " , my_errno ) ;
return 1 ;
}
read_cache . end_of_file + = sizeof ( buff ) ;
}
return 0 ;
}
/* Update state and isamchk_time of indexfile */
2001-08-22 00:45:07 +02:00
static int update_state_info ( N_INFO * info , uint update )
2000-07-31 21:29:14 +02:00
{
ISAM_SHARE * share = info - > s ;
uint base_pos = uint2korr ( info - > s - > state . header . base_pos ) ;
if ( update & ( UPDATE_STAT | UPDATE_SORT | UPDATE_TIME ) )
{
if ( offsetof ( N_BASE_INFO , rec_per_key ) >
uint2korr ( share - > state . header . base_info_length ) )
{
VOID ( fputs ( " Internal error: Trying to change base of old table \n " ,
stderr ) ) ;
}
else
{
if ( update & UPDATE_TIME )
{
share - > base . isamchk_time = ( long ) time ( ( time_t * ) 0 ) ;
if ( ! share - > base . create_time )
share - > base . create_time = share - > base . isamchk_time ;
if ( my_pwrite ( share - > kfile , ( gptr ) & share - > base . create_time ,
sizeof ( long ) * 2 ,
base_pos + offsetof ( N_BASE_INFO , create_time ) ,
MYF ( MY_NABP ) ) )
goto err ;
}
if ( update & ( UPDATE_STAT | UPDATE_SORT ) )
{
if ( my_pwrite ( share - > kfile , ( gptr ) share - > base . rec_per_key ,
sizeof ( long ) * share - > state . keys + sizeof ( uint ) ,
base_pos + offsetof ( N_BASE_INFO , rec_per_key ) ,
MYF ( MY_NABP ) ) )
goto err ;
}
}
}
{ /* Force update of status */
int error ;
uint r_locks = share - > r_locks , w_locks = share - > w_locks ;
share - > r_locks = share - > w_locks = 0 ;
error = _nisam_writeinfo ( info , 2 ) ;
share - > r_locks = r_locks ; share - > w_locks = w_locks ;
if ( ! error )
return 0 ;
}
err :
print_error ( " %d when updateing keyfile " , my_errno ) ;
return 1 ;
}