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