~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/myisamchk.c

  • Committer: Monty Taylor
  • Date: 2008-10-14 21:20:42 UTC
  • mto: (511.1.4 codestyle)
  • mto: This revision was merged to the branch mainline in revision 521.
  • Revision ID: monty@inaugust.com-20081014212042-tef3njx3368b6lwt
Override copy ctr and op= because we have pointer members.

Show diffs side-by-side

added added

removed removed

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