~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/myisamchk.c

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

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