myisamchk.c 56.5 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 5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
unknown's avatar
unknown committed
7

unknown's avatar
unknown committed
8 9 10 11
   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
12

unknown's avatar
unknown committed
13 14 15 16
   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 */

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

19
#include "fulltext.h"
unknown's avatar
unknown committed
20 21 22

#include <m_ctype.h>
#include <stdarg.h>
23
#include <my_getopt.h>
unknown's avatar
unknown committed
24 25 26 27 28 29 30 31 32 33 34 35 36
#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

37 38 39 40
#ifdef OS2
#define _sanity(a,b)
#endif

unknown's avatar
unknown committed
41 42 43
static uint decode_bits;
static char **default_argv;
static const char *load_default_groups[]= { "myisamchk", 0 };
44
static const char *set_charset_name, *opt_tmpdir;
unknown's avatar
unknown committed
45
static CHARSET_INFO *set_charset;
unknown's avatar
unknown committed
46
static long opt_myisam_block_size;
47
static const char *my_progname_short;
48
static int stopwords_inited= 0;
49
static MY_TMPDIR myisamchk_tmpdir;
unknown's avatar
unknown committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

static const char *type_names[]=
{ "?","char","binary", "short", "long", "float",
  "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","?","?"};


static void get_options(int *argc,char * * *argv);
static void print_version(void);
static void usage(void);
static int myisamchk(MI_CHECK *param, char *filename);
static void descript(MI_CHECK *param, register MI_INFO *info, my_string name);
75 76 77
static int mi_sort_records(MI_CHECK *param, register MI_INFO *info,
                           my_string name, uint sort_key,
			   my_bool write_info, my_bool update_index);
unknown's avatar
unknown committed
78
static int sort_record_index(MI_SORT_PARAM *sort_param, MI_INFO *info,
unknown's avatar
unknown committed
79
                             MI_KEYDEF *keyinfo,
unknown's avatar
unknown committed
80 81 82 83 84 85 86 87 88 89 90
			     my_off_t page,uchar *buff,uint sortkey,
			     File new_file, my_bool update_index);

MI_CHECK check_param;

	/* Main program */

int main(int argc, char **argv)
{
  int error;
  MY_INIT(argv[0]);
91
  my_progname_short= my_progname+dirname_length(my_progname);
unknown's avatar
unknown committed
92

93 94 95 96
#ifdef __EMX__
  _wildcard (&argc, &argv);
#endif

unknown's avatar
unknown committed
97 98 99 100 101 102 103 104
  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)
  {
105
    int new_error=myisamchk(&check_param, *(argv++));
106
    if ((check_param.testflag & T_REP_ANY) != T_REP)
107
      check_param.testflag&= ~T_REP;
unknown's avatar
unknown committed
108 109 110 111
    VOID(fflush(stdout));
    VOID(fflush(stderr));
    if ((check_param.error_printed | check_param.warning_printed) &&
	(check_param.testflag & T_FORCE_CREATE) &&
112
	(!(check_param.testflag & (T_REP | T_SORT_RECORDS | T_SORT_INDEX))))
unknown's avatar
unknown committed
113 114
    {
      uint old_testflag=check_param.testflag;
115 116
      if (!(check_param.testflag & T_REP))
	check_param.testflag|= T_REP_BY_SORT;
unknown's avatar
unknown committed
117 118 119 120 121 122
      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));
    }
123 124
    else
      error|=new_error;
unknown's avatar
unknown committed
125 126 127 128 129 130 131 132 133 134 135
    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");
136
    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
137 138 139
	   llstr(check_param.total_deleted,buff2));
  }
  free_defaults(default_argv);
unknown's avatar
unknown committed
140
  free_tmpdir(&myisamchk_tmpdir);
141
  ft_free_stopwords();
unknown's avatar
unknown committed
142 143 144 145 146 147 148
  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 */

unknown's avatar
unknown committed
149 150
enum options {
  OPT_CHARSETS_DIR=256, OPT_SET_CHARSET,OPT_START_CHECK_POS,
151 152 153 154
  OPT_CORRECT_CHECKSUM, OPT_KEY_BUFFER_SIZE, OPT_MYISAM_BLOCK_SIZE,
  OPT_READ_BUFFER_SIZE, OPT_WRITE_BUFFER_SIZE, OPT_SORT_BUFFER_SIZE,
  OPT_SORT_KEY_BLOCKS, OPT_DECODE_BITS, OPT_FT_MIN_WORD_LEN,
  OPT_FT_MAX_WORD_LEN, OPT_FT_MAX_WORD_LEN_FOR_SORT
unknown's avatar
unknown committed
155
};
unknown's avatar
unknown committed
156

157
static struct my_option my_long_options[] =
unknown's avatar
unknown committed
158
{
159
  {"analyze", 'a',
160 161 162 163 164 165
   "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},
  {"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 170 171 172 173
   "Directory where character sets are.",
   (gptr*) &set_charset_name, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"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',
175 176
   "Check only tables that have changed since last check.",
   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 190
   "Max length of data file (when recreating data-file when it's full).",
   (gptr*) &check_param.max_data_file_length,
191 192
   (gptr*) &check_param.max_data_file_length,
   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 197 198
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"fast", 'F',
   "Check only tables that haven't been closed properly.",
   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 215
   (gptr*) &check_param.keys_in_use,
   (gptr*) &check_param.keys_in_use,
   0, GET_ULL, REQUIRED_ARG, -1, 0, 0, 0, 0, 0},
216
  {"medium-check", 'm',
217
   "Faster than extend-check, but only finds 99.99% of all errors. Should be good enough for most cases.",
218 219 220 221 222 223
   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},
224
  {"recover", 'r',
225 226
   "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
227
  {"parallel-recover", 'p',
228
   "Same as '-r' but creates all the keys in parallel.",
229
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
230
  {"safe-recover", 'o',
231 232
   "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},
233 234 235
  {"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},
236
#ifdef DEBUG
237 238 239
  {"start-check-pos", OPT_START_CHECK_POS,
   "No help available.",
   0, 0, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
240
#endif
241
  {"set-auto-increment", 'A',
242 243 244 245
   "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.",
   (gptr*) &check_param.auto_increment_value,
   (gptr*) &check_param.auto_increment_value,
   0, GET_ULL, OPT_ARG, 0, 0, 0, 0, 0, 0},
246
  {"set-character-set", OPT_SET_CHARSET,
247 248
   "Change the character set used by the index",
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
249
  {"set-variable", 'O',
250
   "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.",
251 252
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"silent", 's',
253 254
   "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},
255
  {"sort-index", 'S',
256 257
   "Sort index blocks. This speeds up 'read-next' in applications.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
258
  {"sort-records", 'R',
259
   "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!)",
260 261 262 263 264
   (gptr*) &check_param.opt_sort_key,
   (gptr*) &check_param.opt_sort_key,
   0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"tmpdir", 't',
   "Path for temporary files.",
265
   (gptr*) &opt_tmpdir,
266 267 268 269 270 271 272
   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},
273
  {"verbose", 'v',
unknown's avatar
unknown committed
274
   "Print more information. This can be used with --description and --check. Use many -v for more verbosity!",
275 276 277 278 279 280 281
   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},
282 283
  { "key_buffer_size", OPT_KEY_BUFFER_SIZE, "",
    (gptr*) &check_param.use_buffers, (gptr*) &check_param.use_buffers, 0,
284
    GET_ULONG, REQUIRED_ARG, (long) USE_BUFFER_INIT, (long) MALLOC_OVERHEAD,
285
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) IO_SIZE, 0},
286 287
  { "myisam_block_size", OPT_MYISAM_BLOCK_SIZE,  "",
    (gptr*) &opt_myisam_block_size, (gptr*) &opt_myisam_block_size, 0,
288 289
    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},
290 291
  { "read_buffer_size", OPT_READ_BUFFER_SIZE, "",
    (gptr*) &check_param.read_buffer_length,
292
    (gptr*) &check_param.read_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
293
    (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD,
294
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
295 296
  { "write_buffer_size", OPT_WRITE_BUFFER_SIZE, "",
    (gptr*) &check_param.write_buffer_length,
297
    (gptr*) &check_param.write_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
298
    (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD,
299
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
300
  { "sort_buffer_size", OPT_SORT_BUFFER_SIZE, "",
301
    (gptr*) &check_param.sort_buffer_length,
302
    (gptr*) &check_param.sort_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
303
    (long) SORT_BUFFER_INIT, (long) (MIN_SORT_BUFFER + MALLOC_OVERHEAD),
304
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
305 306
  { "sort_key_blocks", OPT_SORT_KEY_BLOCKS, "",
    (gptr*) &check_param.sort_key_blocks,
307
    (gptr*) &check_param.sort_key_blocks, 0, GET_ULONG, REQUIRED_ARG,
308 309
    BUFFERS_WHEN_SORTING, 4L, 100L, 0L, 1L, 0},
  { "decode_bits", OPT_DECODE_BITS, "", (gptr*) &decode_bits,
310
    (gptr*) &decode_bits, 0, GET_UINT, REQUIRED_ARG, 9L, 4L, 17L, 0L, 1L, 0},
311
  { "ft_min_word_len", OPT_FT_MIN_WORD_LEN, "", (gptr*) &ft_min_word_len,
312
    (gptr*) &ft_min_word_len, 0, GET_ULONG, REQUIRED_ARG, 4, 1, HA_FT_MAXLEN,
313 314
    0, 1, 0},
  { "ft_max_word_len", OPT_FT_MAX_WORD_LEN, "", (gptr*) &ft_max_word_len,
315
    (gptr*) &ft_max_word_len, 0, GET_ULONG, REQUIRED_ARG, HA_FT_MAXLEN, 10,
316
    HA_FT_MAXLEN, 0, 1, 0},
317 318
  { "ft_max_word_len_for_sort", OPT_FT_MAX_WORD_LEN_FOR_SORT, "",
    (gptr*) &ft_max_word_len_for_sort, (gptr*) &ft_max_word_len_for_sort, 0,
319
    GET_ULONG, REQUIRED_ARG, 20, 4, HA_FT_MAXLEN, 0, 1, 0},
320
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
unknown's avatar
unknown committed
321 322
};

323

unknown's avatar
unknown committed
324 325
static void print_version(void)
{
326
  printf("%s  Ver 2.6 for %s at %s\n", my_progname, SYSTEM_TYPE,
unknown's avatar
unknown committed
327 328 329
	 MACHINE_TYPE);
}

330

unknown's avatar
unknown committed
331 332 333 334 335
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");
336
  puts("Description, check and repair of MyISAM tables.");
unknown's avatar
unknown committed
337
  puts("Used without options all tables on the command will be checked for errors");
338
  printf("Usage: %s [OPTIONS] tables[.MYI]\n", my_progname_short);
unknown's avatar
unknown committed
339
  puts("\nGlobal options:\n\
340
  -#, --debug=...     Output debug log. Often this is 'd:t:o,filename'.\n\
unknown's avatar
unknown committed
341
  -?, --help          Display this help and exit.\n\
342
  -O, --set-variable var=option.\n\
343 344 345
                      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\
346
  -t, --tmpdir=path   Path for temporary files. Multiple paths can be\n\
347 348 349 350 351 352 353
                      specified, separated by "
#if defined( __WIN__) || defined(OS2)
   "semicolon (;)"
#else
   "colon (:)"
#endif
                      ", they will be used\n\
354
                      in a round-robin fashion.\n\
unknown's avatar
unknown committed
355
  -s, --silent	      Only print errors.  One can use two -s to make\n\
356
		      myisamchk very silent.\n\
unknown's avatar
unknown committed
357
  -v, --verbose       Print more information. This can be used with\n\
358
                      --description and --check. Use many -v for more verbosity.\n\
unknown's avatar
unknown committed
359 360
  -V, --version       Print version and exit.\n\
  -w, --wait          Wait if table is locked.\n");
361 362 363
#ifdef DEBUG
  puts("  --start-check-pos=# Start reading file at given offset.\n");
#endif
unknown's avatar
unknown committed
364 365

  puts("Check options (check is the default action for myisamchk):\n\
366
  -c, --check	      Check table for errors.\n\
unknown's avatar
unknown committed
367 368
  -e, --extend-check  Check the table VERY throughly.  Only use this in\n\
                      extreme cases as myisamchk should normally be able to\n\
369 370
                      find out if the table is ok even without this switch.\n\
  -F, --fast	      Check only tables that haven't been closed properly.\n\
371
  -C, --check-only-changed\n\
372
		      Check only tables that have changed since last check.\n\
373
  -f, --force         Restart with '-r' if there are any errors in the table.\n\
374 375
		      States will be updated as with '--update-state'.\n\
  -i, --information   Print statistics information about table that is checked.\n\
376
  -m, --medium-check  Faster than extend-check, but only finds 99.99% of\n\
377 378 379
		      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
380

381 382
  puts("Repair options (When using '-r' or '-o'):\n\
  -B, --backup	      Make a backup of the .MYD file as 'filename-time.BAK'.\n\
383
  --correct-checksum  Correct checksum information for table.\n\
unknown's avatar
unknown committed
384
  -D, --data-file-length=#  Max length of data file (when recreating data\n\
385
                      file when it's full).\n\
unknown's avatar
unknown committed
386 387 388 389 390 391
  -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\
392
		      get faster inserts.\n\
unknown's avatar
unknown committed
393 394
  -r, --recover       Can fix almost anything except unique keys that aren't\n\
                      unique.\n\
395
  -n, --sort-recover  Forces recovering with sorting even if the temporary\n\
396
		      file would be very big.\n\
397 398 399
  -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
400
  -o, --safe-recover  Uses old recovery method; Slower than '-r' but can\n\
401 402
		      handle a couple of cases where '-r' reports that it\n\
		      can't fix the data file.\n\
unknown's avatar
unknown committed
403
  --character-sets-dir=...\n\
404
                      Directory where character sets are.\n\
unknown's avatar
unknown committed
405
  --set-character-set=name\n\
406
 		      Change the character set used by the index.\n\
unknown's avatar
unknown committed
407 408
  -q, --quick         Faster repair by not modifying the data file.\n\
                      One can give a second '-q' to force myisamchk to\n\
409
		      modify the original datafile in case of duplicate keys.\n\
unknown's avatar
unknown committed
410 411 412 413 414 415
  -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\
416
		      by using '--description --verbose table_name'.\n\
unknown's avatar
unknown committed
417 418 419 420 421 422
  -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\
423
		      applications.\n\
unknown's avatar
unknown committed
424 425 426
  -R, --sort-records=#\n\
		      Sort records according to an index.  This makes your\n\
		      data much more localized and may speed up things\n\
427
		      (It may be VERY slow to do a sort the first time!).\n\
428 429
  -b,  --block-search=#\n\
                       Find a record, a block at given offset belongs to.");
unknown's avatar
unknown committed
430

431 432
  print_defaults("my", load_default_groups);
  my_print_variables(my_long_options);
unknown's avatar
unknown committed
433 434 435 436 437
}


	 /* Read options */

unknown's avatar
unknown committed
438 439 440 441
static my_bool
get_one_option(int optid,
	       const struct my_option *opt __attribute__((unused)),
	       char *argument)
442
{
unknown's avatar
unknown committed
443
  switch (optid) {
444
  case 'a':
445
    if (argument == disabled_my_option)
446 447 448
      check_param.testflag&= ~T_STATISTICS;
    else
      check_param.testflag|= T_STATISTICS;
449 450 451
    break;
  case 'A':
    if (argument)
452
      check_param.auto_increment_value= strtoull(argument, NULL, 0);
453
    else
454
      check_param.auto_increment_value= 0;	/* Set to max used value */
455 456 457
    check_param.testflag|= T_AUTO_INC;
    break;
  case 'b':
458
    check_param.search_after_block= strtoul(argument, NULL, 10);
459 460
    break;
  case 'B':
461
    if (argument == disabled_my_option)
462 463 464
      check_param.testflag&= ~T_BACKUP_DATA;
    else
      check_param.testflag|= T_BACKUP_DATA;
465 466
    break;
  case 'c':
467
    if (argument == disabled_my_option)
468 469 470
      check_param.testflag&= ~T_CHECK;
    else
      check_param.testflag|= T_CHECK;
471 472
    break;
  case 'C':
473
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
474
      check_param.testflag&= ~(T_CHECK | T_CHECK_ONLY_CHANGED);
475 476
    else
      check_param.testflag|= T_CHECK | T_CHECK_ONLY_CHANGED;
477 478 479 480 481
    break;
  case 'D':
    check_param.max_data_file_length=strtoll(argument, NULL, 10);
    break;
  case 's':				/* silent */
482
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
483
      check_param.testflag&= ~(T_SILENT | T_VERY_SILENT);
484 485 486 487 488 489 490
    else
    {
      if (check_param.testflag & T_SILENT)
	check_param.testflag|= T_VERY_SILENT;
      check_param.testflag|= T_SILENT;
      check_param.testflag&= ~T_WRITE_LOOP;
    }
491 492
    break;
  case 'w':
493
    if (argument == disabled_my_option)
494 495 496
      check_param.testflag&= ~T_WAIT_FOREVER;
    else
      check_param.testflag|= T_WAIT_FOREVER;
497 498
    break;
  case 'd':				/* description if isam-file */
499
    if (argument == disabled_my_option)
500 501 502
      check_param.testflag&= ~T_DESCRIPT;
    else
      check_param.testflag|= T_DESCRIPT;
503 504
    break;
  case 'e':				/* extend check */
505
    if (argument == disabled_my_option)
506 507 508
      check_param.testflag&= ~T_EXTEND;
    else
      check_param.testflag|= T_EXTEND;
509 510
    break;
  case 'i':
511
    if (argument == disabled_my_option)
512 513 514
      check_param.testflag&= ~T_INFO;
    else
      check_param.testflag|= T_INFO;
515 516
    break;
  case 'f':
517
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
518 519 520 521 522 523 524 525 526
    {
      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;
    }
527 528
    break;
  case 'F':
529
    if (argument == disabled_my_option)
530 531 532
      check_param.testflag&= ~T_FAST;
    else
      check_param.testflag|= T_FAST;
533 534 535 536 537
    break;
  case 'k':
    check_param.keys_in_use= (ulonglong) strtoll(argument, NULL, 10);
    break;
  case 'm':
538
    if (argument == disabled_my_option)
539 540 541
      check_param.testflag&= ~T_MEDIUM;
    else
      check_param.testflag|= T_MEDIUM;		/* Medium check */
542 543
    break;
  case 'r':				/* Repair table */
544 545 546
    check_param.testflag&= ~T_REP_ANY;
    if (argument != disabled_my_option)
      check_param.testflag|= T_REP_BY_SORT;
547
    break;
548 549 550 551 552
  case 'p':
    check_param.testflag&= ~T_REP_ANY;
    if (argument != disabled_my_option)
      check_param.testflag|= T_REP_PARALLEL;
    break;
553
  case 'o':
554 555 556
    check_param.testflag&= ~T_REP_ANY;
    check_param.force_sort= 0;
    if (argument != disabled_my_option)
unknown's avatar
merge  
unknown committed
557
    {
558
      check_param.testflag|= T_REP;
unknown's avatar
merge  
unknown committed
559 560
      my_disable_async_io= 1;		/* More safety */
    }
561 562
    break;
  case 'n':
563 564
    check_param.testflag&= ~T_REP_ANY;
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
565 566 567
      check_param.force_sort= 0;
    else
    {
568
      check_param.testflag|= T_REP_BY_SORT;
unknown's avatar
merge  
unknown committed
569 570
      check_param.force_sort= 1;
    }
571 572
    break;
  case 'q':
573
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
574
      check_param.testflag&= ~(T_QUICK | T_FORCE_UNIQUENESS);
575
    else
unknown's avatar
merge  
unknown committed
576 577
      check_param.testflag|=
        (check_param.testflag & T_QUICK) ? T_FORCE_UNIQUENESS : T_QUICK;
578 579
    break;
  case 'u':
580
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
581
      check_param.testflag&= ~(T_UNPACK | T_REP_BY_SORT);
582 583
    else
      check_param.testflag|= T_UNPACK | T_REP_BY_SORT;
584 585
    break;
  case 'v':				/* Verbose */
586
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
587
    {
588
      check_param.testflag&= ~T_VERBOSE;
unknown's avatar
merge  
unknown committed
589 590
      check_param.verbose=0;
    }
591
    else
unknown's avatar
merge  
unknown committed
592
    {
593
      check_param.testflag|= T_VERBOSE;
unknown's avatar
merge  
unknown committed
594 595
      check_param.verbose++;
    }
596 597
    break;
  case 'R':				/* Sort records */
598
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
599 600
      check_param.testflag&= ~T_SORT_RECORDS;
    else
601
    {
unknown's avatar
merge  
unknown committed
602 603 604 605 606 607 608 609 610
      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);
      }
611 612 613
    }
    break;
  case 'S':			      /* Sort index */
614
    if (argument == disabled_my_option)
615 616 617
      check_param.testflag&= ~T_SORT_INDEX;
    else
      check_param.testflag|= T_SORT_INDEX;
618 619
    break;
  case 'T':
620
    if (argument == disabled_my_option)
621 622 623
      check_param.testflag&= ~T_READONLY;
    else
      check_param.testflag|= T_READONLY;
624 625
    break;
  case 'U':
626
    if (argument == disabled_my_option)
627 628 629
      check_param.testflag&= ~T_UPDATE_STATE;
    else
      check_param.testflag|= T_UPDATE_STATE;
630 631
    break;
  case '#':
632
    if (argument == disabled_my_option)
unknown's avatar
merge  
unknown committed
633 634 635 636 637 638 639
    {
      DBUG_POP();
    }
    else
    {
      DBUG_PUSH(argument ? argument : "d:t:o,/tmp/myisamchk.trace");
    }
640 641 642 643 644
    break;
  case 'V':
    print_version();
    exit(0);
  case OPT_CORRECT_CHECKSUM:
645
    if (argument == disabled_my_option)
unknown's avatar
unknown committed
646 647
      check_param.testflag&= ~T_CALC_CHECKSUM;
    else
648
      check_param.testflag|= T_CALC_CHECKSUM;
649 650 651
    break;
#ifdef DEBUG					/* Only useful if debugging */
  case OPT_START_CHECK_POS:
652
    check_param.start_check_pos= strtoull(argument, NULL, 0);
653 654
    break;
#endif
655 656 657
  case 'H':
    my_print_help(my_long_options);
    exit(0);
658 659 660 661
  case '?':
    usage();
    exit(0);
  }
unknown's avatar
unknown committed
662
  return 0;
663 664 665
}


unknown's avatar
unknown committed
666 667
static void get_options(register int *argc,register char ***argv)
{
668 669
  int ho_error;

670
  load_defaults("my", load_default_groups, argc, argv);
unknown's avatar
unknown committed
671 672 673
  default_argv= *argv;
  if (isatty(fileno(stdout)))
    check_param.testflag|=T_WRITE_LOOP;
674

675 676
  if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
    exit(ho_error);
677

unknown's avatar
unknown committed
678 679
  /* If using repair, then update checksum if one uses --update-state */
  if ((check_param.testflag & T_UPDATE_STATE) &&
680
      (check_param.testflag & T_REP_ANY))
unknown's avatar
unknown committed
681 682
    check_param.testflag|= T_CALC_CHECKSUM;

unknown's avatar
unknown committed
683 684 685 686 687
  if (*argc == 0)
  {
    usage();
    exit(-1);
  }
unknown's avatar
unknown committed
688

unknown's avatar
unknown committed
689
  if ((check_param.testflag & T_UNPACK) &&
unknown's avatar
merge  
unknown committed
690
      (check_param.testflag & (T_QUICK | T_SORT_RECORDS)))
unknown's avatar
unknown committed
691 692 693
  {
    VOID(fprintf(stderr,
		 "%s: --unpack can't be used with --quick or --sort-records\n",
694
		 my_progname_short));
unknown's avatar
unknown committed
695 696 697 698
    exit(1);
  }
  if ((check_param.testflag & T_READONLY) &&
      (check_param.testflag &
699
       (T_REP_ANY | T_STATISTICS | T_AUTO_INC |
700
	T_SORT_RECORDS | T_SORT_INDEX | T_FORCE_CREATE)))
unknown's avatar
unknown committed
701 702 703
  {
    VOID(fprintf(stderr,
		 "%s: Can't use --readonly when repairing or sorting\n",
704
		 my_progname_short));
unknown's avatar
unknown committed
705 706 707
    exit(1);
  }

708 709 710 711 712
  if (init_tmpdir(&myisamchk_tmpdir, opt_tmpdir))
    exit(1);

  check_param.tmpdir=&myisamchk_tmpdir;

unknown's avatar
unknown committed
713 714 715 716
  if (set_charset_name)
    if (!(set_charset=get_charset_by_name(set_charset_name, MYF(MY_WME))))
      exit(1);

unknown's avatar
unknown committed
717
  myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size);
unknown's avatar
unknown committed
718 719 720 721 722 723 724 725 726
  return;
} /* get options */


	/* Check table */

static int myisamchk(MI_CHECK *param, my_string filename)
{
  int error,lock_type,recreate;
unknown's avatar
merge  
unknown committed
727
  int rep_quick= param->testflag & (T_QUICK | T_FORCE_UNIQUENESS);
unknown's avatar
unknown committed
728 729 730 731
  uint raid_chunks;
  MI_INFO *info;
  File datafile;
  char llbuff[22],llbuff2[22];
732
  my_bool state_updated=0;
unknown's avatar
unknown committed
733 734 735 736 737 738 739 740 741 742
  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,
743 744 745 746 747
		     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
748 749 750 751 752
  {
    /* Avoid twice printing of isam file name */
    param->error_printed=1;
    switch (my_errno) {
    case HA_ERR_CRASHED:
753 754
      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
755
    case HA_ERR_NOT_A_TABLE:
unknown's avatar
unknown committed
756 757
      mi_check_print_error(param,"'%s' is not a MyISAM-table",filename);
      break;
758 759 760 761 762 763
    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
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
    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 */
788
  share->tot_locks-= share->r_locks;
unknown's avatar
unknown committed
789 790 791 792
  share->r_locks=0;
  raid_chunks=share->base.raid_chunks;

  /*
793
    Skip the checking of the file if:
unknown's avatar
unknown committed
794 795 796
    We are using --fast and the table is closed properly
    We are using --check-only-changed-tables and the table hasn't changed
  */
797
  if (param->testflag & (T_FAST | T_CHECK_ONLY_CHANGED))
unknown's avatar
unknown committed
798
  {
799 800
    my_bool need_to_check= mi_is_crashed(info) || share->state.open_count != 0;

801
    if ((param->testflag & (T_REP_ANY | T_SORT_RECORDS)) &&
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
	((share->state.changed & (STATE_CHANGED | STATE_CRASHED |
				  STATE_CRASHED_ON_REPAIR) ||
	  !(param->testflag & T_CHECK_ONLY_CHANGED))))
      need_to_check=1;

    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;
    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
821
    {
822 823 824 825 826 827 828 829 830
      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
831 832
    }
  }
833
  if ((param->testflag & (T_REP_ANY | T_STATISTICS |
unknown's avatar
unknown committed
834 835 836 837 838 839 840 841 842 843 844
			  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 ||
       ((param->keys_in_use & ~share->state.key_map) &
	(((ulonglong) 1L << share->base.keys)-1)) ||
       test_if_almost_full(info) ||
       info->s->state.header.file_version[3] != myisam_file_magic[3] ||
unknown's avatar
unknown committed
845 846
       (set_charset && set_charset->number != share->state.header.language) ||
       myisam_block_size != MI_KEY_BLOCK_LENGTH))
unknown's avatar
unknown committed
847
  {
848
    if (set_charset)
849 850
      param->language=set_charset->number;
    if (recreate_table(param, &info,filename))
unknown's avatar
unknown committed
851 852 853 854 855 856 857
    {
      VOID(fprintf(stderr,
		   "MyISAM-table '%s' is not fixed because of errors\n",
	      filename));
      return(-1);
    }
    recreate=1;
858
    if (!(param->testflag & T_REP_ANY))
unknown's avatar
unknown committed
859
    {
860
      param->testflag|=T_REP_BY_SORT;		/* if only STATISTICS */
unknown's avatar
unknown committed
861 862
      if (!(param->testflag & T_SILENT))
	printf("- '%s' has old table-format. Recreating index\n",filename);
unknown's avatar
merge  
unknown committed
863
      rep_quick|=T_QUICK;
unknown's avatar
unknown committed
864 865
    }
    share=info->s;
866
    share->tot_locks-= share->r_locks;
unknown's avatar
unknown committed
867 868 869 870 871 872 873 874
    share->r_locks=0;
  }

  if (param->testflag & T_DESCRIPT)
  {
    param->total_files++;
    param->total_records+=info->state->records;
    param->total_deleted+=info->state->del;
875
    descript(param, info, filename);
unknown's avatar
unknown committed
876 877 878
  }
  else
  {
879
    if (!stopwords_inited++)
880
      ft_init_stopwords();
881

unknown's avatar
unknown committed
882 883 884 885 886 887 888 889 890 891 892 893 894 895
    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;
    }
    share->w_locks++;				/* Mark for writeinfo */
896
    share->tot_locks++;
unknown's avatar
unknown committed
897 898 899 900
    info->lock_type= F_EXTRA_LCK;		/* Simulate as locked */
    info->tmp_lock_type=lock_type;
    datafile=info->dfile;

901
    if (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX))
unknown's avatar
unknown committed
902
    {
903
      if (param->testflag & T_REP_ANY)
unknown's avatar
unknown committed
904 905
      {
	ulonglong tmp=share->state.key_map;
unknown's avatar
unknown committed
906 907
	share->state.key_map= (((ulonglong) 1 << share->base.keys)-1)
	  & param->keys_in_use;
unknown's avatar
unknown committed
908 909 910
	if (tmp != share->state.key_map)
	  info->update|=HA_STATE_CHANGED;
      }
911
      if (rep_quick && chk_del(param, info, param->testflag & ~T_VERBOSE))
unknown's avatar
unknown committed
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
      {
	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)
      {
927
	if ((param->testflag & (T_REP_BY_SORT | T_REP_PARALLEL)) &&
unknown's avatar
unknown committed
928
	    (share->state.key_map ||
929
	     (rep_quick && !param->keys_in_use && !recreate)) &&
930
	    mi_test_if_sort_rep(info, info->state->records,
931
				info->s->state.key_map,
932
				param->force_sort))
933
	{
934 935 936 937
          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);
938 939
	  state_updated=1;
	}
940 941
	else if (param->testflag & T_REP_ANY)
	  error=mi_repair(param, info,filename,rep_quick);
unknown's avatar
unknown committed
942 943 944
      }
      if (!error && param->testflag & T_SORT_RECORDS)
      {
945 946 947 948 949
	/*
	  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
950 951 952
	if (param->out_flag & O_NEW_DATA)
	{			/* Change temp file to org file */
	  VOID(my_close(info->dfile,MYF(MY_WME))); /* Close new file */
953
	  error|=change_to_newfile(filename,MI_NAME_DEXT,DATA_TMP_EXT,
954 955
				   raid_chunks,
				   MYF(0));
unknown's avatar
unknown committed
956
	  if (mi_open_datafile(info,info->s, -1))
unknown's avatar
unknown committed
957 958 959 960
	    error=1;
	  param->out_flag&= ~O_NEW_DATA; /* We are using new datafile */
	  param->read_cache.file=info->dfile;
	}
961
#endif
unknown's avatar
unknown committed
962 963 964 965 966
	if (! error)
	{
	  uint key;
	  /*
	    We can't update the index in mi_sort_records if we have a
967
	    prefix compressed or fulltext index
unknown's avatar
unknown committed
968 969 970
	  */
	  my_bool update_index=1;
	  for (key=0 ; key < share->base.keys; key++)
971
	    if (share->keyinfo[key].flag & (HA_BINARY_PACK_KEY|HA_FULLTEXT))
unknown's avatar
unknown committed
972 973
	      update_index=0;

974
	  error=mi_sort_records(param,info,filename,param->opt_sort_key,
975
                             /* what is the following parameter for ? */
unknown's avatar
unknown committed
976 977 978 979 980
				(my_bool) !(param->testflag & T_REP),
				update_index);
	  datafile=info->dfile;	/* This is now locked */
	  if (!error && !update_index)
	  {
981
	    if (param->verbose)
unknown's avatar
unknown committed
982
	      puts("Table had a compressed index;  We must now recreate the index");
983
	    error=mi_repair_by_sort(param,info,filename,1);
unknown's avatar
unknown committed
984 985 986 987
	  }
	}
      }
      if (!error && param->testflag & T_SORT_INDEX)
988
	error=mi_sort_index(param,info,filename);
unknown's avatar
unknown committed
989
      if (!error)
990 991
	share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
				 STATE_CRASHED_ON_REPAIR);
unknown's avatar
unknown committed
992 993 994 995 996 997 998 999 1000 1001 1002
      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));
1003
      error =chk_status(param,info);
unknown's avatar
unknown committed
1004 1005 1006 1007
      share->state.key_map &=param->keys_in_use;
      error =chk_size(param,info);
      if (!error || !(param->testflag & (T_FAST | T_FORCE_CREATE)))
	error|=chk_del(param, info,param->testflag);
1008 1009
      if ((!error || (!(param->testflag & (T_FAST | T_FORCE_CREATE)) &&
		      !param->start_check_pos)))
unknown's avatar
unknown committed
1010 1011 1012
      {
	error|=chk_key(param, info);
	if (!error && (param->testflag & (T_STATISTICS | T_AUTO_INC)))
1013 1014 1015 1016 1017
	  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
1018 1019 1020 1021 1022
      }
      if ((!rep_quick && !error) ||
	  !(param->testflag & (T_FAST | T_FORCE_CREATE)))
      {
	if (param->testflag & (T_EXTEND | T_MEDIUM))
unknown's avatar
unknown committed
1023
	  VOID(init_key_cache(param->use_buffers));
unknown's avatar
unknown committed
1024 1025
	VOID(init_io_cache(&param->read_cache,datafile,
			   (uint) param->read_buffer_length,
1026 1027 1028 1029 1030 1031
			   READ_CACHE,
			   (param->start_check_pos ?
			    param->start_check_pos :
			    share->pack.header_length),
			   1,
			   MYF(MY_WME)));
unknown's avatar
unknown committed
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
	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);
	error|=flush_blocks(param,share->kfile);
	VOID(end_io_cache(&param->read_cache));
      }
      if (!error)
      {
1042 1043
	if ((share->state.changed & STATE_CHANGED) &&
	    (param->testflag & T_UPDATE_STATE))
unknown's avatar
unknown committed
1044
	  info->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
1045 1046
	share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
				 STATE_CRASHED_ON_REPAIR);
unknown's avatar
unknown committed
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
      }
      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) ||
1057
      ((param->testflag & T_REP_ANY) && info->s->base.auto_key))
unknown's avatar
unknown committed
1058 1059 1060 1061 1062 1063 1064 1065
    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 |
1066
			       (((param->testflag & T_REP_ANY) ?
1067 1068
				 UPDATE_TIME : 0) |
				(state_updated ? UPDATE_STAT : 0) |
unknown's avatar
unknown committed
1069 1070 1071 1072 1073 1074
				((param->testflag & T_SORT_RECORDS) ?
				 UPDATE_SORT : 0)));
    VOID(lock_file(param, share->kfile,0L,F_UNLCK,"indexfile",filename));
    info->update&= ~HA_STATE_CHANGED;
  }
  share->w_locks--;
1075
  share->tot_locks--;
unknown's avatar
unknown committed
1076 1077 1078 1079 1080 1081 1082 1083 1084
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)
1085
      error|=change_to_newfile(filename,MI_NAME_DEXT,DATA_TMP_EXT,
1086 1087 1088
			       raid_chunks,
			       ((param->testflag & T_BACKUP_DATA) ?
				MYF(MY_REDEL_MAKE_BACKUP) : MYF(0)));
unknown's avatar
unknown committed
1089
    if (param->out_flag & O_NEW_INDEX)
1090
      error|=change_to_newfile(filename,MI_NAME_IEXT,INDEX_TMP_EXT,0,
1091
			       MYF(0));
unknown's avatar
unknown committed
1092 1093 1094 1095
  }
  VOID(fflush(stdout)); VOID(fflush(stderr));
  if (param->error_printed)
  {
1096
    if (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX))
unknown's avatar
unknown committed
1097 1098 1099 1100
    {
      VOID(fprintf(stderr,
		   "MyISAM-table '%s' is not fixed because of errors\n",
		   filename));
1101
      if (param->testflag & T_REP_ANY)
unknown's avatar
unknown committed
1102
	VOID(fprintf(stderr,
unknown's avatar
unknown committed
1103
		     "Try fixing it by using the --safe-recover (-o) or the --force (-f) option\n"));
unknown's avatar
unknown committed
1104 1105 1106 1107 1108 1109 1110 1111
    }
    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 &&
1112
	   ! (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX |
unknown's avatar
unknown committed
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126
			  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 */

static void descript(MI_CHECK *param, register MI_INFO *info, my_string name)
{
  uint key,keyseg_nr,field,start;
  reg3 MI_KEYDEF *keyinfo;
unknown's avatar
unknown committed
1127
  reg2 HA_KEYSEG *keyseg;
unknown's avatar
unknown committed
1128
  reg4 const char *text;
1129
  char buff[160],length[10],*pos,*end;
unknown's avatar
unknown committed
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160
  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);
    }
1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
    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
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
    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
1194
    if (share->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
1195 1196
      printf("Checksum:  %23s\n",llstr(info->s->state.checksum,llbuff));
;
unknown's avatar
unknown committed
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 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 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
    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);
  if (share->state.key_map != (((ulonglong) 1 << share->base.keys) -1))
  {
    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 ";
1251
    else if (keyinfo->flag & HA_FULLTEXT) text="fulltext ";
unknown's avatar
unknown committed
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 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 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 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
    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);
      int2str((long) share->rec[field].length,length,10);
      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 */

static int mi_sort_records(MI_CHECK *param,
			   register MI_INFO *info, my_string name,
			   uint sort_key,
			   my_bool write_info,
unknown's avatar
unknown committed
1392
			   my_bool update_index)
unknown's avatar
unknown committed
1393 1394 1395 1396 1397 1398 1399 1400 1401
{
  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];
unknown's avatar
unknown committed
1402 1403
  SORT_INFO sort_info;
  MI_SORT_PARAM sort_param;
unknown's avatar
unknown committed
1404 1405
  DBUG_ENTER("sort_records");

unknown's avatar
unknown committed
1406 1407 1408 1409
  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
1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421
  keyinfo= &share->keyinfo[sort_key];
  got_error=1;
  temp_buff=0;
  new_file= -1;

  if (!(((ulonglong) 1 << sort_key) & share->state.key_map))
  {
    mi_check_print_error(param,"Can't sort table '%s' on key %d;  No such key",
		name,sort_key+1);
    param->error_printed=0;
    DBUG_RETURN(-1);
  }
1422 1423 1424 1425 1426 1427
  if (keyinfo->flag & HA_FULLTEXT)
  {
    mi_check_print_error(param,"Can't sort table '%s' on FULLTEXT key %d",
		name,sort_key+1);
    param->error_printed=0;
    DBUG_RETURN(-1);
unknown's avatar
unknown committed
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439
  }
  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
1440
  init_key_cache(param->use_buffers);
unknown's avatar
unknown committed
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451
  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;
  }
unknown's avatar
unknown committed
1452
  if (!(sort_param.record=(byte*) my_malloc((uint) share->base.pack_reclength,
unknown's avatar
unknown committed
1453 1454 1455 1456 1457
					   MYF(0))))
  {
    mi_check_print_error(param,"Not enough memory for record");
    goto err;
  }
1458 1459 1460
  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
1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472
				    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;
  }
1473 1474 1475 1476
  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
1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493
  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;

  if (my_pread(share->kfile,(byte*) temp_buff,
	       (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
1494 1495 1496
  sort_info.info=info;
  sort_info.new_data_file_type=share->data_file_type;
  sort_param.fix_datafile=1;
unknown's avatar
unknown committed
1497
  sort_param.master=1;
unknown's avatar
unknown committed
1498
  sort_param.filepos=share->pack.header_length;
unknown's avatar
unknown committed
1499 1500
  old_record_count=info->state->records;
  info->state->records=0;
unknown's avatar
unknown committed
1501
  if (sort_info.new_data_file_type != COMPRESSED_RECORD)
unknown's avatar
unknown committed
1502 1503
    share->state.checksum=0;

unknown's avatar
unknown committed
1504
  if (sort_record_index(&sort_param,info,keyinfo,share->state.key_root[sort_key],
unknown's avatar
unknown committed
1505
			temp_buff, sort_key,new_file,update_index) ||
unknown's avatar
unknown committed
1506
      write_data_suffix(&sort_info,1) ||
unknown's avatar
unknown committed
1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523
      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
1524
  info->state->data_file_length=sort_param.filepos;
unknown's avatar
unknown committed
1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540
  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));
1541
    (void) my_raid_delete(param->temp_filename, share->base.raid_chunks,
unknown's avatar
unknown committed
1542 1543 1544 1545 1546 1547
			  MYF(MY_WME));
  }
  if (temp_buff)
  {
    my_afree((gptr) temp_buff);
  }
unknown's avatar
unknown committed
1548
  my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR));
unknown's avatar
unknown committed
1549 1550
  info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
  VOID(end_io_cache(&info->rec_cache));
unknown's avatar
unknown committed
1551 1552
  my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
  sort_info.buff=0;
unknown's avatar
unknown committed
1553 1554 1555 1556 1557 1558 1559
  share->state.sortkey=sort_key;
  DBUG_RETURN(flush_blocks(param, share->kfile) | got_error);
} /* sort_records */


	 /* Sort records recursive using one index */

unknown's avatar
unknown committed
1560 1561
static int sort_record_index(MI_SORT_PARAM *sort_param,MI_INFO *info,
                             MI_KEYDEF *keyinfo,
unknown's avatar
unknown committed
1562 1563 1564 1565 1566 1567 1568 1569
			     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;
  uchar lastkey[MI_MAX_KEY_BUFF];
  char llbuff[22];
unknown's avatar
unknown committed
1570 1571
  SORT_INFO *sort_info= sort_param->sort_info;
  MI_CHECK *param=sort_info->param;
unknown's avatar
unknown committed
1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601
  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);
      if (my_pread(info->s->kfile,(byte*) temp_buff,
		  (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
1602
      if (sort_record_index(sort_param, info,keyinfo,next_page,temp_buff,sort_key,
unknown's avatar
unknown committed
1603 1604 1605 1606 1607 1608 1609 1610 1611 1612
			    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
1613
    if ((*info->s->read_rnd)(info,sort_param->record,rec_pos,0))
unknown's avatar
unknown committed
1614 1615 1616 1617
    {
      mi_check_print_error(param,"%d when reading datafile",my_errno);
      goto err;
    }
unknown's avatar
unknown committed
1618
    if (rec_pos != sort_param->filepos && update_index)
unknown's avatar
unknown committed
1619 1620
    {
      _mi_dpointer(info,keypos-nod_flag-info->s->rec_reflength,
unknown's avatar
unknown committed
1621 1622
		   sort_param->filepos);
      if (movepoint(info,sort_param->record,rec_pos,sort_param->filepos,
unknown's avatar
unknown committed
1623 1624 1625 1626 1627 1628
		    sort_key))
      {
	mi_check_print_error(param,"%d when updating key-pointers",my_errno);
	goto err;
      }
    }
unknown's avatar
unknown committed
1629
    if (sort_write_record(sort_param))
unknown's avatar
unknown committed
1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648
      goto err;
  }
  /* Clear end of block to get better compression if the table is backuped */
  bzero((byte*) buff+used_length,keyinfo->block_length-used_length);
  if (my_pwrite(info->s->kfile,(byte*) buff,(uint) keyinfo->block_length,
		page,param->myf_rw))
  {
    mi_check_print_error(param,"%d when updating keyblock",my_errno);
    goto err;
  }
  if (temp_buff)
    my_afree((gptr) temp_buff);
  DBUG_RETURN(0);
err:
  if (temp_buff)
    my_afree((gptr) temp_buff);
  DBUG_RETURN(1);
} /* sort_record_index */

unknown's avatar
unknown committed
1649 1650
volatile bool *killed_ptr(MI_CHECK *param)
{
unknown's avatar
unknown committed
1651
  return (bool *)(& param->thd); /* always NULL */
unknown's avatar
unknown committed
1652
}
unknown's avatar
unknown committed
1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678

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

void mi_check_print_info(MI_CHECK *param __attribute__((unused)),
			 const char *fmt,...)
{
  va_list args;

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

/* VARARGS */

void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
{
  va_list args;
  DBUG_ENTER("mi_check_print_warning");

  fflush(stdout);
  if (!param->warning_printed && !param->error_printed)
  {
    if (param->testflag & T_SILENT)
1679
      fprintf(stderr,"%s: MyISAM file %s\n",my_progname_short,
unknown's avatar
unknown committed
1680
	      param->isam_file_name);
1681
    param->out_flag|= O_DATA_LOST;
unknown's avatar
unknown committed
1682 1683 1684
  }
  param->warning_printed=1;
  va_start(args,fmt);
1685
  fprintf(stderr,"%s: warning: ",my_progname_short);
unknown's avatar
unknown committed
1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704
  VOID(vfprintf(stderr, fmt, args));
  VOID(fputc('\n',stderr));
  fflush(stderr);
  va_end(args);
  DBUG_VOID_RETURN;
}

/* VARARGS */

void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
{
  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)
1705
      fprintf(stderr,"%s: MyISAM file %s\n",my_progname_short,param->isam_file_name);
1706
    param->out_flag|= O_DATA_LOST;
unknown's avatar
unknown committed
1707 1708 1709
  }
  param->error_printed|=1;
  va_start(args,fmt);
1710
  fprintf(stderr,"%s: error: ",my_progname_short);
unknown's avatar
unknown committed
1711 1712 1713 1714 1715 1716
  VOID(vfprintf(stderr, fmt, args));
  VOID(fputc('\n',stderr));
  fflush(stderr);
  va_end(args);
  DBUG_VOID_RETURN;
}