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