~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/myisamchk.c

  • Committer: Monty Taylor
  • Date: 2008-11-07 00:15:51 UTC
  • mto: This revision was merged to the branch mainline in revision 579.
  • Revision ID: monty@inaugust.com-20081107001551-8vxb6sf1ti0i5p09
Cleaned up some headers for PCH.

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