~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/myisamchk.cc

  • Committer: Brian Aker
  • Date: 2009-02-10 00:14:40 UTC
  • Revision ID: brian@tangent.org-20090210001440-qjg8eofh3h93064b
Adding Multi-threaded Scheduler into the system.

Show diffs side-by-side

added added

removed removed

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