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