isamchk.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:102k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
  2.    This program is free software; you can redistribute it and/or modify
  3.    it under the terms of the GNU General Public License as published by
  4.    the Free Software Foundation; either version 2 of the License, or
  5.    (at your option) any later version.
  6.    This program is distributed in the hope that it will be useful,
  7.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  8.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  9.    GNU General Public License for more details.
  10.    You should have received a copy of the GNU General Public License
  11.    along with this program; if not, write to the Free Software
  12.    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
  13. /* Descript, check and repair of ISAM tables */
  14. #include "isamdef.h"
  15. #include <m_ctype.h>
  16. #include <stdarg.h>
  17. #include <my_getopt.h>
  18. #ifdef HAVE_SYS_VADVICE_H
  19. #include <sys/vadvise.h>
  20. #endif
  21. #ifdef HAVE_SYS_MMAN_H
  22. #include <sys/mman.h>
  23. #endif
  24. SET_STACK_SIZE(9000) /* Minimum stack size for program */
  25. #define T_VERBOSE 1
  26. #define T_SILENT 2
  27. #define T_DESCRIPT 4
  28. #define T_EXTEND 8
  29. #define T_INFO 16
  30. #define T_REP 32
  31. #define T_OPT 64 /* Not currently used */
  32. #define T_FORCE_CREATE 128
  33. #define T_WRITE_LOOP 256
  34. #define T_UNPACK 512
  35. #define T_STATISTICS 1024
  36. #define T_VERY_SILENT 2048
  37. #define T_SORT_RECORDS 4096
  38. #define T_SORT_INDEX 8192
  39. #define T_WAIT_FOREVER 16384
  40. #define T_REP_BY_SORT 32768L
  41. #define O_NEW_INDEX 1 /* Bits set in out_flag */
  42. #define O_NEW_DATA 2
  43. #if defined(_MSC_VER) && !defined(__WIN__)
  44. #define USE_BUFFER_INIT 250L*1024L
  45. #define READ_BUFFER_INIT ((uint) 32768-MALLOC_OVERHEAD)
  46. #define SORT_BUFFER_INIT (uint) (65536L-MALLOC_OVERHEAD)
  47. #define MIN_SORT_BUFFER (1024*16-MALLOC_OVERHEAD)
  48. #else
  49. #define USE_BUFFER_INIT (((1024L*512L-MALLOC_OVERHEAD)/IO_SIZE)*IO_SIZE)
  50. #define READ_BUFFER_INIT (1024L*256L-MALLOC_OVERHEAD)
  51. #define SORT_BUFFER_INIT (2048L*1024L-MALLOC_OVERHEAD)
  52. #define MIN_SORT_BUFFER (4096-MALLOC_OVERHEAD)
  53. #endif
  54. #define NEED_MEM ((uint) 10*4*(IO_SIZE+32)+32) /* Nead for recursion */
  55. #define MAXERR 20
  56. #define BUFFERS_WHEN_SORTING 16 /* Alloc for sort-key-tree */
  57. #define WRITE_COUNT MY_HOW_OFTEN_TO_WRITE
  58. #define INDEX_TMP_EXT ".TMM"
  59. #define DATA_TMP_EXT ".TMD"
  60. #define MYF_RW MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL)
  61. #define UPDATE_TIME 1
  62. #define UPDATE_STAT 2
  63. #define UPDATE_SORT 4
  64. typedef struct st_isam_sort_key_blocks { /* Used when sorting */
  65.   uchar *buff,*end_pos;
  66.   uchar lastkey[N_MAX_POSSIBLE_KEY_BUFF];
  67.   uint last_length;
  68.   int inited;
  69. } ISAM_SORT_KEY_BLOCKS;
  70. typedef struct st_isam_sort_info {
  71.   N_INFO *info;
  72.   enum data_file_type new_data_file_type;
  73.   ISAM_SORT_KEY_BLOCKS *key_block,*key_block_end;
  74.   uint key,find_length;
  75.   ulong pos,max_pos,filepos,start_recpos,filelength,dupp,max_records,unique,
  76.     buff_length;
  77.   my_bool fix_datafile;
  78.   char *record,*buff;
  79.   N_KEYDEF *keyinfo;
  80.   N_KEYSEG *keyseg;
  81. } ISAM_SORT_INFO;
  82. enum ic_options {OPT_CHARSETS_DIR_IC=256, OPT_KEY_BUFFER_SIZE,
  83.  OPT_READ_BUFFER_SIZE, OPT_WRITE_BUFFER_SIZE,
  84.  OPT_SORT_BUFFER_SIZE, OPT_SORT_KEY_BLOCKS,
  85.  OPT_DECODE_BITS, OPT_AUTO_CLOSE};
  86. static ulong use_buffers=0,read_buffer_length=0,write_buffer_length=0,
  87. sort_buffer_length=0,sort_key_blocks=0,crc=0,unique_count=0;
  88. static uint testflag=0,out_flag=0,warning_printed=0,error_printed=0,
  89.             verbose=0,opt_follow_links=1;
  90. static my_bool rep_quick= 0;
  91. static uint opt_sort_key=0,total_files=0,max_level=0,max_key=N_MAXKEY;
  92. static ulong keydata=0,totaldata=0,key_blocks=0;
  93. static ulong new_file_pos=0,record_checksum=0,key_file_blocks=0,decode_bits;
  94. static ulong total_records=0,total_deleted=0;
  95. static ulong search_after_block=NI_POS_ERROR;
  96. static byte *record_buff;
  97. static char **defaults_alloc;
  98. static const char *type_names[]=
  99. { "?","text","binary", "short", "long", "float",
  100.   "double","number","unsigned short",
  101.   "unsigned long","longlong","ulonglong","int24",
  102.   "uint24","int8","?",},
  103.   *packed_txt="packed ",
  104.   *diff_txt="stripped ",
  105.   *field_pack[]={"","no endspace", "no prespace",
  106.  "no zeros", "blob", "constant", "table-lookup",
  107.  "always zero","?","?",};
  108. static char temp_filename[FN_REFLEN], *isam_file_name, *default_charset; 
  109. static IO_CACHE read_cache;
  110. static ISAM_SORT_INFO sort_info;
  111. static int tmpfile_createflag=O_RDWR | O_TRUNC | O_EXCL;
  112. static const char *load_default_groups[]= { "isamchk",0 };
  113. /* Functions defined in this file */
  114. extern int main(int argc,char * *argv);
  115. extern void print_error _VARARGS((const char *fmt,...));
  116. static void print_warning _VARARGS((const char *fmt,...));
  117. static void print_info _VARARGS((const char *fmt,...));
  118. static int nisamchk(char *filename);
  119. static void get_options(int *argc,char * * *argv);
  120. static int chk_del(N_INFO *info,uint testflag);
  121. static int check_k_link(N_INFO *info,uint nr);
  122. static int chk_size(N_INFO *info);
  123. static int chk_key(N_INFO *info);
  124. static int chk_index(N_INFO *info, N_KEYDEF *keyinfo, ulong page, uchar *buff,
  125.      ulong *keys, uint level);
  126. static uint isam_key_length(N_INFO *info,N_KEYDEF *keyinfo);
  127. static unsigned long calc_checksum(ulong count);
  128. static int chk_data_link(N_INFO *info,int extend);
  129. static int rep(N_INFO *info,char *name);
  130. static int writekeys(N_INFO *info,byte *buff,ulong filepos);
  131. static void descript(N_INFO *info,char *name);
  132. static int movepoint(N_INFO *info,byte *record,ulong oldpos,ulong newpos,
  133.      uint prot_key);
  134. static void lock_memory(void);
  135. static int flush_blocks(File file);
  136. static int sort_records(N_INFO *,my_string,uint,int);
  137. static int sort_index(N_INFO *info,my_string name);
  138. static int sort_record_index(N_INFO *info,N_KEYDEF *keyinfo,ulong page,
  139.      uchar *buff,uint sortkey,File new_file);
  140. static int sort_one_index(N_INFO *info,N_KEYDEF *keyinfo,uchar *buff,
  141.   File new_file);
  142. static int change_to_newfile(const char * filename,const char * old_ext,
  143.      const char * new_ext);
  144. static int lock_file(File file,ulong start,int lock_type,const char* filetype,
  145.      const char *filename);
  146. static int filecopy(File to,File from,ulong start,ulong length,
  147.     const char * type);
  148. static void print_version(void);
  149. static int rep_by_sort(N_INFO *info,my_string name);
  150. static int sort_key_read(void *key);
  151. static int sort_get_next_record(void);
  152. static int sort_write_record(void);
  153. static int sort_key_cmp(const void *not_used, const void *a,const void *b);
  154. static int sort_key_write(const void *a);
  155. static ulong get_record_for_key(N_INFO *info,N_KEYDEF *keyinfo,
  156. uchar *key);
  157. static int sort_insert_key(reg1 ISAM_SORT_KEY_BLOCKS *key_block,uchar *key,
  158.    ulong prev_block);
  159. static int sort_delete_record(void);
  160. static void usage(void);
  161. static int flush_pending_blocks(void);
  162. static ISAM_SORT_KEY_BLOCKS *alloc_key_blocks(uint blocks,uint buffer_length);
  163. static int test_if_almost_full(N_INFO *info);
  164. static int recreate_database(N_INFO **info,char *filename);
  165. static void save_integer(byte *pos,uint pack_length,ulong value);
  166. static int write_data_suffix(N_INFO *info);
  167. static int update_state_info(N_INFO *info,uint update);
  168. /* Main program */
  169. int main( int argc, char **argv)
  170. {
  171.   int error;
  172.   MY_INIT(argv[0]);
  173. #ifdef __EMX__
  174.   _wildcard (&argc, &argv);
  175. #endif
  176.   get_options(&argc,(char***) &argv);
  177.   nisam_quick_table_bits=(uint) decode_bits;
  178.   error=0;
  179.   while (--argc >= 0)
  180.   {
  181.     error|= nisamchk(*(argv++));
  182.     VOID(fflush(stdout));
  183.     VOID(fflush(stderr));
  184.     if ((error_printed | warning_printed) && (testflag & T_FORCE_CREATE) &&
  185. (!(testflag & (T_REP | T_REP_BY_SORT | T_SORT_RECORDS |
  186.        T_SORT_INDEX))))
  187.     {
  188.       testflag|=T_REP;
  189.       error|=nisamchk(argv[-1]);
  190.       testflag&= ~T_REP;
  191.       VOID(fflush(stdout));
  192.       VOID(fflush(stderr));
  193.     }
  194.     if (argc && (!(testflag & T_SILENT) || testflag & T_INFO))
  195.     {
  196.       puts("n---------n");
  197.       VOID(fflush(stdout));
  198.     }
  199.   }
  200.   if (total_files > 1)
  201.   { /* Only if descript */
  202.     if (!(testflag & T_SILENT) || testflag & T_INFO)
  203.       puts("n---------n");
  204.     printf("nTotal of all %d ISAM-files:nData records: %8lu   Deleted blocks: %8lun",total_files,total_records,total_deleted);
  205.   }
  206.   free_defaults(defaults_alloc);
  207.   my_end(testflag & T_INFO ? MY_CHECK_ERROR | MY_GIVE_INFO : MY_CHECK_ERROR);
  208.   exit(error);
  209. #ifndef _lint
  210.   return 0; /* No compiler warning */
  211. #endif
  212. } /* main */
  213. static struct my_option my_long_options[] =
  214. {
  215.   {"analyze", 'a',
  216.    "Analyze distribution of keys. Will make some joins in MySQL faster.",
  217.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  218. #ifdef __NETWARE__
  219.   {"auto-close", OPT_AUTO_CLOSE, "Auto close the screen on exit for Netware.",
  220.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  221. #endif
  222.   {"character-sets-dir", OPT_CHARSETS_DIR_IC,
  223.    "Directory where character sets are", (gptr*) &charsets_dir,
  224.    (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  225. #ifndef DBUG_OFF
  226.   {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'",
  227.    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  228. #endif
  229.   {"default-character-set", 'C', "Set the default character set",
  230.    (gptr*) &default_charset, (gptr*) &default_charset, 0, GET_STR,
  231.    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  232.   {"description", 'd', "Prints some information about table.",
  233.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  234.   {"extend-check", 'e',
  235.    "Check the table VERY thoroughly. One need to use this only in extreme cases, because isamchk should normally find all errors even without this switch.",
  236.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  237.   {"information", 'i', "Print statistics information about the table",
  238.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  239.   {"force", 'f',
  240.    "Overwrite old temporary files. If one uses -f when checking tables (running isamchk without -r), isamchk will automatically restart with -r on any wrong table.",
  241.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  242.   {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
  243.    0, 0, 0, 0, 0},
  244.   {"keys-used", 'k',
  245.    "Used with '-r'. Tell ISAM to update only the first # keys. This can be used to get faster inserts!",
  246.    (gptr*) &max_key, (gptr*) &max_key, 0, GET_UINT, REQUIRED_ARG, N_MAXKEY, 0,
  247.    0, 0, 0, 0},
  248.   {"no-symlinks", 'l',
  249.    "Do not follow symbolic links when repairing. Normally isamchk repairs the table a symlink points at.",
  250.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  251.   {"quick", 'q', 
  252.    "Used with -r to get a faster repair. (The data file isn't touched.) One can give a second '-q' to force isamchk to modify the original datafile.",
  253.    (gptr*) &rep_quick, (gptr*) &rep_quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
  254.    0},
  255.   {"recover", 'r',
  256.    "Can fix almost anything except unique keys that aren't unique.",
  257.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  258.   {"safe-recover", 'o',
  259.    "Uses old recovery method; slower than '-r' but can handle a couple of cases that '-r' cannot handle.",
  260.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  261.   {"set-variable", 'O',
  262.    "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.",
  263.    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  264.   {"block-search", 'b', "For debugging.", (gptr*) &search_after_block,
  265.    (gptr*) &search_after_block, 0, GET_ULONG, REQUIRED_ARG,
  266.    (longlong) NI_POS_ERROR, 0, 0, 0, 0, 0},
  267.   {"silent", 's',
  268.    "Only print errors. One can use two -s to make isamchk very silent.",
  269.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  270.   {"sort-index", 'S',
  271.    "Sort index blocks. This speeds up 'read-next' in applications.",
  272.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  273.   {"sort-records", 'R',
  274.    "Sort records according to an index. This makes your data much more localized and may speed up things (It may be VERY slow to do a sort the first time!)",
  275.    (gptr*) &opt_sort_key, (gptr*) &opt_sort_key, 0, GET_UINT, REQUIRED_ARG,
  276.    0, 0, (N_MAXKEY - 1), 1, 0, 0},
  277.   {"unpack", 'u', "Unpack file packed with pack_isam.",
  278.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  279.   {"verbose", 'v',
  280.    "Print more information. This can be used with -d and -e. Use many -v for more verbosity!",
  281.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  282.   {"version", 'V', "Print version and exit.", 
  283.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  284.   {"wait", 'w', "Wait if table is locked.",
  285.    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  286.   {"key_buffer_size", OPT_KEY_BUFFER_SIZE, "", (gptr*) &use_buffers,
  287.    (gptr*) &use_buffers, 0, GET_ULONG, REQUIRED_ARG, (long) USE_BUFFER_INIT,
  288.    (long) MALLOC_OVERHEAD, (long) ~0L, (long) MALLOC_OVERHEAD, (long) IO_SIZE,
  289.    0},
  290.   {"read_buffer_size", OPT_READ_BUFFER_SIZE, "",
  291.    (gptr*) &read_buffer_length, (gptr*) &read_buffer_length, 0, GET_ULONG,
  292.    REQUIRED_ARG, (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD,
  293.    (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
  294.   {"write_buffer_size", OPT_WRITE_BUFFER_SIZE, "",
  295.    (gptr*) &write_buffer_length, (gptr*) &write_buffer_length, 0, GET_ULONG,
  296.    REQUIRED_ARG, (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD, (long) ~0L,
  297.    (long) MALLOC_OVERHEAD, (long) 1L, 0},
  298.   {"sort_buffer_size", OPT_SORT_BUFFER_SIZE, "",
  299.    (gptr*) &sort_buffer_length, (gptr*) &sort_buffer_length, 0, GET_ULONG,
  300.    REQUIRED_ARG, (long) SORT_BUFFER_INIT,
  301.    (long) (MIN_SORT_BUFFER + MALLOC_OVERHEAD), (long) ~0L,
  302.    (long) MALLOC_OVERHEAD, (long) 1L, 0},
  303.   {"sort_key_blocks", OPT_SORT_KEY_BLOCKS, "",
  304.    (gptr*) &sort_key_blocks, (gptr*) &sort_key_blocks, 0, GET_ULONG,
  305.    REQUIRED_ARG, BUFFERS_WHEN_SORTING, 4L, 100L, 0L, 1L, 0},
  306.   {"decode_bits", OPT_DECODE_BITS, "",
  307.    (gptr*) &decode_bits, (gptr*) &decode_bits, 0, GET_ULONG, REQUIRED_ARG,
  308.    9L, 4L, 17L, 0L, 1L, 0},
  309.   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
  310. };
  311. #include <help_start.h>
  312. static void print_version(void)
  313. {
  314.   printf("%s  Ver 6.01 for %s at %sn", my_progname, SYSTEM_TYPE,
  315.  MACHINE_TYPE);
  316.   NETWARE_SET_SCREEN_MODE(1);
  317. }
  318. static void usage(void)
  319. {
  320.   print_version();
  321.   puts("MySQL AB, by Monty, for your professional use");
  322.   puts("This software comes with NO WARRANTY: see the PUBLIC for details.n");
  323.   puts("Description, check and repair of ISAM tables.");
  324.   puts("Used without options all tables on the command will be checked for errors");
  325.   printf("Usage: %s [OPTIONS] tables[.ISM]n", my_progname);
  326.   my_print_help(my_long_options);
  327.   print_defaults("my", load_default_groups);
  328.   my_print_variables(my_long_options);
  329. }
  330. #include <help_end.h>
  331. /* Check table */
  332. static int nisamchk(my_string filename)
  333. {
  334.   int error,lock_type,recreate;
  335.   N_INFO *info;
  336.   File datafile;
  337.   char fixed_name[FN_REFLEN];
  338.   ISAM_SHARE *share;
  339.   DBUG_ENTER("nisamchk");
  340.   out_flag=error=warning_printed=error_printed=recreate=0;
  341.   datafile=0;
  342.   isam_file_name=filename; /* For error messages */
  343.   if (!(info=nisam_open(filename,
  344.      (testflag & T_DESCRIPT) ? O_RDONLY : O_RDWR,
  345.      (testflag & T_WAIT_FOREVER) ? HA_OPEN_WAIT_IF_LOCKED :
  346.      (testflag & T_DESCRIPT) ? HA_OPEN_IGNORE_IF_LOCKED :
  347.      HA_OPEN_ABORT_IF_LOCKED)))
  348.   {
  349.     /* Avoid twice printing of isam file name */
  350.     error_printed=1;
  351.     switch (my_errno) {
  352.     case HA_ERR_CRASHED:
  353.       print_error("'%s' is not a ISAM-table",filename);
  354.       break;
  355.     case HA_ERR_OLD_FILE:
  356.       print_error("'%s' is a old type of ISAM-table", filename);
  357.       break;
  358.     case HA_ERR_END_OF_FILE:
  359.       print_error("Couldn't read compleat header from '%s'", filename);
  360.       break;
  361.     case EAGAIN:
  362.       print_error("'%s' is locked. Use -w to wait until unlocked",filename);
  363.       break;
  364.     case ENOENT:
  365.       print_error("File '%s' doesn't exist",filename);
  366.       break;
  367.     case EACCES:
  368.       print_error("You don't have permission to use '%s'",filename);
  369.       break;
  370.     default:
  371.       print_error("%d when opening ISAM-table '%s'",
  372.   my_errno,filename);
  373.       break;
  374.     }
  375.     DBUG_RETURN(1);
  376.   }
  377.   share=info->s;
  378.   share->base.options&= ~HA_OPTION_READ_ONLY_DATA; /* We are modifing it */
  379.   share->r_locks=0;
  380.   if ((testflag & (T_REP_BY_SORT | T_REP | T_STATISTICS |
  381.    T_SORT_RECORDS | T_SORT_INDEX)) &&
  382.       ((testflag & T_UNPACK && share->data_file_type == COMPRESSED_RECORD) ||
  383.        share->state_length != sizeof(share->state) ||
  384.        uint2korr(share->state.header.base_info_length) !=
  385.        sizeof(share->base) ||
  386.        (max_key && ! share->state.keys && share->base.keys) ||
  387.        test_if_almost_full(info) ||
  388.        info->s->state.header.file_version[3] != nisam_file_magic[3]))
  389.   {
  390.     if (recreate_database(&info,filename))
  391.     {
  392.       VOID(fprintf(stderr,
  393.    "ISAM-table '%s' is not fixed because of errorsn",
  394.       filename));
  395.       return(-1);
  396.     }
  397.     recreate=1;
  398.     if (!(testflag & (T_REP | T_REP_BY_SORT)))
  399.     {
  400.       testflag|=T_REP_BY_SORT; /* if only STATISTICS */
  401.       if (!(testflag & T_SILENT))
  402. printf("- '%s' has old table-format. Recreating indexn",filename);
  403.       if (!rep_quick)
  404. rep_quick=1;
  405.     }
  406.     share=info->s;
  407.     share->r_locks=0;
  408.   }
  409.   if (testflag & T_DESCRIPT)
  410.   {
  411.     total_files++;
  412.     total_records+=share->state.records; total_deleted+=share->state.del;
  413.     descript(info,filename);
  414.   }
  415.   else
  416.   {
  417.     if (testflag & (T_REP+T_REP_BY_SORT+T_OPT+T_SORT_RECORDS+T_SORT_INDEX))
  418.       lock_type = F_WRLCK; /* table is changed */
  419.     else
  420.       lock_type= F_RDLCK;
  421.     if (info->lock_type == F_RDLCK)
  422.       info->lock_type=F_UNLCK; /* Read only table */
  423.     if (_nisam_readinfo(info,lock_type,0))
  424.     {
  425.       print_error("Can't lock indexfile of '%s', error: %d",
  426.   filename,my_errno);
  427.       error_printed=0;
  428.       goto end2;
  429.     }
  430.     share->w_locks++; /* Mark (for writeinfo) */
  431.     if (lock_file(info->dfile,0L,lock_type,"datafile of",filename))
  432.       goto end;
  433.     info->lock_type= F_EXTRA_LCK; /* Simulate as locked */
  434.     info->tmp_lock_type=lock_type;
  435.     datafile=info->dfile;
  436.     if (testflag & (T_REP+T_REP_BY_SORT+T_SORT_RECORDS+T_SORT_INDEX))
  437.     {
  438.       if (testflag & (T_REP+T_REP_BY_SORT))
  439. share->state.keys=min(share->base.keys,max_key);
  440.       VOID(fn_format(fixed_name,filename,"",N_NAME_IEXT,
  441.      4+ (opt_follow_links ? 16 : 0)));
  442.       if (rep_quick && (error=chk_del(info,testflag & ~T_VERBOSE)))
  443. print_error("Quick-recover aborted; Run recovery without switch 'q'");
  444.       else
  445.       {
  446. if (testflag & T_REP_BY_SORT &&
  447.     (share->state.keys || (rep_quick && !max_key && ! recreate)))
  448.   error=rep_by_sort(info,fixed_name);
  449. else if (testflag & (T_REP | T_REP_BY_SORT))
  450.   error=rep(info,fixed_name);
  451.       }
  452.       if (!error && testflag & T_SORT_RECORDS)
  453.       {
  454. if (out_flag & O_NEW_DATA)
  455. { /* Change temp file to org file */
  456.   VOID(lock_file(datafile,0L,F_UNLCK,"datafile of",filename));
  457.   VOID(my_close(datafile,MYF(MY_WME)));    /* Close old file */
  458.   VOID(my_close(info->dfile,MYF(MY_WME))); /* Close new file */
  459.   error|=change_to_newfile(fixed_name,N_NAME_DEXT,DATA_TMP_EXT);
  460.   if ((info->dfile=my_open(fn_format(temp_filename,fixed_name,"",
  461.      N_NAME_DEXT,2+4),
  462.    O_RDWR | O_SHARE,
  463.    MYF(MY_WME))) <= 0 ||
  464.       lock_file(info->dfile,0L,F_WRLCK,"datafile",temp_filename))
  465.     error=1;
  466.   out_flag&= ~O_NEW_DATA; /* We are using new datafile */
  467.   read_cache.file=info->dfile;
  468. }
  469. if (! error)
  470.   error=sort_records(info,fixed_name,opt_sort_key,
  471.      test(!(testflag & T_REP)));
  472. datafile=info->dfile; /* This is now locked */
  473.       }
  474.       if (!error && testflag & T_SORT_INDEX)
  475. error=sort_index(info,fixed_name);
  476.     }
  477.     else
  478.     {
  479.       if (!(testflag & T_SILENT) || testflag & T_INFO)
  480. printf("Checking ISAM file: %sn",filename);
  481.       if (!(testflag & T_SILENT))
  482. printf("Data records: %7ld   Deleted blocks: %7ldn",
  483.        share->state.records,share->state.del);
  484.       share->state.keys=min(share->state.keys,max_key);
  485.       error =chk_size(info);
  486.       error|=chk_del(info,testflag);
  487.       error|=chk_key(info);
  488.       if (!rep_quick)
  489.       {
  490. if (testflag & T_EXTEND)
  491.   VOID(init_key_cache(dflt_key_cache,KEY_CACHE_BLOCK_SIZE,
  492.                               use_buffers,0,0));
  493. VOID(init_io_cache(&read_cache,datafile,(uint) read_buffer_length,
  494.   READ_CACHE,share->pack.header_length,1,
  495.   MYF(MY_WME)));
  496. lock_memory();
  497. error|=chk_data_link(info,testflag & T_EXTEND);
  498. error|=flush_blocks(share->kfile);
  499. VOID(end_io_cache(&read_cache));
  500.       }
  501.     }
  502.   }
  503. end:
  504.   if (!(testflag & T_DESCRIPT))
  505.   {
  506.     if (info->update & (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED))
  507.       error|=update_state_info(info,
  508.        ((testflag & (T_REP | T_REP_BY_SORT)) ?
  509. UPDATE_TIME | UPDATE_STAT : 0) |
  510.        ((testflag & T_SORT_RECORDS) ?
  511. UPDATE_SORT : 0));
  512.     VOID(lock_file(share->kfile,0L,F_UNLCK,"indexfile",filename));
  513.     if (datafile > 0)
  514.       VOID(lock_file(datafile,0L,F_UNLCK,"datafile of",filename));
  515.     info->update&= ~(HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
  516.   }
  517.   share->w_locks--;
  518. end2:
  519.   if (datafile && datafile != info->dfile)
  520.     VOID(my_close(datafile,MYF(MY_WME)));
  521.   if (nisam_close(info))
  522.   {
  523.     print_error("%d when closing ISAM-table '%s'",my_errno,filename);
  524.     DBUG_RETURN(1);
  525.   }
  526.   if (error == 0)
  527.   {
  528.     if (out_flag & O_NEW_DATA)
  529.       error|=change_to_newfile(fixed_name,N_NAME_DEXT,DATA_TMP_EXT);
  530.     if (out_flag & O_NEW_INDEX)
  531.       error|=change_to_newfile(fixed_name,N_NAME_IEXT,INDEX_TMP_EXT);
  532.   }
  533.   VOID(fflush(stdout)); VOID(fflush(stderr));
  534.   if (error_printed)
  535.   {
  536.     if (testflag & (T_REP+T_REP_BY_SORT+T_SORT_RECORDS+T_SORT_INDEX))
  537.       VOID(fprintf(stderr,
  538.    "ISAM-table '%s' is not fixed because of errorsn",
  539.    filename));
  540.     else if (! (error_printed & 2) && !(testflag & T_FORCE_CREATE))
  541.       VOID(fprintf(stderr,
  542.       "ISAM-table '%s' is corruptednFix it using switch "-r" or "-o"n",
  543.       filename));
  544.   }
  545.   else if (warning_printed &&
  546.    ! (testflag & (T_REP+T_REP_BY_SORT+T_SORT_RECORDS+T_SORT_INDEX+
  547.   T_FORCE_CREATE)))
  548.     VOID(fprintf(stderr, "ISAM-table '%s' is useable but should be fixedn",
  549.     filename));
  550.   VOID(fflush(stderr));
  551.   DBUG_RETURN(error);
  552. } /* nisamchk */
  553. static my_bool
  554. get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
  555.        char *argument)
  556.      
  557. {
  558.   switch(optid) {
  559. #ifdef __NETWARE__
  560.   case OPT_AUTO_CLOSE:
  561.     setscreenmode(SCR_AUTOCLOSE_ON_EXIT);
  562.     break;
  563. #endif
  564.   case 'a':
  565.     testflag|= T_STATISTICS;
  566.     break;
  567.   case 's': /* silent */
  568.     if (testflag & T_SILENT)
  569.       testflag|=T_VERY_SILENT;
  570.     testflag|= T_SILENT;
  571.     testflag&= ~T_WRITE_LOOP;
  572.     break;
  573.   case 'w':
  574.     testflag|= T_WAIT_FOREVER;
  575.     break;
  576.   case 'd': /* description if isam-file */
  577.     testflag|= T_DESCRIPT;
  578.     break;
  579.   case 'e': /* extend check */
  580.     testflag|= T_EXTEND;
  581.     break;
  582.   case 'i':
  583.     testflag|= T_INFO;
  584.     break;
  585.   case 'f':
  586.     tmpfile_createflag= O_RDWR | O_TRUNC;
  587.     testflag|=T_FORCE_CREATE;
  588.     break;
  589.   case 'l':
  590.     opt_follow_links=0;
  591.     break;
  592.   case 'r': /* Repair table */
  593.     testflag= (testflag & ~T_REP) | T_REP_BY_SORT;
  594.     break;
  595.   case 'o':
  596.     testflag= (testflag & ~T_REP_BY_SORT) | T_REP;
  597.     my_disable_async_io=1;         /* More safety */
  598.     break;
  599.   case 'u':
  600.     testflag|= T_UNPACK | T_REP_BY_SORT;
  601.     break;
  602.   case 'v': /* Verbose */
  603.     testflag|= T_VERBOSE;
  604.     verbose++;
  605.     break;
  606.   case 'R': /* Sort records */
  607.     testflag|= T_SORT_RECORDS;
  608.     if (opt_sort_key >= N_MAXKEY)
  609.     {
  610.       fprintf(stderr,
  611.       "The value of the sort key is bigger than max key: %d.n",
  612.       N_MAXKEY);
  613.       exit(1);
  614.     }
  615.     break;
  616.   case 'S':                         /* Sort index */
  617.     testflag|= T_SORT_INDEX;
  618.     break;
  619.   case '#':
  620.     DBUG_PUSH(argument ? argument : "d:t:o,/tmp/isamchk.trace");
  621.     break;
  622.   case 'V':
  623.     print_version();
  624.     exit(0);
  625.   case '?':
  626.     usage();
  627.     exit(0);
  628.   }
  629.   return 0;
  630. }
  631.  /* Read options */
  632. static void get_options(register int *argc, register char ***argv)
  633. {
  634.   int ho_error;
  635.   load_defaults("my",load_default_groups,argc,argv);
  636.   defaults_alloc=  *argv;
  637.   if (isatty(fileno(stdout)))
  638.     testflag|=T_WRITE_LOOP;
  639.   if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
  640.     exit(ho_error);
  641.   if (*argc == 0)
  642.   {
  643.     usage();
  644.     exit(-1);
  645.   }
  646.   if ((testflag & T_UNPACK) && (rep_quick || (testflag & T_SORT_RECORDS)))
  647.   {
  648.     VOID(fprintf(stderr,
  649.  "%s: --unpack can't be used with --quick or --sort-recordsn",
  650.  my_progname));
  651.     exit(1);
  652.   }
  653.   if (default_charset)
  654.   {
  655.     if (!(default_charset_info= get_charset_by_name(default_charset, MYF(MY_WME))))
  656.       exit(1);
  657.   }
  658.   return;
  659. } /* get options */
  660. /* Check delete links */
  661. static int chk_del( reg1 N_INFO *info, uint test_flag)
  662. {
  663.   reg2 ulong i;
  664.   uint j,delete_link_length;
  665.   ulong empty,next_link;
  666.   uchar buff[8];
  667.   DBUG_ENTER("chk_del");
  668.   if (!(test_flag & T_SILENT)) puts("- check delete-chain");
  669.   record_checksum=0L;
  670.   key_file_blocks=info->s->base.keystart;
  671.   for (j =0 ; j < info->s->state.keys ; j++)
  672.     if (check_k_link(info,j))
  673.       goto wrong;
  674.   delete_link_length=(info->s->base.options & HA_OPTION_PACK_RECORD) ? 8 : 5;
  675.   next_link=info->s->state.dellink;
  676.   if (info->s->state.del == 0)
  677.   {
  678.     if (test_flag & T_VERBOSE)
  679.     {
  680.       puts("No recordlinks");
  681.     }
  682.   }
  683.   else
  684.   {
  685.     if (test_flag & T_VERBOSE)
  686.       printf("Recordlinks:    ");
  687.     empty=0;
  688.     for (i= info->s->state.del ; i > 0L && next_link != NI_POS_ERROR ; i--)
  689.     {
  690.       if (test_flag & T_VERBOSE) printf("%10lu",next_link);
  691.       if (next_link >= info->s->state.data_file_length)
  692. goto wrong;
  693.       if (my_pread(info->dfile,(char*) buff,delete_link_length,
  694.   next_link,MYF(MY_NABP)))
  695.       {
  696. if (test_flag & T_VERBOSE) puts("");
  697. print_error("Can't read delete-link at filepos: %lu",
  698.     (ulong) next_link);
  699. DBUG_RETURN(1);
  700.       }
  701.       if (*buff != '')
  702.       {
  703. if (test_flag & T_VERBOSE) puts("");
  704. print_error("Record at pos: %lu is not remove-marked",
  705.     (ulong) next_link);
  706. goto wrong;
  707.       }
  708.       if (info->s->base.options & HA_OPTION_PACK_RECORD)
  709.       {
  710. next_link=uint4korr(buff+4);
  711. empty+=uint3korr(buff+1);
  712.       }
  713.       else
  714.       {
  715. record_checksum+=next_link;
  716. next_link=uint4korr(buff+1);
  717. empty+=info->s->base.reclength;
  718.       }
  719.       if (next_link == (uint32) ~0) /* Fix for 64 bit long */
  720. next_link=NI_POS_ERROR;
  721.     }
  722.     if (empty != info->s->state.empty)
  723.     {
  724.       if (test_flag & T_VERBOSE) puts("");
  725.       print_warning("Not used space is supposed to be: %lu but is: %lu",
  726.     (ulong) info->s->state.empty,(ulong) empty);
  727.       info->s->state.empty=empty;
  728.     }
  729.     if (i != 0 || next_link != NI_POS_ERROR)
  730.       goto wrong;
  731.     if (test_flag & T_VERBOSE) puts("n");
  732.   }
  733.   DBUG_RETURN(0);
  734. wrong:
  735.   if (test_flag & T_VERBOSE) puts("");
  736.   print_error("delete-link-chain corrupted");
  737.   DBUG_RETURN(1);
  738. } /* chk_del */
  739. /* Kontrollerar l{nkarna i nyckelfilen */
  740. static int check_k_link( register N_INFO *info, uint nr)
  741. {
  742.   ulong next_link,records;
  743.   DBUG_ENTER("check_k_link");
  744.   if (testflag & T_VERBOSE)
  745.     printf("index %2d:       ",nr+1);
  746.   next_link=info->s->state.key_del[nr];
  747.   records= (info->s->state.key_file_length /
  748.     info->s->keyinfo[nr].base.block_length);
  749.   while (next_link != NI_POS_ERROR && records > 0)
  750.   {
  751.     if (testflag & T_VERBOSE) printf("%10lu",next_link);
  752.     if (next_link > info->s->state.key_file_length ||
  753. next_link & (info->s->blocksize-1))
  754.       DBUG_RETURN(1);
  755.     if (my_pread(info->s->kfile,(char*) &next_link,sizeof(long),next_link,
  756.  MYF(MY_NABP)))
  757.       DBUG_RETURN(1);
  758.     records--;
  759.     key_file_blocks+=info->s->keyinfo[nr].base.block_length;
  760.   }
  761.   if (testflag & T_VERBOSE)
  762.   {
  763.     if (next_link != NI_POS_ERROR)
  764.       printf("%10lun",next_link);
  765.     else
  766.       puts("");
  767.   }
  768.   DBUG_RETURN (next_link != NI_POS_ERROR);
  769. } /* check_k_link */
  770. /* Kontrollerar storleken p} filerna */
  771. static int chk_size(register N_INFO *info)
  772. {
  773.   int error=0;
  774.   register my_off_t skr,size;
  775.   DBUG_ENTER("chk_size");
  776.   if (!(testflag & T_SILENT)) puts("- check file-size");
  777.   size=my_seek(info->s->kfile,0L,MY_SEEK_END,MYF(0));
  778.   if ((skr=(my_off_t) info->s->state.key_file_length) != size)
  779.   {
  780.     if (skr > size)
  781.     {
  782.       error=1;
  783.       print_error("Size of indexfile is: %-8lu        Should be: %lu",
  784.   (ulong) size, (ulong)  skr);
  785.     }
  786.     else
  787.       print_warning("Size of indexfile is: %-8lu      Should be: %lu",
  788.     (ulong) size,(ulong) skr);
  789.   }
  790.   if (!(testflag & T_VERY_SILENT) &&
  791.       ! (info->s->base.options & HA_OPTION_COMPRESS_RECORD) &&
  792.       info->s->state.key_file_length >
  793.       (ulong) (ulong_to_double(info->s->base.max_key_file_length)*0.9))
  794.     print_warning("Keyfile is almost full, %10lu of %10lu used",
  795.   info->s->state.key_file_length,
  796.   info->s->base.max_key_file_length-1);
  797.   size=my_seek(info->dfile,0L,MY_SEEK_END,MYF(0));
  798.   skr=(my_off_t) info->s->state.data_file_length;
  799.   if (info->s->base.options & HA_OPTION_COMPRESS_RECORD)
  800.     skr+= MEMMAP_EXTRA_MARGIN;
  801. #ifdef USE_RELOC
  802.   if (info->data_file_type == STATIC_RECORD &&
  803.       skr < (my_off_t) info->s->base.reloc*info->s->base.min_pack_length)
  804.     skr=(my_off_t) info->s->base.reloc*info->s->base.min_pack_length;
  805. #endif
  806.   if (skr != size)
  807.   {
  808.     info->s->state.data_file_length=(ulong) size; /* Skip other errors */
  809.     if (skr > size && skr != size + MEMMAP_EXTRA_MARGIN)
  810.     {
  811.       error=1;
  812.       print_error("Size of datafile is: %-8lu         Should be: %lu",
  813.   (ulong) size,(ulong) skr);
  814.     }
  815.     else
  816.     {
  817.       print_warning("Size of datafile is: %-8lu       Should be: %lu",
  818.     (ulong) size,(ulong) skr);
  819.     }
  820.   }
  821.   if (!(testflag & T_VERY_SILENT) &&
  822.       !(info->s->base.options & HA_OPTION_COMPRESS_RECORD) &&
  823.       info->s->state.data_file_length >
  824.       (ulong) (ulong_to_double(info->s->base.max_data_file_length)*0.9))
  825.     print_warning("Datafile is almost full, %10lu of %10lu used",
  826.   info->s->state.data_file_length,
  827.   info->s->base.max_data_file_length-1);
  828.   DBUG_RETURN(error);
  829. } /* chk_size */
  830. /* Kontrollerar nycklarna */
  831. static int chk_key( register N_INFO *info)
  832. {
  833.   uint key;
  834.   ulong keys,all_keydata,all_totaldata,key_totlength,length,
  835. init_checksum,old_record_checksum;
  836.   ISAM_SHARE *share=info->s;
  837.   N_KEYDEF *keyinfo;
  838.   DBUG_ENTER("chk_key");
  839.   if (!(testflag & T_SILENT)) puts("- check index reference");
  840.   all_keydata=all_totaldata=key_totlength=old_record_checksum=0;
  841.   init_checksum=record_checksum;
  842.   if (!(share->base.options &
  843. (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
  844.     old_record_checksum=calc_checksum(share->state.records+share->state.del-1)*
  845.       share->base.reclength;
  846.   for (key= 0,keyinfo= &share->keyinfo[0]; key < share->state.keys ;
  847.        key++,keyinfo++)
  848.   {
  849.     record_checksum=init_checksum;
  850.     unique_count=0L;
  851.     if ((!(testflag & T_SILENT)) && share->state.keys >1)
  852.       printf ("- check data record references index: %dn",key+1);
  853.     if (share->state.key_root[key] == NI_POS_ERROR &&
  854. share->state.records == 0)
  855.       continue;
  856.     if (!_nisam_fetch_keypage(info,keyinfo,share->state.key_root[key],info->buff,
  857.    0))
  858.     {
  859.       print_error("Can't read indexpage from filepos: %lu",
  860.   (ulong) share->state.key_root[key]);
  861.       DBUG_RETURN(-1);
  862.     }
  863.     key_file_blocks+=keyinfo->base.block_length;
  864.     keys=keydata=totaldata=key_blocks=0; max_level=0;
  865.     if (chk_index(info,keyinfo,share->state.key_root[key],info->buff,&keys,1))
  866.       DBUG_RETURN(-1);
  867.     if (keys != share->state.records)
  868.     {
  869.       print_error("Found %lu keys of %lu",(ulong) keys,
  870.   (ulong) share->state.records);
  871.       DBUG_RETURN(-1);
  872.     }
  873.     if (!key && (share->base.options &
  874.  (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
  875.       old_record_checksum=record_checksum;
  876.     else if (old_record_checksum != record_checksum)
  877.     {
  878.       if (key)
  879. print_error("Key %u doesn't point at same records that key 1",
  880.     key+1);
  881.       else
  882. print_error("Key 1 doesn't point at all records");
  883.       DBUG_RETURN(-1);
  884.     }
  885.     length=(ulong) isam_key_length(info,keyinfo)*keys + key_blocks*2;
  886.     if (testflag & T_INFO && totaldata != 0L && keys != 0L)
  887.       printf("Key: %2d:  Keyblocks used: %3d%%  Packed: %4d%%  Max levels: %2dn",
  888.      key+1,
  889.      (int) (keydata*100.0/totaldata),
  890.      (int) ((long) (length-keydata)*100.0/(double) length),
  891.      max_level);
  892.     all_keydata+=keydata; all_totaldata+=totaldata; key_totlength+=length;
  893.     share->base.rec_per_key[key]=
  894.       unique_count ? ((share->state.records+unique_count/2)/
  895.       unique_count) : 1L;
  896.   }
  897.   if (testflag & T_INFO)
  898.   {
  899.     if (all_totaldata != 0L && share->state.keys != 1)
  900.       printf("Total:    Keyblocks used: %3d%%  Packed: %4d%%nn",
  901.      (int) (all_keydata*100.0/all_totaldata),
  902.      (int) ((long) (key_totlength-all_keydata)*100.0/
  903.     (double) key_totlength));
  904.     else if (all_totaldata != 0L && share->state.keys)
  905.       puts("");
  906.   }
  907.   if (key_file_blocks != share->state.key_file_length)
  908.     print_warning("Some data are unreferenced in keyfile");
  909.   record_checksum-=init_checksum; /* Remove delete links */
  910.   if (testflag & T_STATISTICS)
  911.     DBUG_RETURN(update_state_info(info,UPDATE_STAT));
  912.   DBUG_RETURN(0);
  913. } /* chk_key */
  914. /* Check if index is ok */
  915. static int chk_index(N_INFO *info, N_KEYDEF *keyinfo, ulong page, uchar *buff,
  916.      ulong *keys,uint level)
  917. {
  918.   int flag;
  919.   uint used_length,comp_flag,nod_flag;
  920.   uchar key[N_MAX_POSSIBLE_KEY_BUFF],*temp_buff,*keypos,*endpos;
  921.   ulong next_page,record;
  922.   DBUG_ENTER("chk_index");
  923.   DBUG_DUMP("buff",(byte*) buff,getint(buff));
  924.   if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->base.block_length)))
  925.   {
  926.     print_error("Not Enough memory");
  927.     DBUG_RETURN(-1);
  928.   }
  929.   if (keyinfo->base.flag & HA_NOSAME)
  930.     comp_flag=SEARCH_FIND; /* Not dupplicates */
  931.   else
  932.     comp_flag=SEARCH_SAME; /* Keys in positionorder */
  933.   nod_flag=test_if_nod(buff);
  934.   used_length=getint(buff);
  935.   keypos=buff+2+nod_flag;
  936.   endpos=buff+used_length;
  937.   keydata+=used_length; totaldata+=keyinfo->base.block_length; /* INFO */
  938.   key_blocks++;
  939.   if (level > max_level)
  940.     max_level=level;
  941.   if (used_length > keyinfo->base.block_length)
  942.   {
  943.     print_error("Wrong pageinfo at page: %lu",(ulong) page);
  944.     goto err;
  945.   }
  946.   for ( ;; )
  947.   {
  948.     if (nod_flag)
  949.     {
  950.       next_page=_nisam_kpos(nod_flag,keypos);
  951.       if (next_page > info->s->state.key_file_length ||
  952.   (nod_flag && (next_page & (info->s->blocksize -1))))
  953.       {
  954. my_off_t max_length=my_seek(info->s->kfile,0L,MY_SEEK_END,MYF(0));
  955. print_error("Wrong pagepointer: %lu at page: %lu",
  956.     (ulong) next_page,(ulong) page);
  957. if (next_page+info->s->blocksize > max_length)
  958.   goto err;
  959. info->s->state.key_file_length=(ulong) (max_length &
  960. ~ (my_off_t)
  961. (info->s->blocksize-1));
  962.       }
  963.       if (!_nisam_fetch_keypage(info,keyinfo,next_page,temp_buff,0))
  964.       {
  965. print_error("Can't read key from filepos: %lu",(ulong) next_page);
  966. goto err;
  967.       }
  968.       key_file_blocks+=keyinfo->base.block_length;
  969.       if (chk_index(info,keyinfo,next_page,temp_buff,keys,level+1))
  970. goto err;
  971.     }
  972.     if (keypos >= endpos ||
  973. (*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key) == 0)
  974.       break;
  975.     if ((*keys)++ &&
  976. (flag=_nisam_key_cmp(keyinfo->seg,info->lastkey,key,0,comp_flag)) >=0)
  977.     {
  978.       DBUG_DUMP("old",(byte*) info->lastkey,
  979. _nisam_keylength(keyinfo,info->lastkey));
  980.       DBUG_DUMP("new",(byte*) key,_nisam_keylength(keyinfo,key));
  981.       if (comp_flag == SEARCH_FIND && flag == 0)
  982. print_error("Found dupplicated key at page %lu",(ulong) page);
  983.       else
  984. print_error("Key in wrong position at page %lu",(ulong) page);
  985.       goto err;
  986.     }
  987.     if (testflag & T_STATISTICS)
  988.     {
  989.       if (*keys == 1L ||
  990.   _nisam_key_cmp(keyinfo->seg,info->lastkey,key,0,SEARCH_FIND))
  991. unique_count++;
  992.     }
  993.     VOID(_nisam_move_key(keyinfo,(uchar*) info->lastkey,key));
  994.     record= _nisam_dpos(info,nod_flag,keypos);
  995.     if (record >= info->s->state.data_file_length)
  996.     {
  997.       print_error("Found key at page %lu that points to record outside datafile",page);
  998.       DBUG_PRINT("test",("page: %lu  record: %lu  filelength: %lu",
  999.  (ulong) page,(ulong) record,
  1000.  (ulong) info->s->state.data_file_length));
  1001.       DBUG_DUMP("key",(byte*) info->lastkey,info->s->base.max_key_length);
  1002.       goto err;
  1003.     }
  1004.     record_checksum+=record;
  1005.   }
  1006.   if (keypos != endpos)
  1007.   {
  1008.     print_error("Keyblock size at page %lu is not correct.  Block length: %d  key length: %d",(ulong) page, used_length, (keypos - buff));
  1009.     goto err;
  1010.   }
  1011.   my_afree((byte*) temp_buff);
  1012.   DBUG_RETURN(0);
  1013.  err:
  1014.   my_afree((byte*) temp_buff);
  1015.   DBUG_RETURN(1);
  1016. } /* chk_index */
  1017. /* Calculate a checksum of 1+2+3+4...N = N*(N+1)/2 without overflow */
  1018. static ulong calc_checksum(count)
  1019. ulong count;
  1020. {
  1021.   ulong sum,a,b;
  1022.   DBUG_ENTER("calc_checksum");
  1023.   sum=0;
  1024.   a=count; b=count+1;
  1025.   if (a & 1)
  1026.     b>>=1;
  1027.   else
  1028.     a>>=1;
  1029.   while (b)
  1030.   {
  1031.     if (b & 1)
  1032.       sum+=a;
  1033.     a<<=1; b>>=1;
  1034.   }
  1035.   DBUG_PRINT("exit",("sum: %lx",sum));
  1036.   DBUG_RETURN(sum);
  1037. } /* calc_checksum */
  1038. /* Calc length of key in normal isam */
  1039. static uint isam_key_length( N_INFO *info, reg1 N_KEYDEF *keyinfo)
  1040. {
  1041.   uint length;
  1042.   N_KEYSEG *keyseg;
  1043.   DBUG_ENTER("isam_key_length");
  1044.   length= info->s->rec_reflength;
  1045.   for (keyseg=keyinfo->seg ; keyseg->base.type ; keyseg++)
  1046.     length+= keyseg->base.length;
  1047.   DBUG_PRINT("exit",("length: %d",length));
  1048.   DBUG_RETURN(length);
  1049. } /* key_length */
  1050. /* Check that record-link is ok */
  1051. static int chk_data_link(info,extend)
  1052. reg1 N_INFO *info;
  1053. int extend;
  1054. {
  1055.   int error,got_error,flag;
  1056.   uint key,left_length,b_type;
  1057.   ulong records,del_blocks,used,empty,pos,splitts,start_recpos,
  1058. del_length,link_used,intern_record_checksum,start_block;
  1059.   byte *record,*to;
  1060.   N_KEYDEF *keyinfo;
  1061.   BLOCK_INFO block_info;
  1062.   DBUG_ENTER("chk_data_link");
  1063.   if (! (info->s->base.options & (HA_OPTION_PACK_RECORD |
  1064.        HA_OPTION_COMPRESS_RECORD)) &&
  1065.       ! extend)
  1066.     DBUG_RETURN(0);
  1067.   if (!(testflag & T_SILENT))
  1068.   {
  1069.     if (extend)
  1070.       puts("- check records and index references");
  1071.     else
  1072.       puts("- check record links");
  1073.   }
  1074.   if (!(record= (byte*) my_alloca(info->s->base.reclength)))
  1075.   {
  1076.     print_error("Not Enough memory");
  1077.     DBUG_RETURN(-1);
  1078.   }
  1079.   records=used=link_used=splitts=del_blocks=del_length=
  1080.     intern_record_checksum=crc=0L;
  1081.   LINT_INIT(left_length);  LINT_INIT(start_recpos);  LINT_INIT(to);
  1082.   got_error=error=0;
  1083.   empty=pos=info->s->pack.header_length;
  1084.   while (pos < info->s->state.data_file_length)
  1085.   {
  1086.     switch (info->s->data_file_type) {
  1087.     case STATIC_RECORD:
  1088.       if (my_b_read(&read_cache,(byte*) record,info->s->base.reclength))
  1089. goto err;
  1090.       start_recpos=pos;
  1091.       pos+=info->s->base.reclength;
  1092.       splitts++;
  1093.       if (*record == '')
  1094.       {
  1095. del_blocks++;
  1096. del_length+=info->s->base.reclength;
  1097. continue; /* Record removed */
  1098.       }
  1099.       used+=info->s->base.reclength;
  1100.       break;
  1101.     case DYNAMIC_RECORD:
  1102.       flag=block_info.second_read=0;
  1103.       block_info.next_filepos=pos;
  1104.       do
  1105.       {
  1106. if (_nisam_read_cache(&read_cache,(byte*) block_info.header,
  1107.   (start_block=block_info.next_filepos),
  1108.   sizeof(block_info.header),test(! flag) | 2))
  1109.   goto err;
  1110. b_type=_nisam_get_block_info(&block_info,-1,start_block);
  1111. if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
  1112.       BLOCK_FATAL_ERROR))
  1113. {
  1114.   if (b_type & BLOCK_SYNC_ERROR)
  1115.   {
  1116.     if (flag)
  1117.     {
  1118.       print_error("Unexpected byte: %d at link: %lu",
  1119.   (int) block_info.header[0],(ulong) start_block);
  1120.       goto err2;
  1121.     }
  1122.     pos=block_info.filepos+block_info.block_len;
  1123.     goto next;
  1124.   }
  1125.   if (b_type & BLOCK_DELETED)
  1126.   {
  1127.     if (block_info.block_len < info->s->base.min_block_length ||
  1128. block_info.block_len-4 > (uint) info->s->base.max_pack_length)
  1129.     {
  1130.       print_error("Deleted block with impossible length %u at %lu",
  1131.  block_info.block_len,(ulong) pos);
  1132.       goto err2;
  1133.     }
  1134.     del_blocks++;
  1135.     del_length+=block_info.block_len;
  1136.     pos=block_info.filepos+block_info.block_len;
  1137.     splitts++;
  1138.     goto next;
  1139.   }
  1140.   print_error("Wrong bytesec: %d-%d-%d at linkstart: %lu",
  1141.       block_info.header[0],block_info.header[1],
  1142.       block_info.header[2],(ulong) start_block);
  1143.   goto err2;
  1144. }
  1145. if (info->s->state.data_file_length < block_info.filepos+
  1146.     block_info.block_len)
  1147. {
  1148.   print_error("Recordlink that points outside datafile at %lu",
  1149.       (ulong) pos);
  1150.   got_error=1;
  1151.   break;
  1152. }
  1153. splitts++;
  1154. if (!flag++) /* First block */
  1155. {
  1156.   start_recpos=pos;
  1157.   pos=block_info.filepos+block_info.block_len;
  1158.   if (block_info.rec_len > (uint) info->s->base.max_pack_length)
  1159.   {
  1160.     print_error("Found too long record at %lu",(ulong) start_recpos);
  1161.     got_error=1;
  1162.     break;
  1163.   }
  1164.   if (info->s->base.blobs)
  1165.   {
  1166.     if (!(to=fix_rec_buff_for_blob(info,block_info.rec_len)))
  1167.     {
  1168.       print_error("Not enough memory for blob at %lu",
  1169.   (ulong) start_recpos);
  1170.       got_error=1;
  1171.       break;
  1172.     }
  1173.   }
  1174.   else
  1175.     to= info->rec_buff;
  1176.   left_length=block_info.rec_len;
  1177. }
  1178. if (left_length < block_info.data_len)
  1179. {
  1180.   print_error("Found too long record at %lu",(ulong) start_recpos);
  1181.   got_error=1; break;
  1182. }
  1183. if (_nisam_read_cache(&read_cache,(byte*) to,block_info.filepos,
  1184.   (uint) block_info.data_len, test(flag == 1)))
  1185.   goto err;
  1186. to+=block_info.data_len;
  1187. link_used+= block_info.filepos-start_block;
  1188. used+= block_info.filepos - start_block + block_info.data_len;
  1189. empty+=block_info.block_len-block_info.data_len;
  1190. left_length-=block_info.data_len;
  1191. if (left_length)
  1192. {
  1193.   if (b_type & BLOCK_LAST)
  1194.   {
  1195.     print_error("Record link to short for record at %lu",
  1196. (ulong) start_recpos);
  1197.     got_error=1;
  1198.     break;
  1199.   }
  1200.   if (info->s->state.data_file_length < block_info.next_filepos)
  1201.   {
  1202.     print_error("Found next-recordlink that points outside datafile at %lu",
  1203. (ulong) block_info.filepos);
  1204.     got_error=1;
  1205.     break;
  1206.   }
  1207. }
  1208.       } while (left_length);
  1209.       if (! got_error)
  1210.       {
  1211. if (_nisam_rec_unpack(info,record,info->rec_buff,block_info.rec_len) ==
  1212.     MY_FILE_ERROR)
  1213. {
  1214.   print_error("Found wrong record at %lu",(ulong) start_recpos);
  1215.   got_error=1;
  1216. }
  1217. if (testflag & (T_EXTEND | T_VERBOSE))
  1218. {
  1219.   if (_nisam_rec_check(info,record))
  1220.   {
  1221.     print_error("Found wrong packed record at %lu",
  1222. (ulong) start_recpos);
  1223.     got_error=1;
  1224.   }
  1225. }
  1226.       }
  1227.       else if (!flag)
  1228. pos=block_info.filepos+block_info.block_len;
  1229.       break;
  1230.     case COMPRESSED_RECORD:
  1231.       if (_nisam_read_cache(&read_cache,(byte*) block_info.header,pos, 3,1))
  1232. goto err;
  1233.       start_recpos=pos;
  1234.       splitts++;
  1235.       VOID(_nisam_pack_get_block_info(&block_info,info->s->pack.ref_length,-1,
  1236.    start_recpos));
  1237.       pos=start_recpos+info->s->pack.ref_length+block_info.rec_len;
  1238.       if (block_info.rec_len < (uint) info->s->min_pack_length ||
  1239.   block_info.rec_len > (uint) info->s->max_pack_length)
  1240.       {
  1241. print_error("Found block with wrong recordlength: %d at %lu",
  1242.     block_info.rec_len,(ulong) start_recpos);
  1243. got_error=1;
  1244. break;
  1245.       }
  1246.       if (_nisam_read_cache(&read_cache,(byte*) info->rec_buff,
  1247. block_info.filepos, block_info.rec_len,1))
  1248. goto err;
  1249.       if (_nisam_pack_rec_unpack(info,record,info->rec_buff,block_info.rec_len))
  1250.       {
  1251. print_error("Found wrong record at %lu",(ulong) start_recpos);
  1252. got_error=1;
  1253.       }
  1254.       crc^=_nisam_checksum(record,info->s->base.reclength);
  1255.       link_used+=info->s->pack.ref_length;
  1256.       used+=block_info.rec_len+info->s->pack.ref_length;
  1257.     }
  1258.     if (! got_error)
  1259.     {
  1260.       intern_record_checksum+=start_recpos;
  1261.       records++;
  1262.       if (testflag & T_WRITE_LOOP && records % WRITE_COUNT == 0)
  1263.       {
  1264. printf("%lur",(ulong) records); VOID(fflush(stdout));
  1265.       }
  1266.       if (extend)
  1267.       {
  1268. for (key=0,keyinfo= info->s->keyinfo; key<info->s->state.keys;
  1269.      key++,keyinfo++)
  1270. {
  1271.   VOID(_nisam_make_key(info,key,info->lastkey,record,start_recpos));
  1272.   if (_nisam_search(info,keyinfo,info->lastkey,0,SEARCH_SAME,
  1273.  info->s->state.key_root[key]))
  1274.   {
  1275.     print_error("Record at: %10lu  Can't find key for index: %2d",
  1276. start_recpos,key+1);
  1277.     if (error++ > MAXERR || !(testflag & T_VERBOSE))
  1278.       goto err2;
  1279.   }
  1280. }
  1281.       }
  1282.     }
  1283.     else
  1284.     {
  1285.       got_error=0;
  1286.       if (error++ > MAXERR || !(testflag & T_VERBOSE))
  1287. goto err2;
  1288.     }
  1289.   next:; /* Next record */
  1290.   }
  1291.   if (testflag & T_WRITE_LOOP)
  1292.   {
  1293.     VOID(fputs("          r",stdout)); VOID(fflush(stdout));
  1294.   }
  1295.   if (records != info->s->state.records)
  1296.   {
  1297.     print_error("Record-count is not ok; is %-10lu   Should be: %lu",
  1298. (ulong) records,(ulong) info->s->state.records);
  1299.     error=1;
  1300.   }
  1301.   else if (record_checksum != intern_record_checksum && info->s->state.keys)
  1302.   {
  1303.     print_error("Keypointers and records don't match");
  1304.     error=1;
  1305.   }
  1306.   if (used+empty+del_length != info->s->state.data_file_length)
  1307.   {
  1308.     print_warning("Found %lu record-data and %lu unused data and %lu deleted-datanTotal %lu, Should be: %lu",
  1309.   (ulong) used,(ulong) empty,(ulong) del_length,
  1310.   (ulong) (used+empty+del_length),
  1311.   (ulong) info->s->state.data_file_length);
  1312.   }
  1313.   if (del_blocks != info->s->state.del)
  1314.   {
  1315.     print_warning("Found %10lu deleted blocks       Should be: %lu",
  1316.   (ulong) del_blocks,(ulong) info->s->state.del);
  1317.   }
  1318.   if (splitts != info->s->state.splitt)
  1319.   {
  1320.     print_warning("Found %10lu parts                Should be: %lu parts",
  1321.   (ulong) splitts,(ulong) info->s->state.splitt);
  1322.   }
  1323.   if ((info->s->base.options & HA_OPTION_COMPRESS_RECORD) &&
  1324.       crc != info->s->state.uniq)
  1325.     print_warning("Wrong checksum for records; Restore uncompressed table");
  1326.   if (testflag & T_INFO)
  1327.   {
  1328.     if (warning_printed || error_printed)
  1329.       puts("");
  1330.     if (used != 0 && ! error_printed)
  1331.     {
  1332.       printf("Records:%17lu    M.recordlength:%8lu   Packed:%14.0f%%n",
  1333.      records, (used-link_used)/records,
  1334.      (info->s->base.blobs ? 0 :
  1335.       (ulong_to_double(info->s->base.reclength*records)-used)/
  1336.       ulong_to_double(info->s->base.reclength*records)*100.0));
  1337.       printf("Recordspace used:%8.0f%%   Empty space:%11d%%  Blocks/Record: %6.2fn",
  1338.      (ulong_to_double(used-link_used)/ulong_to_double(used-link_used+empty)*100.0),
  1339.      (!records ? 100 : (int) (ulong_to_double(del_length+empty)/used*100.0)),
  1340.      ulong_to_double(splitts - del_blocks) / records);
  1341.     }
  1342.     printf("Record blocks:%12lu    Delete blocks:%10lun",
  1343.    splitts-del_blocks,del_blocks);
  1344.     printf("Record data:  %12lu    Deleted data :%10lun",
  1345.    used-link_used,del_length);
  1346.     printf("Lost space:   %12lu    Linkdata:    %10lun",
  1347.    empty,link_used);
  1348.   }
  1349.   my_afree((gptr) record);
  1350.   DBUG_RETURN (error);
  1351.  err:
  1352.   print_error("got error: %d when reading datafile",my_errno);
  1353.  err2:
  1354.   my_afree((gptr) record);
  1355.   DBUG_RETURN(1);
  1356. } /* chk_data_link */
  1357. /* Recover old table by reading each record and writing all keys */
  1358. /* Save new datafile-name in temp_filename */
  1359. static int rep(info,name)
  1360. reg1 N_INFO *info;
  1361. my_string name;
  1362. {
  1363.   int error,got_error;
  1364.   uint i;
  1365.   ulong start_records,new_header_length,del;
  1366.   File new_file;
  1367.   ISAM_SHARE *share=info->s;
  1368.   DBUG_ENTER("rep");
  1369.   start_records=share->state.records;
  1370.   new_header_length=(testflag & T_UNPACK) ? 0L : share->pack.header_length;
  1371.   got_error=1;
  1372.   new_file= -1;
  1373.   if (!(testflag & T_SILENT))
  1374.   {
  1375.     printf("- recovering ISAM-table '%s'n",name);
  1376.     printf("Data records: %lun",(ulong) share->state.records);
  1377.   }
  1378.   VOID(init_key_cache(dflt_key_cache,KEY_CACHE_BLOCK_SIZE,use_buffers,0,0));
  1379.   if (init_io_cache(&read_cache,info->dfile,(uint) read_buffer_length,
  1380.    READ_CACHE,share->pack.header_length,1,MYF(MY_WME)))
  1381.     goto err;
  1382.   if (!rep_quick)
  1383.     if (init_io_cache(&info->rec_cache,-1,(uint) write_buffer_length,
  1384.       WRITE_CACHE, new_header_length, 1,
  1385.       MYF(MY_WME | MY_WAIT_IF_FULL)))
  1386.       goto err;
  1387.   info->opt_flag|=WRITE_CACHE_USED;
  1388.   sort_info.start_recpos=0;
  1389.   sort_info.buff=0; sort_info.buff_length=0;
  1390.   if (!(sort_info.record=(byte*) my_alloca((uint) share->base.reclength)))
  1391.   {
  1392.     print_error("Not Enough memory");
  1393.     goto err;
  1394.   }
  1395.   if (!rep_quick)
  1396.   {
  1397.     if ((new_file=my_create(fn_format(temp_filename,name,"",DATA_TMP_EXT,
  1398.       2+4),
  1399.     0,tmpfile_createflag,MYF(0))) < 0)
  1400.     {
  1401.       print_error("Can't create new tempfile: '%s'",temp_filename);
  1402.       goto err;
  1403.     }
  1404.     if (filecopy(new_file,info->dfile,0L,new_header_length,"datafile-header"))
  1405.       goto err;
  1406.     share->state.dellink= NI_POS_ERROR;
  1407.     info->rec_cache.file=new_file;
  1408.     if (testflag & T_UNPACK)
  1409.       share->base.options&= ~HA_OPTION_COMPRESS_RECORD;
  1410.   }
  1411.   sort_info.info=info;
  1412.   sort_info.pos=sort_info.max_pos=share->pack.header_length;
  1413.   sort_info.filepos=new_header_length;
  1414.   read_cache.end_of_file=sort_info.filelength=(ulong)
  1415.     my_seek(info->dfile,0L,MY_SEEK_END,MYF(0));
  1416.   sort_info.dupp=0;
  1417.   sort_info.fix_datafile= (my_bool) (! rep_quick);
  1418.   sort_info.max_records=LONG_MAX;
  1419.   if ((sort_info.new_data_file_type=share->data_file_type) ==
  1420.       COMPRESSED_RECORD && testflag & T_UNPACK)
  1421.   {
  1422.     if (share->base.options & HA_OPTION_PACK_RECORD)
  1423.       sort_info.new_data_file_type = DYNAMIC_RECORD;
  1424.     else
  1425.       sort_info.new_data_file_type = STATIC_RECORD;
  1426.   }
  1427.   del=share->state.del;
  1428.   share->state.records=share->state.del=share->state.empty=
  1429.     share->state.splitt=0;
  1430.   info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
  1431.   for (i=0 ; i < N_MAXKEY ; i++)
  1432.     share->state.key_del[i]=share->state.key_root[i]= NI_POS_ERROR;
  1433.   share->state.key_file_length=share->base.keystart;
  1434.   lock_memory(); /* Everything is alloced */
  1435.   while (!(error=sort_get_next_record()))
  1436.   {
  1437.     if (writekeys(info,(byte*) sort_info.record,sort_info.filepos))
  1438.     {
  1439.       if (my_errno != HA_ERR_FOUND_DUPP_KEY) goto err;
  1440.       DBUG_DUMP("record",(byte*) sort_info.record,share->base.pack_reclength);
  1441.       print_info("Dupplicate key %2d for record at %10lu against new record at %10lu",info->errkey+1,sort_info.start_recpos,info->int_pos);
  1442.       if (testflag & T_VERBOSE)
  1443.       {
  1444. VOID(_nisam_make_key(info,(uint) info->errkey,info->lastkey,
  1445.      sort_info.record,0L));
  1446. _nisam_print_key(stdout,share->keyinfo[info->errkey].seg,info->lastkey);
  1447.       }
  1448.       sort_info.dupp++;
  1449.       if (rep_quick == 1)
  1450.       {
  1451. error_printed=1;
  1452. goto err;
  1453.       }
  1454.       continue;
  1455.     }
  1456.     if (sort_write_record())
  1457.       goto err;
  1458.   }
  1459.   if (error > 0 || write_data_suffix(info) ||
  1460.       flush_io_cache(&info->rec_cache) || read_cache.error < 0)
  1461.     goto err;
  1462.   if (testflag & T_WRITE_LOOP)
  1463.   {
  1464.     VOID(fputs("          r",stdout)); VOID(fflush(stdout));
  1465.   }
  1466.   if (my_chsize(share->kfile, share->state.key_file_length, 0, MYF(0)))
  1467.   {
  1468.     print_warning("Can't change size of indexfile, error: %d",my_errno);
  1469.     goto err;
  1470.   }
  1471.   if (rep_quick && del+sort_info.dupp != share->state.del)
  1472.   {
  1473.     print_error("Couldn't fix table with quick recovery: Found wrong number of deleted records");
  1474.     print_error("Run recovery again without -q");
  1475.     got_error=1;
  1476.     goto err;
  1477.   }
  1478.   if (!rep_quick)
  1479.   {
  1480.     info->dfile=new_file;
  1481.     share->state.data_file_length=sort_info.filepos;
  1482.     share->state.splitt=share->state.records; /* Only hole records */
  1483.     out_flag|=O_NEW_DATA; /* Data in new file */
  1484.     share->state.version=(ulong) time((time_t*) 0); /* Force reopen */
  1485.   }
  1486.   else
  1487.     share->state.data_file_length=sort_info.max_pos;
  1488.   if (!(testflag & T_SILENT))
  1489.   {
  1490.     if (start_records != share->state.records)
  1491.       printf("Data records: %lun",(ulong) share->state.records);
  1492.     if (sort_info.dupp)
  1493.       print_warning("%lu records have been removed",(ulong) sort_info.dupp);
  1494.   }
  1495.   got_error=0;
  1496. err:
  1497.   if (got_error)
  1498.   {
  1499.     if (! error_printed)
  1500.       print_error("%d for record at pos %lu",my_errno,
  1501.   (ulong) sort_info.start_recpos);
  1502.     if (new_file >= 0)
  1503.     {
  1504.       VOID(my_close(new_file,MYF(0)));
  1505.       VOID(my_delete(temp_filename,MYF(MY_WME)));
  1506.     }
  1507.   }
  1508.   if (sort_info.record)
  1509.   {
  1510.     my_afree(sort_info.record);
  1511.   }
  1512.   my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
  1513.   VOID(end_io_cache(&read_cache));
  1514.   info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
  1515.   VOID(end_io_cache(&info->rec_cache));
  1516.   got_error|=flush_blocks(share->kfile);
  1517.   if (!got_error && testflag & T_UNPACK)
  1518.   {
  1519.     share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
  1520.     share->pack.header_length=0;
  1521.     share->data_file_type=sort_info.new_data_file_type;
  1522.   }
  1523.   DBUG_RETURN(got_error);
  1524. } /* rep */
  1525. /* Uppdaterar nyckelfilen i samband med reparation */
  1526. static int writekeys(register N_INFO *info,byte *buff,ulong filepos)
  1527. {
  1528.   register uint i;
  1529.   uchar *key;
  1530.   DBUG_ENTER("writekeys");
  1531.   key=info->lastkey+info->s->base.max_key_length;
  1532.   for (i=0 ; i < info->s->state.keys ; i++)
  1533.   {
  1534.     VOID(_nisam_make_key(info,i,key,buff,filepos));
  1535.     if (_nisam_ck_write(info,i,key)) goto err;
  1536.   }
  1537.   DBUG_RETURN(0);
  1538.  err:
  1539.   if (my_errno == HA_ERR_FOUND_DUPP_KEY)
  1540.   {
  1541.     info->errkey=(int) i; /* This key was found */
  1542.     while ( i-- > 0 )
  1543.     {
  1544.       VOID(_nisam_make_key(info,i,key,buff,filepos));
  1545.       if (_nisam_ck_delete(info,i,key)) break;
  1546.     }
  1547.   }
  1548.   DBUG_PRINT("error",("errno: %d",my_errno));
  1549.   DBUG_RETURN(-1);
  1550. } /* writekeys */
  1551.  /* Write info about table */
  1552. static void descript(info,name)
  1553. reg1 N_INFO *info;
  1554. my_string name;
  1555. {
  1556.   uint key,field,start,len;
  1557.   reg3 N_KEYDEF *keyinfo;
  1558.   reg2 N_KEYSEG *keyseg;
  1559.   reg4 const char *text;
  1560.   char buff[40],length[10],*pos,*end;
  1561.   enum en_fieldtype type;
  1562.   ISAM_SHARE *share=info->s;
  1563.   DBUG_ENTER("describe");
  1564.   printf("nISAM file:     %sn",name);
  1565.   if (testflag & T_VERBOSE)
  1566.   {
  1567.     printf("Isam-version:  %dn",(int) share->state.header.file_version[3]);
  1568.     if (share->base.create_time)
  1569.     {
  1570.       get_date(buff,1,share->base.create_time);
  1571.       printf("Creation time: %sn",buff);
  1572.     }
  1573.     if (share->base.isamchk_time)
  1574.     {
  1575.       get_date(buff,1,share->base.isamchk_time);
  1576.       printf("Recover time:  %sn",buff);
  1577.     }
  1578.   }
  1579.   printf("Data records:        %10lu  Deleted blocks:     %10lun",
  1580.  share->state.records,share->state.del);
  1581.   if (testflag & T_SILENT)
  1582.     DBUG_VOID_RETURN; /* This is enough */
  1583.   if (testflag & T_VERBOSE)
  1584.   {
  1585. #ifdef USE_RELOC
  1586.     printf("Init-relocation:     %10lun",share->base.reloc);
  1587. #endif
  1588.     printf("Datafile  Parts:     %10lu  Deleted data:       %10lun",
  1589.    share->state.splitt,share->state.empty);
  1590.     printf("Datafile pointer (bytes):%6d  Keyfile pointer (bytes):%6dn",
  1591.    share->rec_reflength,share->base.key_reflength);
  1592.     if (info->s->base.reloc == 1L && info->s->base.records == 1L)
  1593.       puts("This is a one-record table");
  1594.     else
  1595.     {
  1596.       if (share->base.max_data_file_length != NI_POS_ERROR ||
  1597.   share->base.max_key_file_length != NI_POS_ERROR)
  1598. printf("Max datafile length: %10lu  Max keyfile length: %10lun",
  1599.        share->base.max_data_file_length-1,
  1600.        share->base.max_key_file_length-1);
  1601.     }
  1602.   }
  1603.   printf("Recordlength:        %10dn",(int) share->base.reclength);
  1604.   VOID(fputs("Record format: ",stdout));
  1605.   if (share->base.options & HA_OPTION_COMPRESS_RECORD)
  1606.     puts("Compressed");
  1607.   else if (share->base.options & HA_OPTION_PACK_RECORD)
  1608.     puts("Packed");
  1609.   else
  1610.     puts("Fixed length");
  1611.   if (share->state.keys != share->base.keys)
  1612.     printf("Using only %d keys of %d possibly keysn",share->state.keys,
  1613.    share->base.keys);
  1614.   puts("ntable description:");
  1615.   printf("Key Start Len Index   Type");
  1616.   if (testflag & T_VERBOSE)
  1617.     printf("                       Root  Blocksize    Rec/key");
  1618.   VOID(putchar('n'));
  1619.   for (key=0, keyinfo= &share->keyinfo[0] ; key < share->base.keys;
  1620.        key++,keyinfo++)
  1621.   {
  1622.     keyseg=keyinfo->seg;
  1623.     if (keyinfo->base.flag & HA_NOSAME) text="unique ";
  1624.     else text="multip.";
  1625.     pos=buff;
  1626.     if (keyseg->base.flag & HA_REVERSE_SORT)
  1627.       *pos++ = '-';
  1628.     pos=strmov(pos,type_names[keyseg->base.type]);
  1629.     *pos++ = ' ';
  1630.     *pos=0;
  1631.     if (keyinfo->base.flag & HA_PACK_KEY)
  1632.       pos=strmov(pos,packed_txt);
  1633.     if (keyseg->base.flag & HA_SPACE_PACK)
  1634.       pos=strmov(pos,diff_txt);
  1635.     printf("%-4d%-6d%-3d %-8s%-21s",
  1636.    key+1,keyseg->base.start+1,keyseg->base.length,text,buff);
  1637.     if (testflag & T_VERBOSE)
  1638.       printf(" %9ld  %9d  %9ld",
  1639.      share->state.key_root[key],keyinfo->base.block_length,
  1640.      share->base.rec_per_key[key]);
  1641.     VOID(putchar('n'));
  1642.     while ((++keyseg)->base.type)
  1643.     {
  1644.       pos=buff;
  1645.       if (keyseg->base.flag & HA_REVERSE_SORT)
  1646. *pos++ = '-';
  1647.       pos=strmov(pos,type_names[keyseg->base.type]);
  1648.       *pos++= ' ';
  1649.       if (keyseg->base.flag & HA_SPACE_PACK)
  1650. pos=strmov(pos,diff_txt);
  1651.       *pos=0;
  1652.       printf("    %-6d%-3d         %-24sn",
  1653.      keyseg->base.start+1,keyseg->base.length,buff);
  1654.     }
  1655.   }
  1656.   if (verbose > 1)
  1657.   {
  1658.     printf("nField Start Length Type");
  1659.     if (share->base.options & HA_OPTION_COMPRESS_RECORD)
  1660.       printf("                         Huff tree  Bits");
  1661.     VOID(putchar('n'));
  1662.     if (verbose > 2 && share->base.pack_bits)
  1663.       printf("-           %-7d%-35sn",share->base.pack_bits,"bit field");
  1664.     start=1;
  1665.     for (field=0 ; field < share->base.fields ; field++)
  1666.     {
  1667.       if (share->base.options & HA_OPTION_COMPRESS_RECORD)
  1668. type=share->rec[field].base_type;
  1669.       else
  1670. type=(enum en_fieldtype) share->rec[field].base.type;
  1671.       end=strmov(buff,field_pack[type]);
  1672. #ifndef NOT_PACKED_DATABASES
  1673.       if (share->base.options & HA_OPTION_COMPRESS_RECORD)
  1674.       {
  1675. if (share->rec[field].pack_type & PACK_TYPE_SELECTED)
  1676.   end=strmov(end,", not_always");
  1677. if (share->rec[field].pack_type & PACK_TYPE_SPACE_FIELDS)
  1678.   end=strmov(end,", no empty");
  1679. if (share->rec[field].pack_type & PACK_TYPE_ZERO_FILL)
  1680. {
  1681.   sprintf(end,", zerofill(%d)",share->rec[field].space_length_bits);
  1682.   end=strend(end);
  1683. }
  1684.       }
  1685.       if (buff[0] == ',')
  1686. strmov(buff,buff+2);
  1687. #endif
  1688.       len=(uint) (int10_to_str((long) share->rec[field].base.length,length,10) -
  1689.   length);
  1690.       if (type == FIELD_BLOB)
  1691.       {
  1692. length[len]='+';
  1693. VOID(int10_to_str((long) sizeof(char*),length+len+1,10));
  1694.       }
  1695.       printf("%-6d%-6d%-7s%-35s",field+1,start,length,buff);
  1696. #ifndef NOT_PACKED_DATABASES
  1697.       if (share->base.options & HA_OPTION_COMPRESS_RECORD)
  1698.       {
  1699. if (share->rec[field].huff_tree)
  1700.   printf("%3d    %2d",
  1701.  (uint) (share->rec[field].huff_tree-share->decode_trees)+1,
  1702.  share->rec[field].huff_tree->quick_table_bits);
  1703.       }
  1704. #endif
  1705.       VOID(putchar('n'));
  1706.       start+=share->rec[field].base.length;
  1707.       if (type == FIELD_BLOB)
  1708. start+=sizeof(char*);
  1709.     }
  1710.   }
  1711.   DBUG_VOID_RETURN;
  1712. } /* describe */
  1713. /* Change all key-pointers that points to a records */
  1714. static int movepoint(info,record,oldpos,newpos,prot_key)
  1715. register N_INFO *info;
  1716. byte *record;
  1717. ulong oldpos,newpos;
  1718. uint prot_key;
  1719. {
  1720.   register uint i;
  1721.   uchar *key;
  1722.   DBUG_ENTER("movepoint");
  1723.   key=info->lastkey+info->s->base.max_key_length;
  1724.   for (i=0 ; i < info->s->state.keys; i++)
  1725.   {
  1726.     if (i != prot_key)
  1727.     {
  1728.       VOID(_nisam_make_key(info,i,key,record,oldpos));
  1729.       if (info->s->keyinfo[i].base.flag & HA_NOSAME)
  1730.       { /* Change pointer direct */
  1731. uint nod_flag;
  1732. N_KEYDEF *keyinfo;
  1733. keyinfo=info->s->keyinfo+i;
  1734. if (_nisam_search(info,keyinfo,key,USE_HOLE_KEY,
  1735.        (uint) (SEARCH_SAME | SEARCH_SAVE_BUFF),
  1736.        info->s->state.key_root[i]))
  1737.   DBUG_RETURN(-1);
  1738. nod_flag=test_if_nod(info->buff);
  1739. _nisam_dpointer(info,info->int_keypos-nod_flag-
  1740.      info->s->rec_reflength,newpos);
  1741. if (_nisam_write_keypage(info,keyinfo,info->int_pos,info->buff))
  1742.   DBUG_RETURN(-1);
  1743.       }
  1744.       else
  1745.       { /* Change old key to new */
  1746. if (_nisam_ck_delete(info,i,key))
  1747.   DBUG_RETURN(-1);
  1748. VOID(_nisam_make_key(info,i,key,record,newpos));
  1749. if (_nisam_ck_write(info,i,key))
  1750.   DBUG_RETURN(-1);
  1751.       }
  1752.     }
  1753.   }
  1754.   DBUG_RETURN(0);
  1755. } /* movepoint */
  1756. /* Tell system that we want all memory for our cache */
  1757. static void lock_memory(void)
  1758. {
  1759. #ifdef SUN_OS /* Key-cacheing thrases on sun 4.1 */
  1760.   int success;
  1761.   success = mlockall(MCL_CURRENT); /* or plock(DATLOCK); */
  1762.   if (geteuid() == 0 && success != 0)
  1763.     print_warning("Failed to lock memory. errno %d",my_errno);
  1764. #endif
  1765. } /* lock_memory */
  1766. /* Flush all changed blocks to disk */
  1767. static int flush_blocks(file)
  1768. File file;
  1769. {
  1770.   if (flush_key_blocks(dflt_key_cache,file,FLUSH_RELEASE))
  1771.   {
  1772.     print_error("%d when trying to write bufferts",my_errno);
  1773.     return(1);
  1774.   }
  1775.   end_key_cache(dflt_key_cache,1);
  1776.   return 0;
  1777. } /* flush_blocks */
  1778. /* Sort records according to one key */
  1779. static int sort_records(info,name,sort_key,write_info)
  1780. register N_INFO *info;
  1781. my_string name;
  1782. uint sort_key;
  1783. int write_info;
  1784. {
  1785.   int got_error;
  1786.   uint key;
  1787.   N_KEYDEF *keyinfo;
  1788.   File new_file;
  1789.   uchar *temp_buff;
  1790.   ulong old_record_count;
  1791.   ISAM_SHARE *share=info->s;
  1792.   DBUG_ENTER("sort_records");
  1793.   keyinfo= &share->keyinfo[sort_key];
  1794.   got_error=1;
  1795.   temp_buff=0; record_buff=0;
  1796.   new_file= -1;
  1797.   if (sort_key >= share->state.keys)
  1798.   {
  1799.     print_error("Can't sort table '%s' on key %d. It has only %d keys",
  1800. name,sort_key+1,share->state.keys);
  1801.     error_printed=0;
  1802.     DBUG_RETURN(-1);
  1803.   }
  1804.   if (!(testflag & T_SILENT))
  1805.   {
  1806.     printf("- Sorting records in ISAM-table '%s'n",name);
  1807.     if (write_info)
  1808.       printf("Data records: %7lu   Deleted: %7lun",
  1809.      share->state.records,share->state.del);
  1810.   }
  1811.   if (share->state.key_root[sort_key] == NI_POS_ERROR)
  1812.     DBUG_RETURN(0); /* Nothing to do */
  1813.   init_key_cache(dflt_key_cache,KEY_CACHE_BLOCK_SIZE,use_buffers, 0, 0);
  1814.   if (init_io_cache(&info->rec_cache,-1,(uint) write_buffer_length,
  1815.    WRITE_CACHE,share->pack.header_length,1,
  1816.    MYF(MY_WME | MY_WAIT_IF_FULL)))
  1817.     goto err;
  1818.   info->opt_flag|=WRITE_CACHE_USED;
  1819.   if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->base.block_length)))
  1820.   {
  1821.     print_error("Not Enough memory");
  1822.     goto err;
  1823.   }
  1824.   if (!(record_buff=(byte*) my_alloca((uint) share->base.reclength)))
  1825.   {
  1826.     print_error("Not Enough memory");
  1827.     goto err;
  1828.   }
  1829.   if ((new_file=my_create(fn_format(temp_filename,name,"",DATA_TMP_EXT,2+4),
  1830.   0,tmpfile_createflag,MYF(0))) <= 0)
  1831.   {
  1832.     print_error("Can't create new tempfile: '%s'",temp_filename);
  1833.     goto err;
  1834.   }
  1835.   if (filecopy(new_file,info->dfile,0L,share->pack.header_length,
  1836.        "datafile-header"))
  1837.     goto err;
  1838.   info->rec_cache.file=new_file; /* Use this file for cacheing*/
  1839.   lock_memory();
  1840.   for (key=0 ; key < share->state.keys ; key++)
  1841.     share->keyinfo[key].base.flag|= HA_SORT_ALLOWS_SAME;
  1842.   if (my_pread(share->kfile,(byte*) temp_buff,
  1843.        (uint) keyinfo->base.block_length,
  1844.        share->state.key_root[sort_key],
  1845.        MYF(MY_NABP+MY_WME)))
  1846.   {
  1847.     print_error("Can't read indexpage from filepos: %lu",
  1848. (ulong) share->state.key_root[sort_key]);
  1849.     goto err;
  1850.   }
  1851.   /* Setup param for sort_write_record */
  1852.   bzero((char*) &sort_info,sizeof(sort_info));
  1853.   sort_info.info=info;
  1854.   sort_info.new_data_file_type=share->data_file_type;
  1855.   sort_info.fix_datafile=1;
  1856.   sort_info.filepos=share->pack.header_length;
  1857.   sort_info.record=record_buff;
  1858.   old_record_count=share->state.records;
  1859.   share->state.records=0;
  1860.   if (sort_record_index(info,keyinfo,share->state.key_root[sort_key],temp_buff,
  1861. sort_key,new_file) ||
  1862.       write_data_suffix(info) ||
  1863.       flush_io_cache(&info->rec_cache))
  1864.     goto err;
  1865.   if (share->state.records != old_record_count)
  1866.   {
  1867.     print_error("found %lu of %lu records",
  1868. (ulong) share->state.records,(ulong) old_record_count);
  1869.     goto err;
  1870.   }
  1871. /* Put same locks as old file */
  1872.   if (lock_file(new_file,0L,F_WRLCK,"tempfile",temp_filename))
  1873.     goto err;
  1874.   VOID(lock_file(info->dfile,0L,F_UNLCK,"datafile of",name));
  1875.   VOID(my_close(info->dfile,MYF(MY_WME)));
  1876.   out_flag|=O_NEW_DATA; /* Data in new file */
  1877.   info->dfile=new_file; /* Use new indexfile */
  1878.   share->state.del=share->state.empty=0;
  1879.   share->state.dellink= NI_POS_ERROR;
  1880.   share->state.data_file_length=sort_info.filepos;
  1881.   share->state.splitt=share->state.records; /* Only hole records */
  1882.   share->state.version=(ulong) time((time_t*) 0);
  1883.   info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
  1884.   if (testflag & T_WRITE_LOOP)
  1885.   {
  1886.     VOID(fputs("          r",stdout)); VOID(fflush(stdout));
  1887.   }
  1888.   got_error=0;
  1889. err:
  1890.   if (got_error && new_file >= 0)
  1891.   {
  1892.     VOID(my_close(new_file,MYF(MY_WME)));
  1893.     VOID(my_delete(temp_filename,MYF(MY_WME)));
  1894.   }
  1895.   if (temp_buff)
  1896.   {
  1897.     my_afree((gptr) temp_buff);
  1898.   }
  1899.   if (record_buff)
  1900.   {
  1901.     my_afree(record_buff);
  1902.   }
  1903.   info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
  1904.   VOID(end_io_cache(&info->rec_cache));
  1905.   share->base.sortkey=sort_key;
  1906.   DBUG_RETURN(flush_blocks(share->kfile) | got_error);
  1907. } /* sort_records */
  1908.  /* Sort records recursive using one index */
  1909. static int sort_record_index(info,keyinfo,page,buff,sort_key,new_file)
  1910. N_INFO *info;
  1911. N_KEYDEF *keyinfo;
  1912. ulong page;
  1913. uchar *buff;
  1914. uint sort_key;
  1915. File new_file;
  1916. {
  1917.   uint nod_flag,used_length;
  1918.   uchar *temp_buff,*keypos,*endpos;
  1919.   ulong next_page,rec_pos;
  1920.   uchar lastkey[N_MAX_KEY_BUFF];
  1921.   DBUG_ENTER("sort_record_index");
  1922.   nod_flag=test_if_nod(buff);
  1923.   temp_buff=0;
  1924.   if (nod_flag)
  1925.   {
  1926.     if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->base.block_length)))
  1927.     {
  1928.       print_error("Not Enough memory");
  1929.       DBUG_RETURN(-1);
  1930.     }
  1931.   }
  1932.   used_length=getint(buff);
  1933.   keypos=buff+2+nod_flag;
  1934.   endpos=buff+used_length;
  1935.   for ( ;; )
  1936.   {
  1937.     _sanity(__FILE__,__LINE__);
  1938.     if (nod_flag)
  1939.     {
  1940.       next_page=_nisam_kpos(nod_flag,keypos);
  1941.       if (my_pread(info->s->kfile,(byte*) temp_buff,
  1942.   (uint) keyinfo->base.block_length, next_page,
  1943.    MYF(MY_NABP+MY_WME)))
  1944.       {
  1945. print_error("Can't read keys from filepos: %lu",(ulong) next_page);
  1946. goto err;
  1947.       }
  1948.       if (sort_record_index(info,keyinfo,next_page,temp_buff,sort_key,
  1949.     new_file))
  1950. goto err;
  1951.     }
  1952.     _sanity(__FILE__,__LINE__);
  1953.     if (keypos >= endpos ||
  1954. (*keyinfo->get_key)(keyinfo,nod_flag,&keypos,lastkey) == 0)
  1955.       break;
  1956.     rec_pos= _nisam_dpos(info,nod_flag,keypos);
  1957.     if ((*info->s->read_rnd)(info,record_buff,rec_pos,0))
  1958.     {
  1959.       print_error("%d when reading datafile",my_errno);
  1960.       goto err;
  1961.     }
  1962.     if (rec_pos != sort_info.filepos)
  1963.     {
  1964.       _nisam_dpointer(info,keypos-nod_flag-info->s->rec_reflength,
  1965.    sort_info.filepos);
  1966.       if (movepoint(info,record_buff,rec_pos,sort_info.filepos,sort_key))
  1967.       {
  1968. print_error("%d when updating key-pointers",my_errno);
  1969. goto err;
  1970.       }
  1971.     }
  1972.     if (sort_write_record())
  1973.       goto err;
  1974.   }
  1975.   bzero((byte*) buff+used_length,keyinfo->base.block_length-used_length);
  1976.   if (my_pwrite(info->s->kfile,(byte*) buff,(uint) keyinfo->base.block_length,
  1977. page,MYF_RW))
  1978.   {
  1979.     print_error("%d when updating keyblock",my_errno);
  1980.     goto err;
  1981.   }
  1982.   if (temp_buff)
  1983.     my_afree((gptr) temp_buff);
  1984.   DBUG_RETURN(0);
  1985. err:
  1986.   if (temp_buff)
  1987.     my_afree((gptr) temp_buff);
  1988.   DBUG_RETURN(1);
  1989. } /* sort_record_index */
  1990. /* Sort index for more efficent reads */
  1991. static int sort_index(info,name)
  1992. register N_INFO *info;
  1993. my_string name;
  1994. {
  1995.   reg2 uint key;
  1996.   reg1 N_KEYDEF *keyinfo;
  1997.   File new_file;
  1998.   ulong index_pos[N_MAXKEY];
  1999.   DBUG_ENTER("sort_index");
  2000.   if (!(testflag & T_SILENT))
  2001.     printf("- Sorting index for ISAM-table '%s'n",name);
  2002.   if ((new_file=my_create(fn_format(temp_filename,name,"",INDEX_TMP_EXT,2+4),
  2003.   0,tmpfile_createflag,MYF(0))) <= 0)
  2004.   {
  2005.     print_error("Can't create new tempfile: '%s'",temp_filename);
  2006.     DBUG_RETURN(-1);
  2007.   }
  2008.   if (filecopy(new_file,info->s->kfile,0L,(ulong) info->s->base.keystart,
  2009.        "headerblock"))
  2010.     goto err;
  2011.   new_file_pos=info->s->base.keystart;
  2012.   for (key= 0,keyinfo= &info->s->keyinfo[0]; key < info->s->state.keys ;
  2013.        key++,keyinfo++)
  2014.   {
  2015.     if (info->s->state.key_root[key] != NI_POS_ERROR)
  2016.     {
  2017.       index_pos[key]=new_file_pos; /* Write first block here */
  2018.       if (!_nisam_fetch_keypage(info,keyinfo,info->s->state.key_root[key],
  2019.      info->buff,0))
  2020.       {
  2021. print_error("Can't read indexpage from filepos: %lu",
  2022.     (ulong) info->s->state.key_root[key]);
  2023. goto err;
  2024.       }
  2025.       if (sort_one_index(info,keyinfo,info->buff,new_file))
  2026. goto err;
  2027.     }
  2028.     else
  2029.       index_pos[key]= NI_POS_ERROR; /* No blocks */
  2030.   }
  2031. /* Put same locks as old file */
  2032.   if (lock_file(new_file,0L,F_WRLCK,"tempfile",temp_filename))
  2033.     goto err;
  2034.   info->s->state.version=(ulong) time((time_t*) 0);
  2035.   VOID(_nisam_writeinfo(info,1)); /* This unlocks table */
  2036.   VOID(my_close(info->s->kfile,MYF(MY_WME)));
  2037.   out_flag|=O_NEW_INDEX; /* Data in new file */
  2038.   info->s->kfile=new_file;
  2039.   info->s->state.key_file_length=new_file_pos;
  2040.   info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
  2041.   for (key=0 ; key < info->s->state.keys ; key++)
  2042.   {
  2043.     info->s->state.key_root[key]=index_pos[key];
  2044.     info->s->state.key_del[key]= NI_POS_ERROR;
  2045.   }
  2046.   DBUG_RETURN(0);
  2047. err:
  2048.   VOID(my_close(new_file,MYF(MY_WME)));
  2049.   VOID(my_delete(temp_filename,MYF(MY_WME)));
  2050.   DBUG_RETURN(-1);
  2051. } /* sort_index */
  2052.  /* Sort records recursive using one index */
  2053. static int sort_one_index(info,keyinfo,buff,new_file)
  2054. N_INFO *info;
  2055. N_KEYDEF *keyinfo;
  2056. uchar *buff;
  2057. File new_file;
  2058. {
  2059.   uint length,nod_flag,used_length;
  2060.   uchar *temp_buff,*keypos,*endpos;
  2061.   ulong new_page_pos,next_page;
  2062.   DBUG_ENTER("sort_one_index");
  2063.   temp_buff=0;
  2064.   new_page_pos=new_file_pos;
  2065.   new_file_pos+=keyinfo->base.block_length;
  2066.   if ((nod_flag=test_if_nod(buff)))
  2067.   {
  2068.     if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->base.block_length)))
  2069.     {
  2070.       print_error("Not Enough memory");
  2071.       DBUG_RETURN(-1);
  2072.     }
  2073.     used_length=getint(buff);
  2074.     keypos=buff+2+nod_flag;
  2075.     endpos=buff+used_length;
  2076.     for ( ;; )
  2077.     {
  2078.       if (nod_flag)
  2079.       {
  2080. next_page=_nisam_kpos(nod_flag,keypos);
  2081. _nisam_kpointer(info,keypos-nod_flag,new_file_pos); /* Save new pos */
  2082. if (!_nisam_fetch_keypage(info,keyinfo,next_page,temp_buff,0))
  2083. {
  2084.   print_error("Can't read keys from filepos: %lu",
  2085.       (ulong) next_page);
  2086.   goto err;
  2087. }
  2088. if (sort_one_index(info,keyinfo,temp_buff,new_file))
  2089.   goto err;
  2090.       }
  2091.       if (keypos >= endpos ||
  2092.   ((*keyinfo->get_key)(keyinfo,nod_flag,&keypos,info->lastkey)) == 0)
  2093. break;
  2094.     }
  2095.     my_afree((gptr) temp_buff);
  2096.   }
  2097. /* Fill block with zero and write it to new file */
  2098.   length=getint(buff);
  2099.   bzero((byte*) buff+length,keyinfo->base.block_length-length);
  2100.   if (my_pwrite(new_file,(byte*) buff,(uint) keyinfo->base.block_length,
  2101. new_page_pos,MYF(MY_NABP | MY_WAIT_IF_FULL)))
  2102.   {
  2103.     print_error("Can't write indexblock, error: %d",my_errno);
  2104.     goto err;
  2105.   }
  2106.   DBUG_RETURN(0);
  2107. err:
  2108.   if (temp_buff)
  2109.     my_afree((gptr) temp_buff);
  2110.   DBUG_RETURN(1);
  2111. } /* sort_one_index */
  2112. /* Change to use new file */
  2113. /* Copy stats from old file to new file, deletes orginal and */
  2114. /* changes new file name to old file name */
  2115. static int change_to_newfile(filename,old_ext,new_ext)
  2116. const char *filename,*old_ext,*new_ext;
  2117. {
  2118.   char old_filename[FN_REFLEN],new_filename[FN_REFLEN];
  2119.   return my_redel(fn_format(old_filename,filename,"",old_ext,2+4),
  2120.   fn_format(new_filename,filename,"",new_ext,2+4),
  2121.   MYF(MY_WME+MY_LINK_WARNING));
  2122. } /* change_to_newfile */
  2123. /* Locks a hole file */
  2124. /* Gives error-message if file can't be locked */
  2125. static int lock_file(file,start,lock_type,filetype,filename)
  2126. File file;
  2127. ulong start;
  2128. int lock_type;
  2129. const char *filetype,*filename;
  2130. {
  2131. #ifndef NO_LOCKING
  2132.   if (my_lock(file,lock_type,start,F_TO_EOF,
  2133.       testflag & T_WAIT_FOREVER ? MYF(MY_SEEK_NOT_DONE) :
  2134.       MYF(MY_SEEK_NOT_DONE |  MY_DONT_WAIT)))
  2135.   {
  2136.     print_error(" %d when %s %s '%s'",my_errno,
  2137. lock_type == F_UNLCK ? "unlocking": "locking",
  2138. filetype,filename);
  2139.     error_printed=2; /* Don't give that data is crashed */
  2140.     return 1;
  2141.   }
  2142. #endif
  2143.   return 0;
  2144. } /* lock_file */
  2145. /* Copy a block between two files */
  2146. static int filecopy(File to,File from,ulong start,ulong length,
  2147.     const char *type)
  2148. {
  2149.   char tmp_buff[IO_SIZE],*buff;
  2150.   ulong buff_length;
  2151.   DBUG_ENTER("filecopy");
  2152.   buff_length=min(write_buffer_length,length);
  2153.   if (!(buff=my_malloc(buff_length,MYF(0))))
  2154.   {
  2155.     buff=tmp_buff; buff_length=IO_SIZE;
  2156.   }
  2157.   VOID(my_seek(from,start,MY_SEEK_SET,MYF(0)));
  2158.   while (length > buff_length)
  2159.   {
  2160.     if (my_read(from,(byte*) buff,buff_length,MYF(MY_NABP)) ||
  2161. my_write(to,(byte*) buff,buff_length,MYF(MY_NABP | MY_WAIT_IF_FULL)))
  2162.       goto err;
  2163.     length-= buff_length;
  2164.   }
  2165.   if (my_read(from,(byte*) buff,(uint) length,MYF(MY_NABP)) ||
  2166.       my_write(to,(byte*) buff,(uint) length,MYF(MY_NABP | MY_WAIT_IF_FULL)))
  2167.     goto err;
  2168.   if (buff != tmp_buff)
  2169.     my_free(buff,MYF(0));
  2170.   DBUG_RETURN(0);
  2171. err:
  2172.   if (buff != tmp_buff)
  2173.     my_free(buff,MYF(0));
  2174.   print_error("Can't copy %s to tempfile, error %d",type,my_errno);
  2175.   DBUG_RETURN(1);
  2176. }
  2177. /* Fix table using sorting */
  2178. /* saves new table in temp_filename */
  2179. static int rep_by_sort(info,name)
  2180. reg1 N_INFO *info;
  2181. my_string name;
  2182. {
  2183.   int got_error;
  2184.   uint i,length;
  2185.   ulong start_records,new_header_length,del;
  2186.   File new_file;
  2187.   SORT_PARAM sort_param;
  2188.   ISAM_SHARE *share=info->s;
  2189.   DBUG_ENTER("rep_by_sort");
  2190.   start_records=share->state.records;
  2191.   got_error=1;
  2192.   new_file= -1;
  2193.   new_header_length=(testflag & T_UNPACK) ? 0 : share->pack.header_length;
  2194.   if (!(testflag & T_SILENT))
  2195.   {
  2196.     printf("- recovering ISAM-table '%s'n",name);
  2197.     printf("Data records: %lun",(ulong) start_records);
  2198.   }
  2199.   bzero((char*) &sort_info,sizeof(sort_info));
  2200.   if (!(sort_info.key_block=alloc_key_blocks((uint) sort_key_blocks,
  2201.      share->base.max_block))
  2202.       || init_io_cache(&read_cache,info->dfile,(uint) read_buffer_length,
  2203.       READ_CACHE,share->pack.header_length,1,MYF(MY_WME)) ||
  2204.       (! rep_quick &&
  2205.        init_io_cache(&info->rec_cache,info->dfile,(uint) write_buffer_length,
  2206.     WRITE_CACHE,new_header_length,1,
  2207.     MYF(MY_WME | MY_WAIT_IF_FULL))))
  2208.     goto err;
  2209.   sort_info.key_block_end=sort_info.key_block+sort_key_blocks;
  2210.   info->opt_flag|=WRITE_CACHE_USED;
  2211.   info->rec_cache.file=info->dfile; /* for sort_delete_record */
  2212.   if (!(sort_info.record=(byte*) my_alloca((uint) share->base.reclength)))
  2213.   {
  2214.     print_error("Not enough memory for extra record");
  2215.     goto err;
  2216.   }
  2217.   if (!rep_quick)
  2218.   {
  2219.     if ((new_file=my_create(fn_format(temp_filename,name,"",DATA_TMP_EXT,
  2220.       2+4),
  2221.     0,tmpfile_createflag,MYF(0))) < 0)
  2222.     {
  2223.       print_error("Can't create new tempfile: '%s'",temp_filename);
  2224.       goto err;
  2225.     }
  2226.     if (filecopy(new_file,info->dfile,0L,new_header_length,"datafile-header"))
  2227.       goto err;
  2228.     if (testflag & T_UNPACK)
  2229.       share->base.options&= ~HA_OPTION_COMPRESS_RECORD;
  2230.     share->state.dellink= NI_POS_ERROR;
  2231.     info->rec_cache.file=new_file;
  2232.   }
  2233.   info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
  2234.   for (i=0 ; i < N_MAXKEY ; i++)
  2235.     share->state.key_del[i]=share->state.key_root[i]= NI_POS_ERROR;
  2236.   share->state.key_file_length=share->base.keystart;
  2237.   sort_info.info=info;
  2238.   if ((sort_info.new_data_file_type=share->data_file_type) ==
  2239.       COMPRESSED_RECORD && testflag & T_UNPACK)
  2240.   {
  2241.     if (share->base.options & HA_OPTION_PACK_RECORD)
  2242.       sort_info.new_data_file_type = DYNAMIC_RECORD;
  2243.     else
  2244.       sort_info.new_data_file_type = STATIC_RECORD;
  2245.   }
  2246.   sort_info.filepos=new_header_length;
  2247.   sort_info.dupp=0;
  2248.   read_cache.end_of_file=sort_info.filelength=
  2249.     (ulong) my_seek(read_cache.file,0L,MY_SEEK_END,MYF(0));
  2250.   if (share->data_file_type == DYNAMIC_RECORD)
  2251.     length=max(share->base.min_pack_length+1,share->base.min_block_length);
  2252.   else if (share->data_file_type == COMPRESSED_RECORD)
  2253.     length=share->base.min_block_length;
  2254.   else
  2255.     length=share->base.reclength;
  2256.   sort_param.max_records=sort_info.max_records=sort_info.filelength/length+1;
  2257.   sort_param.key_cmp=sort_key_cmp;
  2258.   sort_param.key_write=sort_key_write;
  2259.   sort_param.key_read=sort_key_read;
  2260.   sort_param.lock_in_memory=lock_memory;
  2261.   del=share->state.del;
  2262.   for (sort_info.key=0 ; sort_info.key < share->state.keys ; sort_info.key++)
  2263.   {
  2264.     if ((!(testflag & T_SILENT)))
  2265.       printf ("- Fixing index %dn",sort_info.key+1);
  2266.     sort_info.max_pos=sort_info.pos=share->pack.header_length;
  2267.     sort_info.keyinfo=share->keyinfo+sort_info.key;
  2268.     sort_info.keyseg=sort_info.keyinfo->seg;
  2269.     sort_info.fix_datafile= (my_bool) (sort_info.key == 0 && ! rep_quick);
  2270.     sort_info.unique=0;
  2271.     sort_param.key_length=share->rec_reflength;
  2272.     for (i=0 ; sort_info.keyseg[i].base.type ; i++)
  2273.       sort_param.key_length+=sort_info.keyseg[i].base.length+
  2274. (sort_info.keyseg[i].base.flag & HA_SPACE_PACK ? 1 : 0);
  2275.     share->state.records=share->state.del=share->state.empty=share->state.splitt=0;
  2276.     if (_create_index_by_sort(&sort_param,
  2277.       (pbool) (!(testflag & T_VERBOSE)),
  2278.       (uint) sort_buffer_length))
  2279.       goto err;
  2280. /* Set for next loop */
  2281.     sort_param.max_records=sort_info.max_records=share->state.records;
  2282.     share->base.rec_per_key[sort_info.key]=
  2283.       sort_info.unique ? ((sort_info.max_records+sort_info.unique/2)/
  2284.    sort_info.unique)
  2285.       : 1L;
  2286.     if (sort_info.fix_datafile)
  2287.     {
  2288.       info->dfile=new_file;
  2289.       share->state.data_file_length=sort_info.filepos;
  2290.       share->state.splitt=share->state.records; /* Only hole records */
  2291.       share->state.version=(ulong) time((time_t*) 0);
  2292.       out_flag|=O_NEW_DATA; /* Data in new file */
  2293.       read_cache.end_of_file=sort_info.filepos;
  2294.       if (write_data_suffix(info) || end_io_cache(&info->rec_cache))
  2295. goto err;
  2296.       share->data_file_type=sort_info.new_data_file_type;
  2297.       share->pack.header_length=new_header_length;
  2298.     }
  2299.     else
  2300.       share->state.data_file_length=sort_info.max_pos;
  2301.     if (flush_pending_blocks())
  2302.       goto err;
  2303.     read_cache.file=info->dfile; /* re-init read cache */
  2304.     reinit_io_cache(&read_cache,READ_CACHE,share->pack.header_length,1,1);
  2305.   }
  2306.   if (testflag & T_WRITE_LOOP)
  2307.   {
  2308.     VOID(fputs("          r",stdout)); VOID(fflush(stdout));
  2309.   }
  2310.   if (rep_quick && del+sort_info.dupp != share->state.del)
  2311.   {
  2312.     print_error("Couldn't fix table with quick recovery: Found wrong number of deleted records");
  2313.     print_error("Run recovery again without -q");
  2314.     got_error=1;
  2315.     goto err;
  2316.   }
  2317.   if (rep_quick != 1)
  2318.   {
  2319.     ulong skr=share->state.data_file_length+
  2320.       (share->base.options & HA_OPTION_COMPRESS_RECORD ?
  2321.        MEMMAP_EXTRA_MARGIN : 0);
  2322. #ifdef USE_RELOC
  2323.     if (share->data_file_type == STATIC_RECORD &&
  2324. skr < share->base.reloc*share->base.min_pack_length)
  2325.       skr=share->base.reloc*share->base.min_pack_length;
  2326. #endif
  2327.     if (skr != sort_info.filelength)
  2328.       if (my_chsize(info->dfile, skr, 0, MYF(0)))
  2329. print_warning("Can't change size of datafile,  error: %d",my_errno);
  2330.   }
  2331.   if (my_chsize(share->kfile, share->state.key_file_length, 0, MYF(0)))
  2332.     print_warning("Can't change size of indexfile, error: %d",my_errno);
  2333.   if (!(testflag & T_SILENT))
  2334.   {
  2335.     if (start_records != share->state.records)
  2336.       printf("Data records: %lun",(ulong) share->state.records);
  2337.     if (sort_info.dupp)
  2338.       print_warning("%lu records have been removed",(ulong) sort_info.dupp);
  2339.   }
  2340.   got_error=0;
  2341. err:
  2342.   if (got_error)
  2343.   {
  2344.     if (! error_printed)
  2345.       print_error("%d when fixing table",my_errno);
  2346.     if (new_file >= 0)
  2347.     {
  2348.       VOID(end_io_cache(&info->rec_cache));
  2349.       VOID(my_close(new_file,MYF(0)));
  2350.       VOID(my_delete(temp_filename,MYF(MY_WME)));
  2351.     }
  2352.   }
  2353.   if (sort_info.key_block)
  2354.     my_free((gptr) sort_info.key_block,MYF(0));
  2355.   if (sort_info.record)
  2356.   {
  2357.     my_afree(sort_info.record);
  2358.   }
  2359.   VOID(end_io_cache(&read_cache));
  2360.   VOID(end_io_cache(&info->rec_cache));
  2361.   info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
  2362.   if (!got_error && testflag & T_UNPACK)
  2363.   {
  2364.     share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
  2365.     share->pack.header_length=0;
  2366.   }
  2367.   DBUG_RETURN(got_error);
  2368. } /* rep_by_sort */
  2369. /* Read next record and return next key */
  2370. static int sort_key_read(key)
  2371. void *key;
  2372. {
  2373.   int error;
  2374.   N_INFO *info;
  2375.   DBUG_ENTER("sort_key_read");
  2376.   info=sort_info.info;
  2377.   if ((error=sort_get_next_record()))
  2378.     DBUG_RETURN(error);
  2379.   if (info->s->state.records == sort_info.max_records)
  2380.   {
  2381.     print_error("Found too many records; Can`t continue");
  2382.     DBUG_RETURN(1);
  2383.   }
  2384.   VOID(_nisam_make_key(info,sort_info.key,key,sort_info.record,
  2385.     sort_info.filepos));
  2386.   DBUG_RETURN(sort_write_record());
  2387. } /* sort_key_read */
  2388. /* Read next record from file using parameters in sort_info */
  2389. /* Return -1 if end of file, 0 if ok and > 0 if error */
  2390. static int sort_get_next_record()
  2391. {
  2392.   int searching;
  2393.   uint found_record,b_type,left_length;
  2394.   ulong pos;
  2395.   byte *to;
  2396.   BLOCK_INFO block_info;
  2397.   N_INFO *info;
  2398.   ISAM_SHARE *share;
  2399.   DBUG_ENTER("sort_get_next_record");
  2400.   info=sort_info.info;
  2401.   share=info->s;
  2402.   switch (share->data_file_type) {
  2403.   case STATIC_RECORD:
  2404.     for (;;)
  2405.     {
  2406.       if (my_b_read(&read_cache,sort_info.record,share->base.reclength))
  2407. DBUG_RETURN(-1);
  2408.       sort_info.start_recpos=sort_info.pos;
  2409.       if (!sort_info.fix_datafile)
  2410. sort_info.filepos=sort_info.pos;
  2411.       sort_info.max_pos=(sort_info.pos+=share->base.reclength);
  2412.       share->state.splitt++;
  2413.       if (*sort_info.record)
  2414. DBUG_RETURN(0);
  2415.       if (!sort_info.fix_datafile)
  2416.       {
  2417. share->state.del++;
  2418. share->state.empty+=share->base.reclength;
  2419.       }
  2420.     }
  2421.   case DYNAMIC_RECORD:
  2422.     LINT_INIT(to);
  2423.     pos=sort_info.pos;
  2424.     searching=(sort_info.fix_datafile && (testflag & T_EXTEND));
  2425.     for (;;)
  2426.     {
  2427.       found_record=block_info.second_read= 0;
  2428.       left_length=1;
  2429.       do
  2430.       {
  2431. if (pos > sort_info.max_pos)
  2432.   sort_info.max_pos=pos;
  2433. if (found_record && pos == search_after_block)
  2434.   print_info("Block: %lu used by record at %lu",
  2435.      search_after_block,
  2436.      sort_info.start_recpos);
  2437. if (_nisam_read_cache(&read_cache,(byte*) block_info.header,pos,
  2438.   BLOCK_INFO_HEADER_LENGTH, test(! found_record) | 2))
  2439. {
  2440.   if (found_record)
  2441.   {
  2442.     print_info("Can't read whole record at %lu (errno: %d)",
  2443.        (ulong) sort_info.start_recpos,errno);
  2444.     goto try_next;
  2445.   }
  2446.   DBUG_RETURN(-1);
  2447. }
  2448. if (searching && ! sort_info.fix_datafile)
  2449. {
  2450.   error_printed=1;
  2451.   DBUG_RETURN(1); /* Something wrong with data */
  2452. }
  2453. if (((b_type=_nisam_get_block_info(&block_info,-1,pos)) &
  2454.      (BLOCK_ERROR | BLOCK_FATAL_ERROR)) ||
  2455.     ((b_type & BLOCK_FIRST) &&
  2456.      (block_info.rec_len < (uint) share->base.min_pack_length ||
  2457.       block_info.rec_len > (uint) share->base.max_pack_length)))
  2458. {
  2459.   uint i;
  2460.   if (testflag & T_VERBOSE || searching == 0)
  2461.     print_info("Wrong bytesec: %3d-%3d-%3d at %10lu; Skipped",
  2462.        block_info.header[0],block_info.header[1],
  2463.        block_info.header[2],pos);
  2464.   if (found_record)
  2465.     goto try_next;
  2466.   block_info.second_read=0;
  2467.   searching=1;
  2468.   for (i=1 ; i < 11 ; i++) /* Skip from read string */
  2469.     if (block_info.header[i] >= 1 && block_info.header[i] <= 16)
  2470.       break;
  2471.   pos+=(ulong) i;
  2472.   continue;
  2473. }
  2474. if (block_info.block_len+ (uint) (block_info.filepos-pos) <
  2475.     share->base.min_block_length ||
  2476.     block_info.block_len-4 > (uint) share->base.max_pack_length)
  2477. {
  2478.   if (!searching)
  2479.     print_info("Found block with impossible length %u at %lu; Skipped",
  2480.        block_info.block_len+ (uint) (block_info.filepos-pos),
  2481.        (ulong) pos);
  2482.   if (found_record)
  2483.     goto try_next;
  2484.   searching=1;
  2485.   pos++;
  2486.   block_info.second_read=0;
  2487.   continue;
  2488. }
  2489. if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
  2490. {
  2491.   if (!sort_info.fix_datafile && (b_type & BLOCK_DELETED))
  2492.   {
  2493.     share->state.empty+=block_info.block_len;
  2494.     share->state.del++;
  2495.     share->state.splitt++;
  2496.   }
  2497.   if (found_record)
  2498.     goto try_next;
  2499.   /* Check if impossible big deleted block */
  2500.   if (block_info.block_len > share->base.max_pack_length +4)
  2501.     searching=1;
  2502.   if (searching)
  2503.     pos++;
  2504.   else
  2505.     pos=block_info.filepos+block_info.block_len;
  2506.   block_info.second_read=0;
  2507.   continue;
  2508. }
  2509. share->state.splitt++;
  2510. if (! found_record++)
  2511. {
  2512.   sort_info.find_length=left_length=block_info.rec_len;
  2513.   sort_info.start_recpos=pos;
  2514.   if (!sort_info.fix_datafile)
  2515.     sort_info.filepos=sort_info.start_recpos;
  2516.   if (sort_info.fix_datafile && (testflag & T_EXTEND))
  2517.     sort_info.pos=block_info.filepos+1;
  2518.   else
  2519.     sort_info.pos=block_info.filepos+block_info.block_len;
  2520.   if (share->base.blobs)
  2521.   {
  2522.     if (!(to=fix_rec_buff_for_blob(info,block_info.rec_len)))
  2523.     {
  2524.       print_error("Not enough memory for blob at %lu",
  2525.   (ulong) sort_info.start_recpos);
  2526.       DBUG_RETURN(-1);
  2527.     }
  2528.   }
  2529.   else
  2530.     to= info->rec_buff;
  2531. }
  2532. if (left_length < block_info.data_len || ! block_info.data_len)
  2533. {
  2534.   print_info("Found block with too small length at %lu; Skipped",
  2535.      (ulong) sort_info.start_recpos);
  2536.   goto try_next;
  2537. }
  2538. if (block_info.filepos + block_info.data_len > read_cache.end_of_file)
  2539. {
  2540.   print_info("Found block that points outside data file at %lu",
  2541.      (ulong) sort_info.start_recpos);
  2542.   goto try_next;
  2543. }
  2544. if (_nisam_read_cache(&read_cache,to,block_info.filepos,
  2545.    block_info.data_len, test(found_record == 1)))
  2546. {
  2547.   print_info("Read error for block at: %lu (error: %d); Skipped",
  2548.      (ulong) block_info.filepos,my_errno);
  2549.   goto try_next;
  2550. }
  2551. left_length-=block_info.data_len;
  2552. to+=block_info.data_len;
  2553. pos=block_info.next_filepos;
  2554. if (pos == NI_POS_ERROR && left_length)
  2555. {
  2556.   print_info("Wrong block with wrong total length starting at %lu",
  2557.      (ulong) sort_info.start_recpos);
  2558.   goto try_next;
  2559. }
  2560. if (pos + BLOCK_INFO_HEADER_LENGTH > read_cache.end_of_file)
  2561. {
  2562.   print_info("Found link that points at %lu (outside data file) at %lu",
  2563.      (ulong) pos,(ulong) sort_info.start_recpos);
  2564.   goto try_next;
  2565. }
  2566.       } while (left_length);
  2567.       if (_nisam_rec_unpack(info,sort_info.record,info->rec_buff,
  2568.  sort_info.find_length) != MY_FILE_ERROR)
  2569.       {
  2570. if (read_cache.error < 0)
  2571.   DBUG_RETURN(1);
  2572. if ((testflag & (T_EXTEND | T_REP)) || searching)
  2573. {
  2574.   if (_nisam_rec_check(info, sort_info.record))
  2575.   {
  2576.     print_info("Found wrong packed record at %lu",
  2577.        (ulong) sort_info.start_recpos);
  2578.     goto try_next;
  2579.   }
  2580. }
  2581. DBUG_RETURN(0);
  2582.       }
  2583.       if (!searching)
  2584.       {
  2585. print_info("Found wrong packed record at %lu",
  2586.    (ulong) sort_info.start_recpos);
  2587.       }
  2588.     try_next:
  2589.       pos=sort_info.start_recpos+1;
  2590.       searching=1;
  2591.     }
  2592.   case COMPRESSED_RECORD:
  2593.     for (searching=0 ;; searching=1, sort_info.pos++)
  2594.     {
  2595.       if (_nisam_read_cache(&read_cache,(byte*) block_info.header,sort_info.pos,
  2596. share->pack.ref_length,1))
  2597. DBUG_RETURN(-1);
  2598.       if (searching && ! sort_info.fix_datafile)
  2599.       {
  2600. error_printed=1;
  2601. DBUG_RETURN(1); /* Something wrong with data */
  2602.       }
  2603.       sort_info.start_recpos=sort_info.pos;
  2604.       VOID(_nisam_pack_get_block_info(&block_info,share->pack.ref_length,-1,
  2605.    sort_info.pos));
  2606.       if (!block_info.rec_len &&
  2607.   sort_info.pos + MEMMAP_EXTRA_MARGIN == read_cache.end_of_file)
  2608. DBUG_RETURN(-1);
  2609.       if (block_info.rec_len < (uint) share->min_pack_length ||
  2610.   block_info.rec_len > (uint) share->max_pack_length)
  2611.       {
  2612. if (! searching)
  2613.   print_info("Found block with wrong recordlength: %d at %lun",
  2614.      block_info.rec_len, (ulong) sort_info.pos);
  2615. continue;
  2616.       }
  2617.       if (_nisam_read_cache(&read_cache,(byte*) info->rec_buff,
  2618. block_info.filepos, block_info.rec_len,1))
  2619.       {
  2620. if (! searching)
  2621.   print_info("Couldn't read hole record from %lu",
  2622.      (ulong) sort_info.pos);
  2623. continue;
  2624.       }
  2625.       if (_nisam_pack_rec_unpack(info,sort_info.record,info->rec_buff,
  2626.       block_info.rec_len))
  2627.       {
  2628. if (! searching)
  2629.   print_info("Found wrong record at %lu",(ulong) sort_info.pos);
  2630. continue;
  2631.       }
  2632.       if (!sort_info.fix_datafile)
  2633. sort_info.filepos=sort_info.pos;
  2634.       sort_info.max_pos=(sort_info.pos+=share->pack.ref_length+
  2635.  block_info.rec_len);
  2636.       share->state.splitt++;
  2637.       info->packed_length=block_info.rec_len;
  2638.       DBUG_RETURN(0);
  2639.     }
  2640.   }
  2641.   DBUG_RETURN(1); /* Impossible */
  2642. }
  2643. /* Write record to new file */
  2644. static int sort_write_record()
  2645. {
  2646.   int flag;
  2647.   uint block_length,reclength;
  2648.   byte *from;
  2649.   uchar *block_buff[3];
  2650.   N_INFO *info;
  2651.   ISAM_SHARE *share;
  2652.   DBUG_ENTER("sort_write_record");
  2653.   info=sort_info.info;
  2654.   share=info->s;
  2655.   if (sort_info.fix_datafile)
  2656.   {
  2657.     switch (sort_info.new_data_file_type) {
  2658.     case STATIC_RECORD:
  2659.       if (my_b_write(&info->rec_cache,sort_info.record, share->base.reclength))
  2660.       {
  2661. print_error("%d when writing to datafile",my_errno);
  2662. DBUG_RETURN(1);
  2663.       }
  2664.       sort_info.filepos+=share->base.reclength;
  2665.       break;
  2666.     case DYNAMIC_RECORD:
  2667.       if (! info->blobs)
  2668. from=info->rec_buff;
  2669.       else
  2670.       {
  2671. /* must be sure that local buffer is big enough */
  2672. reclength=info->s->base.pack_reclength+
  2673.   _calc_total_blob_length(info,sort_info.record)+
  2674.   ALIGN_SIZE(MAX_DYN_BLOCK_HEADER)+N_SPLITT_LENGTH+
  2675.   DYN_DELETE_BLOCK_HEADER;
  2676. if (sort_info.buff_length < reclength)
  2677. {
  2678.   if (!(sort_info.buff=my_realloc(sort_info.buff, (uint) reclength,
  2679.   MYF(MY_FREE_ON_ERROR |
  2680.       MY_ALLOW_ZERO_PTR))))
  2681.     DBUG_RETURN(1);
  2682.   sort_info.buff_length=reclength;
  2683. }
  2684. from=sort_info.buff+ALIGN_SIZE(MAX_DYN_BLOCK_HEADER);
  2685.       }
  2686.       reclength=_nisam_rec_pack(info,from,sort_info.record);
  2687.       block_length=reclength+ 3 +test(reclength > 65532L);
  2688.       if (block_length < share->base.min_block_length)
  2689. block_length=share->base.min_block_length;
  2690.       flag=0;
  2691.       info->update|=HA_STATE_WRITE_AT_END;
  2692.       if (_nisam_write_part_record(info,0L,block_length,NI_POS_ERROR,
  2693. &from,&reclength,&flag))
  2694.       {
  2695. print_error("%d when writing to datafile",my_errno);
  2696. DBUG_RETURN(1);
  2697.       }
  2698.       sort_info.filepos+=block_length;
  2699.       break;
  2700.     case COMPRESSED_RECORD:
  2701.       reclength=info->packed_length;
  2702.       save_integer((byte*) block_buff,share->pack.ref_length,reclength);
  2703.       if (my_b_write(&info->rec_cache,(byte*) block_buff,share->pack.ref_length)
  2704.   || my_b_write(&info->rec_cache,(byte*) info->rec_buff,reclength))
  2705.       {
  2706. print_error("%d when writing to datafile",my_errno);
  2707. DBUG_RETURN(1);
  2708.       }
  2709.       sort_info.filepos+=reclength+share->pack.ref_length;
  2710.       break;
  2711.     }
  2712.   }
  2713.   share->state.records++;
  2714.   if (testflag & T_WRITE_LOOP && share->state.records % WRITE_COUNT == 0)
  2715.   {
  2716.     printf("%lur",(ulong) share->state.records); VOID(fflush(stdout));
  2717.   }
  2718.   DBUG_RETURN(0);
  2719. } /* sort_write_record */
  2720. /* Compare two keys from _create_index_by_sort */
  2721. static int sort_key_cmp(const void *not_used __attribute__((unused)),
  2722. const void *a,const void *b)
  2723. {
  2724.   return (_nisam_key_cmp(sort_info.keyseg,*((uchar**) a),*((uchar**) b),0,
  2725.       SEARCH_SAME));
  2726. } /* sort_key_cmp */
  2727. static int sort_key_write( const void *a)
  2728. {
  2729.   int cmp=sort_info.key_block->inited ?
  2730.     _nisam_key_cmp(sort_info.keyseg,sort_info.key_block->lastkey,(uchar*) a,
  2731.        0,SEARCH_FIND) : -1L;
  2732.   if ((sort_info.keyinfo->base.flag & HA_NOSAME) &&
  2733.       cmp == 0)
  2734.   {
  2735.     sort_info.dupp++;
  2736.     print_warning("Dupplicate key for record at %10lu against record at %10lu",
  2737.   sort_info.info->lastpos=get_record_for_key(sort_info.info,
  2738.      sort_info.keyinfo,
  2739.      (uchar*) a),
  2740.   get_record_for_key(sort_info.info,sort_info.keyinfo,
  2741.      sort_info.key_block->lastkey));
  2742.     if (testflag & T_VERBOSE)
  2743.       _nisam_print_key(stdout,sort_info.keyseg,(uchar*) a);
  2744.     return(sort_delete_record());
  2745.   }
  2746.   if (cmp)
  2747.     sort_info.unique++;
  2748. #ifndef DBUG_OFF
  2749.   if (cmp > 0)
  2750.   {
  2751.     print_error("Fatal intern error: Keys are not in order from sort");
  2752.     return(1);
  2753.   }
  2754. #endif
  2755.   return (sort_insert_key(sort_info.key_block,(uchar*) a,NI_POS_ERROR));
  2756. } /* sort_key_write */
  2757. /* get pointer to record from a key */
  2758. static ulong get_record_for_key( N_INFO *info, N_KEYDEF *keyinfo, uchar *key)
  2759. {
  2760.   return _nisam_dpos(info,0,key+_nisam_keylength(keyinfo,key));
  2761. } /* get_record_for_key */
  2762. /* Insert a key in sort-key-blocks */
  2763. static int sort_insert_key(reg1 ISAM_SORT_KEY_BLOCKS *key_block,
  2764.    uchar *key, ulong prev_block)
  2765. {
  2766.   uint a_length,t_length,nod_flag;
  2767.   ulong filepos;
  2768.   uchar *anc_buff,*lastkey;
  2769.   S_PARAM s_temp;
  2770.   N_INFO *info;
  2771.   DBUG_ENTER("sort_insert_key");
  2772.   anc_buff=key_block->buff;
  2773.   info=sort_info.info;
  2774.   lastkey=key_block->lastkey;
  2775.   nod_flag= (key_block == sort_info.key_block ? 0 :
  2776.      sort_info.info->s->base.key_reflength);
  2777.   if (!key_block->inited)
  2778.   {
  2779.     key_block->inited=1;
  2780.     if (key_block == sort_info.key_block_end)
  2781.     {
  2782.       print_error("To many keyblocklevels; Try increasing sort_key_blocks");
  2783.       DBUG_RETURN(1);
  2784.     }
  2785.     a_length=2+nod_flag;
  2786.     key_block->end_pos=anc_buff+2;
  2787.     lastkey=0; /* No previous key in block */
  2788.   }
  2789.   else
  2790.     a_length=getint(anc_buff);
  2791. /* Save pointer to previous block */
  2792.   if (nod_flag)
  2793.     _nisam_kpointer(info,key_block->end_pos,prev_block);
  2794.   t_length=_nisam_get_pack_key_length(sort_info.keyinfo,nod_flag,
  2795.    (uchar*) 0,lastkey,key,&s_temp);
  2796.   _nisam_store_key(sort_info.keyinfo,key_block->end_pos+nod_flag,&s_temp);
  2797.   a_length+=t_length;
  2798.   putint(anc_buff,a_length,nod_flag);
  2799.   key_block->end_pos+=t_length;
  2800.   if (a_length <= sort_info.keyinfo->base.block_length)
  2801.   {
  2802.     VOID(_nisam_move_key(sort_info.keyinfo,key_block->lastkey,key));
  2803.     key_block->last_length=a_length-t_length;
  2804.     DBUG_RETURN(0);
  2805.   }
  2806. /* Fill block with end-zero and write filled block */
  2807.   putint(anc_buff,key_block->last_length,nod_flag);
  2808.   bzero((byte*) anc_buff+key_block->last_length,
  2809. sort_info.keyinfo->base.block_length- key_block->last_length);
  2810.   if ((filepos=_nisam_new(info,sort_info.keyinfo)) == NI_POS_ERROR)
  2811.     return 1;
  2812.   if (my_pwrite(info->s->kfile,(byte*) anc_buff,
  2813. (uint) sort_info.keyinfo->base.block_length,filepos,MYF_RW))
  2814.     DBUG_RETURN(1);
  2815.   DBUG_DUMP("buff",(byte*) anc_buff,getint(anc_buff));
  2816. /* Write separator-key to block in next level */
  2817.   if (sort_insert_key(key_block+1,key_block->lastkey,filepos))
  2818.     DBUG_RETURN(1);
  2819. /* clear old block and write new key in it */
  2820.   key_block->inited=0;
  2821.   DBUG_RETURN(sort_insert_key(key_block,key,prev_block));
  2822. } /* sort_insert_key */
  2823. /* Delete record when we found a dupplicated key */
  2824. static int sort_delete_record()
  2825. {
  2826.   uint i;
  2827.   int old_file,error;
  2828.   uchar *key;
  2829.   N_INFO *info;
  2830.   DBUG_ENTER("sort_delete_record");
  2831.   if (rep_quick == 1)
  2832.   {
  2833.     VOID(fputs("Quick-recover aborted; Run recovery without switch 'q' or with switch -qqn",stderr));
  2834.     error_printed=1;
  2835.     DBUG_RETURN(1);
  2836.   }
  2837.   info=sort_info.info;
  2838.   if (info->s->base.options & HA_OPTION_COMPRESS_RECORD)
  2839.   {
  2840.     VOID(fputs("Recover aborted; Can't run standard recovery on compressed tablesnwith errors in data-filenUse switch '--safe-recover' to fix itn",stderr));
  2841.     error_printed=1;
  2842.     DBUG_RETURN(1);
  2843.   }
  2844.   old_file=info->dfile;
  2845.   info->dfile=info->rec_cache.file;
  2846.   if (sort_info.key)
  2847.   {
  2848.     key=info->lastkey+info->s->base.max_key_length;
  2849.     if ((*info->s->read_rnd)(info,sort_info.record,info->lastpos,0) < 0)
  2850.     {
  2851.       print_error("Can't read record to be removed");
  2852.       info->dfile=old_file;
  2853.       DBUG_RETURN(1);
  2854.     }
  2855.     for (i=0 ; i < sort_info.key ; i++)
  2856.     {
  2857.       VOID(_nisam_make_key(info,i,key,sort_info.record,info->lastpos));
  2858.       if (_nisam_ck_delete(info,i,key))
  2859.       {
  2860. print_error("Can't delete key %d from record to be removed",i+1);
  2861. info->dfile=old_file;
  2862. DBUG_RETURN(1);
  2863.       }
  2864.     }
  2865.   }
  2866.   error=flush_io_cache(&info->rec_cache) || (*info->s->delete_record)(info);
  2867.   info->dfile=old_file; /* restore actual value */
  2868.   info->s->state.records--;
  2869.   DBUG_RETURN(error);
  2870. } /* sort_delete_record */
  2871. /* Fix all pending blocks and flush everything to disk */
  2872. static int flush_pending_blocks()
  2873. {
  2874.   uint nod_flag,length;
  2875.   ulong filepos;
  2876.   N_INFO *info;
  2877.   ISAM_SORT_KEY_BLOCKS *key_block;
  2878.   DBUG_ENTER("flush_pending_blocks");
  2879.   filepos= NI_POS_ERROR; /* if empty file */
  2880.   info=sort_info.info;
  2881.   nod_flag=0;
  2882.   for (key_block=sort_info.key_block ; key_block->inited ; key_block++)
  2883.   {
  2884.     key_block->inited=0;
  2885.     length=getint(key_block->buff);
  2886.     if (nod_flag)
  2887.       _nisam_kpointer(info,key_block->end_pos,filepos);
  2888.     if ((filepos=_nisam_new(info,sort_info.keyinfo)) == NI_POS_ERROR)
  2889.       DBUG_RETURN(1);
  2890.     bzero((byte*) key_block->buff+length,
  2891.   sort_info.keyinfo->base.block_length-length);
  2892.     if (my_pwrite(info->s->kfile,(byte*) key_block->buff,
  2893.  (uint) sort_info.keyinfo->base.block_length,filepos,MYF_RW))
  2894.       DBUG_RETURN(1);
  2895.     DBUG_DUMP("buff",(byte*) key_block->buff,length);
  2896.     nod_flag=1;
  2897.   }
  2898.   info->s->state.key_root[sort_info.key]=filepos; /* Last is root for tree */
  2899.   DBUG_RETURN(0);
  2900. } /* flush_pending_blocks */
  2901. /* alloc space and pointers for key_blocks */
  2902. static ISAM_SORT_KEY_BLOCKS *alloc_key_blocks(uint blocks, uint buffer_length)
  2903. {
  2904.   reg1 uint i;
  2905.   ISAM_SORT_KEY_BLOCKS *block;
  2906.   DBUG_ENTER("alloc_key_blocks");
  2907.   if (!(block=(ISAM_SORT_KEY_BLOCKS*) my_malloc((sizeof(ISAM_SORT_KEY_BLOCKS)+
  2908.  buffer_length+IO_SIZE)*blocks,
  2909. MYF(0))))
  2910.   {
  2911.     print_error("Not Enough memory for sort-key-blocks");
  2912.     return(0);
  2913.   }
  2914.   for (i=0 ; i < blocks ; i++)
  2915.   {
  2916.     block[i].inited=0;
  2917.     block[i].buff=(uchar*) (block+blocks)+(buffer_length+IO_SIZE)*i;
  2918.   }
  2919.   DBUG_RETURN(block);
  2920. } /* alloc_key_blocks */
  2921. /* print warnings and errors */
  2922. /* VARARGS */
  2923. static void print_info(const char * fmt,...)
  2924. {
  2925.   va_list args;
  2926.   va_start(args,fmt);
  2927.   VOID(vfprintf(stdout, fmt, args));
  2928.   VOID(fputc('n',stdout));
  2929.   va_end(args);
  2930.   return;
  2931. }
  2932. /* VARARGS */
  2933. static void print_warning(const char * fmt,...)
  2934. {
  2935.   va_list args;
  2936.   DBUG_ENTER("print_warning");
  2937.   if (!warning_printed && !error_printed)
  2938.   {
  2939.     fflush(stdout);
  2940.     if (testflag & T_SILENT)
  2941.       fprintf(stderr,"%s: ISAM file %sn",my_progname,isam_file_name);
  2942.   }
  2943.   warning_printed=1;
  2944.   va_start(args,fmt);
  2945.   fprintf(stderr,"%s: warning: ",my_progname);
  2946.   VOID(vfprintf(stderr, fmt, args));
  2947.   VOID(fputc('n',stderr));
  2948.   va_end(args);
  2949.   DBUG_VOID_RETURN;
  2950. }
  2951. /* VARARGS */
  2952. void print_error(const char *fmt,...)
  2953. {
  2954.   va_list args;
  2955.   DBUG_ENTER("print_error");
  2956.   DBUG_PRINT("enter",("format: %s",fmt));
  2957.   if (!warning_printed && !error_printed)
  2958.   {
  2959.     fflush(stdout);
  2960.     if (testflag & T_SILENT)
  2961.       fprintf(stderr,"%s: ISAM file %sn",my_progname,isam_file_name);
  2962.   }
  2963.   error_printed|=1;
  2964.   va_start(args,fmt);
  2965.   fprintf(stderr,"%s: error: ",my_progname);
  2966.   VOID(vfprintf(stderr, fmt, args));
  2967.   VOID(fputc('n',stderr));
  2968.   va_end(args);
  2969.   DBUG_VOID_RETURN;
  2970. }
  2971. /* Check if file is almost full */
  2972. static int test_if_almost_full(N_INFO *info)
  2973. {
  2974.   double diff= 0.9;
  2975.   if (info->s->base.options & HA_OPTION_COMPRESS_RECORD)
  2976.   { /* Fix problem with pack_isam */
  2977.     diff=1.0;
  2978.     if (info->s->base.rec_reflength == 4)
  2979.       info->s->base.max_data_file_length= (uint32) ~0L;
  2980.     else
  2981.       info->s->base.max_data_file_length=
  2982. 1L << (info->s->base.rec_reflength);
  2983.   }
  2984.   return (my_seek(info->s->kfile,0L,MY_SEEK_END,MYF(0)) >
  2985.   (ulong) (info->s->base.max_key_file_length*diff) ||
  2986.    my_seek(info->dfile,0L,MY_SEEK_END,MYF(0)) >
  2987.    (ulong) (info->s->base.max_data_file_length*diff));
  2988. }
  2989. /* Recreate table with bigger more alloced record-data */
  2990. static int recreate_database(N_INFO **org_info, char *filename)
  2991. {
  2992.   int error;
  2993.   N_INFO info;
  2994.   ISAM_SHARE share;
  2995.   N_KEYDEF *keyinfo;
  2996.   N_RECINFO *recinfo,*rec,*end;
  2997.   uint unpack;
  2998.   ulong max_records;
  2999.   char name[FN_REFLEN];
  3000.   error=1; /* Default error */
  3001.   info= **org_info;
  3002.   share= *(*org_info)->s;
  3003.   unpack= (share.base.options & HA_OPTION_COMPRESS_RECORD) &&
  3004.     (testflag & T_UNPACK);
  3005.   if (!(keyinfo=(N_KEYDEF*) my_alloca(sizeof(N_KEYDEF)*share.base.keys)))
  3006.     return 0;
  3007.   memcpy((byte*) keyinfo,(byte*) share.keyinfo,
  3008.  (size_t) (sizeof(N_KEYDEF)*share.base.keys));
  3009.   if (!(recinfo=(N_RECINFO*)
  3010. my_alloca(sizeof(N_RECINFO)*(share.base.fields+1))))
  3011.   {
  3012.     my_afree((gptr) keyinfo);
  3013.     return 1;
  3014.   }
  3015.   memcpy((byte*) recinfo,(byte*) share.rec,
  3016.  (size_t) (sizeof(N_RECINFO)*(share.base.fields+1)));
  3017.   for (rec=recinfo,end=recinfo+share.base.fields; rec != end ; rec++)
  3018.   {
  3019.     if (rec->base.type == (int) FIELD_BLOB)
  3020.       rec->base.length+=sizeof(char*);
  3021.     else if (unpack && !(share.base.options & HA_OPTION_PACK_RECORD))
  3022.       rec->base.type=(int) FIELD_NORMAL;
  3023.   }
  3024.   if (share.base.options & HA_OPTION_COMPRESS_RECORD)
  3025.     share.base.records=max_records=share.state.records;
  3026.   else if (share.base.min_pack_length)
  3027.     max_records=(ulong) (my_seek(info.dfile,0L,MY_SEEK_END,MYF(0)) /
  3028.  (ulong) share.base.min_pack_length);
  3029.   else
  3030.     max_records=0;
  3031.   unpack= (share.base.options & HA_OPTION_COMPRESS_RECORD) &&
  3032.     (testflag & T_UNPACK);
  3033.   share.base.options&= ~HA_OPTION_TEMP_COMPRESS_RECORD;
  3034.   VOID(nisam_close(*org_info));
  3035.   if (nisam_create(fn_format(name,filename,"",N_NAME_IEXT,
  3036.   4+ (opt_follow_links ? 16 : 0)),
  3037.    share.base.keys,keyinfo,recinfo,
  3038.    max(max_records,share.base.records),share.base.reloc,
  3039.    HA_DONT_TOUCH_DATA,
  3040.    share.base.options |
  3041.    (unpack ? HA_OPTION_TEMP_COMPRESS_RECORD
  3042.     : 0),
  3043.    (ulong) my_seek(info.dfile,0L,MY_SEEK_END,MYF(0))))
  3044.   {
  3045.     print_error("Got error %d when trying to recreate indexfile",my_errno);
  3046.     goto end;
  3047.   }
  3048.   *org_info=nisam_open(name,O_RDWR,
  3049. (testflag & T_WAIT_FOREVER) ? HA_OPEN_WAIT_IF_LOCKED :
  3050. (testflag & T_DESCRIPT) ? HA_OPEN_IGNORE_IF_LOCKED :
  3051. HA_OPEN_ABORT_IF_LOCKED);
  3052.   if (!*org_info)
  3053.   {
  3054.     print_error("Got error %d when trying to open re-created indexfile",
  3055. my_errno);
  3056.     goto end;
  3057.   }
  3058.   /* We are modifing */
  3059.   (*org_info)->s->base.options&= ~HA_OPTION_READ_ONLY_DATA;
  3060.   VOID(_nisam_readinfo(*org_info,F_WRLCK,0));
  3061.   (*org_info)->s->state.records=share.state.records;
  3062.   if (share.base.create_time)
  3063.     (*org_info)->s->base.create_time=share.base.create_time;
  3064.   (*org_info)->s->state.uniq=(*org_info)->this_uniq=
  3065.     share.state.uniq;
  3066.   (*org_info)->s->state.del=share.state.del;
  3067.   (*org_info)->s->state.dellink=share.state.dellink;
  3068.   (*org_info)->s->state.empty=share.state.empty;
  3069.   (*org_info)->s->state.data_file_length=share.state.data_file_length;
  3070.   if (update_state_info(*org_info,UPDATE_TIME | UPDATE_STAT))
  3071.     goto end;
  3072.   error=0;
  3073. end:
  3074.   my_afree((gptr) keyinfo);
  3075.   my_afree((gptr) recinfo);
  3076.   return error;
  3077. }
  3078. /* Store long in 1,2,3 or 4 bytes */
  3079. static void save_integer( byte *pos, uint pack_length, ulong value)
  3080. {
  3081.   switch (pack_length) {
  3082.   case 4: int4store(pos,value); break;
  3083.   case 3: int3store(pos,value); break;
  3084.   case 2: int2store(pos,(uint) value); break;
  3085.   case 1: pos[0]= (char) (uchar) value; break;
  3086.   default: break;
  3087.   }
  3088.   return;
  3089. }
  3090. /* write suffix to data file if neaded */
  3091. static int write_data_suffix( N_INFO *info)
  3092. {
  3093.   if (info->s->base.options & HA_OPTION_COMPRESS_RECORD &&
  3094.       sort_info.fix_datafile)
  3095.   {
  3096.     char buff[MEMMAP_EXTRA_MARGIN];
  3097.     bzero(buff,sizeof(buff));
  3098.     if (my_b_write(&info->rec_cache,buff,sizeof(buff)))
  3099.     {
  3100.       print_error("%d when writing to datafile",my_errno);
  3101.       return 1;
  3102.     }
  3103.     read_cache.end_of_file+=sizeof(buff);
  3104.   }
  3105.   return 0;
  3106. }
  3107. /* Update state and isamchk_time of indexfile */
  3108. static int update_state_info( N_INFO *info, uint update)
  3109. {
  3110.   ISAM_SHARE *share=info->s;
  3111.   uint base_pos=uint2korr(info->s->state.header.base_pos);
  3112.   if (update & (UPDATE_STAT | UPDATE_SORT | UPDATE_TIME))
  3113.   {
  3114.     if (offsetof(N_BASE_INFO,rec_per_key) >
  3115. uint2korr(share->state.header.base_info_length))
  3116.     {
  3117.       VOID(fputs("Internal error: Trying to change base of old tablen",
  3118.  stderr));
  3119.     }
  3120.     else
  3121.     {
  3122.       if (update & UPDATE_TIME)
  3123.       {
  3124. share->base.isamchk_time= (long) time((time_t*) 0);
  3125. if (!share->base.create_time)
  3126.   share->base.create_time=share->base.isamchk_time;
  3127. if (my_pwrite(share->kfile,(gptr) &share->base.create_time,
  3128.       sizeof(long)*2,
  3129.       base_pos+offsetof(N_BASE_INFO,create_time),
  3130.       MYF(MY_NABP)))
  3131.   goto err;
  3132.       }
  3133.       if (update & (UPDATE_STAT | UPDATE_SORT))
  3134.       {
  3135. if (my_pwrite(share->kfile,(gptr) share->base.rec_per_key,
  3136.       sizeof(long)*share->state.keys+sizeof(uint),
  3137.       base_pos+offsetof(N_BASE_INFO,rec_per_key),
  3138.       MYF(MY_NABP)))
  3139.   goto err;
  3140.       }
  3141.     }
  3142.   }
  3143.   { /* Force update of status */
  3144.     int error;
  3145.     uint r_locks=share->r_locks,w_locks=share->w_locks;
  3146.     share->r_locks=share->w_locks=0;
  3147.     error=_nisam_writeinfo(info,2);
  3148.     share->r_locks=r_locks; share->w_locks=w_locks;
  3149.     if (!error)
  3150.       return 0;
  3151.   }
  3152. err:
  3153.   print_error("%d when updating keyfile",my_errno);
  3154.   return 1;
  3155. }