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