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