~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/myisamchk.c

  • Committer: Brian Aker
  • Date: 2008-08-16 15:41:14 UTC
  • mto: This revision was merged to the branch mainline in revision 346.
  • Revision ID: brian@tangent.org-20080816154114-eufmwf31p6ie1nd6
Cleaned up depend in Proto utils. Modified int to bool. Put TmpTable class
into play.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2000-2003 MySQL AB
 
2
 
 
3
   This program is free software; you can redistribute it and/or modify
 
4
   it under the terms of the GNU General Public License as published by
 
5
   the Free Software Foundation; version 2 of the License.
 
6
 
 
7
   This program is distributed in the hope that it will be useful,
 
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
   GNU General Public License for more details.
 
11
 
 
12
   You should have received a copy of the GNU General Public License
 
13
   along with this program; if not, write to the Free Software
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
/* Describe, check and repair of MyISAM tables */
 
17
 
 
18
#include <drizzled/global.h>
 
19
 
 
20
#include <mystrings/m_ctype.h>
 
21
#include <stdarg.h>
 
22
#include <mysys/my_getopt.h>
 
23
#include <mysys/my_bit.h>
 
24
#include <myisam.h>
 
25
#include <mystrings/m_string.h>
 
26
#ifdef HAVE_SYS_VADVICE_H
 
27
#include <sys/vadvise.h>
 
28
#endif
 
29
#ifdef HAVE_SYS_MMAN_H
 
30
#include <sys/mman.h>
 
31
#endif
 
32
SET_STACK_SIZE(9000)                    /* Minimum stack size for program */
 
33
 
 
34
#define my_raid_create(A,B,C,D,E,F,G) my_create(A,B,C,G)
 
35
#define my_raid_delete(A,B,C) my_delete(A,B)
 
36
 
 
37
#include "myisamdef.h"
 
38
 
 
39
static uint decode_bits;
 
40
static char **default_argv;
 
41
static const char *load_default_groups[]= { "myisamchk", 0 };
 
42
static const char *set_collation_name, *opt_tmpdir;
 
43
static const CHARSET_INFO *set_collation;
 
44
static long opt_myisam_block_size;
 
45
static long opt_key_cache_block_size;
 
46
static const char *my_progname_short;
 
47
static MY_TMPDIR myisamchk_tmpdir;
 
48
 
 
49
static const char *type_names[]=
 
50
{ "impossible","char","binary", "short", "long", "float",
 
51
  "double","number","unsigned short",
 
52
  "unsigned long","int64_t","uint64_t","int24",
 
53
  "uint24","int8","varchar", "varbin","?",
 
54
  "?"};
 
55
 
 
56
static const char *prefix_packed_txt="packed ",
 
57
                  *bin_packed_txt="prefix ",
 
58
                  *diff_txt="stripped ",
 
59
                  *null_txt="NULL",
 
60
                  *blob_txt="BLOB ";
 
61
 
 
62
static const char *field_pack[]=
 
63
{"","no endspace", "no prespace",
 
64
 "no zeros", "blob", "constant", "table-lockup",
 
65
 "always zero","varchar","unique-hash","?","?"};
 
66
 
 
67
static const char *myisam_stats_method_str="nulls_unequal";
 
68
 
 
69
static void get_options(int *argc,char * * *argv);
 
70
static void print_version(void);
 
71
static void usage(void);
 
72
static int myisamchk(MI_CHECK *param, char *filename);
 
73
static void descript(MI_CHECK *param, register MI_INFO *info, char * name);
 
74
static int mi_sort_records(MI_CHECK *param, register MI_INFO *info,
 
75
                           char * name, uint sort_key,
 
76
                           bool write_info, bool update_index);
 
77
static int sort_record_index(MI_SORT_PARAM *sort_param, MI_INFO *info,
 
78
                             MI_KEYDEF *keyinfo,
 
79
                             my_off_t page,uchar *buff,uint sortkey,
 
80
                             File new_file, bool update_index);
 
81
 
 
82
MI_CHECK check_param;
 
83
 
 
84
        /* Main program */
 
85
 
 
86
int main(int argc, char **argv)
 
87
{
 
88
  int error;
 
89
  MY_INIT(argv[0]);
 
90
  my_progname_short= my_progname+dirname_length(my_progname);
 
91
 
 
92
  myisamchk_init(&check_param);
 
93
  check_param.opt_lock_memory=1;                /* Lock memory if possible */
 
94
  check_param.using_global_keycache = 0;
 
95
  get_options(&argc,(char***) &argv);
 
96
  myisam_quick_table_bits=decode_bits;
 
97
  error=0;
 
98
  while (--argc >= 0)
 
99
  {
 
100
    int new_error=myisamchk(&check_param, *(argv++));
 
101
    if ((check_param.testflag & T_REP_ANY) != T_REP)
 
102
      check_param.testflag&= ~T_REP;
 
103
    VOID(fflush(stdout));
 
104
    VOID(fflush(stderr));
 
105
    if ((check_param.error_printed | check_param.warning_printed) &&
 
106
        (check_param.testflag & T_FORCE_CREATE) &&
 
107
        (!(check_param.testflag & (T_REP | T_REP_BY_SORT | T_SORT_RECORDS |
 
108
                                   T_SORT_INDEX))))
 
109
    {
 
110
      uint old_testflag=check_param.testflag;
 
111
      if (!(check_param.testflag & T_REP))
 
112
        check_param.testflag|= T_REP_BY_SORT;
 
113
      check_param.testflag&= ~T_EXTEND;                 /* Don't needed  */
 
114
      error|=myisamchk(&check_param, argv[-1]);
 
115
      check_param.testflag= old_testflag;
 
116
      VOID(fflush(stdout));
 
117
      VOID(fflush(stderr));
 
118
    }
 
119
    else
 
120
      error|=new_error;
 
121
    if (argc && (!(check_param.testflag & T_SILENT) || check_param.testflag & T_INFO))
 
122
    {
 
123
      puts("\n---------\n");
 
124
      VOID(fflush(stdout));
 
125
    }
 
126
  }
 
127
  if (check_param.total_files > 1)
 
128
  {                                     /* Only if descript */
 
129
    char buff[22],buff2[22];
 
130
    if (!(check_param.testflag & T_SILENT) || check_param.testflag & T_INFO)
 
131
      puts("\n---------\n");
 
132
    printf("\nTotal of all %d MyISAM-files:\nData records: %9s   Deleted blocks: %9s\n",check_param.total_files,llstr(check_param.total_records,buff),
 
133
           llstr(check_param.total_deleted,buff2));
 
134
  }
 
135
  free_defaults(default_argv);
 
136
  free_tmpdir(&myisamchk_tmpdir);
 
137
  my_end(check_param.testflag & T_INFO ? MY_CHECK_ERROR | MY_GIVE_INFO : MY_CHECK_ERROR);
 
138
  exit(error);
 
139
#ifndef _lint
 
140
  return 0;                             /* No compiler warning */
 
141
#endif
 
142
} /* main */
 
143
 
 
144
enum options_mc {
 
145
  OPT_CHARSETS_DIR=256, OPT_SET_COLLATION,OPT_START_CHECK_POS,
 
146
  OPT_CORRECT_CHECKSUM, OPT_KEY_BUFFER_SIZE,
 
147
  OPT_KEY_CACHE_BLOCK_SIZE, OPT_MYISAM_BLOCK_SIZE,
 
148
  OPT_READ_BUFFER_SIZE, OPT_WRITE_BUFFER_SIZE, OPT_SORT_BUFFER_SIZE,
 
149
  OPT_SORT_KEY_BLOCKS, OPT_DECODE_BITS,
 
150
  OPT_MAX_RECORD_LENGTH, OPT_AUTO_CLOSE, OPT_STATS_METHOD
 
151
};
 
152
 
 
153
static struct my_option my_long_options[] =
 
154
{
 
155
  {"analyze", 'a',
 
156
   "Analyze distribution of keys. Will make some joins in MySQL faster. You can check the calculated distribution.",
 
157
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
158
  {"block-search", 'b',
 
159
   "No help available.",
 
160
   0, 0, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
161
  {"backup", 'B',
 
162
   "Make a backup of the .MYD file as 'filename-time.BAK'.",
 
163
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
164
  {"character-sets-dir", OPT_CHARSETS_DIR,
 
165
   "Directory where character sets are.",
 
166
   (char**) &charsets_dir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
167
  {"check", 'c',
 
168
   "Check table for errors.",
 
169
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
170
  {"check-only-changed", 'C',
 
171
   "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).",
 
172
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
173
  {"correct-checksum", OPT_CORRECT_CHECKSUM,
 
174
   "Correct checksum information for table.",
 
175
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
176
  {"description", 'd',
 
177
   "Prints some information about table.",
 
178
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
179
  {"data-file-length", 'D',
 
180
   "Max length of data file (when recreating data-file when it's full).",
 
181
   (char**) &check_param.max_data_file_length,
 
182
   (char**) &check_param.max_data_file_length,
 
183
   0, GET_LL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
184
  {"extend-check", 'e',
 
185
   "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.",
 
186
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
187
  {"fast", 'F',
 
188
   "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).",
 
189
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
190
  {"force", 'f',
 
191
   "Restart with -r if there are any errors in the table. States will be updated as with --update-state.",
 
192
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
193
  {"HELP", 'H',
 
194
   "Display this help and exit.",
 
195
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
196
  {"help", '?',
 
197
   "Display this help and exit.",
 
198
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
199
  {"information", 'i',
 
200
   "Print statistics information about table that is checked.",
 
201
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
202
  {"keys-used", 'k',
 
203
   "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.",
 
204
   (char**) &check_param.keys_in_use,
 
205
   (char**) &check_param.keys_in_use,
 
206
   0, GET_ULL, REQUIRED_ARG, -1, 0, 0, 0, 0, 0},
 
207
  {"max-record-length", OPT_MAX_RECORD_LENGTH,
 
208
   "Skip rows bigger than this if myisamchk can't allocate memory to hold it",
 
209
   (char**) &check_param.max_record_length,
 
210
   (char**) &check_param.max_record_length,
 
211
   0, GET_ULL, REQUIRED_ARG, INT64_MAX, 0, INT64_MAX, 0, 0, 0},
 
212
  {"medium-check", 'm',
 
213
   "Faster than extend-check, but only finds 99.99% of all errors. Should be good enough for most cases.",
 
214
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
215
  {"quick", 'q', "Faster repair by not modifying the data file.",
 
216
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
217
  {"read-only", 'T',
 
218
   "Don't mark table as checked.",
 
219
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
220
  {"recover", 'r',
 
221
   "Can fix almost anything except unique keys that aren't unique.",
 
222
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
223
  {"parallel-recover", 'p',
 
224
   "Same as '-r' but creates all the keys in parallel.",
 
225
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
226
  {"safe-recover", 'o',
 
227
   "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.",
 
228
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
229
  {"sort-recover", 'n',
 
230
   "Force recovering with sorting even if the temporary file was very big.",
 
231
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
232
#ifdef DEBUG
 
233
  {"start-check-pos", OPT_START_CHECK_POS,
 
234
   "No help available.",
 
235
   0, 0, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
236
#endif
 
237
  {"set-auto-increment", 'A',
 
238
   "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.",
 
239
   (char**) &check_param.auto_increment_value,
 
240
   (char**) &check_param.auto_increment_value,
 
241
   0, GET_ULL, OPT_ARG, 0, 0, 0, 0, 0, 0},
 
242
  {"set-collation", OPT_SET_COLLATION,
 
243
   "Change the collation used by the index",
 
244
   (char**) &set_collation_name, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
245
  {"set-variable", 'O',
 
246
   "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.",
 
247
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
248
  {"silent", 's',
 
249
   "Only print errors. One can use two -s to make myisamchk very silent.",
 
250
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
251
  {"sort-index", 'S',
 
252
   "Sort index blocks. This speeds up 'read-next' in applications.",
 
253
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
254
  {"sort-records", 'R',
 
255
   "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!)",
 
256
   (char**) &check_param.opt_sort_key,
 
257
   (char**) &check_param.opt_sort_key,
 
258
   0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
259
  {"tmpdir", 't',
 
260
   "Path for temporary files.",
 
261
   (char**) &opt_tmpdir,
 
262
   0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
263
  {"update-state", 'U',
 
264
   "Mark tables as crashed if any errors were found.",
 
265
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
266
  {"unpack", 'u',
 
267
   "Unpack file packed with myisampack.",
 
268
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
269
  {"verbose", 'v',
 
270
   "Print more information. This can be used with --description and --check. Use many -v for more verbosity!",
 
271
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
272
  {"version", 'V',
 
273
   "Print version and exit.",
 
274
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
275
  {"wait", 'w',
 
276
   "Wait if table is locked.",
 
277
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
278
  { "key_buffer_size", OPT_KEY_BUFFER_SIZE, "",
 
279
    (char**) &check_param.use_buffers, (char**) &check_param.use_buffers, 0,
 
280
    GET_ULONG, REQUIRED_ARG, (long) USE_BUFFER_INIT, (long) MALLOC_OVERHEAD,
 
281
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) IO_SIZE, 0},
 
282
  { "key_cache_block_size", OPT_KEY_CACHE_BLOCK_SIZE,  "",
 
283
    (char**) &opt_key_cache_block_size,
 
284
    (char**) &opt_key_cache_block_size, 0,
 
285
    GET_LONG, REQUIRED_ARG, MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH,
 
286
    MI_MAX_KEY_BLOCK_LENGTH, 0, MI_MIN_KEY_BLOCK_LENGTH, 0},
 
287
  { "myisam_block_size", OPT_MYISAM_BLOCK_SIZE,  "",
 
288
    (char**) &opt_myisam_block_size, (char**) &opt_myisam_block_size, 0,
 
289
    GET_LONG, REQUIRED_ARG, MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH,
 
290
    MI_MAX_KEY_BLOCK_LENGTH, 0, MI_MIN_KEY_BLOCK_LENGTH, 0},
 
291
  { "read_buffer_size", OPT_READ_BUFFER_SIZE, "",
 
292
    (char**) &check_param.read_buffer_length,
 
293
    (char**) &check_param.read_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
 
294
    (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD,
 
295
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
 
296
  { "write_buffer_size", OPT_WRITE_BUFFER_SIZE, "",
 
297
    (char**) &check_param.write_buffer_length,
 
298
    (char**) &check_param.write_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
 
299
    (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD,
 
300
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
 
301
  { "sort_buffer_size", OPT_SORT_BUFFER_SIZE, "",
 
302
    (char**) &check_param.sort_buffer_length,
 
303
    (char**) &check_param.sort_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
 
304
    (long) SORT_BUFFER_INIT, (long) (MIN_SORT_BUFFER + MALLOC_OVERHEAD),
 
305
    (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
 
306
  { "sort_key_blocks", OPT_SORT_KEY_BLOCKS, "",
 
307
    (char**) &check_param.sort_key_blocks,
 
308
    (char**) &check_param.sort_key_blocks, 0, GET_ULONG, REQUIRED_ARG,
 
309
    BUFFERS_WHEN_SORTING, 4L, 100L, 0L, 1L, 0},
 
310
  { "decode_bits", OPT_DECODE_BITS, "", (char**) &decode_bits,
 
311
    (char**) &decode_bits, 0, GET_UINT, REQUIRED_ARG, 9L, 4L, 17L, 0L, 1L, 0},
 
312
  {"stats_method", OPT_STATS_METHOD,
 
313
   "Specifies how index statistics collection code should treat NULLs. "
 
314
   "Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), "
 
315
   "\"nulls_equal\" (emulate 4.0 behavior), and \"nulls_ignored\".",
 
316
   (char**) &myisam_stats_method_str, (char**) &myisam_stats_method_str, 0,
 
317
    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
318
  { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
 
319
};
 
320
 
 
321
 
 
322
static void print_version(void)
 
323
{
 
324
  printf("%s  Ver 2.7 for %s at %s\n", my_progname, SYSTEM_TYPE,
 
325
         MACHINE_TYPE);
 
326
}
 
327
 
 
328
 
 
329
static void usage(void)
 
330
{
 
331
  print_version();
 
332
  puts("By Monty, for your professional use");
 
333
  puts("This software comes with NO WARRANTY: see the PUBLIC for details.\n");
 
334
  puts("Description, check and repair of MyISAM tables.");
 
335
  puts("Used without options all tables on the command will be checked for errors");
 
336
  printf("Usage: %s [OPTIONS] tables[.MYI]\n", my_progname_short);
 
337
  printf("\nGlobal options:\n");
 
338
  printf("\
 
339
  -?, --help          Display this help and exit.\n\
 
340
  -O, --set-variable var=option.\n\
 
341
                      Change the value of a variable. Please note that\n\
 
342
                      this option is deprecated; you can set variables\n\
 
343
                      directly with '--variable-name=value'.\n\
 
344
  -t, --tmpdir=path   Path for temporary files. Multiple paths can be\n\
 
345
                      specified, separated by ");
 
346
#if defined( __WIN__)
 
347
   printf("semicolon (;)");
 
348
#else
 
349
   printf("colon (:)");
 
350
#endif
 
351
                      printf(", they will be used\n\
 
352
                      in a round-robin fashion.\n\
 
353
  -s, --silent        Only print errors.  One can use two -s to make\n\
 
354
                      myisamchk very silent.\n\
 
355
  -v, --verbose       Print more information. This can be used with\n\
 
356
                      --description and --check. Use many -v for more verbosity.\n\
 
357
  -V, --version       Print version and exit.\n\
 
358
  -w, --wait          Wait if table is locked.\n\n");
 
359
#ifdef DEBUG
 
360
  puts("  --start-check-pos=# Start reading file at given offset.\n");
 
361
#endif
 
362
 
 
363
  puts("Check options (check is the default action for myisamchk):\n\
 
364
  -c, --check         Check table for errors.\n\
 
365
  -e, --extend-check  Check the table VERY throughly.  Only use this in\n\
 
366
                      extreme cases as myisamchk should normally be able to\n\
 
367
                      find out if the table is ok even without this switch.\n\
 
368
  -F, --fast          Check only tables that haven't been closed properly.\n\
 
369
  -C, --check-only-changed\n\
 
370
                      Check only tables that have changed since last check.\n\
 
371
  -f, --force         Restart with '-r' if there are any errors in the table.\n\
 
372
                      States will be updated as with '--update-state'.\n\
 
373
  -i, --information   Print statistics information about table that is checked.\n\
 
374
  -m, --medium-check  Faster than extend-check, but only finds 99.99% of\n\
 
375
                      all errors.  Should be good enough for most cases.\n\
 
376
  -U  --update-state  Mark tables as crashed if you find any errors.\n\
 
377
  -T, --read-only     Don't mark table as checked.\n");
 
378
 
 
379
  puts("Repair options (When using '-r' or '-o'):\n\
 
380
  -B, --backup        Make a backup of the .MYD file as 'filename-time.BAK'.\n\
 
381
  --correct-checksum  Correct checksum information for table.\n\
 
382
  -D, --data-file-length=#  Max length of data file (when recreating data\n\
 
383
                      file when it's full).\n\
 
384
  -e, --extend-check  Try to recover every possible row from the data file\n\
 
385
                      Normally this will also find a lot of garbage rows;\n\
 
386
                      Don't use this option if you are not totally desperate.\n\
 
387
  -f, --force         Overwrite old temporary files.\n\
 
388
  -k, --keys-used=#   Tell MyISAM to update only some specific keys. # is a\n\
 
389
                      bit mask of which keys to use. This can be used to\n\
 
390
                      get faster inserts.\n\
 
391
  --max-record-length=#\n\
 
392
                      Skip rows bigger than this if myisamchk can't allocate\n\
 
393
                      memory to hold it.\n\
 
394
  -r, --recover       Can fix almost anything except unique keys that aren't\n\
 
395
                      unique.\n\
 
396
  -n, --sort-recover  Forces recovering with sorting even if the temporary\n\
 
397
                      file would be very big.\n\
 
398
  -p, --parallel-recover\n\
 
399
                      Uses the same technique as '-r' and '-n', but creates\n\
 
400
                      all the keys in parallel, in different threads.\n\
 
401
  -o, --safe-recover  Uses old recovery method; Slower than '-r' but can\n\
 
402
                      handle a couple of cases where '-r' reports that it\n\
 
403
                      can't fix the data file.\n\
 
404
  --character-sets-dir=...\n\
 
405
                      Directory where character sets are.\n\
 
406
  --set-collation=name\n\
 
407
                      Change the collation used by the index.\n\
 
408
  -q, --quick         Faster repair by not modifying the data file.\n\
 
409
                      One can give a second '-q' to force myisamchk to\n\
 
410
                      modify the original datafile in case of duplicate keys.\n\
 
411
                      NOTE: Tables where the data file is currupted can't be\n\
 
412
                      fixed with this option.\n\
 
413
  -u, --unpack        Unpack file packed with myisampack.\n\
 
414
");
 
415
 
 
416
  puts("Other actions:\n\
 
417
  -a, --analyze       Analyze distribution of keys. Will make some joins in\n\
 
418
                      MySQL faster.  You can check the calculated distribution\n\
 
419
                      by using '--description --verbose table_name'.\n\
 
420
  --stats_method=name Specifies how index statistics collection code should\n\
 
421
                      treat NULLs. Possible values of name are \"nulls_unequal\"\n\
 
422
                      (default for 4.1/5.0), \"nulls_equal\" (emulate 4.0), and \n\
 
423
                      \"nulls_ignored\".\n\
 
424
  -d, --description   Prints some information about table.\n\
 
425
  -A, --set-auto-increment[=value]\n\
 
426
                      Force auto_increment to start at this or higher value\n\
 
427
                      If no value is given, then sets the next auto_increment\n\
 
428
                      value to the highest used value for the auto key + 1.\n\
 
429
  -S, --sort-index    Sort index blocks.  This speeds up 'read-next' in\n\
 
430
                      applications.\n\
 
431
  -R, --sort-records=#\n\
 
432
                      Sort records according to an index.  This makes your\n\
 
433
                      data much more localized and may speed up things\n\
 
434
                      (It may be VERY slow to do a sort the first time!).\n\
 
435
  -b,  --block-search=#\n\
 
436
                       Find a record, a block at given offset belongs to.");
 
437
 
 
438
  print_defaults("my", load_default_groups);
 
439
  my_print_variables(my_long_options);
 
440
}
 
441
 
 
442
const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal",
 
443
                                           "nulls_ignored", NullS};
 
444
TYPELIB myisam_stats_method_typelib= {
 
445
  array_elements(myisam_stats_method_names) - 1, "",
 
446
  myisam_stats_method_names, NULL};
 
447
 
 
448
         /* Read options */
 
449
 
 
450
static bool
 
451
get_one_option(int optid,
 
452
               const struct my_option *opt __attribute__((unused)),
 
453
               char *argument)
 
454
{
 
455
  switch (optid) {
 
456
  case 'a':
 
457
    if (argument == disabled_my_option)
 
458
      check_param.testflag&= ~T_STATISTICS;
 
459
    else
 
460
      check_param.testflag|= T_STATISTICS;
 
461
    break;
 
462
  case 'A':
 
463
    if (argument)
 
464
      check_param.auto_increment_value= strtoull(argument, NULL, 0);
 
465
    else
 
466
      check_param.auto_increment_value= 0;      /* Set to max used value */
 
467
    check_param.testflag|= T_AUTO_INC;
 
468
    break;
 
469
  case 'b':
 
470
    check_param.search_after_block= strtoul(argument, NULL, 10);
 
471
    break;
 
472
  case 'B':
 
473
    if (argument == disabled_my_option)
 
474
      check_param.testflag&= ~T_BACKUP_DATA;
 
475
    else
 
476
      check_param.testflag|= T_BACKUP_DATA;
 
477
    break;
 
478
  case 'c':
 
479
    if (argument == disabled_my_option)
 
480
      check_param.testflag&= ~T_CHECK;
 
481
    else
 
482
      check_param.testflag|= T_CHECK;
 
483
    break;
 
484
  case 'C':
 
485
    if (argument == disabled_my_option)
 
486
      check_param.testflag&= ~(T_CHECK | T_CHECK_ONLY_CHANGED);
 
487
    else
 
488
      check_param.testflag|= T_CHECK | T_CHECK_ONLY_CHANGED;
 
489
    break;
 
490
  case 'D':
 
491
    check_param.max_data_file_length=strtoll(argument, NULL, 10);
 
492
    break;
 
493
  case 's':                             /* silent */
 
494
    if (argument == disabled_my_option)
 
495
      check_param.testflag&= ~(T_SILENT | T_VERY_SILENT);
 
496
    else
 
497
    {
 
498
      if (check_param.testflag & T_SILENT)
 
499
        check_param.testflag|= T_VERY_SILENT;
 
500
      check_param.testflag|= T_SILENT;
 
501
      check_param.testflag&= ~T_WRITE_LOOP;
 
502
    }
 
503
    break;
 
504
  case 'w':
 
505
    if (argument == disabled_my_option)
 
506
      check_param.testflag&= ~T_WAIT_FOREVER;
 
507
    else
 
508
      check_param.testflag|= T_WAIT_FOREVER;
 
509
    break;
 
510
  case 'd':                             /* description if isam-file */
 
511
    if (argument == disabled_my_option)
 
512
      check_param.testflag&= ~T_DESCRIPT;
 
513
    else
 
514
      check_param.testflag|= T_DESCRIPT;
 
515
    break;
 
516
  case 'e':                             /* extend check */
 
517
    if (argument == disabled_my_option)
 
518
      check_param.testflag&= ~T_EXTEND;
 
519
    else
 
520
      check_param.testflag|= T_EXTEND;
 
521
    break;
 
522
  case 'i':
 
523
    if (argument == disabled_my_option)
 
524
      check_param.testflag&= ~T_INFO;
 
525
    else
 
526
      check_param.testflag|= T_INFO;
 
527
    break;
 
528
  case 'f':
 
529
    if (argument == disabled_my_option)
 
530
    {
 
531
      check_param.tmpfile_createflag= O_RDWR | O_TRUNC | O_EXCL;
 
532
      check_param.testflag&= ~(T_FORCE_CREATE | T_UPDATE_STATE);
 
533
    }
 
534
    else
 
535
    {
 
536
      check_param.tmpfile_createflag= O_RDWR | O_TRUNC;
 
537
      check_param.testflag|= T_FORCE_CREATE | T_UPDATE_STATE;
 
538
    }
 
539
    break;
 
540
  case 'F':
 
541
    if (argument == disabled_my_option)
 
542
      check_param.testflag&= ~T_FAST;
 
543
    else
 
544
      check_param.testflag|= T_FAST;
 
545
    break;
 
546
  case 'k':
 
547
    check_param.keys_in_use= (uint64_t) strtoll(argument, NULL, 10);
 
548
    break;
 
549
  case 'm':
 
550
    if (argument == disabled_my_option)
 
551
      check_param.testflag&= ~T_MEDIUM;
 
552
    else
 
553
      check_param.testflag|= T_MEDIUM;          /* Medium check */
 
554
    break;
 
555
  case 'r':                             /* Repair table */
 
556
    check_param.testflag&= ~T_REP_ANY;
 
557
    if (argument != disabled_my_option)
 
558
      check_param.testflag|= T_REP_BY_SORT;
 
559
    break;
 
560
  case 'p':
 
561
    check_param.testflag&= ~T_REP_ANY;
 
562
    if (argument != disabled_my_option)
 
563
      check_param.testflag|= T_REP_PARALLEL;
 
564
    break;
 
565
  case 'o':
 
566
    check_param.testflag&= ~T_REP_ANY;
 
567
    check_param.force_sort= 0;
 
568
    if (argument != disabled_my_option)
 
569
    {
 
570
      check_param.testflag|= T_REP;
 
571
      my_disable_async_io= 1;           /* More safety */
 
572
    }
 
573
    break;
 
574
  case 'n':
 
575
    check_param.testflag&= ~T_REP_ANY;
 
576
    if (argument == disabled_my_option)
 
577
      check_param.force_sort= 0;
 
578
    else
 
579
    {
 
580
      check_param.testflag|= T_REP_BY_SORT;
 
581
      check_param.force_sort= 1;
 
582
    }
 
583
    break;
 
584
  case 'q':
 
585
    if (argument == disabled_my_option)
 
586
      check_param.testflag&= ~(T_QUICK | T_FORCE_UNIQUENESS);
 
587
    else
 
588
      check_param.testflag|=
 
589
        (check_param.testflag & T_QUICK) ? T_FORCE_UNIQUENESS : T_QUICK;
 
590
    break;
 
591
  case 'u':
 
592
    if (argument == disabled_my_option)
 
593
      check_param.testflag&= ~(T_UNPACK | T_REP_BY_SORT);
 
594
    else
 
595
      check_param.testflag|= T_UNPACK | T_REP_BY_SORT;
 
596
    break;
 
597
  case 'v':                             /* Verbose */
 
598
    if (argument == disabled_my_option)
 
599
    {
 
600
      check_param.testflag&= ~T_VERBOSE;
 
601
      check_param.verbose=0;
 
602
    }
 
603
    else
 
604
    {
 
605
      check_param.testflag|= T_VERBOSE;
 
606
      check_param.verbose++;
 
607
    }
 
608
    break;
 
609
  case 'R':                             /* Sort records */
 
610
    if (argument == disabled_my_option)
 
611
      check_param.testflag&= ~T_SORT_RECORDS;
 
612
    else
 
613
    {
 
614
      check_param.testflag|= T_SORT_RECORDS;
 
615
      check_param.opt_sort_key= (uint) atoi(argument) - 1;
 
616
      if (check_param.opt_sort_key >= MI_MAX_KEY)
 
617
      {
 
618
        fprintf(stderr,
 
619
                "The value of the sort key is bigger than max key: %d.\n",
 
620
                MI_MAX_KEY);
 
621
        exit(1);
 
622
      }
 
623
    }
 
624
    break;
 
625
  case 'S':                           /* Sort index */
 
626
    if (argument == disabled_my_option)
 
627
      check_param.testflag&= ~T_SORT_INDEX;
 
628
    else
 
629
      check_param.testflag|= T_SORT_INDEX;
 
630
    break;
 
631
  case 'T':
 
632
    if (argument == disabled_my_option)
 
633
      check_param.testflag&= ~T_READONLY;
 
634
    else
 
635
      check_param.testflag|= T_READONLY;
 
636
    break;
 
637
  case 'U':
 
638
    if (argument == disabled_my_option)
 
639
      check_param.testflag&= ~T_UPDATE_STATE;
 
640
    else
 
641
      check_param.testflag|= T_UPDATE_STATE;
 
642
    break;
 
643
  case 'V':
 
644
    print_version();
 
645
    exit(0);
 
646
  case OPT_CORRECT_CHECKSUM:
 
647
    if (argument == disabled_my_option)
 
648
      check_param.testflag&= ~T_CALC_CHECKSUM;
 
649
    else
 
650
      check_param.testflag|= T_CALC_CHECKSUM;
 
651
    break;
 
652
  case OPT_STATS_METHOD:
 
653
  {
 
654
    int method;
 
655
    enum_mi_stats_method method_conv;
 
656
    myisam_stats_method_str= argument;
 
657
    if ((method=find_type(argument, &myisam_stats_method_typelib, 2)) <= 0)
 
658
    {
 
659
      fprintf(stderr, "Invalid value of stats_method: %s.\n", argument);
 
660
      exit(1);
 
661
    }
 
662
    switch (method-1) {
 
663
    case 0: 
 
664
      method_conv= MI_STATS_METHOD_NULLS_EQUAL;
 
665
      break;
 
666
    case 1:
 
667
      method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL;
 
668
      break;
 
669
    case 2:
 
670
      method_conv= MI_STATS_METHOD_IGNORE_NULLS;
 
671
      break;
 
672
    default: assert(0);                         /* Impossible */
 
673
    }
 
674
    check_param.stats_method= method_conv;
 
675
    break;
 
676
  }
 
677
#ifdef DEBUG                                    /* Only useful if debugging */
 
678
  case OPT_START_CHECK_POS:
 
679
    check_param.start_check_pos= strtoull(argument, NULL, 0);
 
680
    break;
 
681
#endif
 
682
  case 'H':
 
683
    my_print_help(my_long_options);
 
684
    exit(0);
 
685
  case '?':
 
686
    usage();
 
687
    exit(0);
 
688
  }
 
689
  return 0;
 
690
}
 
691
 
 
692
 
 
693
static void get_options(register int *argc,register char ***argv)
 
694
{
 
695
  int ho_error;
 
696
 
 
697
  load_defaults("my", load_default_groups, argc, argv);
 
698
  default_argv= *argv;
 
699
  if (isatty(fileno(stdout)))
 
700
    check_param.testflag|=T_WRITE_LOOP;
 
701
 
 
702
  if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
 
703
    exit(ho_error);
 
704
 
 
705
  /* If using repair, then update checksum if one uses --update-state */
 
706
  if ((check_param.testflag & T_UPDATE_STATE) &&
 
707
      (check_param.testflag & T_REP_ANY))
 
708
    check_param.testflag|= T_CALC_CHECKSUM;
 
709
 
 
710
  if (*argc == 0)
 
711
  {
 
712
    usage();
 
713
    exit(-1);
 
714
  }
 
715
 
 
716
  if ((check_param.testflag & T_UNPACK) &&
 
717
      (check_param.testflag & (T_QUICK | T_SORT_RECORDS)))
 
718
  {
 
719
    VOID(fprintf(stderr,
 
720
                 "%s: --unpack can't be used with --quick or --sort-records\n",
 
721
                 my_progname_short));
 
722
    exit(1);
 
723
  }
 
724
  if ((check_param.testflag & T_READONLY) &&
 
725
      (check_param.testflag &
 
726
       (T_REP_ANY | T_STATISTICS | T_AUTO_INC |
 
727
        T_SORT_RECORDS | T_SORT_INDEX | T_FORCE_CREATE)))
 
728
  {
 
729
    VOID(fprintf(stderr,
 
730
                 "%s: Can't use --readonly when repairing or sorting\n",
 
731
                 my_progname_short));
 
732
    exit(1);
 
733
  }
 
734
 
 
735
  if (init_tmpdir(&myisamchk_tmpdir, opt_tmpdir))
 
736
    exit(1);
 
737
 
 
738
  check_param.tmpdir=&myisamchk_tmpdir;
 
739
  check_param.key_cache_block_size= opt_key_cache_block_size;
 
740
 
 
741
  if (set_collation_name)
 
742
    if (!(set_collation= get_charset_by_name(set_collation_name,
 
743
                                             MYF(MY_WME))))
 
744
      exit(1);
 
745
 
 
746
  myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size);
 
747
  return;
 
748
} /* get options */
 
749
 
 
750
 
 
751
        /* Check table */
 
752
 
 
753
static int myisamchk(MI_CHECK *param, char * filename)
 
754
{
 
755
  int error,lock_type,recreate;
 
756
  int rep_quick= param->testflag & (T_QUICK | T_FORCE_UNIQUENESS);
 
757
  uint raid_chunks;
 
758
  MI_INFO *info;
 
759
  File datafile;
 
760
  char llbuff[22],llbuff2[22];
 
761
  bool state_updated=0;
 
762
  MYISAM_SHARE *share;
 
763
 
 
764
  param->out_flag=error=param->warning_printed=param->error_printed=
 
765
    recreate=0;
 
766
  datafile=0;
 
767
  param->isam_file_name=filename;               /* For error messages */
 
768
  if (!(info=mi_open(filename,
 
769
                     (param->testflag & (T_DESCRIPT | T_READONLY)) ?
 
770
                     O_RDONLY : O_RDWR,
 
771
                     HA_OPEN_FOR_REPAIR |
 
772
                     ((param->testflag & T_WAIT_FOREVER) ?
 
773
                      HA_OPEN_WAIT_IF_LOCKED :
 
774
                      (param->testflag & T_DESCRIPT) ?
 
775
                      HA_OPEN_IGNORE_IF_LOCKED : HA_OPEN_ABORT_IF_LOCKED))))
 
776
  {
 
777
    /* Avoid twice printing of isam file name */
 
778
    param->error_printed=1;
 
779
    switch (my_errno) {
 
780
    case HA_ERR_CRASHED:
 
781
      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);
 
782
      break;
 
783
    case HA_ERR_NOT_A_TABLE:
 
784
      mi_check_print_error(param,"'%s' is not a MyISAM-table",filename);
 
785
      break;
 
786
    case HA_ERR_CRASHED_ON_USAGE:
 
787
      mi_check_print_error(param,"'%s' is marked as crashed",filename);
 
788
      break;
 
789
    case HA_ERR_CRASHED_ON_REPAIR:
 
790
      mi_check_print_error(param,"'%s' is marked as crashed after last repair",filename);
 
791
      break;
 
792
    case HA_ERR_OLD_FILE:
 
793
      mi_check_print_error(param,"'%s' is a old type of MyISAM-table", filename);
 
794
      break;
 
795
    case HA_ERR_END_OF_FILE:
 
796
      mi_check_print_error(param,"Couldn't read complete header from '%s'", filename);
 
797
      break;
 
798
    case EAGAIN:
 
799
      mi_check_print_error(param,"'%s' is locked. Use -w to wait until unlocked",filename);
 
800
      break;
 
801
    case ENOENT:
 
802
      mi_check_print_error(param,"File '%s' doesn't exist",filename);
 
803
      break;
 
804
    case EACCES:
 
805
      mi_check_print_error(param,"You don't have permission to use '%s'",filename);
 
806
      break;
 
807
    default:
 
808
      mi_check_print_error(param,"%d when opening MyISAM-table '%s'",
 
809
                  my_errno,filename);
 
810
      break;
 
811
    }
 
812
    return(1);
 
813
  }
 
814
  share=info->s;
 
815
  share->options&= ~HA_OPTION_READ_ONLY_DATA; /* We are modifing it */
 
816
  share->tot_locks-= share->r_locks;
 
817
  share->r_locks=0;
 
818
  raid_chunks=share->base.raid_chunks;
 
819
 
 
820
  /*
 
821
    Skip the checking of the file if:
 
822
    We are using --fast and the table is closed properly
 
823
    We are using --check-only-changed-tables and the table hasn't changed
 
824
  */
 
825
  if (param->testflag & (T_FAST | T_CHECK_ONLY_CHANGED))
 
826
  {
 
827
    bool need_to_check= mi_is_crashed(info) || share->state.open_count != 0;
 
828
 
 
829
    if ((param->testflag & (T_REP_ANY | T_SORT_RECORDS)) &&
 
830
        ((share->state.changed & (STATE_CHANGED | STATE_CRASHED |
 
831
                                  STATE_CRASHED_ON_REPAIR) ||
 
832
          !(param->testflag & T_CHECK_ONLY_CHANGED))))
 
833
      need_to_check=1;
 
834
 
 
835
    if (info->s->base.keys && info->state->records)
 
836
    {
 
837
      if ((param->testflag & T_STATISTICS) &&
 
838
          (share->state.changed & STATE_NOT_ANALYZED))
 
839
        need_to_check=1;
 
840
      if ((param->testflag & T_SORT_INDEX) &&
 
841
          (share->state.changed & STATE_NOT_SORTED_PAGES))
 
842
        need_to_check=1;
 
843
      if ((param->testflag & T_REP_BY_SORT) &&
 
844
          (share->state.changed & STATE_NOT_OPTIMIZED_KEYS))
 
845
        need_to_check=1;
 
846
    }
 
847
    if ((param->testflag & T_CHECK_ONLY_CHANGED) &&
 
848
        (share->state.changed & (STATE_CHANGED | STATE_CRASHED |
 
849
                                 STATE_CRASHED_ON_REPAIR)))
 
850
      need_to_check=1;
 
851
    if (!need_to_check)
 
852
    {
 
853
      if (!(param->testflag & T_SILENT) || param->testflag & T_INFO)
 
854
        printf("MyISAM file: %s is already checked\n",filename);
 
855
      if (mi_close(info))
 
856
      {
 
857
        mi_check_print_error(param,"%d when closing MyISAM-table '%s'",
 
858
                             my_errno,filename);
 
859
        return(1);
 
860
      }
 
861
      return(0);
 
862
    }
 
863
  }
 
864
  if ((param->testflag & (T_REP_ANY | T_STATISTICS |
 
865
                          T_SORT_RECORDS | T_SORT_INDEX)) &&
 
866
      (((param->testflag & T_UNPACK) &&
 
867
        share->data_file_type == COMPRESSED_RECORD) ||
 
868
       mi_uint2korr(share->state.header.state_info_length) !=
 
869
       MI_STATE_INFO_SIZE ||
 
870
       mi_uint2korr(share->state.header.base_info_length) !=
 
871
       MI_BASE_INFO_SIZE ||
 
872
       mi_is_any_intersect_keys_active(param->keys_in_use, share->base.keys,
 
873
                                       ~share->state.key_map) ||
 
874
       test_if_almost_full(info) ||
 
875
       info->s->state.header.file_version[3] != myisam_file_magic[3] ||
 
876
       (set_collation &&
 
877
        set_collation->number != share->state.header.language) ||
 
878
       myisam_block_size != MI_KEY_BLOCK_LENGTH))
 
879
  {
 
880
    if (set_collation)
 
881
      param->language= set_collation->number;
 
882
    if (recreate_table(param, &info,filename))
 
883
    {
 
884
      VOID(fprintf(stderr,
 
885
                   "MyISAM-table '%s' is not fixed because of errors\n",
 
886
              filename));
 
887
      return(-1);
 
888
    }
 
889
    recreate=1;
 
890
    if (!(param->testflag & T_REP_ANY))
 
891
    {
 
892
      param->testflag|=T_REP_BY_SORT;           /* if only STATISTICS */
 
893
      if (!(param->testflag & T_SILENT))
 
894
        printf("- '%s' has old table-format. Recreating index\n",filename);
 
895
      rep_quick|=T_QUICK;
 
896
    }
 
897
    share=info->s;
 
898
    share->tot_locks-= share->r_locks;
 
899
    share->r_locks=0;
 
900
  }
 
901
 
 
902
  if (param->testflag & T_DESCRIPT)
 
903
  {
 
904
    param->total_files++;
 
905
    param->total_records+=info->state->records;
 
906
    param->total_deleted+=info->state->del;
 
907
    descript(param, info, filename);
 
908
  }
 
909
  else
 
910
  {
 
911
 
 
912
    if (!(param->testflag & T_READONLY))
 
913
      lock_type = F_WRLCK;                      /* table is changed */
 
914
    else
 
915
      lock_type= F_RDLCK;
 
916
    if (info->lock_type == F_RDLCK)
 
917
      info->lock_type=F_UNLCK;                  /* Read only table */
 
918
    if (_mi_readinfo(info,lock_type,0))
 
919
    {
 
920
      mi_check_print_error(param,"Can't lock indexfile of '%s', error: %d",
 
921
                  filename,my_errno);
 
922
      param->error_printed=0;
 
923
      goto end2;
 
924
    }
 
925
    /*
 
926
      _mi_readinfo() has locked the table.
 
927
      We mark the table as locked (without doing file locks) to be able to
 
928
      use functions that only works on locked tables (like row caching).
 
929
    */
 
930
    mi_lock_database(info, F_EXTRA_LCK);
 
931
    datafile=info->dfile;
 
932
 
 
933
    if (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX))
 
934
    {
 
935
      if (param->testflag & T_REP_ANY)
 
936
      {
 
937
        uint64_t tmp=share->state.key_map;
 
938
        mi_copy_keys_active(share->state.key_map, share->base.keys,
 
939
                            param->keys_in_use);
 
940
        if (tmp != share->state.key_map)
 
941
          info->update|=HA_STATE_CHANGED;
 
942
      }
 
943
      if (rep_quick && chk_del(param, info, param->testflag & ~T_VERBOSE))
 
944
      {
 
945
        if (param->testflag & T_FORCE_CREATE)
 
946
        {
 
947
          rep_quick=0;
 
948
          mi_check_print_info(param,"Creating new data file\n");
 
949
        }
 
950
        else
 
951
        {
 
952
          error=1;
 
953
          mi_check_print_error(param,
 
954
                               "Quick-recover aborted; Run recovery without switch 'q'");
 
955
        }
 
956
      }
 
957
      if (!error)
 
958
      {
 
959
        if ((param->testflag & (T_REP_BY_SORT | T_REP_PARALLEL)) &&
 
960
            (mi_is_any_key_active(share->state.key_map) ||
 
961
             (rep_quick && !param->keys_in_use && !recreate)) &&
 
962
            mi_test_if_sort_rep(info, info->state->records,
 
963
                                info->s->state.key_map,
 
964
                                param->force_sort))
 
965
        {
 
966
          if (param->testflag & T_REP_BY_SORT)
 
967
            error=mi_repair_by_sort(param,info,filename,rep_quick);
 
968
          else
 
969
            error=mi_repair_parallel(param,info,filename,rep_quick);
 
970
          state_updated=1;
 
971
        }
 
972
        else if (param->testflag & T_REP_ANY)
 
973
          error=mi_repair(param, info,filename,rep_quick);
 
974
      }
 
975
      if (!error && param->testflag & T_SORT_RECORDS)
 
976
      {
 
977
        uint key;
 
978
        /*
 
979
          We can't update the index in mi_sort_records if we have a
 
980
          prefix compressed or fulltext index
 
981
        */
 
982
        bool update_index=1;
 
983
        for (key=0 ; key < share->base.keys; key++)
 
984
          if (share->keyinfo[key].flag & (HA_BINARY_PACK_KEY))
 
985
            update_index=0;
 
986
 
 
987
        error=mi_sort_records(param,info,filename,param->opt_sort_key,
 
988
                           /* what is the following parameter for ? */
 
989
                              (bool) !(param->testflag & T_REP),
 
990
                              update_index);
 
991
        datafile=info->dfile;   /* This is now locked */
 
992
        if (!error && !update_index)
 
993
        {
 
994
          if (param->verbose)
 
995
            puts("Table had a compressed index;  We must now recreate the index");
 
996
          error=mi_repair_by_sort(param,info,filename,1);
 
997
        }
 
998
      }
 
999
      if (!error && param->testflag & T_SORT_INDEX)
 
1000
        error=mi_sort_index(param,info,filename);
 
1001
      if (!error)
 
1002
        share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
 
1003
                                 STATE_CRASHED_ON_REPAIR);
 
1004
      else
 
1005
        mi_mark_crashed(info);
 
1006
    }
 
1007
    else if ((param->testflag & T_CHECK) || !(param->testflag & T_AUTO_INC))
 
1008
    {
 
1009
      if (!(param->testflag & T_SILENT) || param->testflag & T_INFO)
 
1010
        printf("Checking MyISAM file: %s\n",filename);
 
1011
      if (!(param->testflag & T_SILENT))
 
1012
        printf("Data records: %7s   Deleted blocks: %7s\n",
 
1013
               llstr(info->state->records,llbuff),
 
1014
               llstr(info->state->del,llbuff2));
 
1015
      error =chk_status(param,info);
 
1016
      mi_intersect_keys_active(share->state.key_map, param->keys_in_use);
 
1017
      error =chk_size(param,info);
 
1018
      if (!error || !(param->testflag & (T_FAST | T_FORCE_CREATE)))
 
1019
        error|=chk_del(param, info,param->testflag);
 
1020
      if ((!error || (!(param->testflag & (T_FAST | T_FORCE_CREATE)) &&
 
1021
                      !param->start_check_pos)))
 
1022
      {
 
1023
        error|=chk_key(param, info);
 
1024
        if (!error && (param->testflag & (T_STATISTICS | T_AUTO_INC)))
 
1025
          error=update_state_info(param, info,
 
1026
                                  ((param->testflag & T_STATISTICS) ?
 
1027
                                   UPDATE_STAT : 0) |
 
1028
                                  ((param->testflag & T_AUTO_INC) ?
 
1029
                                   UPDATE_AUTO_INC : 0));
 
1030
      }
 
1031
      if ((!rep_quick && !error) ||
 
1032
          !(param->testflag & (T_FAST | T_FORCE_CREATE)))
 
1033
      {
 
1034
        if (param->testflag & (T_EXTEND | T_MEDIUM))
 
1035
          VOID(init_key_cache(dflt_key_cache,opt_key_cache_block_size,
 
1036
                              param->use_buffers, 0, 0));
 
1037
        VOID(init_io_cache(&param->read_cache,datafile,
 
1038
                           (uint) param->read_buffer_length,
 
1039
                           READ_CACHE,
 
1040
                           (param->start_check_pos ?
 
1041
                            param->start_check_pos :
 
1042
                            share->pack.header_length),
 
1043
                           1,
 
1044
                           MYF(MY_WME)));
 
1045
        lock_memory(param);
 
1046
        if ((info->s->options & (HA_OPTION_PACK_RECORD |
 
1047
                                 HA_OPTION_COMPRESS_RECORD)) ||
 
1048
            (param->testflag & (T_EXTEND | T_MEDIUM)))
 
1049
          error|=chk_data_link(param, info, param->testflag & T_EXTEND);
 
1050
        error|=flush_blocks(param, share->key_cache, share->kfile);
 
1051
        VOID(end_io_cache(&param->read_cache));
 
1052
      }
 
1053
      if (!error)
 
1054
      {
 
1055
        if ((share->state.changed & STATE_CHANGED) &&
 
1056
            (param->testflag & T_UPDATE_STATE))
 
1057
          info->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
 
1058
        share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
 
1059
                                 STATE_CRASHED_ON_REPAIR);
 
1060
      }
 
1061
      else if (!mi_is_crashed(info) &&
 
1062
               (param->testflag & T_UPDATE_STATE))
 
1063
      {                                         /* Mark crashed */
 
1064
        mi_mark_crashed(info);
 
1065
        info->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
 
1066
      }
 
1067
    }
 
1068
  }
 
1069
  if ((param->testflag & T_AUTO_INC) ||
 
1070
      ((param->testflag & T_REP_ANY) && info->s->base.auto_key))
 
1071
    update_auto_increment_key(param, info,
 
1072
                              (bool) !test(param->testflag & T_AUTO_INC));
 
1073
 
 
1074
  if (!(param->testflag & T_DESCRIPT))
 
1075
  {
 
1076
    if (info->update & HA_STATE_CHANGED && ! (param->testflag & T_READONLY))
 
1077
      error|=update_state_info(param, info,
 
1078
                               UPDATE_OPEN_COUNT |
 
1079
                               (((param->testflag & T_REP_ANY) ?
 
1080
                                 UPDATE_TIME : 0) |
 
1081
                                (state_updated ? UPDATE_STAT : 0) |
 
1082
                                ((param->testflag & T_SORT_RECORDS) ?
 
1083
                                 UPDATE_SORT : 0)));
 
1084
    info->update&= ~HA_STATE_CHANGED;
 
1085
  }
 
1086
  mi_lock_database(info, F_UNLCK);
 
1087
end2:
 
1088
  if (mi_close(info))
 
1089
  {
 
1090
    mi_check_print_error(param,"%d when closing MyISAM-table '%s'",my_errno,filename);
 
1091
    return(1);
 
1092
  }
 
1093
  if (error == 0)
 
1094
  {
 
1095
    if (param->out_flag & O_NEW_DATA)
 
1096
      error|=change_to_newfile(filename,MI_NAME_DEXT,DATA_TMP_EXT,
 
1097
                               raid_chunks,
 
1098
                               ((param->testflag & T_BACKUP_DATA) ?
 
1099
                                MYF(MY_REDEL_MAKE_BACKUP) : MYF(0)));
 
1100
    if (param->out_flag & O_NEW_INDEX)
 
1101
      error|=change_to_newfile(filename,MI_NAME_IEXT,INDEX_TMP_EXT,0,
 
1102
                               MYF(0));
 
1103
  }
 
1104
  VOID(fflush(stdout)); VOID(fflush(stderr));
 
1105
  if (param->error_printed)
 
1106
  {
 
1107
    if (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX))
 
1108
    {
 
1109
      VOID(fprintf(stderr,
 
1110
                   "MyISAM-table '%s' is not fixed because of errors\n",
 
1111
                   filename));
 
1112
      if (param->testflag & T_REP_ANY)
 
1113
        VOID(fprintf(stderr,
 
1114
                     "Try fixing it by using the --safe-recover (-o), the --force (-f) option or by not using the --quick (-q) flag\n"));
 
1115
    }
 
1116
    else if (!(param->error_printed & 2) &&
 
1117
             !(param->testflag & T_FORCE_CREATE))
 
1118
      VOID(fprintf(stderr,
 
1119
      "MyISAM-table '%s' is corrupted\nFix it using switch \"-r\" or \"-o\"\n",
 
1120
              filename));
 
1121
  }
 
1122
  else if (param->warning_printed &&
 
1123
           ! (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX |
 
1124
                          T_FORCE_CREATE)))
 
1125
    VOID(fprintf(stderr, "MyISAM-table '%s' is usable but should be fixed\n",
 
1126
                 filename));
 
1127
  VOID(fflush(stderr));
 
1128
  return(error);
 
1129
} /* myisamchk */
 
1130
 
 
1131
 
 
1132
         /* Write info about table */
 
1133
 
 
1134
static void descript(MI_CHECK *param, register MI_INFO *info, char * name)
 
1135
{
 
1136
  uint key,keyseg_nr,field,start;
 
1137
  register MI_KEYDEF *keyinfo;
 
1138
  register HA_KEYSEG *keyseg;
 
1139
  register const char *text;
 
1140
  char buff[160],length[10],*pos,*end;
 
1141
  enum en_fieldtype type;
 
1142
  MYISAM_SHARE *share=info->s;
 
1143
  char llbuff[22],llbuff2[22];
 
1144
 
 
1145
  printf("\nMyISAM file:         %s\n",name);
 
1146
  fputs("Record format:       ",stdout);
 
1147
  if (share->options & HA_OPTION_COMPRESS_RECORD)
 
1148
    puts("Compressed");
 
1149
  else if (share->options & HA_OPTION_PACK_RECORD)
 
1150
    puts("Packed");
 
1151
  else
 
1152
    puts("Fixed length");
 
1153
  printf("Character set:       %s (%d)\n",
 
1154
         get_charset_name(share->state.header.language),
 
1155
         share->state.header.language);
 
1156
 
 
1157
  if (param->testflag & T_VERBOSE)
 
1158
  {
 
1159
    printf("File-version:        %d\n",
 
1160
           (int) share->state.header.file_version[3]);
 
1161
    if (share->state.create_time)
 
1162
    {
 
1163
      get_date(buff,1,share->state.create_time);
 
1164
      printf("Creation time:       %s\n",buff);
 
1165
    }
 
1166
    if (share->state.check_time)
 
1167
    {
 
1168
      get_date(buff,1,share->state.check_time);
 
1169
      printf("Recover time:        %s\n",buff);
 
1170
    }
 
1171
    pos=buff;
 
1172
    if (share->state.changed & STATE_CRASHED)
 
1173
      stpcpy(buff,"crashed");
 
1174
    else
 
1175
    {
 
1176
      if (share->state.open_count)
 
1177
        pos=stpcpy(pos,"open,");
 
1178
      if (share->state.changed & STATE_CHANGED)
 
1179
        pos=stpcpy(pos,"changed,");
 
1180
      else
 
1181
        pos=stpcpy(pos,"checked,");
 
1182
      if (!(share->state.changed & STATE_NOT_ANALYZED))
 
1183
        pos=stpcpy(pos,"analyzed,");
 
1184
      if (!(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))
 
1185
        pos=stpcpy(pos,"optimized keys,");
 
1186
      if (!(share->state.changed & STATE_NOT_SORTED_PAGES))
 
1187
        pos=stpcpy(pos,"sorted index pages,");
 
1188
      pos[-1]=0;                                /* Remove extra ',' */
 
1189
    }      
 
1190
    printf("Status:              %s\n",buff);
 
1191
    if (share->base.auto_key)
 
1192
    {
 
1193
      printf("Auto increment key:  %13d  Last value:         %13s\n",
 
1194
             share->base.auto_key,
 
1195
             llstr(share->state.auto_increment,llbuff));
 
1196
    }
 
1197
    if (share->base.raid_type)
 
1198
    {
 
1199
      printf("RAID:                Type:  %u   Chunks: %u  Chunksize: %lu\n",
 
1200
             share->base.raid_type,
 
1201
             share->base.raid_chunks,
 
1202
             share->base.raid_chunksize);
 
1203
    }
 
1204
    if (share->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
 
1205
      printf("Checksum:  %23s\n",llstr(info->state->checksum,llbuff));
 
1206
;
 
1207
    if (share->options & HA_OPTION_DELAY_KEY_WRITE)
 
1208
      printf("Keys are only flushed at close\n");
 
1209
 
 
1210
  }
 
1211
  printf("Data records:        %13s  Deleted blocks:     %13s\n",
 
1212
         llstr(info->state->records,llbuff),llstr(info->state->del,llbuff2));
 
1213
  if (param->testflag & T_SILENT)
 
1214
    return;                             /* This is enough */
 
1215
 
 
1216
  if (param->testflag & T_VERBOSE)
 
1217
  {
 
1218
#ifdef USE_RELOC
 
1219
    printf("Init-relocation:     %13s\n",llstr(share->base.reloc,llbuff));
 
1220
#endif
 
1221
    printf("Datafile parts:      %13s  Deleted data:       %13s\n",
 
1222
           llstr(share->state.split,llbuff),
 
1223
           llstr(info->state->empty,llbuff2));
 
1224
    printf("Datafile pointer (bytes):%9d  Keyfile pointer (bytes):%9d\n",
 
1225
           share->rec_reflength,share->base.key_reflength);
 
1226
    printf("Datafile length:     %13s  Keyfile length:     %13s\n",
 
1227
           llstr(info->state->data_file_length,llbuff),
 
1228
           llstr(info->state->key_file_length,llbuff2));
 
1229
 
 
1230
    if (info->s->base.reloc == 1L && info->s->base.records == 1L)
 
1231
      puts("This is a one-record table");
 
1232
    else
 
1233
    {
 
1234
      if (share->base.max_data_file_length != HA_OFFSET_ERROR ||
 
1235
          share->base.max_key_file_length != HA_OFFSET_ERROR)
 
1236
        printf("Max datafile length: %13s  Max keyfile length: %13s\n",
 
1237
               llstr(share->base.max_data_file_length-1,llbuff),
 
1238
               llstr(share->base.max_key_file_length-1,llbuff2));
 
1239
    }
 
1240
  }
 
1241
 
 
1242
  printf("Recordlength:        %13d\n",(int) share->base.pack_reclength);
 
1243
  if (! mi_is_all_keys_active(share->state.key_map, share->base.keys))
 
1244
  {
 
1245
    int64_t2str(share->state.key_map,buff,2);
 
1246
    printf("Using only keys '%s' of %d possibly keys\n",
 
1247
           buff, share->base.keys);
 
1248
  }
 
1249
  puts("\ntable description:");
 
1250
  printf("Key Start Len Index   Type");
 
1251
  if (param->testflag & T_VERBOSE)
 
1252
    printf("                     Rec/key         Root  Blocksize");
 
1253
  VOID(putchar('\n'));
 
1254
 
 
1255
  for (key=keyseg_nr=0, keyinfo= &share->keyinfo[0] ;
 
1256
       key < share->base.keys;
 
1257
       key++,keyinfo++)
 
1258
  {
 
1259
    keyseg=keyinfo->seg;
 
1260
    if (keyinfo->flag & HA_NOSAME) text="unique ";
 
1261
    else text="multip.";
 
1262
 
 
1263
    pos=buff;
 
1264
    if (keyseg->flag & HA_REVERSE_SORT)
 
1265
      *pos++ = '-';
 
1266
    pos=stpcpy(pos,type_names[keyseg->type]);
 
1267
    *pos++ = ' ';
 
1268
    *pos=0;
 
1269
    if (keyinfo->flag & HA_PACK_KEY)
 
1270
      pos=stpcpy(pos,prefix_packed_txt);
 
1271
    if (keyinfo->flag & HA_BINARY_PACK_KEY)
 
1272
      pos=stpcpy(pos,bin_packed_txt);
 
1273
    if (keyseg->flag & HA_SPACE_PACK)
 
1274
      pos=stpcpy(pos,diff_txt);
 
1275
    if (keyseg->flag & HA_BLOB_PART)
 
1276
      pos=stpcpy(pos,blob_txt);
 
1277
    if (keyseg->flag & HA_NULL_PART)
 
1278
      pos=stpcpy(pos,null_txt);
 
1279
    *pos=0;
 
1280
 
 
1281
    printf("%-4d%-6ld%-3d %-8s%-21s",
 
1282
           key+1,(long) keyseg->start+1,keyseg->length,text,buff);
 
1283
    if (share->state.key_root[key] != HA_OFFSET_ERROR)
 
1284
      llstr(share->state.key_root[key],buff);
 
1285
    else
 
1286
      buff[0]=0;
 
1287
    if (param->testflag & T_VERBOSE)
 
1288
      printf("%11lu %12s %10d",
 
1289
             share->state.rec_per_key_part[keyseg_nr++],
 
1290
             buff,keyinfo->block_length);
 
1291
    VOID(putchar('\n'));
 
1292
    while ((++keyseg)->type != HA_KEYTYPE_END)
 
1293
    {
 
1294
      pos=buff;
 
1295
      if (keyseg->flag & HA_REVERSE_SORT)
 
1296
        *pos++ = '-';
 
1297
      pos=stpcpy(pos,type_names[keyseg->type]);
 
1298
      *pos++= ' ';
 
1299
      if (keyseg->flag & HA_SPACE_PACK)
 
1300
        pos=stpcpy(pos,diff_txt);
 
1301
      if (keyseg->flag & HA_BLOB_PART)
 
1302
        pos=stpcpy(pos,blob_txt);
 
1303
      if (keyseg->flag & HA_NULL_PART)
 
1304
        pos=stpcpy(pos,null_txt);
 
1305
      *pos=0;
 
1306
      printf("    %-6ld%-3d         %-21s",
 
1307
             (long) keyseg->start+1,keyseg->length,buff);
 
1308
      if (param->testflag & T_VERBOSE)
 
1309
        printf("%11lu", share->state.rec_per_key_part[keyseg_nr++]);
 
1310
      VOID(putchar('\n'));
 
1311
    }
 
1312
    keyseg++;
 
1313
  }
 
1314
  if (share->state.header.uniques)
 
1315
  {
 
1316
    MI_UNIQUEDEF *uniqueinfo;
 
1317
    puts("\nUnique  Key  Start  Len  Nullpos  Nullbit  Type");
 
1318
    for (key=0,uniqueinfo= &share->uniqueinfo[0] ;
 
1319
         key < share->state.header.uniques; key++, uniqueinfo++)
 
1320
    {
 
1321
      bool new_row=0;
 
1322
      char null_bit[8],null_pos[8];
 
1323
      printf("%-8d%-5d",key+1,uniqueinfo->key+1);
 
1324
      for (keyseg=uniqueinfo->seg ; keyseg->type != HA_KEYTYPE_END ; keyseg++)
 
1325
      {
 
1326
        if (new_row)
 
1327
          fputs("             ",stdout);
 
1328
        null_bit[0]=null_pos[0]=0;
 
1329
        if (keyseg->null_bit)
 
1330
        {
 
1331
          sprintf(null_bit,"%d",keyseg->null_bit);
 
1332
          sprintf(null_pos,"%ld",(long) keyseg->null_pos+1);
 
1333
        }
 
1334
        printf("%-7ld%-5d%-9s%-10s%-30s\n",
 
1335
               (long) keyseg->start+1,keyseg->length,
 
1336
               null_pos,null_bit,
 
1337
               type_names[keyseg->type]);
 
1338
        new_row=1;
 
1339
      }
 
1340
    }
 
1341
  }
 
1342
  if (param->verbose > 1)
 
1343
  {
 
1344
    char null_bit[8],null_pos[8];
 
1345
    printf("\nField Start Length Nullpos Nullbit Type");
 
1346
    if (share->options & HA_OPTION_COMPRESS_RECORD)
 
1347
      printf("                         Huff tree  Bits");
 
1348
    VOID(putchar('\n'));
 
1349
    start=1;
 
1350
    for (field=0 ; field < share->base.fields ; field++)
 
1351
    {
 
1352
      if (share->options & HA_OPTION_COMPRESS_RECORD)
 
1353
        type=share->rec[field].base_type;
 
1354
      else
 
1355
        type=(enum en_fieldtype) share->rec[field].type;
 
1356
      end=stpcpy(buff,field_pack[type]);
 
1357
      if (share->options & HA_OPTION_COMPRESS_RECORD)
 
1358
      {
 
1359
        if (share->rec[field].pack_type & PACK_TYPE_SELECTED)
 
1360
          end=stpcpy(end,", not_always");
 
1361
        if (share->rec[field].pack_type & PACK_TYPE_SPACE_FIELDS)
 
1362
          end=stpcpy(end,", no empty");
 
1363
        if (share->rec[field].pack_type & PACK_TYPE_ZERO_FILL)
 
1364
        {
 
1365
          sprintf(end,", zerofill(%d)",share->rec[field].space_length_bits);
 
1366
          end=strend(end);
 
1367
        }
 
1368
      }
 
1369
      if (buff[0] == ',')
 
1370
        stpcpy(buff,buff+2);
 
1371
      int10_to_str((long) share->rec[field].length,length,10);
 
1372
      null_bit[0]=null_pos[0]=0;
 
1373
      if (share->rec[field].null_bit)
 
1374
      {
 
1375
        sprintf(null_bit,"%d",share->rec[field].null_bit);
 
1376
        sprintf(null_pos,"%d",share->rec[field].null_pos+1);
 
1377
      }
 
1378
      printf("%-6d%-6d%-7s%-8s%-8s%-35s",field+1,start,length,
 
1379
             null_pos, null_bit, buff);
 
1380
      if (share->options & HA_OPTION_COMPRESS_RECORD)
 
1381
      {
 
1382
        if (share->rec[field].huff_tree)
 
1383
          printf("%3d    %2d",
 
1384
                 (uint) (share->rec[field].huff_tree-share->decode_trees)+1,
 
1385
                 share->rec[field].huff_tree->quick_table_bits);
 
1386
      }
 
1387
      VOID(putchar('\n'));
 
1388
      start+=share->rec[field].length;
 
1389
    }
 
1390
  }
 
1391
  return;
 
1392
} /* describe */
 
1393
 
 
1394
 
 
1395
        /* Sort records according to one key */
 
1396
 
 
1397
static int mi_sort_records(MI_CHECK *param,
 
1398
                           register MI_INFO *info, char * name,
 
1399
                           uint sort_key,
 
1400
                           bool write_info,
 
1401
                           bool update_index)
 
1402
{
 
1403
  int got_error;
 
1404
  uint key;
 
1405
  MI_KEYDEF *keyinfo;
 
1406
  File new_file;
 
1407
  uchar *temp_buff;
 
1408
  ha_rows old_record_count;
 
1409
  MYISAM_SHARE *share=info->s;
 
1410
  char llbuff[22],llbuff2[22];
 
1411
  SORT_INFO sort_info;
 
1412
  MI_SORT_PARAM sort_param;
 
1413
 
 
1414
  memset(&sort_info, 0, sizeof(sort_info));
 
1415
  memset(&sort_param, 0, sizeof(sort_param));
 
1416
  sort_param.sort_info=&sort_info;
 
1417
  sort_info.param=param;
 
1418
  keyinfo= &share->keyinfo[sort_key];
 
1419
  got_error=1;
 
1420
  temp_buff=0;
 
1421
  new_file= -1;
 
1422
 
 
1423
  if (! mi_is_key_active(share->state.key_map, sort_key))
 
1424
  {
 
1425
    mi_check_print_warning(param,
 
1426
                           "Can't sort table '%s' on key %d;  No such key",
 
1427
                name,sort_key+1);
 
1428
    param->error_printed=0;
 
1429
    return(0);                          /* Nothing to do */
 
1430
  }
 
1431
  if (share->data_file_type == COMPRESSED_RECORD)
 
1432
  {
 
1433
    mi_check_print_warning(param,"Can't sort read-only table '%s'", name);
 
1434
    param->error_printed=0;
 
1435
    return(0);                          /* Nothing to do */
 
1436
  }
 
1437
  if (!(param->testflag & T_SILENT))
 
1438
  {
 
1439
    printf("- Sorting records for MyISAM-table '%s'\n",name);
 
1440
    if (write_info)
 
1441
      printf("Data records: %9s   Deleted: %9s\n",
 
1442
             llstr(info->state->records,llbuff),
 
1443
             llstr(info->state->del,llbuff2));
 
1444
  }
 
1445
  if (share->state.key_root[sort_key] == HA_OFFSET_ERROR)
 
1446
    return(0);                          /* Nothing to do */
 
1447
 
 
1448
  init_key_cache(dflt_key_cache, opt_key_cache_block_size, param->use_buffers,
 
1449
                 0, 0);
 
1450
  if (init_io_cache(&info->rec_cache,-1,(uint) param->write_buffer_length,
 
1451
                   WRITE_CACHE,share->pack.header_length,1,
 
1452
                   MYF(MY_WME | MY_WAIT_IF_FULL)))
 
1453
    goto err;
 
1454
  info->opt_flag|=WRITE_CACHE_USED;
 
1455
 
 
1456
  if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
 
1457
  {
 
1458
    mi_check_print_error(param,"Not enough memory for key block");
 
1459
    goto err;
 
1460
  }
 
1461
 
 
1462
  if (!mi_alloc_rec_buff(info, -1, &sort_param.record))
 
1463
  {
 
1464
    mi_check_print_error(param,"Not enough memory for record");
 
1465
    goto err;
 
1466
  }
 
1467
  fn_format(param->temp_filename,name,"", MI_NAME_DEXT,2+4+32);
 
1468
  new_file=my_raid_create(fn_format(param->temp_filename,
 
1469
                                    param->temp_filename,"",
 
1470
                                    DATA_TMP_EXT,2+4),
 
1471
                          0,param->tmpfile_createflag,
 
1472
                          share->base.raid_type,
 
1473
                          share->base.raid_chunks,
 
1474
                          share->base.raid_chunksize,
 
1475
                          MYF(0));
 
1476
  if (new_file < 0)
 
1477
  {
 
1478
    mi_check_print_error(param,"Can't create new tempfile: '%s'",
 
1479
                         param->temp_filename);
 
1480
    goto err;
 
1481
  }
 
1482
  if (share->pack.header_length)
 
1483
    if (filecopy(param,new_file,info->dfile,0L,share->pack.header_length,
 
1484
                 "datafile-header"))
 
1485
      goto err;
 
1486
  info->rec_cache.file=new_file;                /* Use this file for cacheing*/
 
1487
 
 
1488
  lock_memory(param);
 
1489
  for (key=0 ; key < share->base.keys ; key++)
 
1490
    share->keyinfo[key].flag|= HA_SORT_ALLOWS_SAME;
 
1491
 
 
1492
  if (my_pread(share->kfile,(uchar*) temp_buff,
 
1493
               (uint) keyinfo->block_length,
 
1494
               share->state.key_root[sort_key],
 
1495
               MYF(MY_NABP+MY_WME)))
 
1496
  {
 
1497
    mi_check_print_error(param,"Can't read indexpage from filepos: %s",
 
1498
                (ulong) share->state.key_root[sort_key]);
 
1499
    goto err;
 
1500
  }
 
1501
 
 
1502
  /* Setup param for sort_write_record */
 
1503
  sort_info.info=info;
 
1504
  sort_info.new_data_file_type=share->data_file_type;
 
1505
  sort_param.fix_datafile=1;
 
1506
  sort_param.master=1;
 
1507
  sort_param.filepos=share->pack.header_length;
 
1508
  old_record_count=info->state->records;
 
1509
  info->state->records=0;
 
1510
  if (sort_info.new_data_file_type != COMPRESSED_RECORD)
 
1511
    info->state->checksum=0;
 
1512
 
 
1513
  if (sort_record_index(&sort_param,info,keyinfo,share->state.key_root[sort_key],
 
1514
                        temp_buff, sort_key,new_file,update_index) ||
 
1515
      write_data_suffix(&sort_info,1) ||
 
1516
      flush_io_cache(&info->rec_cache))
 
1517
    goto err;
 
1518
 
 
1519
  if (info->state->records != old_record_count)
 
1520
  {
 
1521
    mi_check_print_error(param,"found %s of %s records",
 
1522
                llstr(info->state->records,llbuff),
 
1523
                llstr(old_record_count,llbuff2));
 
1524
    goto err;
 
1525
  }
 
1526
 
 
1527
  VOID(my_close(info->dfile,MYF(MY_WME)));
 
1528
  param->out_flag|=O_NEW_DATA;                  /* Data in new file */
 
1529
  info->dfile=new_file;                         /* Use new datafile */
 
1530
  info->state->del=0;
 
1531
  info->state->empty=0;
 
1532
  share->state.dellink= HA_OFFSET_ERROR;
 
1533
  info->state->data_file_length=sort_param.filepos;
 
1534
  share->state.split=info->state->records;      /* Only hole records */
 
1535
  share->state.version=(ulong) time((time_t*) 0);
 
1536
 
 
1537
  info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
 
1538
 
 
1539
  if (param->testflag & T_WRITE_LOOP)
 
1540
  {
 
1541
    VOID(fputs("          \r",stdout)); VOID(fflush(stdout));
 
1542
  }
 
1543
  got_error=0;
 
1544
 
 
1545
err:
 
1546
  if (got_error && new_file >= 0)
 
1547
  {
 
1548
    VOID(end_io_cache(&info->rec_cache));
 
1549
    (void) my_close(new_file,MYF(MY_WME));
 
1550
    (void) my_raid_delete(param->temp_filename, share->base.raid_chunks,
 
1551
                          MYF(MY_WME));
 
1552
  }
 
1553
  if (temp_buff)
 
1554
  {
 
1555
    my_afree((uchar*) temp_buff);
 
1556
  }
 
1557
  my_free(mi_get_rec_buff_ptr(info, sort_param.record),
 
1558
          MYF(MY_ALLOW_ZERO_PTR));
 
1559
  info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
 
1560
  VOID(end_io_cache(&info->rec_cache));
 
1561
  my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
 
1562
  sort_info.buff=0;
 
1563
  share->state.sortkey=sort_key;
 
1564
  return(flush_blocks(param, share->key_cache, share->kfile) |
 
1565
              got_error);
 
1566
} /* sort_records */
 
1567
 
 
1568
 
 
1569
         /* Sort records recursive using one index */
 
1570
 
 
1571
static int sort_record_index(MI_SORT_PARAM *sort_param,MI_INFO *info,
 
1572
                             MI_KEYDEF *keyinfo,
 
1573
                             my_off_t page, uchar *buff, uint sort_key,
 
1574
                             File new_file,bool update_index)
 
1575
{
 
1576
  uint  nod_flag,used_length,key_length;
 
1577
  uchar *temp_buff,*keypos,*endpos;
 
1578
  my_off_t next_page,rec_pos;
 
1579
  uchar lastkey[MI_MAX_KEY_BUFF];
 
1580
  char llbuff[22];
 
1581
  SORT_INFO *sort_info= sort_param->sort_info;
 
1582
  MI_CHECK *param=sort_info->param;
 
1583
 
 
1584
  nod_flag=mi_test_if_nod(buff);
 
1585
  temp_buff=0;
 
1586
 
 
1587
  if (nod_flag)
 
1588
  {
 
1589
    if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
 
1590
    {
 
1591
      mi_check_print_error(param,"Not Enough memory");
 
1592
      return(-1);
 
1593
    }
 
1594
  }
 
1595
  used_length=mi_getint(buff);
 
1596
  keypos=buff+2+nod_flag;
 
1597
  endpos=buff+used_length;
 
1598
  for ( ;; )
 
1599
  {
 
1600
    if (nod_flag)
 
1601
    {
 
1602
      next_page=_mi_kpos(nod_flag,keypos);
 
1603
      if (my_pread(info->s->kfile,(uchar*) temp_buff,
 
1604
                  (uint) keyinfo->block_length, next_page,
 
1605
                   MYF(MY_NABP+MY_WME)))
 
1606
      {
 
1607
        mi_check_print_error(param,"Can't read keys from filepos: %s",
 
1608
                    llstr(next_page,llbuff));
 
1609
        goto err;
 
1610
      }
 
1611
      if (sort_record_index(sort_param, info,keyinfo,next_page,temp_buff,sort_key,
 
1612
                            new_file, update_index))
 
1613
        goto err;
 
1614
    }
 
1615
    if (keypos >= endpos ||
 
1616
        (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,lastkey))
 
1617
        == 0)
 
1618
      break;
 
1619
    rec_pos= _mi_dpos(info,0,lastkey+key_length);
 
1620
 
 
1621
    if ((*info->s->read_rnd)(info,sort_param->record,rec_pos,0))
 
1622
    {
 
1623
      mi_check_print_error(param,"%d when reading datafile",my_errno);
 
1624
      goto err;
 
1625
    }
 
1626
    if (rec_pos != sort_param->filepos && update_index)
 
1627
    {
 
1628
      _mi_dpointer(info,keypos-nod_flag-info->s->rec_reflength,
 
1629
                   sort_param->filepos);
 
1630
      if (movepoint(info,sort_param->record,rec_pos,sort_param->filepos,
 
1631
                    sort_key))
 
1632
      {
 
1633
        mi_check_print_error(param,"%d when updating key-pointers",my_errno);
 
1634
        goto err;
 
1635
      }
 
1636
    }
 
1637
    if (sort_write_record(sort_param))
 
1638
      goto err;
 
1639
  }
 
1640
  /* Clear end of block to get better compression if the table is backuped */
 
1641
  memset(buff+used_length, 0, keyinfo->block_length-used_length);
 
1642
  if (my_pwrite(info->s->kfile,(uchar*) buff,(uint) keyinfo->block_length,
 
1643
                page,param->myf_rw))
 
1644
  {
 
1645
    mi_check_print_error(param,"%d when updating keyblock",my_errno);
 
1646
    goto err;
 
1647
  }
 
1648
  if (temp_buff)
 
1649
    my_afree((uchar*) temp_buff);
 
1650
  return(0);
 
1651
err:
 
1652
  if (temp_buff)
 
1653
    my_afree((uchar*) temp_buff);
 
1654
  return(1);
 
1655
} /* sort_record_index */
 
1656
 
 
1657
 
 
1658
 
 
1659
/*
 
1660
  Check if myisamchk was killed by a signal
 
1661
  This is overloaded by other programs that want to be able to abort
 
1662
  sorting
 
1663
*/
 
1664
 
 
1665
static int not_killed= 0;
 
1666
 
 
1667
volatile int *killed_ptr(MI_CHECK *param __attribute__((unused)))
 
1668
{
 
1669
  return &not_killed;                   /* always NULL */
 
1670
}
 
1671
 
 
1672
        /* print warnings and errors */
 
1673
        /* VARARGS */
 
1674
 
 
1675
void mi_check_print_info(MI_CHECK *param __attribute__((unused)),
 
1676
                         const char *fmt,...)
 
1677
{
 
1678
  va_list args;
 
1679
 
 
1680
  va_start(args,fmt);
 
1681
  VOID(vfprintf(stdout, fmt, args));
 
1682
  VOID(fputc('\n',stdout));
 
1683
  va_end(args);
 
1684
}
 
1685
 
 
1686
/* VARARGS */
 
1687
 
 
1688
void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
 
1689
{
 
1690
  va_list args;
 
1691
 
 
1692
  fflush(stdout);
 
1693
  if (!param->warning_printed && !param->error_printed)
 
1694
  {
 
1695
    if (param->testflag & T_SILENT)
 
1696
      fprintf(stderr,"%s: MyISAM file %s\n",my_progname_short,
 
1697
              param->isam_file_name);
 
1698
    param->out_flag|= O_DATA_LOST;
 
1699
  }
 
1700
  param->warning_printed=1;
 
1701
  va_start(args,fmt);
 
1702
  fprintf(stderr,"%s: warning: ",my_progname_short);
 
1703
  VOID(vfprintf(stderr, fmt, args));
 
1704
  VOID(fputc('\n',stderr));
 
1705
  fflush(stderr);
 
1706
  va_end(args);
 
1707
  return;
 
1708
}
 
1709
 
 
1710
/* VARARGS */
 
1711
 
 
1712
void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
 
1713
{
 
1714
  va_list args;
 
1715
 
 
1716
  fflush(stdout);
 
1717
  if (!param->warning_printed && !param->error_printed)
 
1718
  {
 
1719
    if (param->testflag & T_SILENT)
 
1720
      fprintf(stderr,"%s: MyISAM file %s\n",my_progname_short,param->isam_file_name);
 
1721
    param->out_flag|= O_DATA_LOST;
 
1722
  }
 
1723
  param->error_printed|=1;
 
1724
  va_start(args,fmt);
 
1725
  fprintf(stderr,"%s: error: ",my_progname_short);
 
1726
  VOID(vfprintf(stderr, fmt, args));
 
1727
  VOID(fputc('\n',stderr));
 
1728
  fflush(stderr);
 
1729
  va_end(args);
 
1730
  return;
 
1731
}
 
1732