1 /* Copyright (C) 2006-2003 MySQL AB
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
15 
16 /* Describe, check and repair of MARIA tables */
17 
18 #include "ma_fulltext.h"
19 #include <myisamchk.h>
20 #include <my_bit.h>
21 #include <m_ctype.h>
22 #include <my_getopt.h>
23 #include <my_check_opt.h>
24 #include <my_handler_errors.h>
25 /* Remove next line if you want aria_chk to produce a stack trace */
26 #undef HAVE_BACKTRACE
27 #include <my_stacktrace.h>
28 
29 static uint decode_bits;
30 static char **default_argv;
31 static const char *load_default_groups[]= { "aria_chk", 0 };
32 static const char *set_collation_name, *opt_tmpdir, *opt_log_dir;
33 static const char *default_log_dir;
34 static CHARSET_INFO *set_collation;
35 static int stopwords_inited= 0;
36 static MY_TMPDIR maria_chk_tmpdir;
37 static my_bool opt_transaction_logging, opt_debug;
38 static my_bool opt_ignore_control_file, opt_require_control_file;
39 static my_bool opt_warning_for_wrong_transid, opt_update_state;
40 static my_bool have_control_file= 0;
41 
42 static const char *type_names[]=
43 {
44   "impossible","char","binary", "short", "long", "float",
45   "double","number","unsigned short",
46   "unsigned long","longlong","ulonglong","int24",
47   "uint24","int8","varchar", "varbin", "varchar2", "varbin2", "bit",
48   "?","?"
49 };
50 
51 static const char *prefix_packed_txt="packed ",
52 		  *bin_packed_txt="prefix ",
53 		  *diff_txt="stripped ",
54 		  *null_txt="NULL",
55 		  *blob_txt="BLOB ";
56 
57 static const char *field_pack[]=
58 {
59   "","no endspace", "no prespace",
60  "no zeros", "blob", "constant", "table-lockup",
61  "always zero","varchar","unique-hash","?","?"
62 };
63 
64 static const char *record_formats[]=
65 {
66   "Fixed length", "Packed", "Compressed", "Block", "No data", "?", "?"
67 };
68 
69 static const char *bitmap_description[]=
70 {
71   "Empty page", "Part filled head page","Part filled head page",
72   "Part filled head page", "Full head page",
73   "Part filled tail page","Part filled tail page",
74   "Full tail or blob page"
75 };
76 
77 static const char *maria_stats_method_str="nulls_unequal";
78 static char default_open_errmsg[]=  "%d when opening Aria table '%s'";
79 static char default_close_errmsg[]= "%d when closing Aria table '%s'";
80 
81 static void get_options(int *argc,char * * *argv);
82 static void print_version(void);
83 static void usage(void);
84 static int maria_chk(HA_CHECK *param, char *filename);
85 static void descript(HA_CHECK *param, register MARIA_HA *info, char *name);
86 static int maria_sort_records(HA_CHECK *param, register MARIA_HA *info,
87                               char *name, uint sort_key,
88                               my_bool write_info, my_bool update_index);
89 static int sort_record_index(MARIA_SORT_PARAM *sort_param, MARIA_PAGE *page,
90 			     uint sortkey, File new_file,
91                              my_bool update_index);
92 static my_bool write_log_record(HA_CHECK *param);
93 ATTRIBUTE_NORETURN static void my_exit(int exit_code);
94 
95 HA_CHECK check_param;
96 
97 /*
98   Register handler error messages for usage with my_error()
99 
100   NOTES
101     This is safe to call multiple times as my_error_register()
102     will ignore calls to register already registered error numbers.
103 */
104 
get_handler_error_messages(int e)105 static const char **get_handler_error_messages(int e __attribute__((unused)))
106 {
107   return handler_error_messages;
108 }
109 
110 
111 /* Free memory and exit */
112 
my_exit(int exit_code)113 static void my_exit(int exit_code)
114 {
115   free_tmpdir(&maria_chk_tmpdir);
116   free_defaults(default_argv);
117   my_error_unregister(HA_ERR_FIRST,
118                       HA_ERR_FIRST+ array_elements(handler_error_messages)-1);
119   my_end(check_param.testflag & T_INFO ?
120          MY_CHECK_ERROR | MY_GIVE_INFO : MY_CHECK_ERROR);
121   exit(exit_code);
122 }
123 
124 /* Main program */
125 
main(int argc,char ** argv)126 int main(int argc, char **argv)
127 {
128   int error;
129   MY_INIT(argv[0]);
130 
131   my_setup_stacktrace();
132   default_log_dir= opt_log_dir= maria_data_root= ".";
133   maria_chk_init(&check_param);
134   check_param.opt_lock_memory= 1;		/* Lock memory if possible */
135   check_param.using_global_keycache = 0;
136   get_options(&argc,(char***) &argv);
137   maria_quick_table_bits=decode_bits;
138   error=0;
139   maria_init();
140   my_error_register(get_handler_error_messages, HA_ERR_FIRST,
141                     HA_ERR_FIRST+ array_elements(handler_error_messages)-1);
142 
143   maria_block_size= 0;                 /* Use block size from control file */
144   if (!opt_ignore_control_file)
145   {
146     if ((ma_control_file_open(FALSE, opt_require_control_file ||
147                               !(check_param.testflag & T_SILENT),
148                               TRUE)))
149     {
150       if (opt_require_control_file ||
151           (opt_transaction_logging && (check_param.testflag & T_REP_ANY)))
152       {
153         error= 1;
154         goto end;
155       }
156     }
157     else
158       have_control_file= 1;
159   }
160   if (!have_control_file)
161     opt_warning_for_wrong_transid= 0;
162 
163   /*
164     If we are doing a repair, user may want to store this repair into the log
165     so that the log has a complete history and can be used to replay.
166   */
167   if (opt_transaction_logging && (check_param.testflag & T_REP_ANY))
168   {
169     if (init_pagecache(maria_log_pagecache,
170                        TRANSLOG_PAGECACHE_SIZE, 0, 0,
171                        TRANSLOG_PAGE_SIZE, 0, MY_WME) == 0 ||
172         translog_init(opt_log_dir, TRANSLOG_FILE_SIZE,
173                       0, 0, maria_log_pagecache,
174                       TRANSLOG_DEFAULT_FLAGS, 0))
175     {
176       _ma_check_print_error(&check_param,
177                             "Can't initialize transaction logging. Run "
178                             "recovery with switch --skip-transaction-log");
179       error= 1;
180       goto end;
181     }
182   }
183 
184   while (--argc >= 0)
185   {
186     int new_error=maria_chk(&check_param, *(argv++));
187     if ((check_param.testflag & T_REP_ANY) != T_REP)
188       check_param.testflag&= ~T_REP;
189     fflush(stdout);
190     fflush(stderr);
191     if (check_param.wrong_trd_printed &&
192         (check_param.testflag & T_FORCE_CREATE) &&
193         !(check_param.error_printed | check_param.warning_printed))
194     {
195       /* Only wrong create_trd. Run zerofill */
196       ulonglong old_testflag= check_param.testflag;
197       check_param.testflag= T_ZEROFILL;
198       error|= maria_chk(&check_param, argv[-1]);
199       check_param.testflag= old_testflag;
200       check_param.error_printed= 0;
201       check_param.warning_printed= 0;
202       fflush(stdout);
203       fflush(stderr);
204     }
205     if ((check_param.error_printed | check_param.warning_printed) &&
206 	(check_param.testflag & T_FORCE_CREATE) &&
207 	(!(check_param.testflag & (T_REP | T_REP_BY_SORT | T_SORT_RECORDS |
208 				   T_SORT_INDEX))))
209     {
210       ulonglong old_testflag=check_param.testflag;
211       if (!(check_param.testflag & T_REP))
212 	check_param.testflag|= T_REP_BY_SORT;
213       check_param.testflag&= ~T_EXTEND;			/* Not needed  */
214       error|=maria_chk(&check_param, argv[-1]);
215       check_param.testflag= old_testflag;
216       fflush(stdout);
217       fflush(stderr);
218     }
219       error|=new_error;
220     if (argc && (!(check_param.testflag & T_SILENT) ||
221                  check_param.testflag & T_INFO))
222     {
223       puts("\n---------\n");
224       fflush(stdout);
225     }
226   }
227 end:
228   if (check_param.total_files > 1)
229   {					/* Only if descript */
230     char buff[22],buff2[22];
231     if (!(check_param.testflag & T_SILENT) || check_param.testflag & T_INFO)
232       puts("\n---------");
233     printf("\nTotal of all %d Aria-files:\nData records: %9s   Deleted blocks: %9s\n",check_param.total_files,llstr(check_param.total_records,buff),
234 	   llstr(check_param.total_deleted,buff2));
235   }
236   maria_end();
237   my_exit(error);
238 #ifndef _lint
239   return 0;				/* No compiler warning */
240 #endif
241 } /* main */
242 
243 enum options_mc {
244   OPT_CHARSETS_DIR=256, OPT_SET_COLLATION,OPT_START_CHECK_POS,
245   OPT_CORRECT_CHECKSUM, OPT_CREATE_MISSING_KEYS, OPT_PAGE_BUFFER_SIZE,
246   OPT_KEY_CACHE_BLOCK_SIZE, OPT_MARIA_BLOCK_SIZE,
247   OPT_READ_BUFFER_SIZE, OPT_WRITE_BUFFER_SIZE, OPT_SORT_BUFFER_SIZE,
248   OPT_SORT_KEY_BLOCKS, OPT_DECODE_BITS, OPT_FT_MIN_WORD_LEN,
249   OPT_FT_MAX_WORD_LEN, OPT_FT_STOPWORD_FILE,
250   OPT_MAX_RECORD_LENGTH, OPT_AUTO_CLOSE, OPT_STATS_METHOD, OPT_TRANSACTION_LOG,
251   OPT_ZEROFILL_KEEP_LSN,
252   OPT_REQUIRE_CONTROL_FILE, OPT_IGNORE_CONTROL_FILE,
253   OPT_LOG_DIR, OPT_WARNING_FOR_WRONG_TRANSID
254 };
255 
256 static struct my_option my_long_options[] =
257 {
258   {"analyze", 'a',
259    "Analyze distribution of keys. Will make some joins in MySQL faster. You can check the calculated distribution.",
260    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
261 #ifdef __NETWARE__
262   {"autoclose", OPT_AUTO_CLOSE, "Auto close the screen on exit for Netware.",
263    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
264 #endif
265   {"block-search", 'b',
266    "No help available.",
267    0, 0, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
268   {"backup", 'B',
269    "Make a backup of the .MAD file as 'filename-time.BAK'.",
270    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
271   {"character-sets-dir", OPT_CHARSETS_DIR,
272    "Directory where character sets are.",
273    (char**) &charsets_dir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
274   {"check", 'c',
275    "Check table for errors.",
276    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
277   {"check-only-changed", 'C',
278    "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).",
279    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
280   {"correct-checksum", OPT_CORRECT_CHECKSUM,
281    "Correct checksum information for table.",
282    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
283   {"create-missing-keys", OPT_CREATE_MISSING_KEYS,
284    "Create missing keys. This assumes that the data file is correct and that "
285    "the the number of rows stored in the index file is correct. Enables "
286    "--quick",
287    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
288 #ifndef DBUG_OFF
289   {"debug", '#',
290    "Output debug log. Often this is 'd:t:o,filename'.",
291    0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
292 #endif
293   {"description", 'd',
294    "Prints some information about table.",
295    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
296   {"data-file-length", 'D',
297    "Max length of data file (when recreating data-file when it's full).",
298    &check_param.max_data_file_length,
299    &check_param.max_data_file_length,
300    0, GET_LL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
301   {"extend-check", 'e',
302    "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.",
303    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
304   {"fast", 'F',
305    "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).",
306    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
307   {"force", 'f',
308    "Restart with -r if there are any errors in the table. States will be updated as with --update-state.",
309    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
310   {"HELP", 'H',
311    "Print all argument options sorted alphabetically and exit.",
312    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
313   {"help", '?',
314    "Print all options by groups and exit. See also --HELP",
315    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
316   {"information", 'i',
317    "Print statistics information about table that is checked.",
318    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
319   { "ignore-control-file", OPT_IGNORE_CONTROL_FILE,
320     "Ignore the control file",
321     (uchar**)&opt_ignore_control_file, 0, 0, GET_BOOL, NO_ARG,
322     0, 0, 0, 0, 0, 0},
323   {"keys-used", 'k',
324    "Tell Aria to update only some specific keys. # is a bit mask of which keys to use. This can be used to get faster inserts.",
325    &check_param.keys_in_use,
326    &check_param.keys_in_use,
327    0, GET_ULL, REQUIRED_ARG, -1, 0, 0, 0, 0, 0},
328   {"datadir", 'h',
329    "Path for control file (and logs if --logdir not used).",
330    (char**) &maria_data_root, 0, 0, GET_STR, REQUIRED_ARG,
331    0, 0, 0, 0, 0, 0},
332   {"logdir", OPT_LOG_DIR,
333    "Path for log files.",
334    (char**) &opt_log_dir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
335   {"max-record-length", OPT_MAX_RECORD_LENGTH,
336    "Skip rows bigger than this if aria_chk can't allocate memory to hold it",
337    &check_param.max_record_length,
338    &check_param.max_record_length,
339    0, GET_ULL, REQUIRED_ARG, LONGLONG_MAX, 0, LONGLONG_MAX, 0, 0, 0},
340   {"medium-check", 'm',
341    "Faster than extend-check, but only finds 99.99% of all errors. Should be good enough for most cases.",
342    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
343   {"quick", 'q', "Faster repair by not modifying the data file.",
344    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
345   {"read-only", 'T',
346    "Don't mark table as checked.",
347    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
348   {"recover", 'r',
349    "Can fix almost anything except unique keys that aren't unique.",
350    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
351   {"parallel-recover", 'p',
352    "Same as '-r' but creates all the keys in parallel.",
353    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
354   {"safe-recover", 'o',
355    "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.",
356    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
357   {"sort-recover", 'n',
358    "Force recovering with sorting even if the temporary file was very big.",
359    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
360   { "require-control-file", OPT_REQUIRE_CONTROL_FILE,
361     "Abort if cannot find control file",
362     (uchar**)&opt_require_control_file, 0, 0, GET_BOOL, NO_ARG,
363     0, 0, 0, 0, 0, 0},
364 #ifdef DEBUG
365   {"start-check-pos", OPT_START_CHECK_POS,
366    "No help available.",
367    0, 0, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
368 #endif
369   {"set-auto-increment", 'A',
370    "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.",
371    &check_param.auto_increment_value,
372    &check_param.auto_increment_value,
373    0, GET_ULL, OPT_ARG, 0, 0, 0, 0, 0, 0},
374   {"set-collation", OPT_SET_COLLATION,
375    "Change the collation used by the index",
376    (char**) &set_collation_name, 0, 0, GET_STR, REQUIRED_ARG,
377    0, 0, 0, 0, 0, 0},
378   {"silent", 's',
379    "Only print errors. One can use two -s to make aria_chk very silent.",
380    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
381   {"sort-index", 'S',
382    "Sort index blocks. This speeds up 'read-next' in applications.",
383    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
384   {"sort-records", 'R',
385    "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!)",
386    &check_param.opt_sort_key,
387    &check_param.opt_sort_key,
388    0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
389   {"tmpdir", 't', "Path for temporary files.", (char**) &opt_tmpdir,
390    0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
391   {"transaction-log", OPT_TRANSACTION_LOG,
392    "Log repair command to transaction log",
393    &opt_transaction_logging, &opt_transaction_logging,
394    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
395   {"update-state", 'U',
396    "Mark tables as crashed if any errors were found and clean if check "
397    "didn't find any errors but table was marked as 'not clean' before. This "
398    "allows one to get rid of warnings like 'table not properly closed'. "
399    "If table was updated, update also the timestamp for when check was made. "
400    "This option is on by default!",
401    &opt_update_state, &opt_update_state, 0, GET_BOOL, NO_ARG,
402    1, 0, 0, 0, 0, 0},
403   {"unpack", 'u',
404    "Unpack file packed with aria_pack.",
405    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
406   {"verbose", 'v',
407    "Print more information. This can be used with --description and --check. Use many -v for more verbosity!",
408    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
409   {"version", 'V', "Print version and exit.",
410    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
411   {"wait", 'w', "Wait if table is locked.",
412    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
413   {"warning-for-wrong-transaction-id", OPT_WARNING_FOR_WRONG_TRANSID,
414    "Give a warning if we find a transaction id in the table that is bigger"
415    "than what exists in the control file. Use --skip-... to disable warning",
416    &opt_warning_for_wrong_transid, &opt_warning_for_wrong_transid,
417    0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
418   { "page_buffer_size", OPT_PAGE_BUFFER_SIZE,
419     "Size of page buffer. Used by --safe-repair",
420     &check_param.use_buffers, &check_param.use_buffers, 0,
421     GET_ULONG, REQUIRED_ARG, PAGE_BUFFER_INIT, 1024L*1024L,
422     SIZE_T_MAX, (long) MALLOC_OVERHEAD, (long) IO_SIZE, 0},
423   { "read_buffer_size", OPT_READ_BUFFER_SIZE,
424     "Read buffer size for sequential reads during scanning",
425     &check_param.read_buffer_length,
426     &check_param.read_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
427     (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD,
428     ~0ULL, (long) MALLOC_OVERHEAD, (long) 1L, 0},
429   { "write_buffer_size", OPT_WRITE_BUFFER_SIZE,
430     "Write buffer size for sequential writes during repair of fixed size or dynamic size rows",
431     &check_param.write_buffer_length,
432     &check_param.write_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
433     (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD,
434     ~0UL, (long) MALLOC_OVERHEAD, (long) 1L, 0},
435   { "sort_buffer_size", OPT_SORT_BUFFER_SIZE,
436     "Size of sort buffer. Used by --recover",
437     &check_param.orig_sort_buffer_length,
438     &check_param.orig_sort_buffer_length, 0, GET_ULL, REQUIRED_ARG,
439     SORT_BUFFER_INIT, MIN_SORT_BUFFER, SIZE_T_MAX, MALLOC_OVERHEAD, 1L, 0},
440   { "sort_key_blocks", OPT_SORT_KEY_BLOCKS,
441     "Internal buffer for sorting keys; Don't touch :)",
442     &check_param.sort_key_blocks,
443     &check_param.sort_key_blocks, 0, GET_ULONG, REQUIRED_ARG,
444     BUFFERS_WHEN_SORTING, 4L, 100L, 0L, 1L, 0},
445   { "decode_bits", OPT_DECODE_BITS, "", &decode_bits,
446     &decode_bits, 0, GET_UINT, REQUIRED_ARG, 9L, 4L, 17L, 0L, 1L, 0},
447   { "ft_min_word_len", OPT_FT_MIN_WORD_LEN, "", &ft_min_word_len,
448     &ft_min_word_len, 0, GET_ULONG, REQUIRED_ARG, 4, 1, HA_FT_MAXCHARLEN,
449     0, 1, 0},
450   { "ft_max_word_len", OPT_FT_MAX_WORD_LEN, "", &ft_max_word_len,
451     &ft_max_word_len, 0, GET_ULONG, REQUIRED_ARG, HA_FT_MAXCHARLEN, 10,
452     HA_FT_MAXCHARLEN, 0, 1, 0},
453   { "aria_ft_stopword_file", OPT_FT_STOPWORD_FILE,
454     "Use stopwords from this file instead of built-in list.",
455     (char**) &ft_stopword_file, (char**) &ft_stopword_file, 0, GET_STR,
456     REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
457   { "stats_method", OPT_STATS_METHOD,
458     "Specifies how index statistics collection code should treat NULLs. "
459     "Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), "
460     "\"nulls_equal\" (emulate 4.0 behavior), and \"nulls_ignored\".",
461     (char**) &maria_stats_method_str, (char**) &maria_stats_method_str, 0,
462     GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
463   { "zerofill", 'z',
464     "Fill empty space in data and index files with zeroes. This makes the data file movable between different servers. It also fixes any wrong transaction or LSN numbers in the table after a crash or if someone removed the Aria log files.",
465     0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
466   { "zerofill-keep-lsn", OPT_ZEROFILL_KEEP_LSN,
467     "Like --zerofill but does not zero out LSN of data/index pages;"
468     " used only for testing and debugging",
469     0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
470   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
471 };
472 
473 
print_version(void)474 static void print_version(void)
475 {
476   printf("%s  Ver 1.3 for %s on %s\n", my_progname, SYSTEM_TYPE,
477 	 MACHINE_TYPE);
478 }
479 
480 
usage(void)481 static void usage(void)
482 {
483   print_version();
484   puts("By Monty, for your professional use");
485   puts("This software comes with NO WARRANTY: see the PUBLIC for details.\n");
486   puts("Description, check and repair of Aria tables.");
487   puts("Used without options all tables on the command will be checked for errors");
488   printf("Usage: %s [OPTIONS] tables[.MAI]\n", my_progname_short);
489   printf("\nGlobal options:\n");
490 #ifndef DBUG_OFF
491   printf("\
492   -#, --debug=...     Output debug log. Often this is 'd:t:o,filename'.\n");
493 #endif
494   printf("\
495   -H, --HELP          Print all argument options sorted alphabetically.\n\
496   -?, --help          Print all options by groups\n\
497   --datadir=path      Path for control file (and logs if --logdir not used)\n\
498   --logdir=path       Path for log files\n\
499   --ignore-control-file  Don't open the control file. Only use this if you\n\
500                          are sure the tables are not in use by another\n\
501                          program!\n\
502   --require-control-file  Abort if we can't find/read the aria_log_control\n\
503                           file\n\
504   -s, --silent	      Only print errors.  One can use two -s to make\n\
505 		      maria_chk very silent.\n\
506   -t, --tmpdir=path   Path for temporary files. Multiple paths can be\n\
507                       specified, separated by ");
508 #if defined( __WIN__) || defined(__NETWARE__)
509    printf("semicolon (;)");
510 #else
511    printf("colon (:)");
512 #endif
513    printf(", they will be used\n\
514                       in a round-robin fashion.\n\
515   -v, --verbose       Print more information. This can be used with\n\
516                       --description and --check. Use many -v for more verbosity.\n\
517   -V, --version       Print version and exit.\n\
518   -w, --wait          Wait if table is locked.\n\n");
519 #ifdef DEBUG
520   puts("  --start-check-pos=# Start reading file at given offset.\n");
521 #endif
522 
523   puts("Check options (check is the default action for aria_chk):\n\
524   -c, --check	      Check table for errors.\n\
525   -e, --extend-check  Check the table VERY throughly.  Only use this in\n\
526                       extreme cases as aria_chk should normally be able to\n\
527                       find out if the table is ok even without this switch.\n\
528   -F, --fast	      Check only tables that haven't been closed properly.\n\
529   -C, --check-only-changed\n\
530 		      Check only tables that have changed since last check.\n\
531   -f, --force         Restart with '-r' if there are any errors in the table.\n\
532 		      States will be updated as with '--update-state'.\n\
533   -i, --information   Print statistics information about table that is checked.\n\
534   -m, --medium-check  Faster than extend-check, but only finds 99.99% of\n\
535 		      all errors.  Should be good enough for most cases.\n\
536   -T, --read-only     Don't mark table as checked.\n\
537   -U, --update-state  Mark tables as crashed if any errors were found and\n\
538                       clean if check didn't find any errors but table was\n\
539 	              marked as 'not clean' before. This allows one to get\n\
540  		      rid of warnings like 'table not properly closed'. If\n\
541 		      table was updated, update also the timestamp for when\n\
542  		      the check was made. This option is on by default!\n\
543 		      Use --skip-update-state to disable.\n\
544   --warning-for-wrong-transaction-id\n\
545    Give a warning if we find a transaction id in the table that is bigger\n\
546    than what exists in the control file. Use --skip-... to disable warning\n\
547   ");
548 
549   puts("\
550 Recover (repair)/ options (When using '--recover' or '--safe-recover'):\n\
551   -B, --backup	      Make a backup of the .MAD file as 'filename-time.BAK'.\n\
552   --correct-checksum  Correct checksum information for table.\n\
553   -D, --data-file-length=#  Max length of data file (when recreating data\n\
554                       file when it's full).\n\
555  --create-missing-keys\n\
556                       Create missing keys. This assumes that the data\n\
557                       file is correct and that the the number of rows stored\n\
558                       in the index file is correct. Enables --quick.\n\
559   -e, --extend-check  Try to recover every possible row from the data file\n\
560 		      Normally this will also find a lot of garbage rows;\n\
561 		      Don't use this option if you are not totally desperate.\n\
562   -f, --force         Overwrite old temporary files. Add another --force to\n\
563                       avoid 'sort_buffer_size is too small' errors.\n\
564                       In this case we will attempt to do the repair with the\n\
565                       given sort_buffer_size and dynamically allocate\n\
566                       as many management buffers as needed.\n\
567   -k, --keys-used=#   Tell Aria to update only some specific keys. # is a\n\
568 	              bit mask of which keys to use. This can be used to\n\
569 		      get faster inserts.\n\
570   --max-record-length=#\n\
571                       Skip rows bigger than this if aria_chk can't allocate\n\
572 		      memory to hold it.\n\
573   -r, --recover       Can fix almost anything except unique keys that aren't\n\
574                       unique.\n\
575   -n, --sort-recover  Forces recovering with sorting even if the temporary\n\
576 		      file would be very big.\n\
577   -p, --parallel-recover\n\
578                       Uses the same technique as '-r' and '-n', but creates\n\
579                       all the keys in parallel, in different threads.");
580   puts("\
581   -o, --safe-recover  Uses old recovery method; Slower than '-r' but can\n \
582 		      handle a couple of cases where '-r' reports that it\n\
583 		      can't fix the data file.\n\
584   --transaction-log   Log repair command to transaction log. This is needed\n\
585                       if one wants to use the aria_read_log to repeat the \n\
586                       repair\n\
587   --character-sets-dir=...\n\
588                       Directory where character sets are.\n\
589   --set-collation=name\n\
590  		      Change the collation used by the index.\n\
591   -q, --quick         Faster repair by not modifying the data file.\n\
592                       One can give a second '-q' to force aria_chk to\n\
593 		      modify the original datafile in case of duplicate keys.\n\
594 		      NOTE: Tables where the data file is corrupted can't be\n\
595 		      fixed with this option.\n\
596   -u, --unpack        Unpack file packed with ariapack.\n\
597 ");
598 
599   puts("Other actions:\n\
600   -a, --analyze	      Analyze distribution of keys. Will make some joins in\n\
601 		      MariaDB faster.  You can check the calculated distribution\n\
602 		      by using '--description --verbose table_name'.\n\
603   --stats_method=name Specifies how index statistics collection code should\n\
604                       treat NULLs. Possible values of name are \"nulls_unequal\"\n\
605                       (default for 4.1/5.0), \"nulls_equal\" (emulate 4.0), and \n\
606                       \"nulls_ignored\".\n\
607   -d, --description   Prints some information about table.\n\
608   -A, --set-auto-increment[=value]\n\
609 		      Force auto_increment to start at this or higher value\n\
610 		      If no value is given, then sets the next auto_increment\n\
611 		      value to the highest used value for the auto key + 1.\n\
612   -S, --sort-index    Sort index blocks.  This speeds up 'read-next' in\n\
613 		      applications.\n\
614   -R, --sort-records=#\n\
615 		      Sort records according to an index.  This makes your\n\
616 		      data much more localized and may speed up things\n\
617 		      (It may be VERY slow to do a sort the first time!).\n\
618   -b,  --block-search=#\n\
619                       Find a record, a block at given offset belongs to.\n\
620   -z,  --zerofill     Fill empty space in data and index files with zeroes.\n\
621                       This makes the data file movable between different \n\
622                       servers.  It also fixes any wrong transaction or LSN\n\
623                       numbers in the table after a crash or if someone\n\
624                       removed the Aria log files.\n\
625   --zerofill-keep-lsn Like --zerofill but does not zero out LSN of\n\
626                       data/index pages.");
627 
628   puts("Variables:\n\
629 --page_buffer_size=#   Size of page buffer. Used by --safe-repair\n\
630 --read_buffer_size=#   Read buffer size for sequential reads during scanning\n\
631 --sort_buffer_size=#   Size of sort buffer. Used by --recover\n\
632 --sort_key_blocks=#    Internal buffer for sorting keys; Don't touch :)\n\
633 --write_buffer_size=#  Write buffer size for sequential writes during repair");
634 
635   print_defaults("my", load_default_groups);
636   my_print_variables(my_long_options);
637 }
638 
639 const char *maria_stats_method_names[] = {"nulls_unequal", "nulls_equal",
640                                            "nulls_ignored", NullS};
641 TYPELIB maria_stats_method_typelib= {
642   array_elements(maria_stats_method_names) - 1, "",
643   maria_stats_method_names, NULL};
644 
645 	 /* Read options */
646 
647 static my_bool
get_one_option(const struct my_option * opt,const char * argument,const char * filename)648 get_one_option(const struct my_option *opt,
649 	       const char *argument,
650                const char *filename __attribute__((unused)))
651 {
652   switch (opt->id) {
653 #ifdef __NETWARE__
654   case OPT_AUTO_CLOSE:
655     setscreenmode(SCR_AUTOCLOSE_ON_EXIT);
656     break;
657 #endif
658   case 'a':
659     if (argument == disabled_my_option)
660       check_param.testflag&= ~T_STATISTICS;
661     else
662       check_param.testflag|= T_STATISTICS;
663     break;
664   case 'A':
665     if (argument)
666       check_param.auto_increment_value= strtoull(argument, NULL, 0);
667     else
668       check_param.auto_increment_value= 0;	/* Set to max used value */
669     check_param.testflag|= T_AUTO_INC;
670     break;
671   case 'b':
672     check_param.search_after_block= strtoul(argument, NULL, 10);
673     break;
674   case 'B':
675     if (argument == disabled_my_option)
676       check_param.testflag&= ~T_BACKUP_DATA;
677     else
678       check_param.testflag|= T_BACKUP_DATA;
679     break;
680   case 'c':
681     if (argument == disabled_my_option)
682       check_param.testflag&= ~T_CHECK;
683     else
684       check_param.testflag|= T_CHECK;
685     break;
686   case 'C':
687     if (argument == disabled_my_option)
688       check_param.testflag&= ~(T_CHECK | T_CHECK_ONLY_CHANGED);
689     else
690       check_param.testflag|= T_CHECK | T_CHECK_ONLY_CHANGED;
691     break;
692   case 'D':
693     check_param.max_data_file_length=strtoll(argument, NULL, 10);
694     break;
695   case 's':				/* silent */
696     if (argument == disabled_my_option)
697       check_param.testflag&= ~(T_SILENT | T_VERY_SILENT);
698     else
699     {
700       if (check_param.testflag & T_SILENT)
701 	check_param.testflag|= T_VERY_SILENT;
702       check_param.testflag|= T_SILENT;
703       check_param.testflag&= ~T_WRITE_LOOP;
704     }
705     break;
706   case 'w':
707     if (argument == disabled_my_option)
708       check_param.testflag&= ~T_WAIT_FOREVER;
709     else
710       check_param.testflag|= T_WAIT_FOREVER;
711     break;
712   case 'd':				/* description if isam-file */
713     if (argument == disabled_my_option)
714       check_param.testflag&= ~T_DESCRIPT;
715     else
716       check_param.testflag|= T_DESCRIPT;
717     break;
718   case 'e':				/* extend check */
719     if (argument == disabled_my_option)
720       check_param.testflag&= ~T_EXTEND;
721     else
722       check_param.testflag|= T_EXTEND;
723     break;
724   case 'i':
725     if (argument == disabled_my_option)
726       check_param.testflag&= ~T_INFO;
727     else
728       check_param.testflag|= T_INFO;
729     break;
730   case 'f':
731     if (argument == disabled_my_option)
732     {
733       check_param.tmpfile_createflag= O_RDWR | O_TRUNC | O_EXCL;
734      check_param.testflag&= ~(T_FORCE_CREATE | T_UPDATE_STATE |
735                               T_FORCE_SORT_MEMORY);
736     }
737     else
738     {
739       if (check_param.testflag & T_FORCE_CREATE)
740         check_param.testflag= T_FORCE_SORT_MEMORY;
741       check_param.tmpfile_createflag= O_RDWR | O_TRUNC;
742       check_param.testflag|= T_FORCE_CREATE | T_UPDATE_STATE;
743     }
744     break;
745   case 'F':
746     if (argument == disabled_my_option)
747       check_param.testflag&= ~T_FAST;
748     else
749       check_param.testflag|= T_FAST;
750     break;
751   case 'k':
752     check_param.keys_in_use= (ulonglong) strtoll(argument, NULL, 10);
753     break;
754   case 'm':
755     if (argument == disabled_my_option)
756       check_param.testflag&= ~T_MEDIUM;
757     else
758       check_param.testflag|= T_MEDIUM;		/* Medium check */
759     break;
760   case 'r':				/* Repair table */
761     check_param.testflag&= ~T_REP_ANY;
762     if (argument != disabled_my_option)
763       check_param.testflag|= T_REP_BY_SORT;
764     break;
765   case 'p':
766     check_param.testflag&= ~T_REP_ANY;
767     if (argument != disabled_my_option)
768       check_param.testflag|= T_REP_PARALLEL;
769     break;
770   case 'o':
771     check_param.testflag&= ~T_REP_ANY;
772     check_param.force_sort= 0;
773     if (argument != disabled_my_option)
774     {
775       check_param.testflag|= T_REP;
776       my_disable_async_io= 1;		/* More safety */
777     }
778     break;
779   case 'n':
780     check_param.testflag&= ~T_REP_ANY;
781     if (argument == disabled_my_option)
782       check_param.force_sort= 0;
783     else
784     {
785       check_param.testflag|= T_REP_BY_SORT;
786       check_param.force_sort= 1;
787     }
788     break;
789   case 'q':
790     if (argument == disabled_my_option)
791       check_param.testflag&= ~(T_QUICK | T_FORCE_UNIQUENESS);
792     else
793     {
794       /*
795         If T_QUICK was specified before, but not OPT_CREATE_MISSING_KEYS,
796         then add T_FORCE_UNIQUENESS.
797       */
798       check_param.testflag|=
799         ((check_param.testflag & (T_QUICK | T_CREATE_MISSING_KEYS)) ==
800          T_QUICK ? T_FORCE_UNIQUENESS : T_QUICK);
801     }
802     break;
803   case OPT_CREATE_MISSING_KEYS:
804     if (argument == disabled_my_option)
805       check_param.testflag&= ~(T_QUICK | T_CREATE_MISSING_KEYS);
806     else
807     {
808       check_param.testflag|= T_QUICK | T_CREATE_MISSING_KEYS;
809       /* Use repair by sort by default */
810       if (!(check_param.testflag & T_REP_ANY))
811         check_param.testflag|= T_REP_BY_SORT;
812     }
813     break;
814   case 'u':
815     if (argument == disabled_my_option)
816       check_param.testflag&= ~T_UNPACK;
817     else
818     {
819       check_param.testflag|= T_UNPACK;
820       if (!(check_param.testflag & T_REP_ANY))
821         check_param.testflag|= T_REP_BY_SORT;
822     }
823     break;
824   case 'v':				/* Verbose */
825     if (argument == disabled_my_option)
826     {
827       check_param.testflag&= ~T_VERBOSE;
828       check_param.verbose=0;
829     }
830     else
831     {
832       check_param.testflag|= T_VERBOSE;
833       check_param.verbose++;
834     }
835     break;
836   case 'R':				/* Sort records */
837     if (argument == disabled_my_option)
838       check_param.testflag&= ~T_SORT_RECORDS;
839     else
840     {
841       check_param.testflag|= T_SORT_RECORDS;
842       check_param.opt_sort_key= (uint) atoi(argument) - 1;
843       if (check_param.opt_sort_key >= MARIA_MAX_KEY)
844       {
845 	fprintf(stderr,
846 		"The value of the sort key is bigger than max key: %d.\n",
847 		MARIA_MAX_KEY);
848 	my_exit(1);
849       }
850     }
851     break;
852   case 'S':			      /* Sort index */
853     if (argument == disabled_my_option)
854       check_param.testflag&= ~T_SORT_INDEX;
855     else
856       check_param.testflag|= T_SORT_INDEX;
857     break;
858   case 'T':
859     if (argument == disabled_my_option)
860       check_param.testflag&= ~T_READONLY;
861     else
862       check_param.testflag|= T_READONLY;
863     break;
864   case 'U':
865     if (argument == disabled_my_option)
866       check_param.testflag&= ~T_UPDATE_STATE;
867     else
868       check_param.testflag|= T_UPDATE_STATE;
869     break;
870   case '#':
871     DBUG_SET_INITIAL(argument ? argument : "d:t:o,/tmp/aria_chk.trace");
872     opt_debug= 1;
873     break;
874   case 'V':
875     print_version();
876     my_exit(0);
877   case OPT_CORRECT_CHECKSUM:
878     if (argument == disabled_my_option)
879       check_param.testflag&= ~T_CALC_CHECKSUM;
880     else
881       check_param.testflag|= T_CALC_CHECKSUM;
882     break;
883   case OPT_STATS_METHOD:
884   {
885     int method;
886     enum_handler_stats_method UNINIT_VAR(method_conv);
887     maria_stats_method_str= argument;
888     if ((method=find_type(argument, &maria_stats_method_typelib, 2)) <= 0)
889     {
890       fprintf(stderr, "Invalid value of stats_method: %s.\n", argument);
891       my_exit(1);
892     }
893     switch (method-1) {
894     case 0:
895       method_conv= MI_STATS_METHOD_NULLS_EQUAL;
896       break;
897     case 1:
898       method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL;
899       break;
900     case 2:
901       method_conv= MI_STATS_METHOD_IGNORE_NULLS;
902       break;
903     default: abort();                         /* Impossible */
904     }
905     check_param.stats_method= method_conv;
906     break;
907   }
908 #ifdef DEBUG					/* Only useful if debugging */
909   case OPT_START_CHECK_POS:
910     check_param.start_check_pos= strtoull(argument, NULL, 0);
911     break;
912 #endif
913   case 'z':
914     if (argument == disabled_my_option)
915       check_param.testflag&= ~T_ZEROFILL;
916     else
917       check_param.testflag|= T_ZEROFILL;
918     break;
919   case OPT_ZEROFILL_KEEP_LSN:
920     if (argument == disabled_my_option)
921       check_param.testflag&= ~(T_ZEROFILL_KEEP_LSN | T_ZEROFILL);
922     else
923       check_param.testflag|= (T_ZEROFILL_KEEP_LSN | T_ZEROFILL);
924     break;
925   case 'H':
926     my_print_help(my_long_options);
927     my_print_variables(my_long_options);
928     my_exit(0);
929   case '?':
930     usage();
931     my_exit(0);
932   }
933   return 0;
934 }
935 
936 
get_options(register int * argc,register char *** argv)937 static void get_options(register int *argc,register char ***argv)
938 {
939   int ho_error;
940 
941   load_defaults_or_exit("my", load_default_groups, argc, argv);
942   default_argv= *argv;
943   check_param.testflag= T_UPDATE_STATE;
944   if (isatty(fileno(stdout)))
945     check_param.testflag|=T_WRITE_LOOP;
946 
947   if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
948     my_exit(ho_error);
949 
950   /* If using repair, then update checksum if one uses --update-state */
951   if ((check_param.testflag & T_UPDATE_STATE) &&
952       (check_param.testflag & T_REP_ANY))
953     check_param.testflag|= T_CALC_CHECKSUM;
954 
955   if (*argc == 0)
956   {
957     usage();
958     my_exit(-1);
959   }
960 
961   if ((check_param.testflag & T_UNPACK) &&
962       (check_param.testflag & (T_QUICK | T_SORT_RECORDS)))
963   {
964     fprintf(stderr, "%s: --unpack can't be used with --quick or --sort-records\n",
965 		 my_progname_short);
966     my_exit(1);
967   }
968   if ((check_param.testflag & T_READONLY) &&
969       (check_param.testflag &
970        (T_REP_ANY | T_STATISTICS | T_AUTO_INC |
971 	T_SORT_RECORDS | T_SORT_INDEX | T_FORCE_CREATE)))
972   {
973     fprintf(stderr, "%s: Can't use --readonly when repairing or sorting\n",
974 		 my_progname_short);
975     my_exit(1);
976   }
977 
978   if (!opt_debug)
979   {
980     DEBUGGER_OFF;                               /* Speed up things a bit */
981   }
982   if (init_tmpdir(&maria_chk_tmpdir, opt_tmpdir))
983     my_exit(1);
984 
985   check_param.tmpdir=&maria_chk_tmpdir;
986 
987   if (set_collation_name)
988     if (!(set_collation= get_charset_by_name(set_collation_name,
989                                              MYF(MY_WME))))
990       my_exit(1);
991 
992   if (maria_data_root != default_log_dir && opt_log_dir == default_log_dir)
993   {
994     /* --datadir was used and --log-dir was not. Set log-dir to datadir */
995     opt_log_dir= maria_data_root;
996   }
997 
998   /* If we are using zerofill, then we don't need to read the control file */
999   if ((check_param.testflag & (T_ZEROFILL_KEEP_LSN | T_ZEROFILL)) &&
1000       !(check_param.testflag & ~(T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX | T_STATISTICS | T_CHECK | T_FAST | T_CHECK_ONLY_CHANGED)))
1001     opt_ignore_control_file= 1;
1002 
1003   return;
1004 } /* get options */
1005 
1006 
1007 /**
1008   Check/repair table
1009 
1010   @return 0  table is ok
1011   @return 1  Got warning during check
1012   @return 2  Got error during check/repair.
1013 */
1014 
maria_chk(HA_CHECK * param,char * filename)1015 static int maria_chk(HA_CHECK *param, char *filename)
1016 {
1017   int error,lock_type,recreate;
1018   uint warning_printed_by_chk_status;
1019   my_bool rep_quick= MY_TEST(param->testflag & (T_QUICK | T_FORCE_UNIQUENESS));
1020   my_bool born_transactional;
1021   MARIA_HA *info;
1022   File datafile;
1023   char llbuff[22],llbuff2[22];
1024   my_bool state_updated=0;
1025   MARIA_SHARE *share;
1026   DBUG_ENTER("maria_chk");
1027 
1028   param->out_flag= error= param->error_printed= recreate= 0;
1029   param->warning_printed= param->wrong_trd_printed= 0;
1030   datafile=0;
1031   param->isam_file_name=filename;		/* For error messages */
1032   warning_printed_by_chk_status= 0;
1033   if (!(info=maria_open(filename,
1034                         (param->testflag & (T_DESCRIPT | T_READONLY)) ?
1035                         O_RDONLY : O_RDWR,
1036                         HA_OPEN_FOR_REPAIR |
1037                         ((param->testflag & T_WAIT_FOREVER) ?
1038                          HA_OPEN_WAIT_IF_LOCKED :
1039                          (param->testflag & T_DESCRIPT) ?
1040                          HA_OPEN_IGNORE_IF_LOCKED : HA_OPEN_ABORT_IF_LOCKED),
1041                         0)))
1042   {
1043     /* Avoid twice printing of isam file name */
1044     param->error_printed++;
1045     switch (my_errno) {
1046     case HA_ERR_CRASHED:
1047       _ma_check_print_error(param,"'%s' doesn't have a correct index definition. You need to recreate it before you can do a repair",filename);
1048       break;
1049     case HA_ERR_NOT_A_TABLE:
1050       _ma_check_print_error(param,"'%s' is not a Aria table",filename);
1051       break;
1052     case HA_ERR_CRASHED_ON_USAGE:
1053       _ma_check_print_error(param,"'%s' is marked as crashed",filename);
1054       break;
1055     case HA_ERR_CRASHED_ON_REPAIR:
1056       _ma_check_print_error(param,"'%s' is marked as crashed after last repair",filename);
1057       break;
1058     case HA_ERR_OLD_FILE:
1059       _ma_check_print_error(param,"'%s' is a old type of Aria table", filename);
1060       break;
1061     case HA_ERR_NEW_FILE:
1062       _ma_check_print_error(param,"'%s' uses new features not supported by this version of the Aria library", filename);
1063       break;
1064     case HA_ERR_END_OF_FILE:
1065       _ma_check_print_error(param,"Couldn't read complete header from '%s'", filename);
1066       break;
1067     case EAGAIN:
1068       _ma_check_print_error(param,"'%s' is locked. Use -w to wait until unlocked",filename);
1069       break;
1070     case ENOENT:
1071       _ma_check_print_error(param,"File '%s' doesn't exist",filename);
1072       break;
1073     case EACCES:
1074       _ma_check_print_error(param,"You don't have permission to use '%s'",
1075                             filename);
1076       break;
1077     default:
1078       _ma_check_print_error(param,"%d when opening Aria table '%s'",
1079 		  my_errno,filename);
1080       break;
1081     }
1082     DBUG_RETURN(1);
1083   }
1084   share= info->s;
1085   share->tot_locks-= share->r_locks;
1086   share->r_locks=0;
1087   maria_block_size= share->base.block_size;
1088 
1089   if (share->data_file_type == BLOCK_RECORD ||
1090       ((param->testflag & T_UNPACK) &&
1091        share->state.header.org_data_file_type == BLOCK_RECORD))
1092   {
1093     if (param->testflag & T_SORT_RECORDS)
1094     {
1095       _ma_check_print_error(param,
1096                             "Record format used by '%s' is is not yet supported with sort-records",
1097                             filename);
1098       param->error_printed= 0;
1099       error= 1;
1100       goto end2;
1101     }
1102     /* We can't do parallel repair with BLOCK_RECORD yet */
1103     if (param->testflag & T_REP_PARALLEL)
1104     {
1105       param->testflag&= ~T_REP_PARALLEL;
1106       param->testflag|= T_REP_BY_SORT;
1107     }
1108   }
1109   if ((share->base.extra_options & MA_EXTRA_OPTIONS_ENCRYPTED) &&
1110       !(param->testflag & T_DESCRIPT))
1111   {
1112     _ma_check_print_warning(param,
1113                             "Table %s is encrypted. Only --description (-d) "
1114                             "option is supported", filename);
1115     param->warning_printed= 0;
1116     goto end2;
1117   }
1118 
1119   /*
1120     Skip the checking of the file if:
1121     We are using --fast and the table is closed properly
1122     We are using --check-only-changed-tables and the table hasn't changed
1123   */
1124   if (param->testflag & (T_FAST | T_CHECK_ONLY_CHANGED))
1125   {
1126     my_bool need_to_check= (maria_is_crashed(info) ||
1127                             share->state.open_count != 0);
1128 
1129     if ((param->testflag & (T_REP_ANY | T_SORT_RECORDS)) &&
1130 	((share->state.changed & (STATE_CHANGED | STATE_CRASHED_FLAGS |
1131 				  STATE_IN_REPAIR) ||
1132 	  !(param->testflag & T_CHECK_ONLY_CHANGED))))
1133       need_to_check=1;
1134 
1135     if (info->s->base.keys && info->state->records)
1136     {
1137       if ((param->testflag & T_STATISTICS) &&
1138           (share->state.changed & STATE_NOT_ANALYZED))
1139         need_to_check=1;
1140       if ((param->testflag & T_SORT_INDEX) &&
1141           (share->state.changed & STATE_NOT_SORTED_PAGES))
1142         need_to_check=1;
1143       if ((param->testflag & T_REP_BY_SORT) &&
1144           (share->state.changed & STATE_NOT_OPTIMIZED_KEYS))
1145         need_to_check=1;
1146     }
1147     if ((param->testflag & T_CHECK_ONLY_CHANGED) &&
1148 	(share->state.changed & (STATE_CHANGED | STATE_CRASHED_FLAGS |
1149 				 STATE_IN_REPAIR)))
1150       need_to_check=1;
1151     if (!need_to_check)
1152     {
1153       if (!(param->testflag & T_SILENT) || param->testflag & T_INFO)
1154 	printf("Aria file: %s is already checked\n",filename);
1155       if (maria_close(info))
1156       {
1157 	_ma_check_print_error(param,"%d when closing Aria table '%s'",
1158 			     my_errno,filename);
1159 	DBUG_RETURN(1);
1160       }
1161       DBUG_RETURN(0);
1162     }
1163   }
1164   if ((param->testflag & (T_REP_ANY | T_STATISTICS |
1165 			  T_SORT_RECORDS | T_SORT_INDEX)) &&
1166       (((param->testflag & T_UNPACK) &&
1167 	share->data_file_type == COMPRESSED_RECORD) ||
1168        mi_uint2korr(share->state.header.state_info_length) !=
1169        MARIA_STATE_INFO_SIZE ||
1170        mi_uint2korr(share->state.header.base_info_length) !=
1171        MARIA_BASE_INFO_SIZE ||
1172        maria_is_any_intersect_keys_active(param->keys_in_use, share->base.keys,
1173                                        ~share->state.key_map) ||
1174        maria_test_if_almost_full(info) ||
1175        info->s->state.header.file_version[3] != maria_file_magic[3] ||
1176        (set_collation &&
1177         set_collation->number != share->base.language)))
1178   {
1179     if (set_collation)
1180       param->language= set_collation->number;
1181     if (maria_recreate_table(param, &info,filename))
1182     {
1183       fprintf(stderr, "Aria table '%s' is not fixed because of errors\n",
1184 	      filename);
1185       DBUG_RETURN(-1);
1186     }
1187     recreate=1;
1188     if (!(param->testflag & T_REP_ANY))
1189     {
1190       param->testflag|=T_REP_BY_SORT;		/* if only STATISTICS */
1191       if (!(param->testflag & T_SILENT))
1192 	printf("- '%s' has old table-format. Recreating index\n",filename);
1193       rep_quick= 1;
1194     }
1195     share= info->s;
1196     share->tot_locks-= share->r_locks;
1197     share->r_locks=0;
1198   }
1199 
1200   if (param->testflag & T_DESCRIPT)
1201   {
1202     param->total_files++;
1203     param->total_records+=info->state->records;
1204     param->total_deleted+=info->state->del;
1205     descript(param, info, filename);
1206     maria_close(info);                          /* Should always succeed */
1207     DBUG_RETURN(0);
1208   }
1209 
1210   if (!stopwords_inited++)
1211     ft_init_stopwords();
1212 
1213   if (!(param->testflag & T_READONLY))
1214     lock_type = F_WRLCK;			/* table is changed */
1215   else
1216     lock_type= F_RDLCK;
1217   if (info->lock_type == F_RDLCK)
1218     info->lock_type=F_UNLCK;			/* Read only table */
1219   if (_ma_readinfo(info,lock_type,0))
1220   {
1221     _ma_check_print_error(param,"Can't lock indexfile of '%s', error: %d",
1222                           filename,my_errno);
1223     param->error_printed=0;
1224     error= 1;
1225     goto end2;
1226   }
1227   /*
1228     _ma_readinfo() has locked the table.
1229     We mark the table as locked (without doing file locks) to be able to
1230     use functions that only works on locked tables (like row caching).
1231   */
1232   maria_lock_database(info, F_EXTRA_LCK);
1233   datafile= info->dfile.file;
1234   if (init_pagecache(maria_pagecache, (size_t) param->use_buffers, 0, 0,
1235                      maria_block_size, 0, MY_WME) == 0)
1236   {
1237     _ma_check_print_error(param, "Can't initialize page cache with %lu memory",
1238                           (ulong) param->use_buffers);
1239     error= 1;
1240     goto end2;
1241   }
1242 
1243   if (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX |
1244                          T_ZEROFILL))
1245   {
1246     /*
1247       Mark table as not transactional to avoid logging. Should not be needed,
1248       maria_repair and maria_zerofill do it already.
1249     */
1250     _ma_tmp_disable_logging_for_table(info, FALSE);
1251 
1252     if (param->testflag & T_REP_ANY)
1253     {
1254       ulonglong tmp=share->state.key_map;
1255       maria_copy_keys_active(share->state.key_map, share->base.keys,
1256                              param->keys_in_use);
1257       if (tmp != share->state.key_map)
1258         info->update|=HA_STATE_CHANGED;
1259 
1260       if (rep_quick &&
1261           maria_chk_del(param, info, param->testflag & ~T_VERBOSE))
1262       {
1263         if (param->testflag & T_FORCE_CREATE)
1264         {
1265           rep_quick=0;
1266           _ma_check_print_info(param,"Creating new data file\n");
1267         }
1268         else
1269         {
1270           error=1;
1271           _ma_check_print_error(param,
1272                                 "Quick-recover aborted; Run recovery without switch 'q'");
1273         }
1274       }
1275     }
1276     if (!error)
1277     {
1278       /*
1279         Unless this was only --zerofill-keep-lsn, old REDOs are not
1280         applicable, tell the server's Recovery to ignore them; we don't
1281         know what the log's end LSN is now, so we just let the server know
1282         that it will have to find and store it.
1283         This is the only case where create_rename_lsn can be a horizon and not
1284         a LSN.
1285         If this was only --zerofill-keep-lsn, the table can be used in
1286         Recovery and especially in this scenario: do a dirty-copy-based backup
1287         (snapshot-like), --zerofill-keep-lsn on the copies to achieve better
1288         compression, compress the copies with an external tool, and after a
1289         restore, Recovery still works (because pages and state still have
1290         their correct LSNs).
1291       */
1292       if (share->base.born_transactional &&
1293           ((param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX |
1294                                T_ZEROFILL | T_ZEROFILL_KEEP_LSN)) !=
1295            (T_ZEROFILL | T_ZEROFILL_KEEP_LSN)))
1296       {
1297         share->state.create_rename_lsn= share->state.is_of_horizon=
1298           share->state.skip_redo_lsn= LSN_NEEDS_NEW_STATE_LSNS;
1299         share->state.create_trid= 0;
1300       }
1301     }
1302     if (!error && (param->testflag & T_REP_ANY))
1303     {
1304       if ((param->testflag & (T_REP_BY_SORT | T_REP_PARALLEL)) &&
1305           (maria_is_any_key_active(share->state.key_map) ||
1306            (rep_quick && !param->keys_in_use && !recreate)) &&
1307           maria_test_if_sort_rep(info, info->state->records,
1308                                  info->s->state.key_map,
1309                                  param->force_sort))
1310       {
1311         if (param->testflag & T_REP_BY_SORT)
1312           error=maria_repair_by_sort(param,info,filename,rep_quick);
1313         else
1314           error=maria_repair_parallel(param,info,filename,rep_quick);
1315         state_updated=1;
1316       }
1317       else
1318         error=maria_repair(param, info,filename,rep_quick);
1319     }
1320     if (!error && (param->testflag & T_SORT_RECORDS))
1321     {
1322       /*
1323         The data file is nowadays reopened in the repair code so we should
1324         soon remove the following reopen-code
1325       */
1326 #ifndef TO_BE_REMOVED
1327       if (param->out_flag & O_NEW_DATA)
1328       {			/* Change temp file to org file */
1329         mysql_file_close(info->dfile.file, MYF(MY_WME)); /* Close new file */
1330         error|=maria_change_to_newfile(filename,MARIA_NAME_DEXT,DATA_TMP_EXT,
1331                                        0, MYF(0));
1332         if (_ma_open_datafile(info, info->s))
1333           error=1;
1334         param->out_flag&= ~O_NEW_DATA; /* We are using new datafile */
1335         param->read_cache.file= info->dfile.file;
1336       }
1337 #endif
1338       if (! error)
1339       {
1340         uint key;
1341         /*
1342           We can't update the index in maria_sort_records if we have a
1343           prefix compressed or fulltext index
1344         */
1345         my_bool update_index=1;
1346         for (key=0 ; key < share->base.keys; key++)
1347           if (share->keyinfo[key].flag & (HA_BINARY_PACK_KEY|HA_FULLTEXT))
1348             update_index=0;
1349 
1350         error=maria_sort_records(param,info,filename,param->opt_sort_key,
1351                                  /* what is the following parameter for ? */
1352                                  (my_bool) !(param->testflag & T_REP),
1353                                  update_index);
1354         datafile= info->dfile.file;	/* This is now locked */
1355         if (!error && !update_index)
1356         {
1357           if (param->verbose)
1358             puts("Table had a compressed index;  We must now recreate the index");
1359           error=maria_repair_by_sort(param,info,filename,1);
1360         }
1361       }
1362     }
1363     if (!error && (param->testflag & T_SORT_INDEX))
1364       error= maria_sort_index(param,info,filename);
1365     if (!error && (param->testflag & T_ZEROFILL))
1366       error= maria_zerofill(param, info, filename);
1367     if (!error)
1368     {
1369       DBUG_PRINT("info", ("Resetting crashed state"));
1370       share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED_FLAGS |
1371                                STATE_IN_REPAIR);
1372     }
1373     else
1374       maria_mark_crashed(info);
1375   }
1376   else if ((param->testflag & T_CHECK) || !(param->testflag & T_AUTO_INC))
1377   {
1378     if (!(param->testflag & T_VERY_SILENT) || param->testflag & T_INFO)
1379       printf("Checking Aria file: %s\n",filename);
1380     if (!(param->testflag & T_SILENT))
1381       printf("Data records: %7s   Deleted blocks: %7s\n",
1382              llstr(info->state->records,llbuff),
1383              llstr(info->state->del,llbuff2));
1384     maria_chk_init_for_check(param, info);
1385     if (opt_warning_for_wrong_transid == 0)
1386       param->max_trid= ~ (ulonglong) 0;
1387 
1388     error= maria_chk_status(param,info);
1389     /* Forget warning printed by maria_chk_status if no problems found */
1390     warning_printed_by_chk_status= param->warning_printed;
1391     param->warning_printed= 0;
1392 
1393     maria_intersect_keys_active(share->state.key_map, param->keys_in_use);
1394     error|= maria_chk_size(param,info);
1395     if (!error || !(param->testflag & (T_FAST | T_FORCE_CREATE)))
1396       error|=maria_chk_del(param, info,param->testflag);
1397     if ((!error || (!(param->testflag & (T_FAST | T_FORCE_CREATE)) &&
1398                     !param->start_check_pos)))
1399     {
1400       error|=maria_chk_key(param, info);
1401       if (!error && (param->testflag & (T_STATISTICS | T_AUTO_INC)))
1402         error=maria_update_state_info(param, info,
1403                                       ((param->testflag & T_STATISTICS) ?
1404                                        UPDATE_STAT : 0) |
1405                                       ((param->testflag & T_AUTO_INC) ?
1406                                        UPDATE_AUTO_INC : 0));
1407     }
1408     if ((!rep_quick && !error) ||
1409         !(param->testflag & (T_FAST | T_FORCE_CREATE)))
1410     {
1411       init_io_cache(&param->read_cache,datafile,
1412                          (uint) param->read_buffer_length,
1413                          READ_CACHE,
1414                          (param->start_check_pos ?
1415                           param->start_check_pos :
1416                           share->pack.header_length),
1417                          1,
1418                          MYF(MY_WME));
1419       maria_lock_memory(param);
1420       if ((info->s->data_file_type != STATIC_RECORD) ||
1421           (param->testflag & (T_EXTEND | T_MEDIUM)))
1422         error|=maria_chk_data_link(param, info,
1423                                    MY_TEST(param->testflag & T_EXTEND));
1424       end_io_cache(&param->read_cache);
1425     }
1426     if (!error)
1427     {
1428       if (((share->state.changed &
1429             (STATE_CHANGED | STATE_CRASHED_FLAGS | STATE_IN_REPAIR)) ||
1430            share->state.open_count != 0)
1431           && (param->testflag & T_UPDATE_STATE))
1432         info->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
1433       DBUG_PRINT("info", ("Resetting crashed state"));
1434       share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED_FLAGS |
1435                                STATE_IN_REPAIR);
1436     }
1437     else if (!maria_is_crashed(info) &&
1438              (param->testflag & T_UPDATE_STATE))
1439     {						/* Mark crashed */
1440       maria_mark_crashed(info);
1441       info->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
1442     }
1443   }
1444 
1445   if ((param->testflag & T_AUTO_INC) ||
1446       ((param->testflag & T_REP_ANY) && info->s->base.auto_key))
1447     _ma_update_auto_increment_key(param, info,
1448                                   (my_bool)
1449                                   !MY_TEST(param->testflag & T_AUTO_INC));
1450 
1451   if (info->update & HA_STATE_CHANGED && ! (param->testflag & T_READONLY))
1452   {
1453     error|=maria_update_state_info(param, info,
1454                                    UPDATE_OPEN_COUNT |
1455                                    (((param->testflag &
1456                                       (T_REP_ANY | T_UPDATE_STATE)) ?
1457                                      UPDATE_TIME : 0) |
1458                                     (state_updated ? UPDATE_STAT : 0) |
1459                                     ((param->testflag & T_SORT_RECORDS) ?
1460                                      UPDATE_SORT : 0)));
1461     if (warning_printed_by_chk_status)
1462       _ma_check_print_info(param, "Aria table '%s' was ok. Status updated",
1463                           filename);
1464     else if (!(param->testflag & T_SILENT))
1465       printf("State updated\n");
1466     warning_printed_by_chk_status= 0;
1467   }
1468   info->update&= ~HA_STATE_CHANGED;
1469   _ma_reenable_logging_for_table(info, FALSE);
1470   maria_lock_database(info, F_UNLCK);
1471 
1472 end2:
1473   born_transactional= share->base.born_transactional;
1474   if (maria_close(info))
1475   {
1476     _ma_check_print_error(param, default_close_errmsg, my_errno, filename);
1477     DBUG_RETURN(1);
1478   }
1479   end_pagecache(maria_pagecache, 1);
1480   if (error == 0)
1481   {
1482     if (param->out_flag & O_NEW_DATA)
1483       error|=maria_change_to_newfile(filename,MARIA_NAME_DEXT,DATA_TMP_EXT,
1484                                      param->backup_time,
1485                                      ((param->testflag & T_BACKUP_DATA) ?
1486                                       MYF(MY_REDEL_MAKE_BACKUP) : MYF(0)));
1487   }
1488   if (opt_transaction_logging &&
1489       born_transactional && !error &&
1490       (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX |
1491                           T_ZEROFILL)))
1492     error= write_log_record(param);
1493 
1494   if (param->not_visible_rows_found && (param->testflag & T_VERBOSE))
1495   {
1496     char buff[22];
1497     printf("Max transaction id found: %s\n",
1498            llstr(param->max_found_trid, buff));
1499   }
1500 
1501   fflush(stdout);
1502   fflush(stderr);
1503 
1504   if (param->error_printed)
1505   {
1506     error= 2;
1507     if (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX))
1508     {
1509       fprintf(stderr, "Aria table '%s' is not fixed because of errors\n",
1510 		   filename);
1511       if (param->testflag & T_REP_ANY)
1512 	fprintf(stderr, "Try fixing it by using the --safe-recover (-o), "
1513                 "the --force (-f) option or by not using the --quick (-q) "
1514                 "flag\n");
1515     }
1516     else if (!(param->testflag & T_FORCE_CREATE))
1517       fprintf(stderr, "Aria table '%s' is corrupted\nFix it using switch "
1518               "\"-r\" or \"-o\"\n", filename);
1519   }
1520   else if ((param->warning_printed || warning_printed_by_chk_status) &&
1521 	   ! (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX |
1522 			  T_FORCE_CREATE)))
1523   {
1524     if (!error)
1525       error= 1;
1526     (void) fprintf(stderr, "Aria table '%s' is usable but should be fixed\n",
1527                    filename);
1528   }
1529 
1530   (void) fflush(stderr);
1531   DBUG_RETURN(error);
1532 } /* maria_chk */
1533 
1534 
1535 /* Write info about table */
1536 
descript(HA_CHECK * param,register MARIA_HA * info,char * name)1537 static void descript(HA_CHECK *param, register MARIA_HA *info, char *name)
1538 {
1539   uint key,keyseg_nr,field;
1540   reg3 MARIA_KEYDEF *keyinfo;
1541   reg2 HA_KEYSEG *keyseg;
1542   reg4 const char *text;
1543   char buff[200],length[10],*pos,*end;
1544   enum en_fieldtype type;
1545   MARIA_SHARE *share= info->s;
1546   char llbuff[22],llbuff2[22];
1547   DBUG_ENTER("descript");
1548 
1549   if (param->testflag & T_VERY_SILENT)
1550   {
1551     longlong checksum= info->state->checksum;
1552     if (!(share->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)))
1553       checksum= 0;
1554     printf("%s %s %s\n", name, llstr(info->state->records,llbuff),
1555            llstr(checksum, llbuff2));
1556     DBUG_VOID_RETURN;
1557   }
1558 
1559   printf("Aria file:           %s\n",name);
1560   printf("Record format:       %s\n", record_formats[share->data_file_type]);
1561   printf("Crashsafe:           %s\n",
1562          share->base.born_transactional ? "yes" : "no");
1563   printf("Character set:       %s (%d)\n",
1564 	 get_charset_name(share->base.language),
1565          (int) share->base.language);
1566 
1567   if (param->testflag & T_VERBOSE)
1568   {
1569     if (share->base.extra_options & MA_EXTRA_OPTIONS_ENCRYPTED)
1570       printf("Encrypted:           yes\n");
1571     printf("File-version:        %d\n",
1572 	   (int) share->state.header.file_version[3]);
1573     if (share->state.create_time)
1574     {
1575       get_date(buff,1,share->state.create_time);
1576       printf("Creation time:       %s\n",buff);
1577     }
1578     if (share->state.check_time)
1579     {
1580       get_date(buff,1,share->state.check_time);
1581       printf("Check/recover time:  %s\n",buff);
1582     }
1583     if (share->base.born_transactional)
1584     {
1585       printf("LSNs:                create_rename " LSN_FMT ","
1586              " state_horizon " LSN_FMT ", skip_redo " LSN_FMT "\n",
1587              LSN_IN_PARTS(share->state.create_rename_lsn),
1588              LSN_IN_PARTS(share->state.is_of_horizon),
1589              LSN_IN_PARTS(share->state.skip_redo_lsn));
1590       printf("create_trid:         %s\n",
1591              llstr(share->state.create_trid, llbuff));
1592     }
1593     compile_time_assert((MY_UUID_STRING_LENGTH + 1) <= sizeof(buff));
1594     buff[MY_UUID_STRING_LENGTH]= 0;
1595     my_uuid2str(share->base.uuid, buff);
1596     printf("UUID:                %s\n", buff);
1597     pos=buff;
1598     if (share->state.changed & STATE_CRASHED)
1599       strmov(buff, share->state.changed & STATE_CRASHED_ON_REPAIR ?
1600              "crashed on repair" : "crashed");
1601     else if (have_control_file &&
1602              (share->state.changed & (STATE_MOVED | STATE_NOT_ZEROFILLED)) ==
1603              (STATE_MOVED | STATE_NOT_ZEROFILLED))
1604       strmov(buff, "moved from another system. Use --zerofill to fix it");
1605     else
1606     {
1607       if (share->state.open_count)
1608 	pos=strmov(pos,"open,");
1609       if (share->state.changed & STATE_CHANGED)
1610 	pos=strmov(pos,"changed,");
1611       else
1612 	pos=strmov(pos,"checked,");
1613       if (!(share->state.changed & STATE_NOT_ANALYZED))
1614 	pos=strmov(pos,"analyzed,");
1615       if (!(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))
1616 	pos=strmov(pos,"optimized keys,");
1617       if (!(share->state.changed & STATE_NOT_SORTED_PAGES))
1618 	pos=strmov(pos,"sorted index pages,");
1619       if (!(share->state.changed & STATE_NOT_ZEROFILLED))
1620 	pos=strmov(pos,"zerofilled,");
1621       if (!(share->state.changed & STATE_NOT_MOVABLE))
1622 	pos=strmov(pos,"movable,");
1623       if (have_control_file && (share->state.changed & STATE_MOVED))
1624 	pos=strmov(pos,"moved,");
1625       pos[-1]=0;				/* Remove extra ',' */
1626     }
1627     printf("Status:              %s\n",buff);
1628     if (share->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
1629       printf("Checksum:  %26s\n",llstr(info->state->checksum,llbuff));
1630 ;
1631     if (share->options & HA_OPTION_DELAY_KEY_WRITE)
1632       printf("Keys are only flushed at close\n");
1633 
1634     if (share->options & HA_OPTION_PAGE_CHECKSUM)
1635       printf("Page checksums are used\n");
1636     if (share->base.auto_key)
1637     {
1638       printf("Auto increment key:  %16d  Last value:         %18s\n",
1639 	     share->base.auto_key,
1640 	     llstr(share->state.auto_increment,llbuff));
1641     }
1642   }
1643   printf("Data records:        %16s  Deleted blocks:     %18s\n",
1644 	 llstr(info->state->records,llbuff),llstr(info->state->del,llbuff2));
1645   if (param->testflag & T_SILENT)
1646     DBUG_VOID_RETURN;				/* This is enough */
1647 
1648   if (param->testflag & T_VERBOSE)
1649   {
1650 #ifdef USE_RELOC
1651     printf("Init-relocation:     %16s\n",llstr(share->base.reloc,llbuff));
1652 #endif
1653     printf("Datafile parts:      %16s  Deleted data:       %18s\n",
1654 	   llstr(share->state.split,llbuff),
1655 	   llstr(info->state->empty,llbuff2));
1656     printf("Datafile pointer (bytes): %11d  Keyfile pointer (bytes): %13d\n",
1657 	   share->rec_reflength,share->base.key_reflength);
1658     printf("Datafile length:     %16s  Keyfile length:     %18s\n",
1659 	   llstr(info->state->data_file_length,llbuff),
1660 	   llstr(info->state->key_file_length,llbuff2));
1661 
1662     if (info->s->base.reloc == 1L && info->s->base.records == 1L)
1663       puts("This is a one-record table");
1664     else
1665     {
1666       if (share->base.max_data_file_length != HA_OFFSET_ERROR ||
1667 	  share->base.max_key_file_length != HA_OFFSET_ERROR)
1668 	printf("Max datafile length: %16s  Max keyfile length: %18s\n",
1669 	       ullstr(share->base.max_data_file_length,llbuff),
1670 	       ullstr(share->base.max_key_file_length,llbuff2));
1671     }
1672   }
1673   printf("Block_size:          %16d\n",(int) share->block_size);
1674   printf("Recordlength:        %16d\n",(int) share->base.pack_reclength);
1675   if (! maria_is_all_keys_active(share->state.key_map, share->base.keys))
1676   {
1677     longlong2str(share->state.key_map,buff,2);
1678     printf("Using only keys '%s' of %d possibly keys\n",
1679 	   buff, share->base.keys);
1680   }
1681   puts("\nTable description:");
1682   printf("Key Start Len Index    Type");
1683   if (param->testflag & T_VERBOSE)
1684     printf("                     Rec/key         Root  Blocksize");
1685   putchar('\n');
1686 
1687   for (key=keyseg_nr=0, keyinfo= &share->keyinfo[0] ;
1688        key < share->base.keys;
1689        key++,keyinfo++)
1690   {
1691     keyseg=keyinfo->seg;
1692     if (keyinfo->flag & HA_NOSAME) text="unique ";
1693     else if (keyinfo->flag & HA_FULLTEXT) text="fulltext ";
1694     else text="multip.";
1695 
1696     pos=buff;
1697     if (keyseg->flag & HA_REVERSE_SORT)
1698       *pos++ = '-';
1699     pos=strmov(pos,type_names[keyseg->type]);
1700     *pos++ = ' ';
1701     *pos=0;
1702     if (keyinfo->flag & HA_PACK_KEY)
1703       pos=strmov(pos,prefix_packed_txt);
1704     if (keyinfo->flag & HA_BINARY_PACK_KEY)
1705       pos=strmov(pos,bin_packed_txt);
1706     if (keyseg->flag & HA_SPACE_PACK)
1707       pos=strmov(pos,diff_txt);
1708     if (keyseg->flag & HA_BLOB_PART)
1709       pos=strmov(pos,blob_txt);
1710     if (keyseg->flag & HA_NULL_PART)
1711       pos=strmov(pos,null_txt);
1712     *pos=0;
1713 
1714     printf("%-4d%-6ld%-3d %-9s%-23s",
1715 	   key+1,(long) keyseg->start+1,keyseg->length,text,buff);
1716     if (share->state.key_root[key] != HA_OFFSET_ERROR)
1717       llstr(share->state.key_root[key],buff);
1718     else
1719       buff[0]=0;
1720     if (param->testflag & T_VERBOSE)
1721       printf("%9.0f %12s %10d",
1722 	     share->state.rec_per_key_part[keyseg_nr++],
1723 	     buff,keyinfo->block_length);
1724     putchar('\n');
1725     while ((++keyseg)->type != HA_KEYTYPE_END)
1726     {
1727       pos=buff;
1728       if (keyseg->flag & HA_REVERSE_SORT)
1729 	*pos++ = '-';
1730       pos=strmov(pos,type_names[keyseg->type]);
1731       *pos++= ' ';
1732       if (keyseg->flag & HA_SPACE_PACK)
1733 	pos=strmov(pos,diff_txt);
1734       if (keyseg->flag & HA_BLOB_PART)
1735 	pos=strmov(pos,blob_txt);
1736       if (keyseg->flag & HA_NULL_PART)
1737 	pos=strmov(pos,null_txt);
1738       *pos=0;
1739       printf("    %-6ld%-3d          %-21s",
1740 	     (long) keyseg->start+1,keyseg->length,buff);
1741       if (param->testflag & T_VERBOSE)
1742 	printf("%11.0f", share->state.rec_per_key_part[keyseg_nr++]);
1743       putchar('\n');
1744     }
1745     keyseg++;
1746   }
1747   if (share->state.header.uniques)
1748   {
1749     MARIA_UNIQUEDEF *uniqueinfo;
1750     puts("\nUnique  Key  Start  Len  Nullpos  Nullbit  Type");
1751     for (key=0,uniqueinfo= &share->uniqueinfo[0] ;
1752 	 key < share->state.header.uniques; key++, uniqueinfo++)
1753     {
1754       my_bool new_row=0;
1755       char null_bit[8],null_pos[8];
1756       printf("%-8d%-5d",key+1,uniqueinfo->key+1);
1757       for (keyseg=uniqueinfo->seg ; keyseg->type != HA_KEYTYPE_END ; keyseg++)
1758       {
1759 	if (new_row)
1760 	  fputs("             ",stdout);
1761 	null_bit[0]=null_pos[0]=0;
1762 	if (keyseg->null_bit)
1763 	{
1764 	  my_snprintf(null_bit, sizeof(null_bit), "%d", keyseg->null_bit);
1765 	  my_snprintf(null_pos, sizeof(null_pos), "%ld", (long) keyseg->null_pos+1);
1766 	}
1767 	printf("%-7ld%-5d%-9s%-10s%-30s\n",
1768 	       (long) keyseg->start+1,keyseg->length,
1769 	       null_pos,null_bit,
1770 	       type_names[keyseg->type]);
1771 	new_row=1;
1772       }
1773     }
1774   }
1775   if (param->verbose > 1)
1776   {
1777     char null_bit[8],null_pos[8];
1778     printf("\nField Start Length Nullpos Nullbit Type");
1779     if (share->options & HA_OPTION_COMPRESS_RECORD)
1780       printf("                         Huff tree  Bits");
1781     putchar('\n');
1782 
1783     for (field=0 ; field < share->base.fields ; field++)
1784     {
1785       if (share->options & HA_OPTION_COMPRESS_RECORD)
1786 	type=share->columndef[field].base_type;
1787       else
1788 	type=(enum en_fieldtype) share->columndef[field].type;
1789       end= strmov(buff, field_pack[type]);
1790       if (end != buff)
1791       {
1792         *(end++)=',';
1793         *(end++)=' ';
1794       }
1795       if (share->options & HA_OPTION_COMPRESS_RECORD)
1796       {
1797 	if (share->columndef[field].pack_type & PACK_TYPE_SELECTED)
1798 	  end=strmov(end,"not_always, ");
1799 	if (share->columndef[field].pack_type & PACK_TYPE_SPACE_FIELDS)
1800 	  end=strmov(end,"no empty, ");
1801 	if (share->columndef[field].pack_type & PACK_TYPE_ZERO_FILL)
1802 	{
1803 	  sprintf(end,"zerofill(%d), ",share->columndef[field].space_length_bits);
1804 	  end=strend(end);
1805 	}
1806       }
1807       if (end != buff)
1808         end[-2]= 0;
1809       int10_to_str((long) share->columndef[field].length,length,10);
1810       null_bit[0]=null_pos[0]=0;
1811       if (share->columndef[field].null_bit)
1812       {
1813 	sprintf(null_bit,"%d",share->columndef[field].null_bit);
1814 	sprintf(null_pos,"%d",share->columndef[field].null_pos+1);
1815       }
1816       printf("%-6d%-6u%-7s%-8s%-8s%-35s",field+1,
1817              (uint) share->columndef[field].offset+1,
1818              length, null_pos, null_bit, buff);
1819       if (share->options & HA_OPTION_COMPRESS_RECORD)
1820       {
1821 	if (share->columndef[field].huff_tree)
1822 	  printf("%3d    %2d",
1823 		 (uint) (share->columndef[field].huff_tree-share->decode_trees)+1,
1824 		 share->columndef[field].huff_tree->quick_table_bits);
1825       }
1826       putchar('\n');
1827     }
1828     if (share->data_file_type == BLOCK_RECORD)
1829     {
1830       uint i;
1831       puts("\nBitmap  Data size  Description");
1832       for (i=0 ; i <= 7 ; i++)
1833         printf("%u           %5u  %s\n", i, share->bitmap.sizes[i],
1834                bitmap_description[i]);
1835     }
1836   }
1837   DBUG_VOID_RETURN;
1838 } /* describe */
1839 
1840 
1841 	/* Sort records according to one key */
1842 
maria_sort_records(HA_CHECK * param,register MARIA_HA * info,char * name,uint sort_key,my_bool write_info,my_bool update_index)1843 static int maria_sort_records(HA_CHECK *param,
1844 			   register MARIA_HA *info, char *name,
1845 			   uint sort_key,
1846 			   my_bool write_info,
1847 			   my_bool update_index)
1848 {
1849   int got_error;
1850   uint key;
1851   MARIA_KEYDEF *keyinfo;
1852   File new_file;
1853   uchar *temp_buff;
1854   ha_rows old_record_count;
1855   MARIA_SHARE *share= info->s;
1856   char llbuff[22],llbuff2[22];
1857   MARIA_SORT_INFO sort_info;
1858   MARIA_SORT_PARAM sort_param;
1859   MARIA_PAGE page;
1860   DBUG_ENTER("sort_records");
1861 
1862   bzero((char*)&sort_info,sizeof(sort_info));
1863   bzero((char*)&sort_param,sizeof(sort_param));
1864   sort_param.sort_info=&sort_info;
1865   sort_info.param=param;
1866   keyinfo= &share->keyinfo[sort_key];
1867   got_error=1;
1868   temp_buff=0;
1869   new_file= -1;
1870 
1871   if (! maria_is_key_active(share->state.key_map, sort_key))
1872   {
1873     _ma_check_print_warning(param,
1874 			   "Can't sort table '%s' on key %d;  No such key",
1875                             name,sort_key+1);
1876     param->error_printed=0;
1877     DBUG_RETURN(0);				/* Nothing to do */
1878   }
1879   if (keyinfo->flag & HA_FULLTEXT)
1880   {
1881     _ma_check_print_warning(param,"Can't sort table '%s' on FULLTEXT key %d",
1882                             name,sort_key+1);
1883     param->error_printed=0;
1884     DBUG_RETURN(0);				/* Nothing to do */
1885   }
1886   if (keyinfo->flag & HA_BINARY_PACK_KEY)
1887   {
1888     _ma_check_print_warning(param,
1889                             "Can't sort table '%s' on a key with prefix "
1890                             "packing %d",
1891                             name,sort_key+1);
1892     param->error_printed=0;
1893     DBUG_RETURN(0);
1894   }
1895 
1896 
1897   if (share->data_file_type == COMPRESSED_RECORD)
1898   {
1899     _ma_check_print_warning(param,"Can't sort read-only table '%s'", name);
1900     param->error_printed=0;
1901     DBUG_RETURN(0);				/* Nothing to do */
1902   }
1903   if (!(param->testflag & T_SILENT))
1904   {
1905     printf("- Sorting records for Aria table '%s'\n",name);
1906     if (write_info)
1907       printf("Data records: %9s   Deleted: %9s\n",
1908 	     llstr(info->state->records,llbuff),
1909 	     llstr(info->state->del,llbuff2));
1910   }
1911   if (share->state.key_root[sort_key] == HA_OFFSET_ERROR)
1912     DBUG_RETURN(0);				/* Nothing to do */
1913 
1914   if (init_io_cache(&info->rec_cache,-1,(uint) param->write_buffer_length,
1915 		   WRITE_CACHE,share->pack.header_length,1,
1916 		   MYF(MY_WME | MY_WAIT_IF_FULL)))
1917     goto err;
1918   info->opt_flag|=WRITE_CACHE_USED;
1919 
1920   if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
1921   {
1922     _ma_check_print_error(param,"Not enough memory for key block");
1923     goto err;
1924   }
1925 
1926   if (!(sort_param.record= (uchar*) my_malloc(PSI_INSTRUMENT_ME,
1927                            (uint) share->base.default_rec_buff_size, MYF(0))))
1928   {
1929     _ma_check_print_error(param,"Not enough memory for record");
1930     goto err;
1931   }
1932 
1933   fn_format(param->temp_filename,name,"", MARIA_NAME_DEXT,2+4+32);
1934   new_file= mysql_file_create(key_file_tmp,
1935                               fn_format(param->temp_filename,
1936                                         param->temp_filename, "",
1937                                         DATA_TMP_EXT,
1938                                         MY_REPLACE_EXT | MY_UNPACK_FILENAME),
1939                               0, param->tmpfile_createflag, MYF(0));
1940   if (new_file < 0)
1941   {
1942     _ma_check_print_error(param,"Can't create new tempfile: '%s'",
1943 			 param->temp_filename);
1944     goto err;
1945   }
1946   if (share->pack.header_length)
1947     if (maria_filecopy(param, new_file, info->dfile.file, 0L,
1948                        share->pack.header_length,
1949                        "datafile-header"))
1950       goto err;
1951   info->rec_cache.file=new_file;		/* Use this file for cacheing*/
1952 
1953   maria_lock_memory(param);
1954   for (key=0 ; key < share->base.keys ; key++)
1955     share->keyinfo[key].flag|= HA_SORT_ALLOWS_SAME;
1956 
1957   if (mysql_file_pread(share->kfile.file, temp_buff,
1958                        (uint) keyinfo->block_length,
1959                        share->state.key_root[sort_key],
1960                        MYF(MY_NABP+MY_WME)))
1961   {
1962     _ma_check_print_error(param, "Can't read indexpage from filepos: %s",
1963                           llstr(share->state.key_root[sort_key], llbuff));
1964     goto err;
1965   }
1966 
1967   /* Setup param for _ma_sort_write_record */
1968   sort_info.info=info;
1969   sort_info.new_data_file_type=share->data_file_type;
1970   sort_param.fix_datafile=1;
1971   sort_param.master=1;
1972   sort_param.filepos=share->pack.header_length;
1973   old_record_count=info->state->records;
1974   info->state->records=0;
1975   if (sort_info.new_data_file_type != COMPRESSED_RECORD)
1976     info->state->checksum=0;
1977 
1978   _ma_page_setup(&page, info, keyinfo, share->state.key_root[sort_key],
1979                  temp_buff);
1980   if (sort_record_index(&sort_param, &page, sort_key,new_file,update_index) ||
1981       maria_write_data_suffix(&sort_info,1) ||
1982       flush_io_cache(&info->rec_cache))
1983     goto err;
1984 
1985   if (info->state->records != old_record_count)
1986   {
1987     _ma_check_print_error(param,"found %s of %s records",
1988 		llstr(info->state->records,llbuff),
1989 		llstr(old_record_count,llbuff2));
1990     goto err;
1991   }
1992 
1993   mysql_file_close(info->dfile.file, MYF(MY_WME));
1994   param->out_flag|=O_NEW_DATA;			/* Data in new file */
1995   info->dfile.file= new_file;                   /* Use new datafile */
1996   _ma_set_data_pagecache_callbacks(&info->dfile, info->s);
1997 
1998   info->state->del=0;
1999   info->state->empty=0;
2000   share->state.dellink= HA_OFFSET_ERROR;
2001   info->state->data_file_length=sort_param.filepos;
2002   share->state.split=info->state->records;	/* Only hole records */
2003   share->state.version=(ulong) time((time_t*) 0);
2004 
2005   info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
2006 
2007   if (param->testflag & T_WRITE_LOOP)
2008   {
2009     fputs("          \r",stdout);
2010     fflush(stdout);
2011   }
2012   got_error=0;
2013 
2014 err:
2015   if (got_error && new_file >= 0)
2016   {
2017     end_io_cache(&info->rec_cache);
2018     (void) mysql_file_close(new_file,MYF(MY_WME));
2019     (void) mysql_file_delete(key_file_tmp, param->temp_filename, MYF(MY_WME));
2020   }
2021   if (temp_buff)
2022   {
2023     my_afree(temp_buff);
2024   }
2025   my_free(sort_param.record);
2026   info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
2027   end_io_cache(&info->rec_cache);
2028   my_free(sort_info.buff);
2029   sort_info.buff=0;
2030   share->state.sortkey=sort_key;
2031   DBUG_RETURN(got_error);
2032 } /* sort_records */
2033 
2034 
2035 /* Sort records recursive using one index */
2036 
sort_record_index(MARIA_SORT_PARAM * sort_param,MARIA_PAGE * ma_page,uint sort_key,File new_file,my_bool update_index)2037 static int sort_record_index(MARIA_SORT_PARAM *sort_param,
2038                              MARIA_PAGE *ma_page, uint sort_key,
2039 			     File new_file,my_bool update_index)
2040 {
2041   MARIA_HA *info= ma_page->info;
2042   MARIA_SHARE *share= info->s;
2043   uint	page_flag, nod_flag,used_length;
2044   my_bool buff_alloced;
2045   uchar *temp_buff,*keypos,*endpos;
2046   my_off_t next_page,rec_pos;
2047   uchar *lastkey;
2048   char llbuff[22];
2049   MARIA_SORT_INFO *sort_info= sort_param->sort_info;
2050   HA_CHECK *param=sort_info->param;
2051   MARIA_KEY tmp_key;
2052   MARIA_PAGE new_page;
2053   const MARIA_KEYDEF *keyinfo= ma_page->keyinfo;
2054   DBUG_ENTER("sort_record_index");
2055 
2056   temp_buff=0;
2057   page_flag= ma_page->flag;
2058   nod_flag=  ma_page->node;
2059   tmp_key.keyinfo= (MARIA_KEYDEF*) keyinfo;
2060 
2061   alloc_on_stack(*info->stack_end_ptr, lastkey, buff_alloced,
2062                  (nod_flag ? keyinfo->block_length  : 0) +
2063                  ALIGN_SIZE(keyinfo->max_store_length));
2064   if (!lastkey)
2065   {
2066     _ma_check_print_error(param,"Not Enough memory");
2067     DBUG_RETURN(-1);
2068   }
2069   if (nod_flag)
2070     temp_buff= lastkey + ALIGN_SIZE(keyinfo->max_store_length);
2071 
2072   tmp_key.data=    lastkey;
2073 
2074   used_length= ma_page->size;
2075   keypos= ma_page->buff + share->keypage_header + nod_flag;
2076   endpos= ma_page->buff + used_length;
2077   for ( ;; )
2078   {
2079     if (nod_flag)
2080     {
2081       next_page= _ma_kpos(nod_flag, keypos);
2082       if (mysql_file_pread(share->kfile.file, temp_buff,
2083                            (uint) tmp_key.keyinfo->block_length, next_page,
2084                            MYF(MY_NABP+MY_WME)))
2085       {
2086 	_ma_check_print_error(param,"Can't read keys from filepos: %s",
2087 		    llstr(next_page,llbuff));
2088 	goto err;
2089       }
2090       _ma_page_setup(&new_page, info, ma_page->keyinfo, next_page, temp_buff);
2091 
2092       if (sort_record_index(sort_param, &new_page, sort_key,
2093 			    new_file, update_index))
2094 	goto err;
2095     }
2096     if (keypos >= endpos ||
2097 	!(*keyinfo->get_key)(&tmp_key, page_flag, nod_flag, &keypos))
2098       break;
2099     rec_pos= _ma_row_pos_from_key(&tmp_key);
2100 
2101     if ((*share->read_record)(info,sort_param->record,rec_pos))
2102     {
2103       _ma_check_print_error(param,"%d when reading datafile",my_errno);
2104       goto err;
2105     }
2106     if (rec_pos != sort_param->filepos && update_index)
2107     {
2108       _ma_dpointer(share, keypos - nod_flag - tmp_key.ref_length,
2109 		   sort_param->filepos);
2110       if (maria_movepoint(info,sort_param->record,rec_pos,sort_param->filepos,
2111                           sort_key))
2112       {
2113 	_ma_check_print_error(param,"%d when updating key-pointers",my_errno);
2114 	goto err;
2115       }
2116     }
2117     if (_ma_sort_write_record(sort_param))
2118       goto err;
2119   }
2120   /* Clear end of block to get better compression if the table is backuped */
2121   bzero(ma_page->buff + used_length, keyinfo->block_length - used_length);
2122   if (my_pwrite(share->kfile.file, ma_page->buff, (uint)keyinfo->block_length,
2123 		ma_page->pos, param->myf_rw))
2124   {
2125     _ma_check_print_error(param,"%d when updating keyblock",my_errno);
2126     goto err;
2127   }
2128   stack_alloc_free(lastkey, buff_alloced);
2129   DBUG_RETURN(0);
2130 
2131 err:
2132   stack_alloc_free(lastkey, buff_alloced);
2133   DBUG_RETURN(1);
2134 } /* sort_record_index */
2135 
2136 
write_log_record(HA_CHECK * param)2137 static my_bool write_log_record(HA_CHECK *param)
2138 {
2139   /*
2140     Now that all operations including O_NEW_DATA|INDEX are successfully
2141     done, we can write a log record.
2142   */
2143   MARIA_HA *info= maria_open(param->isam_file_name, O_RDWR, 0, 0);
2144   if (info == NULL)
2145     _ma_check_print_error(param, default_open_errmsg, my_errno,
2146                           param->isam_file_name);
2147   else
2148   {
2149     if (write_log_record_for_repair(param, info))
2150       _ma_check_print_error(param, "%d when writing log record for"
2151                             " Aria table '%s'", my_errno,
2152                             param->isam_file_name);
2153     else if (maria_close(info))
2154       _ma_check_print_error(param, default_close_errmsg, my_errno,
2155                             param->isam_file_name);
2156     else
2157       return FALSE;
2158   }
2159   return TRUE;
2160 }
2161 
2162 #include "ma_check_standalone.h"
2163