~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/myisamchk.cc

Merged from Toru - removal of my_time_t.

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