myisamchk.c 60.7 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2003 MySQL AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6

unknown's avatar
unknown committed
7 8 9 10
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
unknown's avatar
unknown committed
11

unknown's avatar
unknown committed
12 13 14 15
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

16
/* Describe, check and repair of MyISAM tables */
unknown's avatar
unknown committed
17

18
#include "fulltext.h"
unknown's avatar
unknown committed
19 20
#include <m_ctype.h>
#include <stdarg.h>
21
#include <my_getopt.h>
unknown's avatar
unknown committed
22
#include <my_bit.h>
unknown's avatar
unknown committed
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
#ifdef HAVE_SYS_VADVICE_H
#include <sys/vadvise.h>
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
SET_STACK_SIZE(9000)			/* Minimum stack size for program */

#ifndef USE_RAID
#define my_raid_create(A,B,C,D,E,F,G) my_create(A,B,C,G)
#define my_raid_delete(A,B,C) my_delete(A,B)
#endif

static uint decode_bits;
static char **default_argv;
static const char *load_default_groups[]= { "myisamchk", 0 };
39 40
static const char *set_collation_name, *opt_tmpdir;
static CHARSET_INFO *set_collation;
unknown's avatar
unknown committed
41
static long opt_myisam_block_size;
unknown's avatar
unknown committed
42
static long opt_key_cache_block_size;
43
static const char *my_progname_short;
44
static int stopwords_inited= 0;
45
static MY_TMPDIR myisamchk_tmpdir;
unknown's avatar
unknown committed
46 47

static const char *type_names[]=
48
{ "impossible","char","binary", "short", "long", "float",
unknown's avatar
unknown committed
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
  "double","number","unsigned short",
  "unsigned long","longlong","ulonglong","int24",
  "uint24","int8","varchar", "varbin","?",
  "?"};

static const char *prefix_packed_txt="packed ",
		  *bin_packed_txt="prefix ",
		  *diff_txt="stripped ",
		  *null_txt="NULL",
		  *blob_txt="BLOB ";

static const char *field_pack[]=
{"","no endspace", "no prespace",
 "no zeros", "blob", "constant", "table-lockup",
 "always zero","varchar","unique-hash","?","?"};

65
static const char *myisam_stats_method_str="nulls_unequal";
unknown's avatar
unknown committed
66 67 68 69

static void get_options(int *argc,char * * *argv);
static void print_version(void);
static void usage(void);
70
static int myisamchk(HA_CHECK *param, char *filename);
71
static void descript(HA_CHECK *param, register MI_INFO *info, char * name);
72
static int mi_sort_records(HA_CHECK *param, register MI_INFO *info,
73
                           char * name, uint sort_key,
74
			   my_bool write_info, my_bool update_index);
unknown's avatar
unknown committed
75
static int sort_record_index(MI_SORT_PARAM *sort_param, MI_INFO *info,
unknown's avatar
unknown committed
76
                             MI_KEYDEF *keyinfo,
unknown's avatar
unknown committed
77 78 79
			     my_off_t page,uchar *buff,uint sortkey,
			     File new_file, my_bool update_index);

80
HA_CHECK check_param;
unknown's avatar
unknown committed
81 82 83 84 85 86 87

	/* Main program */

int main(int argc, char **argv)
{
  int error;
  MY_INIT(argv[0]);
88
  my_progname_short= my_progname+dirname_length(my_progname);
unknown's avatar
unknown committed
89 90 91 92 93 94 95 96 97

  myisamchk_init(&check_param);
  check_param.opt_lock_memory=1;		/* Lock memory if possible */
  check_param.using_global_keycache = 0;
  get_options(&argc,(char***) &argv);
  myisam_quick_table_bits=decode_bits;
  error=0;
  while (--argc >= 0)
  {
98
    int new_error=myisamchk(&check_param, *(argv++));
99
    if ((check_param.testflag & T_REP_ANY) != T_REP)
100
      check_param.testflag&= ~T_REP;
unknown's avatar
unknown committed
101 102 103 104
    VOID(fflush(stdout));
    VOID(fflush(stderr));
    if ((check_param.error_printed | check_param.warning_printed) &&
	(check_param.testflag & T_FORCE_CREATE) &&
105 106
	(!(check_param.testflag & (T_REP | T_REP_BY_SORT | T_SORT_RECORDS |
				   T_SORT_INDEX))))
unknown's avatar
unknown committed
107 108
    {
      uint old_testflag=check_param.testflag;
109 110
      if (!(check_param.testflag & T_REP))
	check_param.testflag|= T_REP_BY_SORT;
unknown's avatar
unknown committed
111 112 113 114 115 116
      check_param.testflag&= ~T_EXTEND;			/* Don't needed  */
      error|=myisamchk(&check_param, argv[-1]);
      check_param.testflag= old_testflag;
      VOID(fflush(stdout));
      VOID(fflush(stderr));
    }
117 118
    else
      error|=new_error;
unknown's avatar
unknown committed
119 120 121 122 123 124 125 126 127 128 129
    if (argc && (!(check_param.testflag & T_SILENT) || check_param.testflag & T_INFO))
    {
      puts("\n---------\n");
      VOID(fflush(stdout));
    }
  }
  if (check_param.total_files > 1)
  {					/* Only if descript */
    char buff[22],buff2[22];
    if (!(check_param.testflag & T_SILENT) || check_param.testflag & T_INFO)
      puts("\n---------\n");
130
    printf("\nTotal of all %d MyISAM-files:\nData records: %9s   Deleted blocks: %9s\n",check_param.total_files,llstr(check_param.total_records,buff),
unknown's avatar
unknown committed
131 132 133
	   llstr(check_param.total_deleted,buff2));
  }
  free_defaults(default_argv);
unknown's avatar
unknown committed
134
  free_tmpdir(&myisamchk_tmpdir);
135
  ft_free_stopwords();
unknown's avatar
unknown committed
136 137 138 139 140 141 142
  my_end(check_param.testflag & T_INFO ? MY_CHECK_ERROR | MY_GIVE_INFO : MY_CHECK_ERROR);
  exit(error);
#ifndef _lint
  return 0;				/* No compiler warning */
#endif
} /* main */

143
enum options_mc {
144
  OPT_CHARSETS_DIR=256, OPT_SET_COLLATION,OPT_START_CHECK_POS,
unknown's avatar
unknown committed
145 146
  OPT_CORRECT_CHECKSUM, OPT_KEY_BUFFER_SIZE,
  OPT_KEY_CACHE_BLOCK_SIZE, OPT_MYISAM_BLOCK_SIZE,
147 148
  OPT_READ_BUFFER_SIZE, OPT_WRITE_BUFFER_SIZE, OPT_SORT_BUFFER_SIZE,
  OPT_SORT_KEY_BLOCKS, OPT_DECODE_BITS, OPT_FT_MIN_WORD_LEN,
149
  OPT_FT_MAX_WORD_LEN, OPT_FT_STOPWORD_FILE,
150
  OPT_MAX_RECORD_LENGTH, OPT_AUTO_CLOSE, OPT_STATS_METHOD
unknown's avatar
unknown committed
151
};
unknown's avatar
unknown committed
152

153
static struct my_option my_long_options[] =
unknown's avatar
unknown committed
154
{
155
  {"analyze", 'a',
156 157
   "Analyze distribution of keys. Will make some joins in MySQL faster. You can check the calculated distribution.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
158
#ifdef __NETWARE__
159
  {"autoclose", OPT_AUTO_CLOSE, "Auto close the screen on exit for Netware.",
unknown's avatar
unknown committed
160 161
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
162 163 164 165
  {"block-search", 'b',
   "No help available.",
   0, 0, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"backup", 'B',
166
   "Make a backup of the .MYD file as 'filename-time.BAK'.",
167
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
168
  {"character-sets-dir", OPT_CHARSETS_DIR,
169
   "Directory where character sets are.",
170
   (uchar**) &charsets_dir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
171 172 173
  {"check", 'c',
   "Check table for errors.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
174
  {"check-only-changed", 'C',
unknown's avatar
unknown committed
175
   "Check only tables that have changed since last check. It also applies to other requested actions (e.g. --analyze will be ignored if the table is already analyzed).",
176
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
177
  {"correct-checksum", OPT_CORRECT_CHECKSUM,
178 179
   "Correct checksum information for table.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
180
#ifndef DBUG_OFF
181 182 183
  {"debug", '#',
   "Output debug log. Often this is 'd:t:o,filename'.",
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
184
#endif
185 186 187
  {"description", 'd',
   "Prints some information about table.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
188
  {"data-file-length", 'D',
189
   "Max length of data file (when recreating data-file when it's full).",
190 191
   (uchar**) &check_param.max_data_file_length,
   (uchar**) &check_param.max_data_file_length,
192
   0, GET_LL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
193
  {"extend-check", 'e',
unknown's avatar
unknown committed
194
   "If used when checking a table, ensure that the table is 100 percent consistent, which will take a long time. If used when repairing a table, try to recover every possible row from the data file. Normally this will also find a lot of garbage rows; Don't use this option with repair if you are not totally desperate.",
195 196
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"fast", 'F',
unknown's avatar
unknown committed
197
   "Check only tables that haven't been closed properly. It also applies to other requested actions (e.g. --analyze will be ignored if the table is already analyzed).",
198
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
199
  {"force", 'f',
200 201
   "Restart with -r if there are any errors in the table. States will be updated as with --update-state.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
202 203 204
  {"HELP", 'H',
   "Display this help and exit.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
205 206 207
  {"help", '?',
   "Display this help and exit.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
208
  {"information", 'i',
209 210
   "Print statistics information about table that is checked.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
211
  {"keys-used", 'k',
212
   "Tell MyISAM to update only some specific keys. # is a bit mask of which keys to use. This can be used to get faster inserts.",
213 214
   (uchar**) &check_param.keys_in_use,
   (uchar**) &check_param.keys_in_use,
215
   0, GET_ULL, REQUIRED_ARG, -1, 0, 0, 0, 0, 0},
216 217
  {"max-record-length", OPT_MAX_RECORD_LENGTH,
   "Skip rows bigger than this if myisamchk can't allocate memory to hold it",
218 219
   (uchar**) &check_param.max_record_length,
   (uchar**) &check_param.max_record_length,
220
   0, GET_ULL, REQUIRED_ARG, LONGLONG_MAX, 0, LONGLONG_MAX, 0, 0, 0},
221
  {"medium-check", 'm',
222
   "Faster than extend-check, but only finds 99.99% of all errors. Should be good enough for most cases.",
223 224 225 226 227 228
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"quick", 'q', "Faster repair by not modifying the data file.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"read-only", 'T',
   "Don't mark table as checked.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
229
  {"recover", 'r',
230 231
   "Can fix almost anything except unique keys that aren't unique.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
unknown's avatar
unknown committed
232
  {"parallel-recover", 'p',
233
   "Same as '-r' but creates all the keys in parallel.",
234
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
235
  {"safe-recover", 'o',
236 237
   "Uses old recovery method; Slower than '-r' but can handle a couple of cases where '-r' reports that it can't fix the data file.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
238 239 240
  {"sort-recover", 'n',
   "Force recovering with sorting even if the temporary file was very big.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
241
#ifdef DEBUG
242 243 244
  {"start-check-pos", OPT_START_CHECK_POS,
   "No help available.",
   0, 0, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
245
#endif
246
  {"set-auto-increment", 'A',
247
   "Force auto_increment to start at this or higher value. If no value is given, then sets the next auto_increment value to the highest used value for the auto key + 1.",
248 249
   (uchar**) &check_param.auto_increment_value,
   (uchar**) &check_param.auto_increment_value,
250
   0, GET_ULL, OPT_ARG, 0, 0, 0, 0, 0, 0},
251 252
  {"set-collation", OPT_SET_COLLATION,
   "Change the collation used by the index",
253
   (uchar**) &set_collation_name, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
254
  {"set-variable", 'O',
255
   "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.",
256 257
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"silent", 's',
258 259
   "Only print errors. One can use two -s to make myisamchk very silent.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
260
  {"sort-index", 'S',
261 262
   "Sort index blocks. This speeds up 'read-next' in applications.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
263
  {"sort-records", 'R',
264
   "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!)",
265 266
   (uchar**) &check_param.opt_sort_key,
   (uchar**) &check_param.opt_sort_key,
267 268 269
   0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"tmpdir", 't',
   "Path for temporary files.",
270
   (uchar**) &opt_tmpdir,
271 272 273 274 275 276 277
   0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"update-state", 'U',
   "Mark tables as crashed if any errors were found.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"unpack", 'u',
   "Unpack file packed with myisampack.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
278
  {"verbose", 'v',
unknown's avatar
unknown committed
279
   "Print more information. This can be used with --description and --check. Use many -v for more verbosity!",
280 281 282 283 284 285 286
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"version", 'V',
   "Print version and exit.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"wait", 'w',
   "Wait if table is locked.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
287
  { "key_buffer_size", OPT_KEY_BUFFER_SIZE, "",
288
    (uchar**) &check_param.use_buffers, (uchar**) &check_param.use_buffers, 0,
289
    GET_ULONG, REQUIRED_ARG, (long) USE_BUFFER_INIT, (long) MALLOC_OVERHEAD,
290
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) IO_SIZE, 0},
unknown's avatar
unknown committed
291
  { "key_cache_block_size", OPT_KEY_CACHE_BLOCK_SIZE,  "",
292 293
    (uchar**) &opt_key_cache_block_size,
    (uchar**) &opt_key_cache_block_size, 0,
unknown's avatar
unknown committed
294 295
    GET_LONG, REQUIRED_ARG, MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH,
    MI_MAX_KEY_BLOCK_LENGTH, 0, MI_MIN_KEY_BLOCK_LENGTH, 0},
296
  { "myisam_block_size", OPT_MYISAM_BLOCK_SIZE,  "",
297
    (uchar**) &opt_myisam_block_size, (uchar**) &opt_myisam_block_size, 0,
298 299
    GET_LONG, REQUIRED_ARG, MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH,
    MI_MAX_KEY_BLOCK_LENGTH, 0, MI_MIN_KEY_BLOCK_LENGTH, 0},
300
  { "read_buffer_size", OPT_READ_BUFFER_SIZE, "",
301 302
    (uchar**) &check_param.read_buffer_length,
    (uchar**) &check_param.read_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
303
    (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD,
304
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
305
  { "write_buffer_size", OPT_WRITE_BUFFER_SIZE, "",
306 307
    (uchar**) &check_param.write_buffer_length,
    (uchar**) &check_param.write_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
308
    (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD,
309
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
310
  { "sort_buffer_size", OPT_SORT_BUFFER_SIZE, "",
311 312
    (uchar**) &check_param.sort_buffer_length,
    (uchar**) &check_param.sort_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
313
    (long) SORT_BUFFER_INIT, (long) (MIN_SORT_BUFFER + MALLOC_OVERHEAD),
314
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
315
  { "sort_key_blocks", OPT_SORT_KEY_BLOCKS, "",
316 317
    (uchar**) &check_param.sort_key_blocks,
    (uchar**) &check_param.sort_key_blocks, 0, GET_ULONG, REQUIRED_ARG,
318
    BUFFERS_WHEN_SORTING, 4L, 100L, 0L, 1L, 0},
319 320 321 322
  { "decode_bits", OPT_DECODE_BITS, "", (uchar**) &decode_bits,
    (uchar**) &decode_bits, 0, GET_UINT, REQUIRED_ARG, 9L, 4L, 17L, 0L, 1L, 0},
  { "ft_min_word_len", OPT_FT_MIN_WORD_LEN, "", (uchar**) &ft_min_word_len,
    (uchar**) &ft_min_word_len, 0, GET_ULONG, REQUIRED_ARG, 4, 1, HA_FT_MAXCHARLEN,
323
    0, 1, 0},
324 325
  { "ft_max_word_len", OPT_FT_MAX_WORD_LEN, "", (uchar**) &ft_max_word_len,
    (uchar**) &ft_max_word_len, 0, GET_ULONG, REQUIRED_ARG, HA_FT_MAXCHARLEN, 10,
326
    HA_FT_MAXCHARLEN, 0, 1, 0},
327 328
  { "ft_stopword_file", OPT_FT_STOPWORD_FILE,
    "Use stopwords from this file instead of built-in list.",
329
    (uchar**) &ft_stopword_file, (uchar**) &ft_stopword_file, 0, GET_STR,
330
    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
331 332
  {"stats_method", OPT_STATS_METHOD,
   "Specifies how index statistics collection code should threat NULLs. "
333 334
   "Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), "
   "\"nulls_equal\" (emulate 4.0 behavior), and \"nulls_ignored\".",
335
   (uchar**) &myisam_stats_method_str, (uchar**) &myisam_stats_method_str, 0,
336
    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
337
  { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
unknown's avatar
unknown committed
338 339
};

340

unknown's avatar
unknown committed
341 342
#include <help_start.h>

unknown's avatar
unknown committed
343 344
static void print_version(void)
{
345
  printf("%s  Ver 2.7 for %s at %s\n", my_progname, SYSTEM_TYPE,
unknown's avatar
unknown committed
346
	 MACHINE_TYPE);
unknown's avatar
unknown committed
347
  NETWARE_SET_SCREEN_MODE(1);
unknown's avatar
unknown committed
348 349
}

350

unknown's avatar
unknown committed
351 352 353 354 355
static void usage(void)
{
  print_version();
  puts("By Monty, for your professional use");
  puts("This software comes with NO WARRANTY: see the PUBLIC for details.\n");
356
  puts("Description, check and repair of MyISAM tables.");
unknown's avatar
unknown committed
357
  puts("Used without options all tables on the command will be checked for errors");
358
  printf("Usage: %s [OPTIONS] tables[.MYI]\n", my_progname_short);
359 360 361 362 363 364
  printf("\nGlobal options:\n");
#ifndef DBUG_OFF
  printf("\
  -#, --debug=...     Output debug log. Often this is 'd:t:o,filename'.\n");
#endif
  printf("\
unknown's avatar
unknown committed
365
  -?, --help          Display this help and exit.\n\
366
  -O, --set-variable var=option.\n\
367 368 369
                      Change the value of a variable. Please note that\n\
                      this option is deprecated; you can set variables\n\
                      directly with '--variable-name=value'.\n\
370
  -t, --tmpdir=path   Path for temporary files. Multiple paths can be\n\
371
                      specified, separated by ");
372
#if defined( __WIN__) || defined(__NETWARE__)
unknown's avatar
unknown committed
373
   printf("semicolon (;)");
374
#else
unknown's avatar
unknown committed
375
   printf("colon (:)");
376
#endif
unknown's avatar
unknown committed
377
                      printf(", they will be used\n\
378
                      in a round-robin fashion.\n\
unknown's avatar
unknown committed
379
  -s, --silent	      Only print errors.  One can use two -s to make\n\
380
		      myisamchk very silent.\n\
unknown's avatar
unknown committed
381
  -v, --verbose       Print more information. This can be used with\n\
382
                      --description and --check. Use many -v for more verbosity.\n\
unknown's avatar
unknown committed
383
  -V, --version       Print version and exit.\n\
unknown's avatar
unknown committed
384
  -w, --wait          Wait if table is locked.\n\n");
385 386 387
#ifdef DEBUG
  puts("  --start-check-pos=# Start reading file at given offset.\n");
#endif
unknown's avatar
unknown committed
388 389

  puts("Check options (check is the default action for myisamchk):\n\
390
  -c, --check	      Check table for errors.\n\
unknown's avatar
unknown committed
391 392
  -e, --extend-check  Check the table VERY throughly.  Only use this in\n\
                      extreme cases as myisamchk should normally be able to\n\
393 394
                      find out if the table is ok even without this switch.\n\
  -F, --fast	      Check only tables that haven't been closed properly.\n\
395
  -C, --check-only-changed\n\
396
		      Check only tables that have changed since last check.\n\
397
  -f, --force         Restart with '-r' if there are any errors in the table.\n\
398 399
		      States will be updated as with '--update-state'.\n\
  -i, --information   Print statistics information about table that is checked.\n\
400
  -m, --medium-check  Faster than extend-check, but only finds 99.99% of\n\
401 402 403
		      all errors.  Should be good enough for most cases.\n\
  -U  --update-state  Mark tables as crashed if you find any errors.\n\
  -T, --read-only     Don't mark table as checked.\n");
unknown's avatar
unknown committed
404

405 406
  puts("Repair options (When using '-r' or '-o'):\n\
  -B, --backup	      Make a backup of the .MYD file as 'filename-time.BAK'.\n\
407
  --correct-checksum  Correct checksum information for table.\n\
unknown's avatar
unknown committed
408
  -D, --data-file-length=#  Max length of data file (when recreating data\n\
409
                      file when it's full).\n\
unknown's avatar
unknown committed
410 411 412 413 414 415
  -e, --extend-check  Try to recover every possible row from the data file\n\
		      Normally this will also find a lot of garbage rows;\n\
		      Don't use this option if you are not totally desperate.\n\
  -f, --force         Overwrite old temporary files.\n\
  -k, --keys-used=#   Tell MyISAM to update only some specific keys. # is a\n\
	              bit mask of which keys to use. This can be used to\n\
416
		      get faster inserts.\n\
417 418 419
  --max-record-length=#\n\
                      Skip rows bigger than this if myisamchk can't allocate\n\
		      memory to hold it.\n\
unknown's avatar
unknown committed
420 421
  -r, --recover       Can fix almost anything except unique keys that aren't\n\
                      unique.\n\
422
  -n, --sort-recover  Forces recovering with sorting even if the temporary\n\
423
		      file would be very big.\n\
424 425 426
  -p, --parallel-recover\n\
                      Uses the same technique as '-r' and '-n', but creates\n\
                      all the keys in parallel, in different threads.\n\
unknown's avatar
unknown committed
427
  -o, --safe-recover  Uses old recovery method; Slower than '-r' but can\n\
428 429
		      handle a couple of cases where '-r' reports that it\n\
		      can't fix the data file.\n\
unknown's avatar
unknown committed
430
  --character-sets-dir=...\n\
431
                      Directory where character sets are.\n\
unknown's avatar
unknown committed
432 433
  --set-collation=name\n\
 		      Change the collation used by the index.\n\
unknown's avatar
unknown committed
434 435
  -q, --quick         Faster repair by not modifying the data file.\n\
                      One can give a second '-q' to force myisamchk to\n\
436
		      modify the original datafile in case of duplicate keys.\n\
unknown's avatar
unknown committed
437 438
		      NOTE: Tables where the data file is currupted can't be\n\
		      fixed with this option.\n\
unknown's avatar
unknown committed
439 440 441 442 443 444
  -u, --unpack        Unpack file packed with myisampack.\n\
");

  puts("Other actions:\n\
  -a, --analyze	      Analyze distribution of keys. Will make some joins in\n\
		      MySQL faster.  You can check the calculated distribution\n\
445
		      by using '--description --verbose table_name'.\n\
446 447 448 449
  --stats_method=name Specifies how index statistics collection code should\n\
                      threat NULLs. Possible values of name are \"nulls_unequal\"\n\
                      (default for 4.1/5.0), \"nulls_equal\" (emulate 4.0), and \n\
                      \"nulls_ignored\".\n\
unknown's avatar
unknown committed
450 451 452 453 454 455
  -d, --description   Prints some information about table.\n\
  -A, --set-auto-increment[=value]\n\
		      Force auto_increment to start at this or higher value\n\
		      If no value is given, then sets the next auto_increment\n\
		      value to the highest used value for the auto key + 1.\n\
  -S, --sort-index    Sort index blocks.  This speeds up 'read-next' in\n\
456
		      applications.\n\
unknown's avatar
unknown committed
457 458 459
  -R, --sort-records=#\n\
		      Sort records according to an index.  This makes your\n\
		      data much more localized and may speed up things\n\
460
		      (It may be VERY slow to do a sort the first time!).\n\
461 462
  -b,  --block-search=#\n\
                       Find a record, a block at given offset belongs to.");
unknown's avatar
unknown committed
463

464 465
  print_defaults("my", load_default_groups);
  my_print_variables(my_long_options);
unknown's avatar
unknown committed
466 467
}

unknown's avatar
unknown committed
468
#include <help_end.h>
unknown's avatar
unknown committed
469

470
const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal",
471
                                           "nulls_ignored", NullS};
472 473 474 475
TYPELIB myisam_stats_method_typelib= {
  array_elements(myisam_stats_method_names) - 1, "",
  myisam_stats_method_names, NULL};

unknown's avatar
unknown committed
476 477
	 /* Read options */

unknown's avatar
unknown committed
478 479 480 481
static my_bool
get_one_option(int optid,
	       const struct my_option *opt __attribute__((unused)),
	       char *argument)
482
{
unknown's avatar
unknown committed
483
  switch (optid) {
unknown's avatar
unknown committed
484 485 486 487 488
#ifdef __NETWARE__
  case OPT_AUTO_CLOSE:
    setscreenmode(SCR_AUTOCLOSE_ON_EXIT);
    break;
#endif
489
  case 'a':
490
    if (argument == disabled_my_option)
491 492 493
      check_param.testflag&= ~T_STATISTICS;
    else
      check_param.testflag|= T_STATISTICS;
494 495 496
    break;
  case 'A':
    if (argument)
497
      check_param.auto_increment_value= strtoull(argument, NULL, 0);
498
    else
499
      check_param.auto_increment_value= 0;	/* Set to max used value */
500 501 502
    check_param.testflag|= T_AUTO_INC;
    break;
  case 'b':
503
    check_param.search_after_block= strtoul(argument, NULL, 10);
504 505
    break;
  case 'B':
506
    if (argument == disabled_my_option)
507 508 509
      check_param.testflag&= ~T_BACKUP_DATA;
    else
      check_param.testflag|= T_BACKUP_DATA;
510 511
    break;
  case 'c':
512
    if (argument == disabled_my_option)
513 514 515
      check_param.testflag&= ~T_CHECK;
    else
      check_param.testflag|= T_CHECK;
516 517
    break;
  case 'C':
518
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
519
      check_param.testflag&= ~(T_CHECK | T_CHECK_ONLY_CHANGED);
520 521
    else
      check_param.testflag|= T_CHECK | T_CHECK_ONLY_CHANGED;
522 523 524 525 526
    break;
  case 'D':
    check_param.max_data_file_length=strtoll(argument, NULL, 10);
    break;
  case 's':				/* silent */
527
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
528
      check_param.testflag&= ~(T_SILENT | T_VERY_SILENT);
529 530 531 532 533 534 535
    else
    {
      if (check_param.testflag & T_SILENT)
	check_param.testflag|= T_VERY_SILENT;
      check_param.testflag|= T_SILENT;
      check_param.testflag&= ~T_WRITE_LOOP;
    }
536 537
    break;
  case 'w':
538
    if (argument == disabled_my_option)
539 540 541
      check_param.testflag&= ~T_WAIT_FOREVER;
    else
      check_param.testflag|= T_WAIT_FOREVER;
542 543
    break;
  case 'd':				/* description if isam-file */
544
    if (argument == disabled_my_option)
545 546 547
      check_param.testflag&= ~T_DESCRIPT;
    else
      check_param.testflag|= T_DESCRIPT;
548 549
    break;
  case 'e':				/* extend check */
550
    if (argument == disabled_my_option)
551 552 553
      check_param.testflag&= ~T_EXTEND;
    else
      check_param.testflag|= T_EXTEND;
554 555
    break;
  case 'i':
556
    if (argument == disabled_my_option)
557 558 559
      check_param.testflag&= ~T_INFO;
    else
      check_param.testflag|= T_INFO;
560 561
    break;
  case 'f':
562
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
563 564 565 566 567 568 569 570 571
    {
      check_param.tmpfile_createflag= O_RDWR | O_TRUNC | O_EXCL;
      check_param.testflag&= ~(T_FORCE_CREATE | T_UPDATE_STATE);
    }
    else
    {
      check_param.tmpfile_createflag= O_RDWR | O_TRUNC;
      check_param.testflag|= T_FORCE_CREATE | T_UPDATE_STATE;
    }
572 573
    break;
  case 'F':
574
    if (argument == disabled_my_option)
575 576 577
      check_param.testflag&= ~T_FAST;
    else
      check_param.testflag|= T_FAST;
578 579 580 581 582
    break;
  case 'k':
    check_param.keys_in_use= (ulonglong) strtoll(argument, NULL, 10);
    break;
  case 'm':
583
    if (argument == disabled_my_option)
584 585 586
      check_param.testflag&= ~T_MEDIUM;
    else
      check_param.testflag|= T_MEDIUM;		/* Medium check */
587 588
    break;
  case 'r':				/* Repair table */
589 590 591
    check_param.testflag&= ~T_REP_ANY;
    if (argument != disabled_my_option)
      check_param.testflag|= T_REP_BY_SORT;
592
    break;
593 594 595 596 597
  case 'p':
    check_param.testflag&= ~T_REP_ANY;
    if (argument != disabled_my_option)
      check_param.testflag|= T_REP_PARALLEL;
    break;
598
  case 'o':
599 600 601
    check_param.testflag&= ~T_REP_ANY;
    check_param.force_sort= 0;
    if (argument != disabled_my_option)
unknown's avatar
merge  
unknown committed
602
    {
603
      check_param.testflag|= T_REP;
unknown's avatar
merge  
unknown committed
604 605
      my_disable_async_io= 1;		/* More safety */
    }
606 607
    break;
  case 'n':
608 609
    check_param.testflag&= ~T_REP_ANY;
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
610 611 612
      check_param.force_sort= 0;
    else
    {
613
      check_param.testflag|= T_REP_BY_SORT;
unknown's avatar
merge  
unknown committed
614 615
      check_param.force_sort= 1;
    }
616 617
    break;
  case 'q':
618
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
619
      check_param.testflag&= ~(T_QUICK | T_FORCE_UNIQUENESS);
620
    else
unknown's avatar
merge  
unknown committed
621 622
      check_param.testflag|=
        (check_param.testflag & T_QUICK) ? T_FORCE_UNIQUENESS : T_QUICK;
623 624
    break;
  case 'u':
625
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
626
      check_param.testflag&= ~(T_UNPACK | T_REP_BY_SORT);
627 628
    else
      check_param.testflag|= T_UNPACK | T_REP_BY_SORT;
629 630
    break;
  case 'v':				/* Verbose */
631
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
632
    {
633
      check_param.testflag&= ~T_VERBOSE;
unknown's avatar
merge  
unknown committed
634 635
      check_param.verbose=0;
    }
636
    else
unknown's avatar
merge  
unknown committed
637
    {
638
      check_param.testflag|= T_VERBOSE;
unknown's avatar
merge  
unknown committed
639 640
      check_param.verbose++;
    }
641 642
    break;
  case 'R':				/* Sort records */
643
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
644 645
      check_param.testflag&= ~T_SORT_RECORDS;
    else
646
    {
unknown's avatar
merge  
unknown committed
647 648 649 650 651 652 653 654 655
      check_param.testflag|= T_SORT_RECORDS;
      check_param.opt_sort_key= (uint) atoi(argument) - 1;
      if (check_param.opt_sort_key >= MI_MAX_KEY)
      {
	fprintf(stderr,
		"The value of the sort key is bigger than max key: %d.\n",
		MI_MAX_KEY);
	exit(1);
      }
656 657 658
    }
    break;
  case 'S':			      /* Sort index */
659
    if (argument == disabled_my_option)
660 661 662
      check_param.testflag&= ~T_SORT_INDEX;
    else
      check_param.testflag|= T_SORT_INDEX;
663 664
    break;
  case 'T':
665
    if (argument == disabled_my_option)
666 667 668
      check_param.testflag&= ~T_READONLY;
    else
      check_param.testflag|= T_READONLY;
669 670
    break;
  case 'U':
671
    if (argument == disabled_my_option)
672 673 674
      check_param.testflag&= ~T_UPDATE_STATE;
    else
      check_param.testflag|= T_UPDATE_STATE;
675 676
    break;
  case '#':
677
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
678 679 680 681 682 683 684
    {
      DBUG_POP();
    }
    else
    {
      DBUG_PUSH(argument ? argument : "d:t:o,/tmp/myisamchk.trace");
    }
685 686 687 688 689
    break;
  case 'V':
    print_version();
    exit(0);
  case OPT_CORRECT_CHECKSUM:
690
    if (argument == disabled_my_option)
unknown's avatar
unknown committed
691 692
      check_param.testflag&= ~T_CALC_CHECKSUM;
    else
693
      check_param.testflag|= T_CALC_CHECKSUM;
694
    break;
695 696 697
  case OPT_STATS_METHOD:
  {
    int method;
698
    enum_handler_stats_method method_conv;
unknown's avatar
unknown committed
699
    LINT_INIT(method_conv);
unknown's avatar
unknown committed
700
    myisam_stats_method_str= argument;
701 702 703 704 705
    if ((method=find_type(argument, &myisam_stats_method_typelib, 2)) <= 0)
    {
      fprintf(stderr, "Invalid value of stats_method: %s.\n", argument);
      exit(1);
    }
706 707 708 709 710 711 712 713 714 715
    switch (method-1) {
    case 0: 
      method_conv= MI_STATS_METHOD_NULLS_EQUAL;
      break;
    case 1:
      method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL;
      break;
    case 2:
      method_conv= MI_STATS_METHOD_IGNORE_NULLS;
      break;
unknown's avatar
unknown committed
716
    default: assert(0);                         /* Impossible */
717 718
    }
    check_param.stats_method= method_conv;
719 720
    break;
  }
721 722
#ifdef DEBUG					/* Only useful if debugging */
  case OPT_START_CHECK_POS:
723
    check_param.start_check_pos= strtoull(argument, NULL, 0);
724 725
    break;
#endif
726 727 728
  case 'H':
    my_print_help(my_long_options);
    exit(0);
729 730 731 732
  case '?':
    usage();
    exit(0);
  }
unknown's avatar
unknown committed
733
  return 0;
734 735 736
}


unknown's avatar
unknown committed
737 738
static void get_options(register int *argc,register char ***argv)
{
739 740
  int ho_error;

741
  load_defaults("my", load_default_groups, argc, argv);
unknown's avatar
unknown committed
742 743 744
  default_argv= *argv;
  if (isatty(fileno(stdout)))
    check_param.testflag|=T_WRITE_LOOP;
745

746 747
  if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
    exit(ho_error);
748

unknown's avatar
unknown committed
749 750
  /* If using repair, then update checksum if one uses --update-state */
  if ((check_param.testflag & T_UPDATE_STATE) &&
751
      (check_param.testflag & T_REP_ANY))
unknown's avatar
unknown committed
752 753
    check_param.testflag|= T_CALC_CHECKSUM;

unknown's avatar
unknown committed
754 755 756 757 758
  if (*argc == 0)
  {
    usage();
    exit(-1);
  }
unknown's avatar
unknown committed
759

unknown's avatar
unknown committed
760
  if ((check_param.testflag & T_UNPACK) &&
unknown's avatar
merge  
unknown committed
761
      (check_param.testflag & (T_QUICK | T_SORT_RECORDS)))
unknown's avatar
unknown committed
762 763 764
  {
    VOID(fprintf(stderr,
		 "%s: --unpack can't be used with --quick or --sort-records\n",
765
		 my_progname_short));
unknown's avatar
unknown committed
766 767 768 769
    exit(1);
  }
  if ((check_param.testflag & T_READONLY) &&
      (check_param.testflag &
770
       (T_REP_ANY | T_STATISTICS | T_AUTO_INC |
771
	T_SORT_RECORDS | T_SORT_INDEX | T_FORCE_CREATE)))
unknown's avatar
unknown committed
772 773 774
  {
    VOID(fprintf(stderr,
		 "%s: Can't use --readonly when repairing or sorting\n",
775
		 my_progname_short));
unknown's avatar
unknown committed
776 777 778
    exit(1);
  }

779 780 781 782
  if (init_tmpdir(&myisamchk_tmpdir, opt_tmpdir))
    exit(1);

  check_param.tmpdir=&myisamchk_tmpdir;
783
  check_param.key_cache_block_size= opt_key_cache_block_size;
784

785 786 787
  if (set_collation_name)
    if (!(set_collation= get_charset_by_name(set_collation_name,
                                             MYF(MY_WME))))
unknown's avatar
unknown committed
788 789
      exit(1);

unknown's avatar
unknown committed
790
  myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size);
unknown's avatar
unknown committed
791 792 793 794 795 796
  return;
} /* get options */


	/* Check table */

797
static int myisamchk(HA_CHECK *param, char * filename)
unknown's avatar
unknown committed
798 799
{
  int error,lock_type,recreate;
unknown's avatar
merge  
unknown committed
800
  int rep_quick= param->testflag & (T_QUICK | T_FORCE_UNIQUENESS);
unknown's avatar
unknown committed
801 802 803 804
  uint raid_chunks;
  MI_INFO *info;
  File datafile;
  char llbuff[22],llbuff2[22];
805
  my_bool state_updated=0;
unknown's avatar
unknown committed
806 807 808 809 810 811 812 813 814 815
  MYISAM_SHARE *share;
  DBUG_ENTER("myisamchk");

  param->out_flag=error=param->warning_printed=param->error_printed=
    recreate=0;
  datafile=0;
  param->isam_file_name=filename;		/* For error messages */
  if (!(info=mi_open(filename,
		     (param->testflag & (T_DESCRIPT | T_READONLY)) ?
		     O_RDONLY : O_RDWR,
816 817 818 819 820
		     HA_OPEN_FOR_REPAIR |
		     ((param->testflag & T_WAIT_FOREVER) ?
		      HA_OPEN_WAIT_IF_LOCKED :
		      (param->testflag & T_DESCRIPT) ?
		      HA_OPEN_IGNORE_IF_LOCKED : HA_OPEN_ABORT_IF_LOCKED))))
unknown's avatar
unknown committed
821 822 823 824 825
  {
    /* Avoid twice printing of isam file name */
    param->error_printed=1;
    switch (my_errno) {
    case HA_ERR_CRASHED:
826 827
      mi_check_print_error(param,"'%s' doesn't have a correct index definition. You need to recreate it before you can do a repair",filename);
      break;
unknown's avatar
unknown committed
828
    case HA_ERR_NOT_A_TABLE:
unknown's avatar
unknown committed
829 830
      mi_check_print_error(param,"'%s' is not a MyISAM-table",filename);
      break;
831 832 833 834 835 836
    case HA_ERR_CRASHED_ON_USAGE:
      mi_check_print_error(param,"'%s' is marked as crashed",filename);
      break;
    case HA_ERR_CRASHED_ON_REPAIR:
      mi_check_print_error(param,"'%s' is marked as crashed after last repair",filename);
      break;
unknown's avatar
unknown committed
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
    case HA_ERR_OLD_FILE:
      mi_check_print_error(param,"'%s' is a old type of MyISAM-table", filename);
      break;
    case HA_ERR_END_OF_FILE:
      mi_check_print_error(param,"Couldn't read complete header from '%s'", filename);
      break;
    case EAGAIN:
      mi_check_print_error(param,"'%s' is locked. Use -w to wait until unlocked",filename);
      break;
    case ENOENT:
      mi_check_print_error(param,"File '%s' doesn't exist",filename);
      break;
    case EACCES:
      mi_check_print_error(param,"You don't have permission to use '%s'",filename);
      break;
    default:
      mi_check_print_error(param,"%d when opening MyISAM-table '%s'",
		  my_errno,filename);
      break;
    }
    DBUG_RETURN(1);
  }
  share=info->s;
  share->options&= ~HA_OPTION_READ_ONLY_DATA; /* We are modifing it */
861
  share->tot_locks-= share->r_locks;
unknown's avatar
unknown committed
862 863 864 865
  share->r_locks=0;
  raid_chunks=share->base.raid_chunks;

  /*
866
    Skip the checking of the file if:
unknown's avatar
unknown committed
867 868 869
    We are using --fast and the table is closed properly
    We are using --check-only-changed-tables and the table hasn't changed
  */
870
  if (param->testflag & (T_FAST | T_CHECK_ONLY_CHANGED))
unknown's avatar
unknown committed
871
  {
872 873
    my_bool need_to_check= mi_is_crashed(info) || share->state.open_count != 0;

874
    if ((param->testflag & (T_REP_ANY | T_SORT_RECORDS)) &&
875 876 877 878 879
	((share->state.changed & (STATE_CHANGED | STATE_CRASHED |
				  STATE_CRASHED_ON_REPAIR) ||
	  !(param->testflag & T_CHECK_ONLY_CHANGED))))
      need_to_check=1;

880 881 882 883 884 885 886 887 888 889 890 891
    if (info->s->base.keys && info->state->records)
    {
      if ((param->testflag & T_STATISTICS) &&
          (share->state.changed & STATE_NOT_ANALYZED))
        need_to_check=1;
      if ((param->testflag & T_SORT_INDEX) &&
          (share->state.changed & STATE_NOT_SORTED_PAGES))
        need_to_check=1;
      if ((param->testflag & T_REP_BY_SORT) &&
          (share->state.changed & STATE_NOT_OPTIMIZED_KEYS))
        need_to_check=1;
    }
892 893 894 895 896
    if ((param->testflag & T_CHECK_ONLY_CHANGED) &&
	(share->state.changed & (STATE_CHANGED | STATE_CRASHED |
				 STATE_CRASHED_ON_REPAIR)))
      need_to_check=1;
    if (!need_to_check)
unknown's avatar
unknown committed
897
    {
898 899 900 901 902 903 904 905 906
      if (!(param->testflag & T_SILENT) || param->testflag & T_INFO)
	printf("MyISAM file: %s is already checked\n",filename);
      if (mi_close(info))
      {
	mi_check_print_error(param,"%d when closing MyISAM-table '%s'",
			     my_errno,filename);
	DBUG_RETURN(1);
      }
      DBUG_RETURN(0);
unknown's avatar
unknown committed
907 908
    }
  }
909
  if ((param->testflag & (T_REP_ANY | T_STATISTICS |
unknown's avatar
unknown committed
910 911 912 913 914 915 916
			  T_SORT_RECORDS | T_SORT_INDEX)) &&
      (((param->testflag & T_UNPACK) &&
	share->data_file_type == COMPRESSED_RECORD) ||
       mi_uint2korr(share->state.header.state_info_length) !=
       MI_STATE_INFO_SIZE ||
       mi_uint2korr(share->state.header.base_info_length) !=
       MI_BASE_INFO_SIZE ||
917 918
       mi_is_any_intersect_keys_active(param->keys_in_use, share->base.keys,
                                       ~share->state.key_map) ||
unknown's avatar
unknown committed
919 920
       test_if_almost_full(info) ||
       info->s->state.header.file_version[3] != myisam_file_magic[3] ||
921 922
       (set_collation &&
        set_collation->number != share->state.header.language) ||
unknown's avatar
unknown committed
923
       myisam_block_size != MI_KEY_BLOCK_LENGTH))
unknown's avatar
unknown committed
924
  {
925 926
    if (set_collation)
      param->language= set_collation->number;
927
    if (recreate_table(param, &info,filename))
unknown's avatar
unknown committed
928 929 930 931 932 933 934
    {
      VOID(fprintf(stderr,
		   "MyISAM-table '%s' is not fixed because of errors\n",
	      filename));
      return(-1);
    }
    recreate=1;
935
    if (!(param->testflag & T_REP_ANY))
unknown's avatar
unknown committed
936
    {
937
      param->testflag|=T_REP_BY_SORT;		/* if only STATISTICS */
unknown's avatar
unknown committed
938 939
      if (!(param->testflag & T_SILENT))
	printf("- '%s' has old table-format. Recreating index\n",filename);
unknown's avatar
merge  
unknown committed
940
      rep_quick|=T_QUICK;
unknown's avatar
unknown committed
941 942
    }
    share=info->s;
943
    share->tot_locks-= share->r_locks;
unknown's avatar
unknown committed
944 945 946 947 948 949 950 951
    share->r_locks=0;
  }

  if (param->testflag & T_DESCRIPT)
  {
    param->total_files++;
    param->total_records+=info->state->records;
    param->total_deleted+=info->state->del;
952
    descript(param, info, filename);
unknown's avatar
unknown committed
953 954 955
  }
  else
  {
956
    if (!stopwords_inited++)
957
      ft_init_stopwords();
958

unknown's avatar
unknown committed
959 960 961 962 963 964 965 966 967 968 969 970 971
    if (!(param->testflag & T_READONLY))
      lock_type = F_WRLCK;			/* table is changed */
    else
      lock_type= F_RDLCK;
    if (info->lock_type == F_RDLCK)
      info->lock_type=F_UNLCK;			/* Read only table */
    if (_mi_readinfo(info,lock_type,0))
    {
      mi_check_print_error(param,"Can't lock indexfile of '%s', error: %d",
		  filename,my_errno);
      param->error_printed=0;
      goto end2;
    }
972 973 974 975 976
    /*
      _mi_readinfo() has locked the table.
      We mark the table as locked (without doing file locks) to be able to
      use functions that only works on locked tables (like row caching).
    */
977
    mi_lock_database(info, F_EXTRA_LCK);
unknown's avatar
unknown committed
978 979
    datafile=info->dfile;

980
    if (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX))
unknown's avatar
unknown committed
981
    {
982
      if (param->testflag & T_REP_ANY)
unknown's avatar
unknown committed
983 984
      {
	ulonglong tmp=share->state.key_map;
985 986
	mi_copy_keys_active(share->state.key_map, share->base.keys,
                            param->keys_in_use);
unknown's avatar
unknown committed
987 988 989
	if (tmp != share->state.key_map)
	  info->update|=HA_STATE_CHANGED;
      }
990
      if (rep_quick && chk_del(param, info, param->testflag & ~T_VERBOSE))
unknown's avatar
unknown committed
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
      {
	if (param->testflag & T_FORCE_CREATE)
	{
	  rep_quick=0;
	  mi_check_print_info(param,"Creating new data file\n");
	}
	else
	{
	  error=1;
	  mi_check_print_error(param,
			       "Quick-recover aborted; Run recovery without switch 'q'");
	}
      }
      if (!error)
      {
1006
	if ((param->testflag & (T_REP_BY_SORT | T_REP_PARALLEL)) &&
1007
	    (mi_is_any_key_active(share->state.key_map) ||
1008
	     (rep_quick && !param->keys_in_use && !recreate)) &&
1009
	    mi_test_if_sort_rep(info, info->state->records,
1010
				info->s->state.key_map,
1011
				param->force_sort))
1012
	{
1013 1014 1015 1016
          if (param->testflag & T_REP_BY_SORT)
            error=mi_repair_by_sort(param,info,filename,rep_quick);
          else
            error=mi_repair_parallel(param,info,filename,rep_quick);
1017 1018
	  state_updated=1;
	}
1019 1020
	else if (param->testflag & T_REP_ANY)
	  error=mi_repair(param, info,filename,rep_quick);
unknown's avatar
unknown committed
1021 1022 1023
      }
      if (!error && param->testflag & T_SORT_RECORDS)
      {
1024 1025 1026 1027 1028
	/*
	  The data file is nowadays reopened in the repair code so we should
	  soon remove the following reopen-code
	*/
#ifndef TO_BE_REMOVED
unknown's avatar
unknown committed
1029 1030 1031
	if (param->out_flag & O_NEW_DATA)
	{			/* Change temp file to org file */
	  VOID(my_close(info->dfile,MYF(MY_WME))); /* Close new file */
1032
	  error|=change_to_newfile(filename,MI_NAME_DEXT,DATA_TMP_EXT,
1033 1034
				   raid_chunks,
				   MYF(0));
unknown's avatar
unknown committed
1035
	  if (mi_open_datafile(info,info->s, -1))
unknown's avatar
unknown committed
1036 1037 1038 1039
	    error=1;
	  param->out_flag&= ~O_NEW_DATA; /* We are using new datafile */
	  param->read_cache.file=info->dfile;
	}
1040
#endif
unknown's avatar
unknown committed
1041 1042 1043 1044 1045
	if (! error)
	{
	  uint key;
	  /*
	    We can't update the index in mi_sort_records if we have a
1046
	    prefix compressed or fulltext index
unknown's avatar
unknown committed
1047 1048 1049
	  */
	  my_bool update_index=1;
	  for (key=0 ; key < share->base.keys; key++)
1050
	    if (share->keyinfo[key].flag & (HA_BINARY_PACK_KEY|HA_FULLTEXT))
unknown's avatar
unknown committed
1051 1052
	      update_index=0;

1053
	  error=mi_sort_records(param,info,filename,param->opt_sort_key,
1054
                             /* what is the following parameter for ? */
unknown's avatar
unknown committed
1055 1056 1057 1058 1059
				(my_bool) !(param->testflag & T_REP),
				update_index);
	  datafile=info->dfile;	/* This is now locked */
	  if (!error && !update_index)
	  {
1060
	    if (param->verbose)
unknown's avatar
unknown committed
1061
	      puts("Table had a compressed index;  We must now recreate the index");
1062
	    error=mi_repair_by_sort(param,info,filename,1);
unknown's avatar
unknown committed
1063 1064 1065 1066
	  }
	}
      }
      if (!error && param->testflag & T_SORT_INDEX)
1067
	error=mi_sort_index(param,info,filename);
unknown's avatar
unknown committed
1068
      if (!error)
1069 1070
	share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
				 STATE_CRASHED_ON_REPAIR);
unknown's avatar
unknown committed
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
      else
	mi_mark_crashed(info);
    }
    else if ((param->testflag & T_CHECK) || !(param->testflag & T_AUTO_INC))
    {
      if (!(param->testflag & T_SILENT) || param->testflag & T_INFO)
	printf("Checking MyISAM file: %s\n",filename);
      if (!(param->testflag & T_SILENT))
	printf("Data records: %7s   Deleted blocks: %7s\n",
	       llstr(info->state->records,llbuff),
	       llstr(info->state->del,llbuff2));
1082
      error =chk_status(param,info);
1083
      mi_intersect_keys_active(share->state.key_map, param->keys_in_use);
unknown's avatar
unknown committed
1084 1085 1086
      error =chk_size(param,info);
      if (!error || !(param->testflag & (T_FAST | T_FORCE_CREATE)))
	error|=chk_del(param, info,param->testflag);
1087 1088
      if ((!error || (!(param->testflag & (T_FAST | T_FORCE_CREATE)) &&
		      !param->start_check_pos)))
unknown's avatar
unknown committed
1089 1090 1091
      {
	error|=chk_key(param, info);
	if (!error && (param->testflag & (T_STATISTICS | T_AUTO_INC)))
1092 1093 1094 1095 1096
	  error=update_state_info(param, info,
				  ((param->testflag & T_STATISTICS) ?
				   UPDATE_STAT : 0) |
				  ((param->testflag & T_AUTO_INC) ?
				   UPDATE_AUTO_INC : 0));
unknown's avatar
unknown committed
1097 1098 1099 1100 1101
      }
      if ((!rep_quick && !error) ||
	  !(param->testflag & (T_FAST | T_FORCE_CREATE)))
      {
	if (param->testflag & (T_EXTEND | T_MEDIUM))
unknown's avatar
unknown committed
1102 1103
	  VOID(init_key_cache(dflt_key_cache,opt_key_cache_block_size,
                              param->use_buffers, 0, 0));
unknown's avatar
unknown committed
1104 1105
	VOID(init_io_cache(&param->read_cache,datafile,
			   (uint) param->read_buffer_length,
1106 1107 1108 1109 1110 1111
			   READ_CACHE,
			   (param->start_check_pos ?
			    param->start_check_pos :
			    share->pack.header_length),
			   1,
			   MYF(MY_WME)));
unknown's avatar
unknown committed
1112 1113 1114 1115 1116
	lock_memory(param);
	if ((info->s->options & (HA_OPTION_PACK_RECORD |
				 HA_OPTION_COMPRESS_RECORD)) ||
	    (param->testflag & (T_EXTEND | T_MEDIUM)))
	  error|=chk_data_link(param, info, param->testflag & T_EXTEND);
unknown's avatar
unknown committed
1117
	error|=flush_blocks(param, share->key_cache, share->kfile);
unknown's avatar
unknown committed
1118 1119 1120 1121
	VOID(end_io_cache(&param->read_cache));
      }
      if (!error)
      {
1122 1123
	if ((share->state.changed & STATE_CHANGED) &&
	    (param->testflag & T_UPDATE_STATE))
unknown's avatar
unknown committed
1124
	  info->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
1125 1126
	share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
				 STATE_CRASHED_ON_REPAIR);
unknown's avatar
unknown committed
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
      }
      else if (!mi_is_crashed(info) &&
	       (param->testflag & T_UPDATE_STATE))
      {						/* Mark crashed */
	mi_mark_crashed(info);
	info->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
      }
    }
  }
  if ((param->testflag & T_AUTO_INC) ||
1137
      ((param->testflag & T_REP_ANY) && info->s->base.auto_key))
unknown's avatar
unknown committed
1138 1139 1140 1141 1142 1143 1144 1145
    update_auto_increment_key(param, info,
			      (my_bool) !test(param->testflag & T_AUTO_INC));

  if (!(param->testflag & T_DESCRIPT))
  {
    if (info->update & HA_STATE_CHANGED && ! (param->testflag & T_READONLY))
      error|=update_state_info(param, info,
			       UPDATE_OPEN_COUNT |
1146
			       (((param->testflag & T_REP_ANY) ?
1147 1148
				 UPDATE_TIME : 0) |
				(state_updated ? UPDATE_STAT : 0) |
unknown's avatar
unknown committed
1149 1150 1151 1152 1153
				((param->testflag & T_SORT_RECORDS) ?
				 UPDATE_SORT : 0)));
    VOID(lock_file(param, share->kfile,0L,F_UNLCK,"indexfile",filename));
    info->update&= ~HA_STATE_CHANGED;
  }
1154
  mi_lock_database(info, F_UNLCK);
unknown's avatar
unknown committed
1155 1156 1157 1158 1159 1160 1161 1162 1163
end2:
  if (mi_close(info))
  {
    mi_check_print_error(param,"%d when closing MyISAM-table '%s'",my_errno,filename);
    DBUG_RETURN(1);
  }
  if (error == 0)
  {
    if (param->out_flag & O_NEW_DATA)
1164
      error|=change_to_newfile(filename,MI_NAME_DEXT,DATA_TMP_EXT,
1165 1166 1167
			       raid_chunks,
			       ((param->testflag & T_BACKUP_DATA) ?
				MYF(MY_REDEL_MAKE_BACKUP) : MYF(0)));
unknown's avatar
unknown committed
1168
    if (param->out_flag & O_NEW_INDEX)
1169
      error|=change_to_newfile(filename,MI_NAME_IEXT,INDEX_TMP_EXT,0,
1170
			       MYF(0));
unknown's avatar
unknown committed
1171 1172 1173 1174
  }
  VOID(fflush(stdout)); VOID(fflush(stderr));
  if (param->error_printed)
  {
1175
    if (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX))
unknown's avatar
unknown committed
1176 1177 1178 1179
    {
      VOID(fprintf(stderr,
		   "MyISAM-table '%s' is not fixed because of errors\n",
		   filename));
1180
      if (param->testflag & T_REP_ANY)
unknown's avatar
unknown committed
1181
	VOID(fprintf(stderr,
unknown's avatar
unknown committed
1182
		     "Try fixing it by using the --safe-recover (-o), the --force (-f) option or by not using the --quick (-q) flag\n"));
unknown's avatar
unknown committed
1183 1184 1185 1186 1187 1188 1189 1190
    }
    else if (!(param->error_printed & 2) &&
	     !(param->testflag & T_FORCE_CREATE))
      VOID(fprintf(stderr,
      "MyISAM-table '%s' is corrupted\nFix it using switch \"-r\" or \"-o\"\n",
	      filename));
  }
  else if (param->warning_printed &&
1191
	   ! (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX |
unknown's avatar
unknown committed
1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
			  T_FORCE_CREATE)))
    VOID(fprintf(stderr, "MyISAM-table '%s' is usable but should be fixed\n",
		 filename));
  VOID(fflush(stderr));
  DBUG_RETURN(error);
} /* myisamchk */


	 /* Write info about table */

1202
static void descript(HA_CHECK *param, register MI_INFO *info, char * name)
unknown's avatar
unknown committed
1203 1204 1205
{
  uint key,keyseg_nr,field,start;
  reg3 MI_KEYDEF *keyinfo;
unknown's avatar
unknown committed
1206
  reg2 HA_KEYSEG *keyseg;
unknown's avatar
unknown committed
1207
  reg4 const char *text;
1208
  char buff[160],length[10],*pos,*end;
unknown's avatar
unknown committed
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239
  enum en_fieldtype type;
  MYISAM_SHARE *share=info->s;
  char llbuff[22],llbuff2[22];
  DBUG_ENTER("describe");

  printf("\nMyISAM file:         %s\n",name);
  fputs("Record format:       ",stdout);
  if (share->options & HA_OPTION_COMPRESS_RECORD)
    puts("Compressed");
  else if (share->options & HA_OPTION_PACK_RECORD)
    puts("Packed");
  else
    puts("Fixed length");
  printf("Character set:       %s (%d)\n",
	 get_charset_name(share->state.header.language),
	 share->state.header.language);

  if (param->testflag & T_VERBOSE)
  {
    printf("File-version:        %d\n",
	   (int) share->state.header.file_version[3]);
    if (share->state.create_time)
    {
      get_date(buff,1,share->state.create_time);
      printf("Creation time:       %s\n",buff);
    }
    if (share->state.check_time)
    {
      get_date(buff,1,share->state.check_time);
      printf("Recover time:        %s\n",buff);
    }
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259
    pos=buff;
    if (share->state.changed & STATE_CRASHED)
      strmov(buff,"crashed");
    else
    {
      if (share->state.open_count)
	pos=strmov(pos,"open,");
      if (share->state.changed & STATE_CHANGED)
	pos=strmov(pos,"changed,");
      else
	pos=strmov(pos,"checked,");
      if (!(share->state.changed & STATE_NOT_ANALYZED))
	pos=strmov(pos,"analyzed,");
      if (!(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))
	pos=strmov(pos,"optimized keys,");
      if (!(share->state.changed & STATE_NOT_SORTED_PAGES))
	pos=strmov(pos,"sorted index pages,");
      pos[-1]=0;				/* Remove extra ',' */
    }      
    printf("Status:              %s\n",buff);
unknown's avatar
unknown committed
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272
    if (share->base.auto_key)
    {
      printf("Auto increment key:  %13d  Last value:         %13s\n",
	     share->base.auto_key,
	     llstr(share->state.auto_increment,llbuff));
    }
    if (share->base.raid_type)
    {
      printf("RAID:                Type:  %u   Chunks: %u  Chunksize: %lu\n",
	     share->base.raid_type,
	     share->base.raid_chunks,
	     share->base.raid_chunksize);
    }
unknown's avatar
unknown committed
1273
    if (share->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
1274
      printf("Checksum:  %23s\n",llstr(info->state->checksum,llbuff));
1275
;
unknown's avatar
unknown committed
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311
    if (share->options & HA_OPTION_DELAY_KEY_WRITE)
      printf("Keys are only flushed at close\n");

  }
  printf("Data records:        %13s  Deleted blocks:     %13s\n",
	 llstr(info->state->records,llbuff),llstr(info->state->del,llbuff2));
  if (param->testflag & T_SILENT)
    DBUG_VOID_RETURN;				/* This is enough */

  if (param->testflag & T_VERBOSE)
  {
#ifdef USE_RELOC
    printf("Init-relocation:     %13s\n",llstr(share->base.reloc,llbuff));
#endif
    printf("Datafile parts:      %13s  Deleted data:       %13s\n",
	   llstr(share->state.split,llbuff),
	   llstr(info->state->empty,llbuff2));
    printf("Datafile pointer (bytes):%9d  Keyfile pointer (bytes):%9d\n",
	   share->rec_reflength,share->base.key_reflength);
    printf("Datafile length:     %13s  Keyfile length:     %13s\n",
	   llstr(info->state->data_file_length,llbuff),
	   llstr(info->state->key_file_length,llbuff2));

    if (info->s->base.reloc == 1L && info->s->base.records == 1L)
      puts("This is a one-record table");
    else
    {
      if (share->base.max_data_file_length != HA_OFFSET_ERROR ||
	  share->base.max_key_file_length != HA_OFFSET_ERROR)
	printf("Max datafile length: %13s  Max keyfile length: %13s\n",
	       llstr(share->base.max_data_file_length-1,llbuff),
	       llstr(share->base.max_key_file_length-1,llbuff2));
    }
  }

  printf("Recordlength:        %13d\n",(int) share->base.pack_reclength);
1312
  if (! mi_is_all_keys_active(share->state.key_map, share->base.keys))
unknown's avatar
unknown committed
1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
  {
    longlong2str(share->state.key_map,buff,2);
    printf("Using only keys '%s' of %d possibly keys\n",
	   buff, share->base.keys);
  }
  puts("\ntable description:");
  printf("Key Start Len Index   Type");
  if (param->testflag & T_VERBOSE)
    printf("                     Rec/key         Root  Blocksize");
  VOID(putchar('\n'));

  for (key=keyseg_nr=0, keyinfo= &share->keyinfo[0] ;
       key < share->base.keys;
       key++,keyinfo++)
  {
    keyseg=keyinfo->seg;
    if (keyinfo->flag & HA_NOSAME) text="unique ";
1330
    else if (keyinfo->flag & HA_FULLTEXT) text="fulltext ";
unknown's avatar
unknown committed
1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
    else text="multip.";

    pos=buff;
    if (keyseg->flag & HA_REVERSE_SORT)
      *pos++ = '-';
    pos=strmov(pos,type_names[keyseg->type]);
    *pos++ = ' ';
    *pos=0;
    if (keyinfo->flag & HA_PACK_KEY)
      pos=strmov(pos,prefix_packed_txt);
    if (keyinfo->flag & HA_BINARY_PACK_KEY)
      pos=strmov(pos,bin_packed_txt);
    if (keyseg->flag & HA_SPACE_PACK)
      pos=strmov(pos,diff_txt);
    if (keyseg->flag & HA_BLOB_PART)
      pos=strmov(pos,blob_txt);
    if (keyseg->flag & HA_NULL_PART)
      pos=strmov(pos,null_txt);
    *pos=0;

    printf("%-4d%-6ld%-3d %-8s%-21s",
	   key+1,(long) keyseg->start+1,keyseg->length,text,buff);
    if (share->state.key_root[key] != HA_OFFSET_ERROR)
      llstr(share->state.key_root[key],buff);
    else
      buff[0]=0;
    if (param->testflag & T_VERBOSE)
      printf("%11lu %12s %10d",
	     share->state.rec_per_key_part[keyseg_nr++],
	     buff,keyinfo->block_length);
    VOID(putchar('\n'));
    while ((++keyseg)->type != HA_KEYTYPE_END)
    {
      pos=buff;
      if (keyseg->flag & HA_REVERSE_SORT)
	*pos++ = '-';
      pos=strmov(pos,type_names[keyseg->type]);
      *pos++= ' ';
      if (keyseg->flag & HA_SPACE_PACK)
	pos=strmov(pos,diff_txt);
      if (keyseg->flag & HA_BLOB_PART)
	pos=strmov(pos,blob_txt);
      if (keyseg->flag & HA_NULL_PART)
	pos=strmov(pos,null_txt);
      *pos=0;
      printf("    %-6ld%-3d         %-21s",
	     (long) keyseg->start+1,keyseg->length,buff);
      if (param->testflag & T_VERBOSE)
	printf("%11lu", share->state.rec_per_key_part[keyseg_nr++]);
      VOID(putchar('\n'));
    }
    keyseg++;
  }
  if (share->state.header.uniques)
  {
    MI_UNIQUEDEF *uniqueinfo;
    puts("\nUnique  Key  Start  Len  Nullpos  Nullbit  Type");
    for (key=0,uniqueinfo= &share->uniqueinfo[0] ;
	 key < share->state.header.uniques; key++, uniqueinfo++)
    {
      my_bool new_row=0;
      char null_bit[8],null_pos[8];
      printf("%-8d%-5d",key+1,uniqueinfo->key+1);
      for (keyseg=uniqueinfo->seg ; keyseg->type != HA_KEYTYPE_END ; keyseg++)
      {
	if (new_row)
	  fputs("             ",stdout);
	null_bit[0]=null_pos[0]=0;
	if (keyseg->null_bit)
	{
	  sprintf(null_bit,"%d",keyseg->null_bit);
	  sprintf(null_pos,"%ld",(long) keyseg->null_pos+1);
	}
	printf("%-7ld%-5d%-9s%-10s%-30s\n",
	       (long) keyseg->start+1,keyseg->length,
	       null_pos,null_bit,
	       type_names[keyseg->type]);
	new_row=1;
      }
    }
  }
  if (param->verbose > 1)
  {
    char null_bit[8],null_pos[8];
    printf("\nField Start Length Nullpos Nullbit Type");
    if (share->options & HA_OPTION_COMPRESS_RECORD)
      printf("                         Huff tree  Bits");
    VOID(putchar('\n'));
    start=1;
    for (field=0 ; field < share->base.fields ; field++)
    {
      if (share->options & HA_OPTION_COMPRESS_RECORD)
	type=share->rec[field].base_type;
      else
	type=(enum en_fieldtype) share->rec[field].type;
      end=strmov(buff,field_pack[type]);
      if (share->options & HA_OPTION_COMPRESS_RECORD)
      {
	if (share->rec[field].pack_type & PACK_TYPE_SELECTED)
	  end=strmov(end,", not_always");
	if (share->rec[field].pack_type & PACK_TYPE_SPACE_FIELDS)
	  end=strmov(end,", no empty");
	if (share->rec[field].pack_type & PACK_TYPE_ZERO_FILL)
	{
	  sprintf(end,", zerofill(%d)",share->rec[field].space_length_bits);
	  end=strend(end);
	}
      }
      if (buff[0] == ',')
	strmov(buff,buff+2);
1441
      int10_to_str((long) share->rec[field].length,length,10);
unknown's avatar
unknown committed
1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466
      null_bit[0]=null_pos[0]=0;
      if (share->rec[field].null_bit)
      {
	sprintf(null_bit,"%d",share->rec[field].null_bit);
	sprintf(null_pos,"%d",share->rec[field].null_pos+1);
      }
      printf("%-6d%-6d%-7s%-8s%-8s%-35s",field+1,start,length,
	     null_pos, null_bit, buff);
      if (share->options & HA_OPTION_COMPRESS_RECORD)
      {
	if (share->rec[field].huff_tree)
	  printf("%3d    %2d",
		 (uint) (share->rec[field].huff_tree-share->decode_trees)+1,
		 share->rec[field].huff_tree->quick_table_bits);
      }
      VOID(putchar('\n'));
      start+=share->rec[field].length;
    }
  }
  DBUG_VOID_RETURN;
} /* describe */


	/* Sort records according to one key */

1467
static int mi_sort_records(HA_CHECK *param,
1468
			   register MI_INFO *info, char * name,
unknown's avatar
unknown committed
1469 1470
			   uint sort_key,
			   my_bool write_info,
unknown's avatar
unknown committed
1471
			   my_bool update_index)
unknown's avatar
unknown committed
1472 1473 1474 1475 1476 1477 1478 1479 1480
{
  int got_error;
  uint key;
  MI_KEYDEF *keyinfo;
  File new_file;
  uchar *temp_buff;
  ha_rows old_record_count;
  MYISAM_SHARE *share=info->s;
  char llbuff[22],llbuff2[22];
1481
  MI_SORT_INFO sort_info;
unknown's avatar
unknown committed
1482
  MI_SORT_PARAM sort_param;
unknown's avatar
unknown committed
1483 1484
  DBUG_ENTER("sort_records");

unknown's avatar
unknown committed
1485 1486 1487 1488
  bzero((char*)&sort_info,sizeof(sort_info));
  bzero((char*)&sort_param,sizeof(sort_param));
  sort_param.sort_info=&sort_info;
  sort_info.param=param;
unknown's avatar
unknown committed
1489 1490 1491 1492 1493
  keyinfo= &share->keyinfo[sort_key];
  got_error=1;
  temp_buff=0;
  new_file= -1;

1494
  if (! mi_is_key_active(share->state.key_map, sort_key))
unknown's avatar
unknown committed
1495
  {
1496 1497
    mi_check_print_warning(param,
			   "Can't sort table '%s' on key %d;  No such key",
unknown's avatar
unknown committed
1498 1499
		name,sort_key+1);
    param->error_printed=0;
1500
    DBUG_RETURN(0);				/* Nothing to do */
unknown's avatar
unknown committed
1501
  }
1502 1503
  if (keyinfo->flag & HA_FULLTEXT)
  {
1504 1505
    mi_check_print_warning(param,"Can't sort table '%s' on FULLTEXT key %d",
			   name,sort_key+1);
1506
    param->error_printed=0;
1507
    DBUG_RETURN(0);				/* Nothing to do */
unknown's avatar
unknown committed
1508
  }
1509 1510
  if (share->data_file_type == COMPRESSED_RECORD)
  {
1511
    mi_check_print_warning(param,"Can't sort read-only table '%s'", name);
1512
    param->error_printed=0;
1513
    DBUG_RETURN(0);				/* Nothing to do */
1514
  }
unknown's avatar
unknown committed
1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525
  if (!(param->testflag & T_SILENT))
  {
    printf("- Sorting records for MyISAM-table '%s'\n",name);
    if (write_info)
      printf("Data records: %9s   Deleted: %9s\n",
	     llstr(info->state->records,llbuff),
	     llstr(info->state->del,llbuff2));
  }
  if (share->state.key_root[sort_key] == HA_OFFSET_ERROR)
    DBUG_RETURN(0);				/* Nothing to do */

unknown's avatar
unknown committed
1526 1527
  init_key_cache(dflt_key_cache, opt_key_cache_block_size, param->use_buffers,
                 0, 0);
unknown's avatar
unknown committed
1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538
  if (init_io_cache(&info->rec_cache,-1,(uint) param->write_buffer_length,
		   WRITE_CACHE,share->pack.header_length,1,
		   MYF(MY_WME | MY_WAIT_IF_FULL)))
    goto err;
  info->opt_flag|=WRITE_CACHE_USED;

  if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
  {
    mi_check_print_error(param,"Not enough memory for key block");
    goto err;
  }
1539
  if (!(sort_param.record=(uchar*) my_malloc((uint) share->base.pack_reclength,
unknown's avatar
unknown committed
1540 1541 1542 1543 1544
					   MYF(0))))
  {
    mi_check_print_error(param,"Not enough memory for record");
    goto err;
  }
1545 1546 1547
  fn_format(param->temp_filename,name,"", MI_NAME_DEXT,2+4+32);
  new_file=my_raid_create(fn_format(param->temp_filename,
				    param->temp_filename,"",
unknown's avatar
unknown committed
1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
				    DATA_TMP_EXT,2+4),
			  0,param->tmpfile_createflag,
			  share->base.raid_type,
			  share->base.raid_chunks,
			  share->base.raid_chunksize,
			  MYF(0));
  if (new_file < 0)
  {
    mi_check_print_error(param,"Can't create new tempfile: '%s'",
			 param->temp_filename);
    goto err;
  }
1560 1561 1562 1563
  if (share->pack.header_length)
    if (filecopy(param,new_file,info->dfile,0L,share->pack.header_length,
		 "datafile-header"))
      goto err;
unknown's avatar
unknown committed
1564 1565 1566 1567 1568 1569
  info->rec_cache.file=new_file;		/* Use this file for cacheing*/

  lock_memory(param);
  for (key=0 ; key < share->base.keys ; key++)
    share->keyinfo[key].flag|= HA_SORT_ALLOWS_SAME;

1570
  if (my_pread(share->kfile,(uchar*) temp_buff,
unknown's avatar
unknown committed
1571 1572 1573 1574 1575 1576 1577 1578 1579 1580
	       (uint) keyinfo->block_length,
	       share->state.key_root[sort_key],
	       MYF(MY_NABP+MY_WME)))
  {
    mi_check_print_error(param,"Can't read indexpage from filepos: %s",
		(ulong) share->state.key_root[sort_key]);
    goto err;
  }

  /* Setup param for sort_write_record */
unknown's avatar
unknown committed
1581 1582 1583
  sort_info.info=info;
  sort_info.new_data_file_type=share->data_file_type;
  sort_param.fix_datafile=1;
unknown's avatar
unknown committed
1584
  sort_param.master=1;
unknown's avatar
unknown committed
1585
  sort_param.filepos=share->pack.header_length;
unknown's avatar
unknown committed
1586 1587
  old_record_count=info->state->records;
  info->state->records=0;
unknown's avatar
unknown committed
1588
  if (sort_info.new_data_file_type != COMPRESSED_RECORD)
1589
    info->state->checksum=0;
unknown's avatar
unknown committed
1590

unknown's avatar
unknown committed
1591
  if (sort_record_index(&sort_param,info,keyinfo,share->state.key_root[sort_key],
unknown's avatar
unknown committed
1592
			temp_buff, sort_key,new_file,update_index) ||
unknown's avatar
unknown committed
1593
      write_data_suffix(&sort_info,1) ||
unknown's avatar
unknown committed
1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610
      flush_io_cache(&info->rec_cache))
    goto err;

  if (info->state->records != old_record_count)
  {
    mi_check_print_error(param,"found %s of %s records",
		llstr(info->state->records,llbuff),
		llstr(old_record_count,llbuff2));
    goto err;
  }

  VOID(my_close(info->dfile,MYF(MY_WME)));
  param->out_flag|=O_NEW_DATA;			/* Data in new file */
  info->dfile=new_file;				/* Use new datafile */
  info->state->del=0;
  info->state->empty=0;
  share->state.dellink= HA_OFFSET_ERROR;
unknown's avatar
unknown committed
1611
  info->state->data_file_length=sort_param.filepos;
unknown's avatar
unknown committed
1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627
  share->state.split=info->state->records;	/* Only hole records */
  share->state.version=(ulong) time((time_t*) 0);

  info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);

  if (param->testflag & T_WRITE_LOOP)
  {
    VOID(fputs("          \r",stdout)); VOID(fflush(stdout));
  }
  got_error=0;

err:
  if (got_error && new_file >= 0)
  {
    VOID(end_io_cache(&info->rec_cache));
    (void) my_close(new_file,MYF(MY_WME));
1628
    (void) my_raid_delete(param->temp_filename, share->base.raid_chunks,
unknown's avatar
unknown committed
1629 1630 1631 1632
			  MYF(MY_WME));
  }
  if (temp_buff)
  {
1633
    my_afree((uchar*) temp_buff);
unknown's avatar
unknown committed
1634
  }
unknown's avatar
unknown committed
1635
  my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
1636 1637
  info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
  VOID(end_io_cache(&info->rec_cache));
unknown's avatar
unknown committed
1638 1639
  my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
  sort_info.buff=0;
unknown's avatar
unknown committed
1640
  share->state.sortkey=sort_key;
unknown's avatar
unknown committed
1641
  DBUG_RETURN(flush_blocks(param, share->key_cache, share->kfile) |
1642
	      got_error);
unknown's avatar
unknown committed
1643 1644 1645 1646 1647
} /* sort_records */


	 /* Sort records recursive using one index */

unknown's avatar
unknown committed
1648 1649
static int sort_record_index(MI_SORT_PARAM *sort_param,MI_INFO *info,
                             MI_KEYDEF *keyinfo,
unknown's avatar
unknown committed
1650 1651 1652 1653 1654 1655
			     my_off_t page, uchar *buff, uint sort_key,
			     File new_file,my_bool update_index)
{
  uint	nod_flag,used_length,key_length;
  uchar *temp_buff,*keypos,*endpos;
  my_off_t next_page,rec_pos;
1656
  uchar lastkey[HA_MAX_KEY_BUFF];
unknown's avatar
unknown committed
1657
  char llbuff[22];
1658 1659
  MI_SORT_INFO *sort_info= sort_param->sort_info;
  HA_CHECK *param=sort_info->param;
unknown's avatar
unknown committed
1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681
  DBUG_ENTER("sort_record_index");

  nod_flag=mi_test_if_nod(buff);
  temp_buff=0;

  if (nod_flag)
  {
    if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
    {
      mi_check_print_error(param,"Not Enough memory");
      DBUG_RETURN(-1);
    }
  }
  used_length=mi_getint(buff);
  keypos=buff+2+nod_flag;
  endpos=buff+used_length;
  for ( ;; )
  {
    _sanity(__FILE__,__LINE__);
    if (nod_flag)
    {
      next_page=_mi_kpos(nod_flag,keypos);
1682
      if (my_pread(info->s->kfile,(uchar*) temp_buff,
unknown's avatar
unknown committed
1683 1684 1685 1686 1687 1688 1689
		  (uint) keyinfo->block_length, next_page,
		   MYF(MY_NABP+MY_WME)))
      {
	mi_check_print_error(param,"Can't read keys from filepos: %s",
		    llstr(next_page,llbuff));
	goto err;
      }
unknown's avatar
unknown committed
1690
      if (sort_record_index(sort_param, info,keyinfo,next_page,temp_buff,sort_key,
unknown's avatar
unknown committed
1691 1692 1693 1694 1695 1696 1697 1698 1699 1700
			    new_file, update_index))
	goto err;
    }
    _sanity(__FILE__,__LINE__);
    if (keypos >= endpos ||
	(key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,lastkey))
	== 0)
      break;
    rec_pos= _mi_dpos(info,0,lastkey+key_length);

unknown's avatar
unknown committed
1701
    if ((*info->s->read_rnd)(info,sort_param->record,rec_pos,0))
unknown's avatar
unknown committed
1702 1703 1704 1705
    {
      mi_check_print_error(param,"%d when reading datafile",my_errno);
      goto err;
    }
unknown's avatar
unknown committed
1706
    if (rec_pos != sort_param->filepos && update_index)
unknown's avatar
unknown committed
1707 1708
    {
      _mi_dpointer(info,keypos-nod_flag-info->s->rec_reflength,
unknown's avatar
unknown committed
1709 1710
		   sort_param->filepos);
      if (movepoint(info,sort_param->record,rec_pos,sort_param->filepos,
unknown's avatar
unknown committed
1711 1712 1713 1714 1715 1716
		    sort_key))
      {
	mi_check_print_error(param,"%d when updating key-pointers",my_errno);
	goto err;
      }
    }
unknown's avatar
unknown committed
1717
    if (sort_write_record(sort_param))
unknown's avatar
unknown committed
1718 1719 1720
      goto err;
  }
  /* Clear end of block to get better compression if the table is backuped */
1721 1722
  bzero((uchar*) buff+used_length,keyinfo->block_length-used_length);
  if (my_pwrite(info->s->kfile,(uchar*) buff,(uint) keyinfo->block_length,
unknown's avatar
unknown committed
1723 1724 1725 1726 1727 1728
		page,param->myf_rw))
  {
    mi_check_print_error(param,"%d when updating keyblock",my_errno);
    goto err;
  }
  if (temp_buff)
1729
    my_afree((uchar*) temp_buff);
unknown's avatar
unknown committed
1730 1731 1732
  DBUG_RETURN(0);
err:
  if (temp_buff)
1733
    my_afree((uchar*) temp_buff);
unknown's avatar
unknown committed
1734 1735 1736
  DBUG_RETURN(1);
} /* sort_record_index */

1737 1738 1739 1740 1741 1742 1743 1744


/*
  Check if myisamchk was killed by a signal
  This is overloaded by other programs that want to be able to abort
  sorting
*/

unknown's avatar
unknown committed
1745
static int not_killed= 0;
1746

1747
volatile int *killed_ptr(HA_CHECK *param __attribute__((unused)))
unknown's avatar
unknown committed
1748
{
unknown's avatar
unknown committed
1749
  return &not_killed;			/* always NULL */
unknown's avatar
unknown committed
1750
}
unknown's avatar
unknown committed
1751 1752 1753 1754

	/* print warnings and errors */
	/* VARARGS */

1755
void mi_check_print_info(HA_CHECK *param __attribute__((unused)),
unknown's avatar
unknown committed
1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767
			 const char *fmt,...)
{
  va_list args;

  va_start(args,fmt);
  VOID(vfprintf(stdout, fmt, args));
  VOID(fputc('\n',stdout));
  va_end(args);
}

/* VARARGS */

1768
void mi_check_print_warning(HA_CHECK *param, const char *fmt,...)
unknown's avatar
unknown committed
1769 1770 1771 1772 1773 1774 1775 1776
{
  va_list args;
  DBUG_ENTER("mi_check_print_warning");

  fflush(stdout);
  if (!param->warning_printed && !param->error_printed)
  {
    if (param->testflag & T_SILENT)
1777
      fprintf(stderr,"%s: MyISAM file %s\n",my_progname_short,
unknown's avatar
unknown committed
1778
	      param->isam_file_name);
1779
    param->out_flag|= O_DATA_LOST;
unknown's avatar
unknown committed
1780 1781 1782
  }
  param->warning_printed=1;
  va_start(args,fmt);
1783
  fprintf(stderr,"%s: warning: ",my_progname_short);
unknown's avatar
unknown committed
1784 1785 1786 1787 1788 1789 1790 1791 1792
  VOID(vfprintf(stderr, fmt, args));
  VOID(fputc('\n',stderr));
  fflush(stderr);
  va_end(args);
  DBUG_VOID_RETURN;
}

/* VARARGS */

1793
void mi_check_print_error(HA_CHECK *param, const char *fmt,...)
unknown's avatar
unknown committed
1794 1795 1796 1797 1798 1799 1800 1801 1802
{
  va_list args;
  DBUG_ENTER("mi_check_print_error");
  DBUG_PRINT("enter",("format: %s",fmt));

  fflush(stdout);
  if (!param->warning_printed && !param->error_printed)
  {
    if (param->testflag & T_SILENT)
1803
      fprintf(stderr,"%s: MyISAM file %s\n",my_progname_short,param->isam_file_name);
1804
    param->out_flag|= O_DATA_LOST;
unknown's avatar
unknown committed
1805 1806 1807
  }
  param->error_printed|=1;
  va_start(args,fmt);
1808
  fprintf(stderr,"%s: error: ",my_progname_short);
unknown's avatar
unknown committed
1809 1810 1811 1812 1813 1814
  VOID(vfprintf(stderr, fmt, args));
  VOID(fputc('\n',stderr));
  fflush(stderr);
  va_end(args);
  DBUG_VOID_RETURN;
}