~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/myisamchk.cc

  • Committer: Eric Herman
  • Date: 2008-12-07 15:29:44 UTC
  • mto: (656.1.14 devel)
  • mto: This revision was merged to the branch mainline in revision 670.
  • Revision ID: eric@mysql.com-20081207152944-cq1nx1cyi0huqj0f
Added pointer to online version of the FAQ

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