~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/myisamchk.c

  • Committer: Monty Taylor
  • Date: 2008-10-09 22:38:27 UTC
  • mto: This revision was merged to the branch mainline in revision 497.
  • Revision ID: monty@inaugust.com-20081009223827-bc9gvpiplsmvpwyq
Moved test() to its own file.
Made a new function to possibly replace int10_to_str.

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