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