diff options
Diffstat (limited to 'isam/isamchk.c')
-rw-r--r-- | isam/isamchk.c | 3448 |
1 files changed, 3448 insertions, 0 deletions
diff --git a/isam/isamchk.c b/isam/isamchk.c new file mode 100644 index 00000000000..97d190b7115 --- /dev/null +++ b/isam/isamchk.c @@ -0,0 +1,3448 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Descript, check and repair of ISAM tables */ + +#include "isamdef.h" + +#include <m_ctype.h> +#include <stdarg.h> +#include <getopt.h> +#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 +#define T_REP_BY_SORT 32768 + + +#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 + +#define NEAD_MEM ((uint) 10*4*(IO_SIZE+32)+32) /* Nead for recursion */ +#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 + +typedef struct st_sort_key_blocks { /* Used when sorting */ + uchar *buff,*end_pos; + uchar lastkey[N_MAX_POSSIBLE_KEY_BUFF]; + uint last_length; + int inited; +} SORT_KEY_BLOCKS; + +typedef struct st_sort_info { + N_INFO *info; + enum data_file_type new_data_file_type; + SORT_KEY_BLOCKS *key_block,*key_block_end; + 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; +} SORT_INFO; + +enum options {OPT_CHARSETS_DIR=256}; + +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, + rep_quick=0,verbose=0,opt_follow_links=1; +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; +static SORT_INFO sort_info; +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); +static int chk_index(N_INFO *info,N_KEYDEF *keyinfo,ulong page,uchar *buff, + ulong *keys,uint level); +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); +static int sort_insert_key(reg1 SORT_KEY_BLOCKS *key_block,uchar *key, + ulong prev_block); +static int sort_delete_record(void); +static void usage(void); +static int flush_pending_blocks(void); +static SORT_KEY_BLOCKS *alloc_key_blocks(uint blocks,uint buffer_length); +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 */ + +int main(argc,argv) +int argc; +char **argv; +{ + int error; + MY_INIT(argv[0]); + + 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("\nTotal of all %d ISAM-files:\nData 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 */ + + +static CHANGEABLE_VAR changeable_vars[] = { + { "key_buffer_size",(long*) &use_buffers,(long) USE_BUFFER_INIT, + (long) MALLOC_OVERHEAD, (long) ~0L,(long) MALLOC_OVERHEAD,(long) IO_SIZE }, + { "read_buffer_size", (long*) &read_buffer_length,(long) READ_BUFFER_INIT, + (long) MALLOC_OVERHEAD,(long) ~0L,(long) MALLOC_OVERHEAD,(long) 1L }, + { "write_buffer_size", (long*) &write_buffer_length,(long) READ_BUFFER_INIT, + (long) MALLOC_OVERHEAD,(long) ~0L,(long) MALLOC_OVERHEAD,(long) 1L }, + { "sort_buffer_size",(long*) &sort_buffer_length,(long) SORT_BUFFER_INIT, + (long) (MIN_SORT_BUFFER+MALLOC_OVERHEAD),(long) ~0L, + (long) MALLOC_OVERHEAD,(long) 1L }, + { "sort_key_blocks",(long*) &sort_key_blocks,BUFFERS_WHEN_SORTING,4L,100L,0L, + 1L }, + { "decode_bits",(long*) &decode_bits,9L,4L,17L,0L,1L }, + { NullS,(long*) 0,0L,0L,0L,0L,0L,} }; + + +static struct option long_options[] = +{ + {"analyze", no_argument, 0, 'a'}, + {"character-sets-dir", required_argument, 0, OPT_CHARSETS_DIR}, +#ifndef DBUG_OFF + {"debug", required_argument, 0, '#'}, +#endif + {"default-character-set", required_argument, 0, 'C'}, + {"description", no_argument, 0, 'd'}, + {"extend-check", no_argument, 0, 'e'}, + {"information", no_argument, 0, 'i'}, + {"force", no_argument, 0, 'f'}, + {"help", no_argument, 0, '?'}, + {"keys-used", required_argument, 0, 'k'}, + {"no-symlinks", no_argument, 0, 'l'}, + {"quick", no_argument, 0, 'q'}, + {"recover", no_argument, 0, 'r'}, + {"safe-recover", no_argument, 0, 'o'}, + {"block-search", required_argument, 0, 'b'}, + {"set-variable", required_argument, 0, 'O'}, + {"silent", no_argument, 0, 's'}, + {"sort-index", no_argument, 0, 'S'}, + {"sort-records", required_argument, 0, 'R'}, + {"unpack", no_argument, 0, 'u'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"wait", no_argument, 0, 'w'}, + {0, 0, 0, 0} +}; + +static void print_version(void) +{ + printf("%s Ver 5.15 for %s at %s\n",my_progname,SYSTEM_TYPE, + MACHINE_TYPE); +} + +static void usage(void) +{ + uint i; + print_version(); + puts("TCX Datakonsult AB, by Monty, for your professional use"); + 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); + puts("\n\ + -a, --analyze Analyze distribution of keys. Will make some joins in\n\ + MySQL faster.\n\ + -#, --debug=... Output debug log. Often this is 'd:t:o,filename`\n\ + --character-sets-dir=...\n\ + Directory where character sets are\n\ + -C, --default-character-set=...\n\ + Set the default character set\n\ + -d, --description Prints some information about table.\n\ + -e, --extend-check Check the table VERY thoroughly. One need use this\n\ + only in extreme cases as isamchk should normally find\n\ + all errors even without this switch\n\ + -f, --force Overwrite old temporary files.\n\ + If one uses -f when checking tables (running isamchk\n\ + without -r), isamchk will automatically restart with\n\ + -r on any wrong table.\n\ + -?, --help Display this help and exit.\n\ + -i, --information Print statistics information about the table\n\ + -k, --keys-used=# Used with '-r'. Tell ISAM to update only the first\n\ + # keys. This can be used to get faster inserts!\n\ + -l, --no-symlinks Do not follow symbolic links when repairing. Normally\n\ + isamchk repairs the table a symlink points at.\n\ + -q, --quick Used with -r to get a faster repair. (The data file\n\ + isn't touched.) One can give a second '-q' to force\n\ + isamchk to modify the original datafile."); + puts("\ + -r, --recover Can fix almost anything except unique keys that aren't\n\ + unique.\n\ + -o, --safe-recover Uses old recovery method; slower than '-r' but can\n\ + handle a couple of cases that '-r' cannot handle.\n\ + -O, --set-variable var=option\n\ + Change the value of a variable.\n\ + -s, --silent Only print errors. One can use two -s to make isamchk\n\ + very silent\n\ + -S, --sort-index Sort index blocks. This speeds up 'read-next' in\n\ + applications\n\ + -R, --sort-records=#\n\ + Sort records according to an index. This makes your\n\ + data much more localized and may speed up things\n\ + (It may be VERY slow to do a sort the first time!)\n\ + -u, --unpack Unpack file packed with pack_isam.\n\ + -v, --verbose Print more information. This can be used with\n\ + -d and -e. Use many -v for more verbosity!\n\ + -V, --version Print version and exit.\n\ + -w, --wait Wait if table is locked."); + print_defaults("my",load_default_groups); + printf("\nPossible variables for option --set-variable (-O) are:\n"); + for (i=0; changeable_vars[i].name ; i++) + printf("%-20s current value: %lu\n", + changeable_vars[i].name, + *changeable_vars[i].varptr); +} + + /* 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) + VOID(init_key_cache(use_buffers,(uint) NEAD_MEM)); + 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\nFix 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 */ + + + /* Read options */ + +static void get_options(register int *argc,register char ***argv) +{ + int c,option_index=0; + + load_defaults("my",load_default_groups,argc,argv); + defaults_alloc= *argv; + set_all_changeable_vars(changeable_vars); + if (isatty(fileno(stdout))) + testflag|=T_WRITE_LOOP; + while ((c=getopt_long(*argc,*argv,"adeif?lqrosSuvVw#:b:k:O:R:C:", + long_options, &option_index)) != EOF) + { + switch(c) { + case 'a': + testflag|= T_STATISTICS; + break; + case 'C': + default_charset=optarg; + break; + case OPT_CHARSETS_DIR: + charsets_dir = optarg; + break; + case 'b': + search_after_block=strtoul(optarg,NULL,10); + 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 'k': + max_key= (uint) atoi(optarg); + 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 'q': + rep_quick++; + break; + case 'u': + testflag|= T_UNPACK | T_REP_BY_SORT; + break; + case 'v': /* Verbose */ + testflag|= T_VERBOSE; + verbose++; + break; + case 'O': + if (set_changeable_var(optarg, changeable_vars)) + { + usage(); + exit(1); + } + break; + case 'R': /* Sort records */ + testflag|= T_SORT_RECORDS; + opt_sort_key=(uint) atoi(optarg)-1; + 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(optarg ? optarg : "d:t:o,/tmp/isamchk.trace"); + break; + case 'V': + print_version(); + exit(0); + case '?': + usage(); + exit(0); + } + } + (*argc)-=optind; + (*argv)+=optind; + 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) + { + if (set_default_charset_by_name(default_charset, MYF(MY_WME))) + exit(1); + } + return; +} /* get options */ + + + /* Check delete links */ + +static int chk_del(info,test_flag) +reg1 N_INFO *info; +uint test_flag; +{ + 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 */ + +static int check_k_link(info,nr) +register N_INFO *info; +uint nr; +{ + 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 */ + +static int chk_key(info) +register N_INFO *info; +{ + 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 */ + +static int chk_index(info,keyinfo,page,buff,keys,level) +N_INFO *info; +N_KEYDEF *keyinfo; +ulong page,*keys; +uchar *buff; +uint level; +{ + 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 */ + +static uint isam_key_length(info,keyinfo) +N_INFO *info; +reg1 N_KEYDEF *keyinfo; +{ + 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) + { + print_error("Keypointers and records dosen't match"); + 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\nTotal %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); + } + + VOID(init_key_cache(use_buffers,NEAD_MEM)); + 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)); + } + if (my_chsize(share->kfile,share->state.key_file_length,MYF(0))) + { + 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("\nISAM 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("\ntable 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("\nField Start Length Type"); + if (share->base.options & HA_OPTION_COMPRESS_RECORD) + printf(" Huff tree Bits"); + VOID(putchar('\n')); + 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 */ + + init_key_cache(use_buffers,NEAD_MEM); + 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) + if (my_chsize(info->dfile,skr,MYF(0))) + print_warning("Can't change size of datafile, error: %d",my_errno); + } + if (my_chsize(share->kfile,share->state.key_file_length,MYF(0))) + 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); + } + 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 */ + + +static int sort_key_write(a) +const void *a; +{ + 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 */ + +static ulong get_record_for_key(info,keyinfo,key) +N_INFO *info; +N_KEYDEF *keyinfo; +uchar *key; +{ + return _nisam_dpos(info,0,key+_nisam_keylength(keyinfo,key)); +} /* get_record_for_key */ + + + /* Insert a key in sort-key-blocks */ + +static int sort_insert_key(key_block,key,prev_block) +reg1 SORT_KEY_BLOCKS *key_block; +uchar *key; +ulong prev_block; +{ + 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\nwith errors in data-file\nUse 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; + SORT_KEY_BLOCKS *key_block; + 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 */ + +static SORT_KEY_BLOCKS *alloc_key_blocks(blocks,buffer_length) +uint blocks,buffer_length; +{ + reg1 uint i; + SORT_KEY_BLOCKS *block; + DBUG_ENTER("alloc_key_blocks"); + + if (!(block=(SORT_KEY_BLOCKS*) my_malloc((sizeof(SORT_KEY_BLOCKS)+ + buffer_length+IO_SIZE)*blocks, + MYF(0)))) + { + 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 */ + +static int test_if_almost_full(info) +N_INFO *info; +{ + 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 */ + +static int recreate_database(org_info,filename) +N_INFO **org_info; +char *filename; +{ + 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 */ + +static void save_integer(pos,pack_length,value) +byte *pos; +uint pack_length; +ulong value; +{ + 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 */ + +static int write_data_suffix(info) +N_INFO *info; +{ + 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 */ + +static int update_state_info(info,update) +N_INFO *info; +uint update; +{ + 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; +} |