~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/myisamchk.c

  • Committer: Brian Aker
  • Date: 2008-10-06 06:47:29 UTC
  • Revision ID: brian@tangent.org-20081006064729-2i9mhjkzyvow9xsm
RemoveĀ uint.

Show diffs side-by-side

added added

removed removed

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