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(&param->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(&param->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 &not_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