1 : /* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 : /*
19 : About checksum calculation.
20 :
21 : There are two types of checksums. Table checksum and row checksum.
22 :
23 : Row checksum is an additional uchar at the end of dynamic length
24 : records. It must be calculated if the table is configured for them.
25 : Otherwise they must not be used. The variable
26 : MYISAM_SHARE::calc_checksum determines if row checksums are used.
27 : MI_INFO::checksum is used as temporary storage during row handling.
28 : For parallel repair we must assure that only one thread can use this
29 : variable. There is no problem on the write side as this is done by one
30 : thread only. But when checking a record after read this could go
31 : wrong. But since all threads read through a common read buffer, it is
32 : sufficient if only one thread checks it.
33 :
34 : Table checksum is an eight uchar value in the header of the index file.
35 : It can be calculated even if row checksums are not used. The variable
36 : MI_CHECK::glob_crc is calculated over all records.
37 : MI_SORT_PARAM::calc_checksum determines if this should be done. This
38 : variable is not part of MI_CHECK because it must be set per thread for
39 : parallel repair. The global glob_crc must be changed by one thread
40 : only. And it is sufficient to calculate the checksum once only.
41 : */
42 :
43 : #include "ma_ftdefs.h"
44 : #include "ma_rt_index.h"
45 : #include "ma_blockrec.h"
46 : #include "trnman.h"
47 : #include "ma_key_recover.h"
48 :
49 : #include <stdarg.h>
50 : #include <my_getopt.h>
51 : #ifdef HAVE_SYS_VADVISE_H
52 : #include <sys/vadvise.h>
53 : #endif
54 : #ifdef HAVE_SYS_MMAN_H
55 : #include <sys/mman.h>
56 : #endif
57 :
58 : /* Functions defined in this file */
59 :
60 : static int check_k_link(HA_CHECK *param, MARIA_HA *info, my_off_t next_link);
61 : static int chk_index(HA_CHECK *param, MARIA_HA *info, MARIA_KEYDEF *keyinfo,
62 : MARIA_PAGE *page, ha_rows *keys,
63 : ha_checksum *key_checksum, uint level);
64 : static uint isam_key_length(MARIA_HA *info,MARIA_KEYDEF *keyinfo);
65 : static ha_checksum calc_checksum(ha_rows count);
66 : static int writekeys(MARIA_SORT_PARAM *sort_param);
67 : static int sort_one_index(HA_CHECK *param, MARIA_HA *info,
68 : MARIA_KEYDEF *keyinfo,
69 : my_off_t pagepos, File new_file);
70 : static int sort_key_read(MARIA_SORT_PARAM *sort_param, uchar *key);
71 : static int sort_maria_ft_key_read(MARIA_SORT_PARAM *sort_param, uchar *key);
72 : static int sort_get_next_record(MARIA_SORT_PARAM *sort_param);
73 : static int sort_key_cmp(MARIA_SORT_PARAM *sort_param, const void *a,
74 : const void *b);
75 : static int sort_maria_ft_key_write(MARIA_SORT_PARAM *sort_param,
76 : const uchar *a);
77 : static int sort_key_write(MARIA_SORT_PARAM *sort_param, const uchar *a);
78 : static my_off_t get_record_for_key(MARIA_KEYDEF *keyinfo, const uchar *key);
79 : static int sort_insert_key(MARIA_SORT_PARAM *sort_param,
80 : reg1 SORT_KEY_BLOCKS *key_block,
81 : const uchar *key, my_off_t prev_block);
82 : static int sort_delete_record(MARIA_SORT_PARAM *sort_param);
83 : /*static int _ma_flush_pending_blocks(HA_CHECK *param);*/
84 : static SORT_KEY_BLOCKS *alloc_key_blocks(HA_CHECK *param, uint blocks,
85 : uint buffer_length);
86 : static ha_checksum maria_byte_checksum(const uchar *buf, uint length);
87 : static void set_data_file_type(MARIA_SORT_INFO *sort_info, MARIA_SHARE *share);
88 : static void restore_data_file_type(MARIA_SHARE *share);
89 : static void change_data_file_descriptor(MARIA_HA *info, File new_file);
90 : static void unuse_data_file_descriptor(MARIA_HA *info);
91 : static int _ma_safe_scan_block_record(MARIA_SORT_INFO *sort_info,
92 : MARIA_HA *info, uchar *record);
93 : static void copy_data_file_state(MARIA_STATE_INFO *to,
94 : MARIA_STATE_INFO *from);
95 : static void report_keypage_fault(HA_CHECK *param, MARIA_HA *info,
96 : my_off_t position);
97 : static my_bool create_new_data_handle(MARIA_SORT_PARAM *param, File new_file);
98 : static my_bool _ma_flush_table_files_before_swap(HA_CHECK *param,
99 : MARIA_HA *info);
100 : static TrID max_trid_in_system(void);
101 : static void _ma_check_print_not_visible_error(HA_CHECK *param, TrID used_trid);
102 : void retry_if_quick(MARIA_SORT_PARAM *param, int error);
103 :
104 :
105 : /* Initialize check param with default values */
106 :
107 : void maria_chk_init(HA_CHECK *param)
108 1743 : {
109 1743 : bzero((uchar*) param,sizeof(*param));
110 1743 : param->opt_follow_links=1;
111 1743 : param->keys_in_use= ~(ulonglong) 0;
112 1743 : param->search_after_block=HA_OFFSET_ERROR;
113 1743 : param->auto_increment_value= 0;
114 1743 : param->use_buffers=USE_BUFFER_INIT;
115 1743 : param->read_buffer_length=READ_BUFFER_INIT;
116 1743 : param->write_buffer_length=READ_BUFFER_INIT;
117 1743 : param->sort_buffer_length=SORT_BUFFER_INIT;
118 1743 : param->sort_key_blocks=BUFFERS_WHEN_SORTING;
119 1743 : param->tmpfile_createflag=O_RDWR | O_TRUNC | O_EXCL;
120 1743 : param->myf_rw=MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL);
121 1743 : param->start_check_pos=0;
122 1743 : param->max_record_length= LONGLONG_MAX;
123 1743 : param->pagecache_block_size= KEY_CACHE_BLOCK_SIZE;
124 1743 : param->stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
125 : }
126 :
127 :
128 : /* Initialize check param and maria handler for check of table */
129 :
130 : void maria_chk_init_for_check(HA_CHECK *param, MARIA_HA *info)
131 675 : {
132 675 : param->not_visible_rows_found= 0;
133 675 : param->max_found_trid= 0;
134 :
135 : /*
136 : Set up transaction handler so that we can see all rows. When rows is read
137 : we will check the found id against param->max_tried
138 : */
139 675 : if (!ma_control_file_inited())
140 10 : param->max_trid= 0; /* Give warning for first trid found */
141 : else
142 665 : param->max_trid= max_trid_in_system();
143 :
144 675 : maria_ignore_trids(info);
145 : }
146 :
147 :
148 : /* Check the status flags for the table */
149 :
150 : int maria_chk_status(HA_CHECK *param, MARIA_HA *info)
151 675 : {
152 675 : MARIA_SHARE *share= info->s;
153 :
154 675 : if (maria_is_crashed_on_repair(info))
155 0 : _ma_check_print_warning(param,
156 : "Table is marked as crashed and last repair failed");
157 675 : else if (maria_is_crashed(info))
158 0 : _ma_check_print_warning(param,
159 : "Table is marked as crashed");
160 675 : if (share->state.open_count != (uint) (share->global_changed ? 1 : 0))
161 : {
162 : /* Don't count this as a real warning, as check can correct this ! */
163 0 : uint save=param->warning_printed;
164 0 : _ma_check_print_warning(param,
165 : share->state.open_count==1 ?
166 : "%d client is using or hasn't closed the table properly" :
167 : "%d clients are using or haven't closed the table properly",
168 : share->state.open_count);
169 : /* If this will be fixed by the check, forget the warning */
170 0 : if (param->testflag & T_UPDATE_STATE)
171 0 : param->warning_printed=save;
172 : }
173 675 : return 0;
174 : }
175 :
176 : /*
177 : Check delete links in row data
178 : */
179 :
180 : int maria_chk_del(HA_CHECK *param, register MARIA_HA *info,
181 : ulonglong test_flag)
182 710 : {
183 710 : MARIA_SHARE *share= info->s;
184 : reg2 ha_rows i;
185 : uint delete_link_length;
186 : my_off_t empty,next_link,old_link;
187 : char buff[22],buff2[22];
188 710 : DBUG_ENTER("maria_chk_del");
189 :
190 710 : LINT_INIT(old_link);
191 :
192 710 : param->record_checksum=0;
193 :
194 710 : if (share->data_file_type == BLOCK_RECORD)
195 526 : DBUG_RETURN(0); /* No delete links here */
196 :
197 184 : delete_link_length=((share->options & HA_OPTION_PACK_RECORD) ? 20 :
198 : share->rec_reflength+1);
199 :
200 184 : if (!(test_flag & T_SILENT))
201 0 : puts("- check record delete-chain");
202 :
203 184 : next_link=share->state.dellink;
204 184 : if (share->state.state.del == 0)
205 : {
206 93 : if (test_flag & T_VERBOSE)
207 : {
208 0 : puts("No recordlinks");
209 : }
210 : }
211 : else
212 : {
213 91 : if (test_flag & T_VERBOSE)
214 0 : printf("Recordlinks: ");
215 91 : empty=0;
216 35662 : for (i= share->state.state.del ; i > 0L && next_link != HA_OFFSET_ERROR ; i--)
217 : {
218 35571 : if (*_ma_killed_ptr(param))
219 0 : DBUG_RETURN(1);
220 35571 : if (test_flag & T_VERBOSE)
221 0 : printf(" %9s",llstr(next_link,buff));
222 35571 : if (next_link >= share->state.state.data_file_length)
223 35571 : goto wrong;
224 35571 : if (my_pread(info->dfile.file, (uchar*) buff, delete_link_length,
225 : next_link,MYF(MY_NABP)))
226 : {
227 0 : if (test_flag & T_VERBOSE) puts("");
228 0 : _ma_check_print_error(param,"Can't read delete-link at filepos: %s",
229 : llstr(next_link,buff));
230 0 : DBUG_RETURN(1);
231 : }
232 35571 : if (*buff != '\0')
233 : {
234 0 : if (test_flag & T_VERBOSE) puts("");
235 0 : _ma_check_print_error(param,"Record at pos: %s is not remove-marked",
236 : llstr(next_link,buff));
237 0 : goto wrong;
238 : }
239 35571 : if (share->options & HA_OPTION_PACK_RECORD)
240 : {
241 17240 : my_off_t prev_link=mi_sizekorr(buff+12);
242 17240 : if (empty && prev_link != old_link)
243 : {
244 0 : if (test_flag & T_VERBOSE) puts("");
245 0 : _ma_check_print_error(param,"Deleted block at %s doesn't point back at previous delete link",llstr(next_link,buff2));
246 0 : goto wrong;
247 : }
248 17240 : old_link=next_link;
249 17240 : next_link=mi_sizekorr(buff+4);
250 17240 : empty+=mi_uint3korr(buff+1);
251 : }
252 : else
253 : {
254 18331 : param->record_checksum+=(ha_checksum) next_link;
255 18331 : next_link= _ma_rec_pos(share, (uchar *) buff + 1);
256 18331 : empty+=share->base.pack_reclength;
257 : }
258 : }
259 91 : if (share->state.state.del && (test_flag & T_VERBOSE))
260 0 : puts("\n");
261 91 : if (empty != share->state.state.empty)
262 : {
263 0 : _ma_check_print_warning(param,
264 : "Found %s deleted space in delete link chain. Should be %s",
265 : llstr(empty,buff2),
266 : llstr(share->state.state.empty,buff));
267 : }
268 91 : if (next_link != HA_OFFSET_ERROR)
269 : {
270 0 : _ma_check_print_error(param,
271 : "Found more than the expected %s deleted rows in delete link chain",
272 : llstr(share->state.state.del, buff));
273 0 : goto wrong;
274 : }
275 91 : if (i != 0)
276 : {
277 0 : _ma_check_print_error(param,
278 : "Found %s deleted rows in delete link chain. Should be %s",
279 : llstr(share->state.state.del - i, buff2),
280 : llstr(share->state.state.del, buff));
281 0 : goto wrong;
282 : }
283 : }
284 184 : DBUG_RETURN(0);
285 :
286 0 : wrong:
287 0 : param->testflag|=T_RETRY_WITHOUT_QUICK;
288 0 : if (test_flag & T_VERBOSE)
289 0 : puts("");
290 0 : _ma_check_print_error(param,"record delete-link-chain corrupted");
291 0 : DBUG_RETURN(1);
292 : } /* maria_chk_del */
293 :
294 :
295 : /* Check delete links in index file */
296 :
297 : static int check_k_link(HA_CHECK *param, register MARIA_HA *info,
298 : my_off_t next_link)
299 675 : {
300 675 : MARIA_SHARE *share= info->s;
301 675 : uint block_size= share->block_size;
302 : ha_rows records;
303 : char llbuff[21], llbuff2[21];
304 : uchar *buff;
305 675 : DBUG_ENTER("check_k_link");
306 :
307 675 : if (next_link == HA_OFFSET_ERROR)
308 493 : DBUG_RETURN(0); /* Avoid printing empty line */
309 :
310 182 : records= (ha_rows) (share->state.state.key_file_length / block_size);
311 6021 : while (next_link != HA_OFFSET_ERROR && records > 0)
312 : {
313 5657 : if (*_ma_killed_ptr(param))
314 0 : DBUG_RETURN(1);
315 5657 : if (param->testflag & T_VERBOSE)
316 0 : printf("%16s",llstr(next_link,llbuff));
317 :
318 : /* Key blocks must lay within the key file length entirely. */
319 5657 : if (next_link + block_size > share->state.state.key_file_length)
320 : {
321 : /* purecov: begin tested */
322 0 : _ma_check_print_error(param, "Invalid key block position: %s "
323 : "key block size: %u file_length: %s",
324 : llstr(next_link, llbuff), block_size,
325 : llstr(share->state.state.key_file_length, llbuff2));
326 0 : DBUG_RETURN(1);
327 : /* purecov: end */
328 : }
329 :
330 : /* Key blocks must be aligned at block_size */
331 5657 : if (next_link & (block_size -1))
332 : {
333 : /* purecov: begin tested */
334 0 : _ma_check_print_error(param, "Mis-aligned key block: %s "
335 : "minimum key block length: %u",
336 : llstr(next_link, llbuff),
337 : block_size);
338 0 : DBUG_RETURN(1);
339 : /* purecov: end */
340 : }
341 :
342 5657 : DBUG_ASSERT(share->pagecache->block_size == block_size);
343 5657 : if (!(buff= pagecache_read(share->pagecache,
344 : &share->kfile,
345 : (pgcache_page_no_t) (next_link / block_size),
346 : DFLT_INIT_HITS,
347 : info->buff, PAGECACHE_READ_UNKNOWN_PAGE,
348 : PAGECACHE_LOCK_LEFT_UNLOCKED, 0)))
349 : {
350 : /* purecov: begin tested */
351 0 : _ma_check_print_error(param, "key cache read error for block: %s",
352 : llstr(next_link,llbuff));
353 0 : DBUG_RETURN(1);
354 : /* purecov: end */
355 : }
356 5657 : if (_ma_get_keynr(info->s, buff) != MARIA_DELETE_KEY_NR)
357 0 : _ma_check_print_error(param, "Page at %s is not delete marked",
358 : llstr(next_link, llbuff));
359 :
360 5657 : next_link= mi_sizekorr(buff + share->keypage_header);
361 5657 : records--;
362 5657 : param->key_file_blocks+=block_size;
363 : }
364 182 : if (param->testflag & T_VERBOSE)
365 : {
366 0 : if (next_link != HA_OFFSET_ERROR)
367 0 : printf("%16s\n",llstr(next_link,llbuff));
368 : else
369 0 : puts("");
370 : }
371 182 : DBUG_RETURN (next_link != HA_OFFSET_ERROR);
372 : } /* check_k_link */
373 :
374 :
375 : /* Check sizes of files */
376 :
377 : int maria_chk_size(HA_CHECK *param, register MARIA_HA *info)
378 675 : {
379 675 : MARIA_SHARE *share= info->s;
380 : int error;
381 : register my_off_t skr,size;
382 : char buff[22],buff2[22];
383 675 : DBUG_ENTER("maria_chk_size");
384 :
385 675 : if (!(param->testflag & T_SILENT))
386 0 : puts("- check file-size");
387 :
388 : /*
389 : The following is needed if called externally (not from maria_chk).
390 : To get a correct physical size we need to flush them.
391 : */
392 675 : if ((error= _ma_flush_table_files(info,
393 : MARIA_FLUSH_DATA | MARIA_FLUSH_INDEX,
394 : FLUSH_FORCE_WRITE, FLUSH_FORCE_WRITE)))
395 0 : _ma_check_print_error(param, "Failed to flush data or index file");
396 :
397 675 : size= my_seek(share->kfile.file, 0L, MY_SEEK_END, MYF(MY_THREADSAFE));
398 675 : if ((skr=(my_off_t) share->state.state.key_file_length) != size)
399 : {
400 : /* Don't give error if file generated by mariapack */
401 15 : if (skr > size && maria_is_any_key_active(share->state.key_map))
402 : {
403 0 : error=1;
404 0 : _ma_check_print_error(param,
405 : "Size of indexfile is: %-8s Should be: %s",
406 : llstr(size,buff), llstr(skr,buff2));
407 : }
408 15 : else if (!(param->testflag & T_VERY_SILENT))
409 5 : _ma_check_print_warning(param,
410 : "Size of indexfile is: %-8s Should be: %s",
411 : llstr(size,buff), llstr(skr,buff2));
412 : }
413 675 : if (!(param->testflag & T_VERY_SILENT) &&
414 : ! (share->options & HA_OPTION_COMPRESS_RECORD) &&
415 : ulonglong2double(share->state.state.key_file_length) >
416 : ulonglong2double(share->base.margin_key_file_length)*0.9)
417 0 : _ma_check_print_warning(param,"Keyfile is almost full, %10s of %10s used",
418 : llstr(share->state.state.key_file_length,buff),
419 : llstr(share->base.max_key_file_length-1,buff));
420 :
421 675 : size= my_seek(info->dfile.file, 0L, MY_SEEK_END, MYF(0));
422 675 : skr=(my_off_t) share->state.state.data_file_length;
423 675 : if (share->options & HA_OPTION_COMPRESS_RECORD)
424 35 : skr+= MEMMAP_EXTRA_MARGIN;
425 : #ifdef USE_RELOC
426 : if (share->data_file_type == STATIC_RECORD &&
427 : skr < (my_off_t) share->base.reloc*share->base.min_pack_length)
428 : skr=(my_off_t) share->base.reloc*share->base.min_pack_length;
429 : #endif
430 675 : if (skr != size)
431 : {
432 0 : if (skr > size && skr != size + MEMMAP_EXTRA_MARGIN)
433 : {
434 0 : share->state.state.data_file_length=size; /* Skip other errors */
435 0 : error=1;
436 0 : _ma_check_print_error(param,"Size of datafile is: %-9s Should be: %s",
437 : llstr(size,buff), llstr(skr,buff2));
438 0 : param->testflag|=T_RETRY_WITHOUT_QUICK;
439 : }
440 : else
441 : {
442 0 : _ma_check_print_warning(param,
443 : "Size of datafile is: %-9s Should be: %s",
444 : llstr(size,buff), llstr(skr,buff2));
445 : }
446 : }
447 675 : if (!(param->testflag & T_VERY_SILENT) &&
448 : !(share->options & HA_OPTION_COMPRESS_RECORD) &&
449 : ulonglong2double(share->state.state.data_file_length) >
450 : (ulonglong2double(share->base.max_data_file_length)*0.9))
451 1 : _ma_check_print_warning(param, "Datafile is almost full, %10s of %10s used",
452 : llstr(share->state.state.data_file_length,buff),
453 : llstr(share->base.max_data_file_length-1,buff2));
454 675 : DBUG_RETURN(error);
455 : } /* maria_chk_size */
456 :
457 :
458 : /* Check keys */
459 :
460 : int maria_chk_key(HA_CHECK *param, register MARIA_HA *info)
461 675 : {
462 675 : uint key,found_keys=0,full_text_keys=0,result=0;
463 : ha_rows keys;
464 : ha_checksum old_record_checksum,init_checksum;
465 : my_off_t all_keydata,all_totaldata,key_totlength,length;
466 : double *rec_per_key_part;
467 675 : MARIA_SHARE *share= info->s;
468 : MARIA_KEYDEF *keyinfo;
469 : char buff[22],buff2[22];
470 : MARIA_PAGE page;
471 675 : DBUG_ENTER("maria_chk_key");
472 :
473 675 : if (!(param->testflag & T_SILENT))
474 0 : puts("- check key delete-chain");
475 :
476 675 : param->key_file_blocks=share->base.keystart;
477 675 : if (check_k_link(param, info, share->state.key_del))
478 : {
479 0 : if (param->testflag & T_VERBOSE) puts("");
480 0 : _ma_check_print_error(param,"key delete-link-chain corrupted");
481 0 : DBUG_RETURN(-1);
482 : }
483 :
484 675 : if (!(param->testflag & T_SILENT))
485 0 : puts("- check index reference");
486 :
487 675 : all_keydata=all_totaldata=key_totlength=0;
488 675 : init_checksum=param->record_checksum;
489 675 : old_record_checksum=0;
490 675 : if (share->data_file_type == STATIC_RECORD)
491 65 : old_record_checksum= (calc_checksum(share->state.state.records +
492 : share->state.state.del-1) *
493 : share->base.pack_reclength);
494 675 : rec_per_key_part= param->new_rec_per_key_part;
495 3110 : for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
496 1760 : rec_per_key_part+=keyinfo->keysegs, key++, keyinfo++)
497 : {
498 1760 : param->key_crc[key]=0;
499 1760 : if (! maria_is_key_active(share->state.key_map, key))
500 : {
501 : /* Remember old statistics for key */
502 15 : memcpy((char*) rec_per_key_part,
503 : (char*) (share->state.rec_per_key_part +
504 : (uint) (rec_per_key_part - param->new_rec_per_key_part)),
505 : keyinfo->keysegs*sizeof(*rec_per_key_part));
506 15 : continue;
507 : }
508 1745 : found_keys++;
509 :
510 1745 : param->record_checksum=init_checksum;
511 :
512 1745 : bzero((char*) ¶m->unique_count,sizeof(param->unique_count));
513 1745 : bzero((char*) ¶m->notnull_count,sizeof(param->notnull_count));
514 :
515 1745 : if ((!(param->testflag & T_SILENT)))
516 0 : printf ("- check data record references index: %d\n",key+1);
517 1745 : if (keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL))
518 0 : full_text_keys++;
519 1745 : if (share->state.key_root[key] == HA_OFFSET_ERROR)
520 : {
521 1026 : if (share->state.state.records != 0 && !(keyinfo->flag & HA_FULLTEXT))
522 0 : _ma_check_print_error(param, "Key tree %u is empty", key + 1);
523 : goto do_stat;
524 : }
525 719 : if (_ma_fetch_keypage(&page, info, keyinfo, share->state.key_root[key],
526 : PAGECACHE_LOCK_LEFT_UNLOCKED, DFLT_INIT_HITS,
527 : info->buff, 0))
528 : {
529 0 : report_keypage_fault(param, info, share->state.key_root[key]);
530 0 : if (!(param->testflag & T_INFO))
531 0 : DBUG_RETURN(-1);
532 0 : result= -1;
533 0 : continue;
534 : }
535 719 : param->key_file_blocks+=keyinfo->block_length;
536 719 : keys=0;
537 719 : param->keydata=param->totaldata=0;
538 719 : param->key_blocks=0;
539 719 : param->max_level=0;
540 719 : if (chk_index(param, info,keyinfo, &page, &keys, param->key_crc+key,1))
541 0 : DBUG_RETURN(-1);
542 719 : if (!(keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL | HA_RTREE_INDEX)))
543 : {
544 719 : if (keys != share->state.state.records)
545 : {
546 0 : _ma_check_print_error(param,"Found %s keys of %s",llstr(keys,buff),
547 : llstr(share->state.state.records,buff2));
548 0 : if (!(param->testflag & T_INFO))
549 0 : DBUG_RETURN(-1);
550 0 : result= -1;
551 0 : continue;
552 : }
553 1153 : if ((found_keys - full_text_keys == 1 &&
554 : !(share->data_file_type == STATIC_RECORD)) ||
555 : (param->testflag & T_DONT_CHECK_CHECKSUM))
556 434 : old_record_checksum= param->record_checksum;
557 285 : else if (old_record_checksum != param->record_checksum)
558 : {
559 0 : if (key)
560 0 : _ma_check_print_error(param,
561 : "Key %u doesn't point at same records as "
562 : "key 1",
563 : key+1);
564 : else
565 0 : _ma_check_print_error(param,"Key 1 doesn't point at all records");
566 0 : if (!(param->testflag & T_INFO))
567 0 : DBUG_RETURN(-1);
568 0 : result= -1;
569 0 : continue;
570 : }
571 : }
572 719 : if ((uint) share->base.auto_key -1 == key)
573 : {
574 : /* Check that auto_increment key is bigger than max key value */
575 : ulonglong auto_increment;
576 0 : const HA_KEYSEG *keyseg= share->keyinfo[share->base.auto_key-1].seg;
577 0 : info->lastinx=key;
578 0 : _ma_read_key_record(info, info->rec_buff, 0);
579 0 : auto_increment=
580 : ma_retrieve_auto_increment(info->rec_buff + keyseg->start,
581 : keyseg->type);
582 0 : if (auto_increment > share->state.auto_increment)
583 : {
584 0 : _ma_check_print_warning(param, "Auto-increment value: %s is smaller "
585 : "than max used value: %s",
586 : llstr(share->state.auto_increment,buff2),
587 : llstr(auto_increment, buff));
588 : }
589 0 : if (param->testflag & T_AUTO_INC)
590 : {
591 0 : set_if_bigger(share->state.auto_increment,
592 : auto_increment);
593 0 : set_if_bigger(share->state.auto_increment,
594 : param->auto_increment_value);
595 : }
596 :
597 : /* Check that there isn't a row with auto_increment = 0 in the table */
598 0 : maria_extra(info,HA_EXTRA_KEYREAD,0);
599 0 : bzero(info->lastkey_buff, keyinfo->seg->length);
600 0 : if (!maria_rkey(info, info->rec_buff, key,
601 : info->lastkey_buff,
602 : (key_part_map) 1, HA_READ_KEY_EXACT))
603 : {
604 : /* Don't count this as a real warning, as maria_chk can't correct it */
605 0 : uint save=param->warning_printed;
606 0 : _ma_check_print_warning(param, "Found row where the auto_increment "
607 : "column has the value 0");
608 0 : param->warning_printed=save;
609 : }
610 0 : maria_extra(info,HA_EXTRA_NO_KEYREAD,0);
611 : }
612 :
613 719 : length=(my_off_t) isam_key_length(info,keyinfo)*keys + param->key_blocks*2;
614 719 : if (param->testflag & T_INFO && param->totaldata != 0L && keys != 0L)
615 0 : printf("Key: %2d: Keyblocks used: %3d%% Packed: %4d%% Max levels: %2d\n",
616 : key+1,
617 : (int) (my_off_t2double(param->keydata)*100.0/my_off_t2double(param->totaldata)),
618 : (int) ((my_off_t2double(length) - my_off_t2double(param->keydata))*100.0/
619 : my_off_t2double(length)),
620 : param->max_level);
621 719 : all_keydata+=param->keydata; all_totaldata+=param->totaldata; key_totlength+=length;
622 :
623 1745 : do_stat:
624 1745 : if (param->testflag & T_STATISTICS)
625 0 : maria_update_key_parts(keyinfo, rec_per_key_part, param->unique_count,
626 : param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
627 : param->notnull_count: NULL,
628 : (ulonglong)share->state.state.records);
629 : }
630 675 : if (param->testflag & T_INFO)
631 : {
632 0 : if (all_totaldata != 0L && found_keys > 0)
633 0 : printf("Total: Keyblocks used: %3d%% Packed: %4d%%\n\n",
634 : (int) (my_off_t2double(all_keydata)*100.0/
635 : my_off_t2double(all_totaldata)),
636 : (int) ((my_off_t2double(key_totlength) -
637 : my_off_t2double(all_keydata))*100.0/
638 : my_off_t2double(key_totlength)));
639 0 : else if (all_totaldata != 0L && maria_is_any_key_active(share->state.key_map))
640 0 : puts("");
641 : }
642 675 : if (param->key_file_blocks != share->state.state.key_file_length &&
643 : share->state.key_map == ~(ulonglong) 0)
644 0 : _ma_check_print_warning(param, "Some data are unreferenced in keyfile");
645 675 : if (found_keys != full_text_keys)
646 660 : param->record_checksum=old_record_checksum-init_checksum; /* Remove delete links */
647 : else
648 15 : param->record_checksum=0;
649 675 : DBUG_RETURN(result);
650 : } /* maria_chk_key */
651 :
652 :
653 :
654 : static int chk_index_down(HA_CHECK *param, MARIA_HA *info,
655 : MARIA_KEYDEF *keyinfo,
656 : my_off_t page, uchar *buff, ha_rows *keys,
657 : ha_checksum *key_checksum, uint level)
658 442 : {
659 : char llbuff[22],llbuff2[22];
660 442 : MARIA_SHARE *share= info->s;
661 : MARIA_PAGE ma_page;
662 442 : DBUG_ENTER("chk_index_down");
663 :
664 : /* Key blocks must lay within the key file length entirely. */
665 442 : if (page + keyinfo->block_length > share->state.state.key_file_length)
666 : {
667 : /* purecov: begin tested */
668 : /* Give it a chance to fit in the real file size. */
669 : my_off_t max_length= my_seek(info->s->kfile.file, 0L, MY_SEEK_END,
670 0 : MYF(MY_THREADSAFE));
671 0 : _ma_check_print_error(param, "Invalid key block position: %s "
672 : "key block size: %u file_length: %s",
673 : llstr(page, llbuff), keyinfo->block_length,
674 : llstr(share->state.state.key_file_length, llbuff2));
675 0 : if (page + keyinfo->block_length > max_length)
676 0 : goto err;
677 : /* Fix the remembered key file length. */
678 0 : share->state.state.key_file_length= (max_length &
679 : ~ (my_off_t) (keyinfo->block_length -
680 : 1));
681 : /* purecov: end */
682 : }
683 :
684 : /* Key blocks must be aligned at block length */
685 442 : if (page & (info->s->block_size -1))
686 : {
687 : /* purecov: begin tested */
688 0 : _ma_check_print_error(param, "Mis-aligned key block: %s "
689 : "key block length: %u",
690 : llstr(page, llbuff), info->s->block_size);
691 0 : goto err;
692 : /* purecov: end */
693 : }
694 :
695 442 : if (_ma_fetch_keypage(&ma_page, info, keyinfo, page,
696 : PAGECACHE_LOCK_LEFT_UNLOCKED,
697 : DFLT_INIT_HITS, buff, 0))
698 : {
699 0 : report_keypage_fault(param, info, page);
700 0 : goto err;
701 : }
702 442 : param->key_file_blocks+=keyinfo->block_length;
703 442 : if (chk_index(param, info, keyinfo, &ma_page, keys, key_checksum,level))
704 442 : goto err;
705 :
706 442 : DBUG_RETURN(0);
707 :
708 : /* purecov: begin tested */
709 0 : err:
710 0 : DBUG_RETURN(1);
711 : /* purecov: end */
712 : }
713 :
714 :
715 : /*
716 : "Ignore NULLs" statistics collection method: process first index tuple.
717 :
718 : SYNOPSIS
719 : maria_collect_stats_nonulls_first()
720 : keyseg IN Array of key part descriptions
721 : notnull INOUT Array, notnull[i] = (number of {keypart1...keypart_i}
722 : tuples that don't contain NULLs)
723 : key IN Key values tuple
724 :
725 : DESCRIPTION
726 : Process the first index tuple - find out which prefix tuples don't
727 : contain NULLs, and update the array of notnull counters accordingly.
728 : */
729 :
730 : static
731 : void maria_collect_stats_nonulls_first(HA_KEYSEG *keyseg, ulonglong *notnull,
732 : const uchar *key)
733 0 : {
734 : uint first_null, kp;
735 0 : first_null= ha_find_null(keyseg, key) - keyseg;
736 : /*
737 : All prefix tuples that don't include keypart_{first_null} are not-null
738 : tuples (and all others aren't), increment counters for them.
739 : */
740 0 : for (kp= 0; kp < first_null; kp++)
741 0 : notnull[kp]++;
742 : }
743 :
744 :
745 : /*
746 : "Ignore NULLs" statistics collection method: process next index tuple.
747 :
748 : SYNOPSIS
749 : maria_collect_stats_nonulls_next()
750 : keyseg IN Array of key part descriptions
751 : notnull INOUT Array, notnull[i] = (number of {keypart1...keypart_i}
752 : tuples that don't contain NULLs)
753 : prev_key IN Previous key values tuple
754 : last_key IN Next key values tuple
755 :
756 : DESCRIPTION
757 : Process the next index tuple:
758 : 1. Find out which prefix tuples of last_key don't contain NULLs, and
759 : update the array of notnull counters accordingly.
760 : 2. Find the first keypart number where the prev_key and last_key tuples
761 : are different(A), or last_key has NULL value(B), and return it, so the
762 : caller can count number of unique tuples for each key prefix. We don't
763 : need (B) to be counted, and that is compensated back in
764 : maria_update_key_parts().
765 :
766 : RETURN
767 : 1 + number of first keypart where values differ or last_key tuple has NULL
768 : */
769 :
770 : static
771 : int maria_collect_stats_nonulls_next(HA_KEYSEG *keyseg, ulonglong *notnull,
772 : const uchar *prev_key,
773 : const uchar *last_key)
774 0 : {
775 : uint diffs[2];
776 : uint first_null_seg, kp;
777 : HA_KEYSEG *seg;
778 :
779 : /*
780 : Find the first keypart where values are different or either of them is
781 : NULL. We get results in diffs array:
782 : diffs[0]= 1 + number of first different keypart
783 : diffs[1]=offset: (last_key + diffs[1]) points to first value in
784 : last_key that is NULL or different from corresponding
785 : value in prev_key.
786 : */
787 0 : ha_key_cmp(keyseg, prev_key, last_key, USE_WHOLE_KEY,
788 : SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diffs);
789 0 : seg= keyseg + diffs[0] - 1;
790 :
791 : /* Find first NULL in last_key */
792 0 : first_null_seg= ha_find_null(seg, last_key + diffs[1]) - keyseg;
793 0 : for (kp= 0; kp < first_null_seg; kp++)
794 0 : notnull[kp]++;
795 :
796 : /*
797 : Return 1+ number of first key part where values differ. Don't care if
798 : these were NULLs and not .... We compensate for that in
799 : maria_update_key_parts.
800 : */
801 0 : return diffs[0];
802 : }
803 :
804 :
805 : /* Check if index is ok */
806 :
807 : static int chk_index(HA_CHECK *param, MARIA_HA *info, MARIA_KEYDEF *keyinfo,
808 : MARIA_PAGE *anc_page, ha_rows *keys,
809 : ha_checksum *key_checksum, uint level)
810 1161 : {
811 : int flag;
812 : uint comp_flag, page_flag, nod_flag;
813 : uchar *temp_buff, *keypos, *old_keypos, *endpos;
814 : my_off_t next_page,record;
815 1161 : MARIA_SHARE *share= info->s;
816 : char llbuff[22];
817 : uint diff_pos[2];
818 : uchar tmp_key_buff[MARIA_MAX_KEY_BUFF];
819 : MARIA_KEY tmp_key;
820 1161 : DBUG_ENTER("chk_index");
821 1161 : DBUG_DUMP("buff", anc_page->buff, anc_page->size);
822 :
823 : /* TODO: implement appropriate check for RTree keys */
824 1161 : if (keyinfo->flag & (HA_SPATIAL | HA_RTREE_INDEX))
825 0 : DBUG_RETURN(0);
826 :
827 1161 : if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
828 : {
829 0 : _ma_check_print_error(param,"Not enough memory for keyblock");
830 0 : DBUG_RETURN(-1);
831 : }
832 :
833 1161 : if (keyinfo->flag & HA_NOSAME)
834 : {
835 : /* Not real duplicates */
836 543 : comp_flag=SEARCH_FIND | SEARCH_UPDATE | SEARCH_INSERT;
837 : }
838 : else
839 618 : comp_flag=SEARCH_SAME; /* Keys in positionorder */
840 :
841 1161 : page_flag= anc_page->flag;
842 1161 : nod_flag= anc_page->node;
843 1161 : old_keypos= anc_page->buff + share->keypage_header;
844 1161 : keypos= old_keypos + nod_flag;
845 1161 : endpos= anc_page->buff + anc_page->size;
846 :
847 1161 : param->keydata+= anc_page->size;
848 1161 : param->totaldata+= keyinfo->block_length; /* INFO */
849 1161 : param->key_blocks++;
850 1161 : if (level > param->max_level)
851 835 : param->max_level=level;
852 :
853 1161 : if (_ma_get_keynr(share, anc_page->buff) !=
854 : (uint) (keyinfo - share->keyinfo))
855 0 : _ma_check_print_error(param, "Page at %s is not marked for index %u",
856 : llstr(anc_page->pos, llbuff),
857 : (uint) (keyinfo - share->keyinfo));
858 1161 : if ((page_flag & KEYPAGE_FLAG_HAS_TRANSID) &&
859 : !share->base.born_transactional)
860 : {
861 0 : _ma_check_print_error(param,
862 : "Page at %s is marked with HAS_TRANSID even if "
863 : "table is not transactional",
864 : llstr(anc_page->pos, llbuff));
865 : }
866 :
867 1161 : if (anc_page->size > (uint) keyinfo->block_length - KEYPAGE_CHECKSUM_SIZE)
868 : {
869 0 : _ma_check_print_error(param,
870 : "Page at %s has impossible (too big) pagelength",
871 : llstr(anc_page->pos, llbuff));
872 0 : goto err;
873 : }
874 :
875 1161 : info->last_key.keyinfo= tmp_key.keyinfo= keyinfo;
876 1161 : tmp_key.data= tmp_key_buff;
877 : for ( ;; )
878 : {
879 177983 : if (*_ma_killed_ptr(param))
880 177983 : goto err;
881 177983 : if (nod_flag)
882 : {
883 442 : next_page= _ma_kpos(nod_flag,keypos);
884 442 : if (chk_index_down(param,info,keyinfo,next_page,
885 : temp_buff,keys,key_checksum,level+1))
886 : {
887 0 : DBUG_DUMP("page_data", old_keypos, (uint) (keypos - old_keypos));
888 0 : goto err;
889 : }
890 : }
891 177983 : old_keypos=keypos;
892 177983 : if (keypos >= endpos ||
893 : !(*keyinfo->get_key)(&tmp_key, page_flag, nod_flag, &keypos))
894 : break;
895 176822 : if (keypos > endpos)
896 : {
897 0 : _ma_check_print_error(param,
898 : "Page length and length of keys don't match at "
899 : "page: %s",
900 : llstr(anc_page->pos,llbuff));
901 0 : goto err;
902 : }
903 176822 : if (share->data_file_type == BLOCK_RECORD &&
904 : !(page_flag & KEYPAGE_FLAG_HAS_TRANSID) &&
905 : key_has_transid(tmp_key.data + tmp_key.data_length +
906 : share->rec_reflength-1))
907 : {
908 0 : _ma_check_print_error(param,
909 : "Found key marked for transid on page that is not "
910 : "marked for transid at: %s",
911 : llstr(anc_page->pos,llbuff));
912 0 : goto err;
913 : }
914 :
915 176822 : if ((*keys)++ &&
916 : (flag=ha_key_cmp(keyinfo->seg, info->last_key.data, tmp_key.data,
917 : tmp_key.data_length + tmp_key.ref_length,
918 : (comp_flag | SEARCH_INSERT | (tmp_key.flag >> 1) |
919 : info->last_key.flag), diff_pos)) >=0)
920 : {
921 0 : DBUG_DUMP_KEY("old", &info->last_key);
922 0 : DBUG_DUMP_KEY("new", &tmp_key);
923 0 : DBUG_DUMP("new_in_page", old_keypos, (uint) (keypos-old_keypos));
924 :
925 0 : if ((comp_flag & SEARCH_FIND) && flag == 0)
926 0 : _ma_check_print_error(param,"Found duplicated key at page %s",
927 : llstr(anc_page->pos,llbuff));
928 : else
929 0 : _ma_check_print_error(param,"Key in wrong position at page %s",
930 : llstr(anc_page->pos,llbuff));
931 : goto err;
932 : }
933 :
934 176822 : if (param->testflag & T_STATISTICS)
935 : {
936 0 : if (*keys != 1L) /* not first_key */
937 : {
938 0 : if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL)
939 0 : ha_key_cmp(keyinfo->seg, info->last_key.data,
940 : tmp_key.data, tmp_key.data_length,
941 : SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL,
942 : diff_pos);
943 0 : else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
944 : {
945 0 : diff_pos[0]= maria_collect_stats_nonulls_next(keyinfo->seg,
946 : param->notnull_count,
947 : info->last_key.data,
948 : tmp_key.data);
949 : }
950 0 : param->unique_count[diff_pos[0]-1]++;
951 : }
952 : else
953 : {
954 0 : if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
955 0 : maria_collect_stats_nonulls_first(keyinfo->seg, param->notnull_count,
956 : tmp_key.data);
957 : }
958 : }
959 176822 : _ma_copy_key(&info->last_key, &tmp_key);
960 176822 : (*key_checksum)+= maria_byte_checksum(tmp_key.data, tmp_key.data_length);
961 176822 : record= _ma_row_pos_from_key(&tmp_key);
962 :
963 176822 : if (keyinfo->flag & HA_FULLTEXT) /* special handling for ft2 */
964 : {
965 : uint off;
966 : int subkeys;
967 0 : get_key_full_length_rdonly(off, tmp_key.data);
968 0 : subkeys= ft_sintXkorr(tmp_key.data + off);
969 0 : if (subkeys < 0)
970 : {
971 0 : ha_rows tmp_keys=0;
972 0 : if (chk_index_down(param,info,&share->ft2_keyinfo,record,
973 : temp_buff,&tmp_keys,key_checksum,1))
974 0 : goto err;
975 0 : if (tmp_keys + subkeys)
976 : {
977 0 : _ma_check_print_error(param,
978 : "Number of words in the 2nd level tree "
979 : "does not match the number in the header. "
980 : "Parent word in on the page %s, offset %u",
981 : llstr(anc_page->pos,llbuff),
982 : (uint) (old_keypos - anc_page->buff));
983 0 : goto err;
984 : }
985 0 : (*keys)+=tmp_keys-1;
986 0 : continue;
987 : }
988 : /* fall through */
989 : }
990 176822 : if ((share->data_file_type != BLOCK_RECORD &&
991 : record >= share->state.state.data_file_length) ||
992 : (share->data_file_type == BLOCK_RECORD &&
993 : ma_recordpos_to_page(record) * share->base.min_block_length >=
994 : share->state.state.data_file_length))
995 : {
996 : #ifndef DBUG_OFF
997 : char llbuff2[22], llbuff3[22];
998 : #endif
999 0 : _ma_check_print_error(param,
1000 : "Found key at page %s that points to record "
1001 : "outside datafile",
1002 : llstr(anc_page->pos,llbuff));
1003 0 : DBUG_PRINT("test",("page: %s record: %s filelength: %s",
1004 : llstr(anc_page->pos,llbuff),llstr(record,llbuff2),
1005 : llstr(share->state.state.data_file_length,llbuff3)));
1006 0 : DBUG_DUMP_KEY("key", &tmp_key);
1007 0 : DBUG_DUMP("new_in_page", old_keypos, (uint) (keypos-old_keypos));
1008 0 : goto err;
1009 : }
1010 176822 : param->record_checksum+= (ha_checksum) record;
1011 : }
1012 1161 : if (keypos != endpos)
1013 : {
1014 0 : _ma_check_print_error(param,
1015 : "Keyblock size at page %s is not correct. "
1016 : "Block length: %u key length: %u",
1017 : llstr(anc_page->pos, llbuff), anc_page->size,
1018 : (uint) (keypos - anc_page->buff));
1019 0 : goto err;
1020 : }
1021 : my_afree(temp_buff);
1022 1161 : DBUG_RETURN(0);
1023 0 : err:
1024 : my_afree(temp_buff);
1025 0 : DBUG_RETURN(1);
1026 : } /* chk_index */
1027 :
1028 :
1029 : /* Calculate a checksum of 1+2+3+4...N = N*(N+1)/2 without overflow */
1030 :
1031 : static ha_checksum calc_checksum(ha_rows count)
1032 65 : {
1033 : ulonglong sum,a,b;
1034 65 : DBUG_ENTER("calc_checksum");
1035 :
1036 65 : sum=0;
1037 65 : a=count; b=count+1;
1038 65 : if (a & 1)
1039 13 : b>>=1;
1040 : else
1041 52 : a>>=1;
1042 437 : while (b)
1043 : {
1044 372 : if (b & 1)
1045 246 : sum+=a;
1046 372 : a<<=1; b>>=1;
1047 : }
1048 65 : DBUG_PRINT("exit",("sum: %lx",(ulong) sum));
1049 65 : DBUG_RETURN((ha_checksum) sum);
1050 : } /* calc_checksum */
1051 :
1052 :
1053 : /* Calc length of key in normal isam */
1054 :
1055 : static uint isam_key_length(MARIA_HA *info, register MARIA_KEYDEF *keyinfo)
1056 719 : {
1057 : uint length;
1058 : HA_KEYSEG *keyseg;
1059 719 : DBUG_ENTER("isam_key_length");
1060 :
1061 719 : length= info->s->rec_reflength;
1062 1476 : for (keyseg=keyinfo->seg ; keyseg->type ; keyseg++)
1063 757 : length+= keyseg->length;
1064 :
1065 719 : DBUG_PRINT("exit",("length: %d",length));
1066 719 : DBUG_RETURN(length);
1067 : } /* key_length */
1068 :
1069 :
1070 :
1071 : static void record_pos_to_txt(MARIA_HA *info, my_off_t recpos,
1072 : char *buff)
1073 0 : {
1074 0 : if (info->s->data_file_type != BLOCK_RECORD)
1075 0 : llstr(recpos, buff);
1076 : else
1077 : {
1078 0 : my_off_t page= ma_recordpos_to_page(recpos);
1079 0 : uint row= ma_recordpos_to_dir_entry(recpos);
1080 0 : char *end= longlong10_to_str(page, buff, 10);
1081 0 : *(end++)= ':';
1082 0 : longlong10_to_str(row, end, 10);
1083 : }
1084 : }
1085 :
1086 :
1087 : /*
1088 : Check that keys in records exist in index tree
1089 :
1090 : SYNOPSIS
1091 : check_keys_in_record()
1092 : param Check paramenter
1093 : info Maria handler
1094 : extend Type of check (extended or normal)
1095 : start_recpos Position to row
1096 : record Record buffer
1097 :
1098 : NOTES
1099 : This function also calculates record checksum & number of rows
1100 : */
1101 :
1102 : static int check_keys_in_record(HA_CHECK *param, MARIA_HA *info, int extend,
1103 : my_off_t start_recpos, uchar *record)
1104 37192 : {
1105 37192 : MARIA_SHARE *share= info->s;
1106 : MARIA_KEYDEF *keyinfo;
1107 : char llbuff[22+4];
1108 : uint keynr;
1109 :
1110 37192 : param->tmp_record_checksum+= (ha_checksum) start_recpos;
1111 37192 : param->records++;
1112 37192 : if (param->testflag & T_WRITE_LOOP && param->records % WRITE_COUNT == 0)
1113 : {
1114 0 : printf("%s\r", llstr(param->records, llbuff));
1115 0 : VOID(fflush(stdout));
1116 : }
1117 :
1118 : /* Check if keys match the record */
1119 251506 : for (keynr=0, keyinfo= share->keyinfo; keynr < share->base.keys;
1120 177122 : keynr++, keyinfo++)
1121 : {
1122 177122 : if (maria_is_key_active(share->state.key_map, keynr))
1123 : {
1124 : MARIA_KEY key;
1125 176822 : if (!(keyinfo->flag & HA_FULLTEXT))
1126 : {
1127 176822 : (*keyinfo->make_key)(info, &key, keynr, info->lastkey_buff, record,
1128 : start_recpos, 0);
1129 176822 : if (extend)
1130 : {
1131 : /* We don't need to lock the key tree here as we don't allow
1132 : concurrent threads when running maria_chk
1133 : */
1134 : int search_result=
1135 : #ifdef HAVE_RTREE_KEYS
1136 : (keyinfo->flag & (HA_SPATIAL | HA_RTREE_INDEX)) ?
1137 : maria_rtree_find_first(info, &key, MBR_EQUAL | MBR_DATA) :
1138 : #endif
1139 154609 : _ma_search(info, &key, SEARCH_SAME, share->state.key_root[keynr]);
1140 154609 : if (search_result)
1141 : {
1142 0 : record_pos_to_txt(info, start_recpos, llbuff);
1143 0 : _ma_check_print_error(param,
1144 : "Record at: %14s "
1145 : "Can't find key for index: %2d",
1146 : llbuff, keynr+1);
1147 0 : if (param->err_count++ > MAXERR || !(param->testflag & T_VERBOSE))
1148 0 : return -1;
1149 : }
1150 : }
1151 : else
1152 22213 : param->tmp_key_crc[keynr]+=
1153 : maria_byte_checksum(key.data, key.data_length);
1154 : }
1155 : }
1156 : }
1157 37192 : return 0;
1158 : }
1159 :
1160 :
1161 : /*
1162 : Functions to loop through all rows and check if they are ok
1163 :
1164 : NOTES
1165 : One function for each record format
1166 :
1167 : RESULT
1168 : 0 ok
1169 : -1 Interrupted by user
1170 : 1 Error
1171 : */
1172 :
1173 : static int check_static_record(HA_CHECK *param, MARIA_HA *info, int extend,
1174 : uchar *record)
1175 65 : {
1176 65 : MARIA_SHARE *share= info->s;
1177 : my_off_t start_recpos, pos;
1178 : char llbuff[22];
1179 :
1180 65 : pos= 0;
1181 23772 : while (pos < share->state.state.data_file_length)
1182 : {
1183 23642 : if (*_ma_killed_ptr(param))
1184 0 : return -1;
1185 23642 : if (my_b_read(¶m->read_cache, record,
1186 : share->base.pack_reclength))
1187 : {
1188 0 : _ma_check_print_error(param,
1189 : "got error: %d when reading datafile at position: "
1190 : "%s",
1191 : my_errno, llstr(pos, llbuff));
1192 0 : return 1;
1193 : }
1194 23642 : start_recpos= pos;
1195 23642 : pos+= share->base.pack_reclength;
1196 23642 : param->splits++;
1197 23642 : if (*record == '\0')
1198 : {
1199 18331 : param->del_blocks++;
1200 18331 : param->del_length+= share->base.pack_reclength;
1201 18331 : continue; /* Record removed */
1202 : }
1203 5311 : param->glob_crc+= _ma_static_checksum(info,record);
1204 5311 : param->used+= share->base.pack_reclength;
1205 5311 : if (check_keys_in_record(param, info, extend, start_recpos, record))
1206 0 : return 1;
1207 : }
1208 65 : return 0;
1209 : }
1210 :
1211 :
1212 : static int check_dynamic_record(HA_CHECK *param, MARIA_HA *info, int extend,
1213 : uchar *record)
1214 64 : {
1215 : MARIA_BLOCK_INFO block_info;
1216 64 : MARIA_SHARE *share= info->s;
1217 : my_off_t start_recpos, start_block, pos;
1218 : uchar *to;
1219 : ulong left_length;
1220 : uint b_type;
1221 : char llbuff[22],llbuff2[22],llbuff3[22];
1222 64 : DBUG_ENTER("check_dynamic_record");
1223 :
1224 64 : LINT_INIT(left_length);
1225 64 : LINT_INIT(start_recpos);
1226 64 : LINT_INIT(to);
1227 :
1228 64 : pos= 0;
1229 25079 : while (pos < share->state.state.data_file_length)
1230 : {
1231 24951 : my_bool got_error= 0;
1232 : int flag;
1233 24951 : if (*_ma_killed_ptr(param))
1234 0 : DBUG_RETURN(-1);
1235 :
1236 24951 : flag= block_info.second_read=0;
1237 24951 : block_info.next_filepos=pos;
1238 : do
1239 : {
1240 25463 : if (_ma_read_cache(¶m->read_cache, block_info.header,
1241 : (start_block=block_info.next_filepos),
1242 : sizeof(block_info.header),
1243 : (flag ? 0 : READING_NEXT) | READING_HEADER))
1244 : {
1245 0 : _ma_check_print_error(param,
1246 : "got error: %d when reading datafile at "
1247 : "position: %s",
1248 : my_errno, llstr(start_block, llbuff));
1249 0 : DBUG_RETURN(1);
1250 : }
1251 :
1252 25463 : if (start_block & (MARIA_DYN_ALIGN_SIZE-1))
1253 : {
1254 0 : _ma_check_print_error(param,"Wrong aligned block at %s",
1255 : llstr(start_block,llbuff));
1256 0 : DBUG_RETURN(1);
1257 : }
1258 25463 : b_type= _ma_get_block_info(&block_info,-1,start_block);
1259 25463 : if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
1260 : BLOCK_FATAL_ERROR))
1261 : {
1262 17752 : if (b_type & BLOCK_SYNC_ERROR)
1263 : {
1264 512 : if (flag)
1265 : {
1266 0 : _ma_check_print_error(param,"Unexpected byte: %d at link: %s",
1267 : (int) block_info.header[0],
1268 : llstr(start_block,llbuff));
1269 0 : DBUG_RETURN(1);
1270 : }
1271 512 : pos=block_info.filepos+block_info.block_len;
1272 512 : goto next;
1273 : }
1274 17240 : if (b_type & BLOCK_DELETED)
1275 : {
1276 17240 : if (block_info.block_len < share->base.min_block_length)
1277 : {
1278 0 : _ma_check_print_error(param,
1279 : "Deleted block with impossible length %lu "
1280 : "at %s",
1281 : block_info.block_len,llstr(pos,llbuff));
1282 0 : DBUG_RETURN(1);
1283 : }
1284 17240 : if ((block_info.next_filepos != HA_OFFSET_ERROR &&
1285 : block_info.next_filepos >= share->state.state.data_file_length) ||
1286 : (block_info.prev_filepos != HA_OFFSET_ERROR &&
1287 : block_info.prev_filepos >= share->state.state.data_file_length))
1288 : {
1289 0 : _ma_check_print_error(param,"Delete link points outside datafile "
1290 : "at %s",
1291 : llstr(pos,llbuff));
1292 0 : DBUG_RETURN(1);
1293 : }
1294 17240 : param->del_blocks++;
1295 17240 : param->del_length+= block_info.block_len;
1296 17240 : param->splits++;
1297 17240 : pos= block_info.filepos+block_info.block_len;
1298 17240 : goto next;
1299 : }
1300 0 : _ma_check_print_error(param,"Wrong bytesec: %d-%d-%d at linkstart: %s",
1301 : block_info.header[0],block_info.header[1],
1302 : block_info.header[2],
1303 : llstr(start_block,llbuff));
1304 0 : DBUG_RETURN(1);
1305 : }
1306 7711 : if (share->state.state.data_file_length < block_info.filepos+
1307 : block_info.block_len)
1308 : {
1309 0 : _ma_check_print_error(param,
1310 : "Recordlink that points outside datafile at %s",
1311 : llstr(pos,llbuff));
1312 0 : got_error=1;
1313 0 : break;
1314 : }
1315 7711 : param->splits++;
1316 7711 : if (!flag++) /* First block */
1317 : {
1318 7199 : start_recpos=pos;
1319 7199 : pos=block_info.filepos+block_info.block_len;
1320 7199 : if (block_info.rec_len > (uint) share->base.max_pack_length)
1321 : {
1322 0 : _ma_check_print_error(param,"Found too long record (%lu) at %s",
1323 : (ulong) block_info.rec_len,
1324 : llstr(start_recpos,llbuff));
1325 0 : got_error=1;
1326 0 : break;
1327 : }
1328 7199 : if (share->base.blobs)
1329 : {
1330 62 : if (_ma_alloc_buffer(&info->rec_buff, &info->rec_buff_size,
1331 : block_info.rec_len +
1332 : share->base.extra_rec_buff_size))
1333 :
1334 : {
1335 0 : _ma_check_print_error(param,
1336 : "Not enough memory (%lu) for blob at %s",
1337 : (ulong) block_info.rec_len,
1338 : llstr(start_recpos,llbuff));
1339 0 : got_error=1;
1340 0 : break;
1341 : }
1342 : }
1343 7199 : to= info->rec_buff;
1344 7199 : left_length= block_info.rec_len;
1345 : }
1346 7711 : if (left_length < block_info.data_len)
1347 : {
1348 0 : _ma_check_print_error(param,"Found too long record (%lu) at %s",
1349 : (ulong) block_info.data_len,
1350 : llstr(start_recpos,llbuff));
1351 0 : got_error=1;
1352 0 : break;
1353 : }
1354 7711 : if (_ma_read_cache(¶m->read_cache, to, block_info.filepos,
1355 : (uint) block_info.data_len,
1356 : flag == 1 ? READING_NEXT : 0))
1357 : {
1358 0 : _ma_check_print_error(param,
1359 : "got error: %d when reading datafile at "
1360 : "position: %s", my_errno,
1361 : llstr(block_info.filepos, llbuff));
1362 :
1363 0 : DBUG_RETURN(1);
1364 : }
1365 7711 : to+=block_info.data_len;
1366 7711 : param->link_used+= block_info.filepos-start_block;
1367 7711 : param->used+= block_info.filepos - start_block + block_info.data_len;
1368 7711 : param->empty+= block_info.block_len-block_info.data_len;
1369 7711 : left_length-= block_info.data_len;
1370 7711 : if (left_length)
1371 : {
1372 512 : if (b_type & BLOCK_LAST)
1373 : {
1374 0 : _ma_check_print_error(param,
1375 : "Wrong record length %s of %s at %s",
1376 : llstr(block_info.rec_len-left_length,llbuff),
1377 : llstr(block_info.rec_len, llbuff2),
1378 : llstr(start_recpos,llbuff3));
1379 0 : got_error=1;
1380 0 : break;
1381 : }
1382 512 : if (share->state.state.data_file_length < block_info.next_filepos)
1383 : {
1384 0 : _ma_check_print_error(param,
1385 : "Found next-recordlink that points outside "
1386 : "datafile at %s",
1387 : llstr(block_info.filepos,llbuff));
1388 0 : got_error=1;
1389 0 : break;
1390 : }
1391 : }
1392 7711 : } while (left_length);
1393 :
1394 7199 : if (! got_error)
1395 : {
1396 7199 : if (_ma_rec_unpack(info,record,info->rec_buff,block_info.rec_len) ==
1397 : MY_FILE_ERROR)
1398 : {
1399 0 : _ma_check_print_error(param,"Found wrong record at %s",
1400 : llstr(start_recpos,llbuff));
1401 0 : got_error=1;
1402 : }
1403 : else
1404 : {
1405 7199 : ha_checksum checksum= 0;
1406 7199 : if (share->calc_checksum)
1407 4837 : checksum= (*share->calc_checksum)(info, record);
1408 :
1409 7199 : if (param->testflag & (T_EXTEND | T_MEDIUM | T_VERBOSE))
1410 : {
1411 7199 : if (_ma_rec_check(info,record, info->rec_buff,block_info.rec_len,
1412 : test(share->calc_checksum), checksum))
1413 : {
1414 0 : _ma_check_print_error(param,"Found wrong packed record at %s",
1415 : llstr(start_recpos,llbuff));
1416 0 : got_error= 1;
1417 : }
1418 : }
1419 7199 : param->glob_crc+= checksum;
1420 : }
1421 :
1422 7199 : if (! got_error)
1423 : {
1424 7199 : if (check_keys_in_record(param, info, extend, start_recpos, record))
1425 0 : DBUG_RETURN(1);
1426 : }
1427 : else
1428 : {
1429 0 : if (param->err_count++ > MAXERR || !(param->testflag & T_VERBOSE))
1430 0 : DBUG_RETURN(1);
1431 : }
1432 : }
1433 0 : else if (!flag)
1434 0 : pos= block_info.filepos+block_info.block_len;
1435 24951 : next:;
1436 : }
1437 64 : DBUG_RETURN(0);
1438 : }
1439 :
1440 :
1441 : static int check_compressed_record(HA_CHECK *param, MARIA_HA *info, int extend,
1442 : uchar *record)
1443 35 : {
1444 : MARIA_BLOCK_INFO block_info;
1445 35 : MARIA_SHARE *share= info->s;
1446 : my_off_t start_recpos, pos;
1447 : char llbuff[22];
1448 35 : my_bool got_error= 0;
1449 35 : DBUG_ENTER("check_compressed_record");
1450 :
1451 35 : pos= share->pack.header_length; /* Skip header */
1452 670 : while (pos < share->state.state.data_file_length)
1453 : {
1454 600 : if (*_ma_killed_ptr(param))
1455 0 : DBUG_RETURN(-1);
1456 :
1457 600 : if (_ma_read_cache(¶m->read_cache, block_info.header, pos,
1458 : share->pack.ref_length, READING_NEXT))
1459 : {
1460 0 : _ma_check_print_error(param,
1461 : "got error: %d when reading datafile at position: "
1462 : "%s",
1463 : my_errno, llstr(pos, llbuff));
1464 0 : DBUG_RETURN(1);
1465 : }
1466 :
1467 600 : start_recpos= pos;
1468 600 : param->splits++;
1469 600 : VOID(_ma_pack_get_block_info(info, &info->bit_buff, &block_info,
1470 : &info->rec_buff, &info->rec_buff_size, -1,
1471 : start_recpos));
1472 600 : pos=block_info.filepos+block_info.rec_len;
1473 600 : if (block_info.rec_len < (uint) share->min_pack_length ||
1474 : block_info.rec_len > (uint) share->max_pack_length)
1475 : {
1476 0 : _ma_check_print_error(param,
1477 : "Found block with wrong recordlength: %lu at %s",
1478 : block_info.rec_len, llstr(start_recpos,llbuff));
1479 0 : got_error=1;
1480 0 : goto end;
1481 : }
1482 600 : if (_ma_read_cache(¶m->read_cache, info->rec_buff,
1483 : block_info.filepos, block_info.rec_len, READING_NEXT))
1484 : {
1485 0 : _ma_check_print_error(param,
1486 : "got error: %d when reading datafile at position: "
1487 : "%s",
1488 : my_errno, llstr(block_info.filepos, llbuff));
1489 0 : DBUG_RETURN(1);
1490 : }
1491 600 : if (_ma_pack_rec_unpack(info, &info->bit_buff, record,
1492 : info->rec_buff, block_info.rec_len))
1493 : {
1494 0 : _ma_check_print_error(param,"Found wrong record at %s",
1495 : llstr(start_recpos,llbuff));
1496 0 : got_error=1;
1497 0 : goto end;
1498 : }
1499 600 : param->glob_crc+= (*share->calc_checksum)(info,record);
1500 600 : param->link_used+= (block_info.filepos - start_recpos);
1501 600 : param->used+= (pos-start_recpos);
1502 :
1503 600 : end:
1504 600 : if (! got_error)
1505 : {
1506 600 : if (check_keys_in_record(param, info, extend, start_recpos, record))
1507 0 : DBUG_RETURN(1);
1508 : }
1509 : else
1510 : {
1511 0 : got_error= 0; /* Reset for next loop */
1512 0 : if (param->err_count++ > MAXERR || !(param->testflag & T_VERBOSE))
1513 0 : DBUG_RETURN(1);
1514 : }
1515 : }
1516 35 : DBUG_RETURN(0);
1517 : }
1518 :
1519 :
1520 : /*
1521 : Check if layout on head or tail page is ok
1522 :
1523 : NOTES
1524 : This is for rows-in-block format.
1525 : */
1526 :
1527 : static int check_page_layout(HA_CHECK *param, MARIA_HA *info,
1528 : my_off_t page_pos, uchar *page,
1529 : uint row_count, uint head_empty,
1530 : uint *real_rows_found, uint *free_slots_found)
1531 1030 : {
1532 : uint empty, last_row_end, row, first_dir_entry, free_entry, block_size;
1533 : uint free_entries, prev_free_entry;
1534 : uchar *dir_entry;
1535 : char llbuff[22];
1536 1030 : my_bool error_in_free_list= 0;
1537 1030 : DBUG_ENTER("check_page_layout");
1538 :
1539 1030 : block_size= info->s->block_size;
1540 1030 : empty= 0;
1541 1030 : last_row_end= PAGE_HEADER_SIZE;
1542 1030 : *real_rows_found= 0;
1543 :
1544 : /* Check free directory list */
1545 1030 : free_entry= (uint) page[DIR_FREE_OFFSET];
1546 1030 : free_entries= 0;
1547 1030 : prev_free_entry= END_OF_DIR_FREE_LIST;
1548 3192 : while (free_entry != END_OF_DIR_FREE_LIST)
1549 : {
1550 : uchar *dir;
1551 1132 : if (free_entry > row_count)
1552 : {
1553 0 : _ma_check_print_error(param,
1554 : "Page %9s: Directory free entry points outside "
1555 : "directory",
1556 : llstr(page_pos, llbuff));
1557 0 : error_in_free_list= 1;
1558 0 : break;
1559 : }
1560 1132 : dir= dir_entry_pos(page, block_size, free_entry);
1561 1132 : if (uint2korr(dir) != 0)
1562 : {
1563 0 : _ma_check_print_error(param,
1564 : "Page %9s: Directory free entry points to "
1565 : "not deleted entry",
1566 : llstr(page_pos, llbuff));
1567 0 : error_in_free_list= 1;
1568 0 : break;
1569 : }
1570 1132 : if (dir[2] != prev_free_entry)
1571 : {
1572 0 : _ma_check_print_error(param,
1573 : "Page %9s: Directory free list back pointer "
1574 : "points to wrong entry",
1575 : llstr(page_pos, llbuff));
1576 0 : error_in_free_list= 1;
1577 0 : break;
1578 : }
1579 1132 : prev_free_entry= free_entry;
1580 1132 : free_entry= dir[3];
1581 1132 : free_entries++;
1582 : }
1583 1030 : *free_slots_found= free_entries;
1584 :
1585 : /* Check directry */
1586 1030 : dir_entry= page+ block_size - PAGE_SUFFIX_SIZE;
1587 1030 : first_dir_entry= (block_size - row_count * DIR_ENTRY_SIZE -
1588 : PAGE_SUFFIX_SIZE);
1589 26351 : for (row= 0 ; row < row_count ; row++)
1590 : {
1591 : uint pos, length;
1592 25321 : dir_entry-= DIR_ENTRY_SIZE;
1593 25321 : pos= uint2korr(dir_entry);
1594 25321 : if (!pos)
1595 : {
1596 1132 : free_entries--;
1597 1132 : if (row == row_count -1)
1598 : {
1599 0 : _ma_check_print_error(param,
1600 : "Page %9s: First entry in directory is 0",
1601 : llstr(page_pos, llbuff));
1602 0 : if (param->err_count++ > MAXERR || !(param->testflag & T_VERBOSE))
1603 0 : DBUG_RETURN(1);
1604 : }
1605 : continue; /* Deleted row */
1606 : }
1607 24189 : (*real_rows_found)++;
1608 24189 : length= uint2korr(dir_entry+2);
1609 24189 : param->used+= length;
1610 24189 : if (pos < last_row_end)
1611 : {
1612 0 : _ma_check_print_error(param,
1613 : "Page %9s: Row %3u overlapps with previous row",
1614 : llstr(page_pos, llbuff), row);
1615 0 : DBUG_RETURN(1);
1616 : }
1617 24189 : empty+= (pos - last_row_end);
1618 24189 : last_row_end= pos + length;
1619 24189 : if (last_row_end > first_dir_entry)
1620 : {
1621 0 : _ma_check_print_error(param,
1622 : "Page %9s: Row %3u overlapps with directory",
1623 : llstr(page_pos, llbuff), row);
1624 0 : DBUG_RETURN(1);
1625 : }
1626 : }
1627 1030 : empty+= (first_dir_entry - last_row_end);
1628 :
1629 1030 : if (empty != head_empty)
1630 : {
1631 0 : _ma_check_print_error(param,
1632 : "Page %9s: Wrong empty size. Stored: %5u "
1633 : "Actual: %5u",
1634 : llstr(page_pos, llbuff), head_empty, empty);
1635 0 : param->err_count++;
1636 : }
1637 1030 : if (free_entries != 0 && !error_in_free_list)
1638 : {
1639 0 : _ma_check_print_error(param,
1640 : "Page %9s: Directory free link don't include "
1641 : "all free entries",
1642 : llstr(page_pos, llbuff));
1643 0 : param->err_count++;
1644 : }
1645 1030 : DBUG_RETURN(param->err_count &&
1646 : (param->err_count >= MAXERR || !(param->testflag & T_VERBOSE)));
1647 : }
1648 :
1649 :
1650 : /*
1651 : Check all rows on head page
1652 :
1653 : NOTES
1654 : This is for rows-in-block format.
1655 :
1656 : Before this, we have already called check_page_layout(), so
1657 : we know the block is logicaly correct (even if the rows may not be that)
1658 :
1659 : RETURN
1660 : 0 ok
1661 : 1 error
1662 : */
1663 :
1664 :
1665 : static my_bool check_head_page(HA_CHECK *param, MARIA_HA *info, uchar *record,
1666 : int extend, my_off_t page_pos, uchar *page_buff,
1667 : uint row_count)
1668 967 : {
1669 967 : MARIA_SHARE *share= info->s;
1670 : uchar *dir_entry;
1671 : uint row;
1672 : char llbuff[22], llbuff2[22];
1673 967 : ulonglong page= page_pos / share->block_size;
1674 967 : DBUG_ENTER("check_head_page");
1675 :
1676 967 : dir_entry= page_buff+ share->block_size - PAGE_SUFFIX_SIZE;
1677 26176 : for (row= 0 ; row < row_count ; row++)
1678 : {
1679 : uint pos, length, flag;
1680 25209 : dir_entry-= DIR_ENTRY_SIZE;
1681 25209 : pos= uint2korr(dir_entry);
1682 25209 : if (!pos)
1683 24082 : continue;
1684 24082 : length= uint2korr(dir_entry+2);
1685 24082 : if (length < share->base.min_block_length)
1686 : {
1687 0 : _ma_check_print_error(param,
1688 : "Page %9s: Row %3u is too short "
1689 : "(%d of min %d bytes)",
1690 : llstr(page, llbuff), row, length,
1691 : (uint) share->base.min_block_length);
1692 0 : DBUG_RETURN(1);
1693 : }
1694 24082 : flag= (uint) (uchar) page_buff[pos];
1695 24082 : if (flag & ~(ROW_FLAG_ALL))
1696 0 : _ma_check_print_error(param,
1697 : "Page %9s: Row %3u has wrong flag: %u",
1698 : llstr(page, llbuff), row, flag);
1699 :
1700 24082 : DBUG_PRINT("info", ("rowid: %s page: %lu row: %u",
1701 : llstr(ma_recordpos(page, row), llbuff),
1702 : (ulong) page, row));
1703 24082 : info->cur_row.trid= 0;
1704 24082 : if (_ma_read_block_record2(info, record, page_buff+pos,
1705 : page_buff+pos+length))
1706 : {
1707 0 : _ma_check_print_error(param,
1708 : "Page %9s: Row %3d is crashed",
1709 : llstr(page, llbuff), row);
1710 0 : if (param->err_count++ > MAXERR || !(param->testflag & T_VERBOSE))
1711 0 : DBUG_RETURN(1);
1712 : continue;
1713 : }
1714 24082 : set_if_bigger(param->max_found_trid, info->cur_row.trid);
1715 24082 : if (info->cur_row.trid > param->max_trid)
1716 0 : _ma_check_print_not_visible_error(param, info->cur_row.trid);
1717 :
1718 24082 : if (share->calc_checksum)
1719 : {
1720 22930 : ha_checksum checksum= (*share->calc_checksum)(info, record);
1721 22930 : if (info->cur_row.checksum != (checksum & 255))
1722 0 : _ma_check_print_error(param, "Page %9s: Row %3d has wrong checksum",
1723 : llstr(page, llbuff), row);
1724 22930 : param->glob_crc+= checksum;
1725 : }
1726 24082 : if (info->cur_row.extents_count)
1727 : {
1728 142 : uchar *extents= info->cur_row.extents;
1729 : uint i;
1730 : /* Check that bitmap has the right marker for the found extents */
1731 391 : for (i= 0 ; i < info->cur_row.extents_count ; i++)
1732 : {
1733 : pgcache_page_no_t extent_page;
1734 : uint page_count, page_type;
1735 249 : extent_page= uint5korr(extents);
1736 249 : page_count= uint2korr(extents+5) & ~START_EXTENT_BIT;
1737 249 : extents+= ROW_EXTENT_SIZE;
1738 249 : page_type= BLOB_PAGE;
1739 249 : if (page_count & TAIL_BIT)
1740 : {
1741 107 : page_count= 1;
1742 107 : page_type= TAIL_PAGE;
1743 : }
1744 : /*
1745 : TODO OPTIMIZE:
1746 : Check the whole extent with one test and only do the loop if
1747 : something is wrong (for exact error reporting)
1748 : */
1749 493 : for ( ; page_count--; extent_page++)
1750 : {
1751 : uint bitmap_pattern;
1752 493 : if (_ma_check_if_right_bitmap_type(info, page_type, extent_page,
1753 : &bitmap_pattern))
1754 : {
1755 0 : _ma_check_print_error(param,
1756 : "Page %9s: Row: %3d has an extent with "
1757 : "wrong information in bitmap: "
1758 : "Page %9s Page_type: %d Bitmap: %d",
1759 : llstr(page, llbuff), row,
1760 : llstr(extent_page, llbuff2),
1761 : page_type, bitmap_pattern);
1762 0 : if (param->err_count++ > MAXERR || !(param->testflag & T_VERBOSE))
1763 0 : DBUG_RETURN(1);
1764 : }
1765 : }
1766 : }
1767 : }
1768 24082 : param->full_page_count+= info->cur_row.full_page_count;
1769 24082 : param->tail_count+= info->cur_row.tail_count;
1770 24082 : if (check_keys_in_record(param, info, extend,
1771 : ma_recordpos(page, row), record))
1772 0 : DBUG_RETURN(1);
1773 : }
1774 967 : DBUG_RETURN(0);
1775 : }
1776 :
1777 :
1778 : /*
1779 : Check if rows-in-block data file is consistent
1780 : */
1781 :
1782 : static int check_block_record(HA_CHECK *param, MARIA_HA *info, int extend,
1783 : uchar *record)
1784 511 : {
1785 511 : MARIA_SHARE *share= info->s;
1786 : my_off_t pos;
1787 : pgcache_page_no_t page;
1788 : uchar *page_buff, *bitmap_buff, *data;
1789 : char llbuff[22], llbuff2[22];
1790 511 : uint block_size= share->block_size;
1791 : ha_rows full_page_count, tail_count;
1792 : my_bool full_dir;
1793 : uint offset_page, offset, free_count;
1794 :
1795 511 : LINT_INIT(full_dir);
1796 :
1797 511 : if (_ma_scan_init_block_record(info))
1798 : {
1799 0 : _ma_check_print_error(param, "got error %d when initializing scan",
1800 : my_errno);
1801 0 : return 1;
1802 : }
1803 511 : bitmap_buff= info->scan.bitmap_buff;
1804 511 : page_buff= info->scan.page_buff;
1805 511 : full_page_count= tail_count= 0;
1806 511 : param->full_page_count= param->tail_count= 0;
1807 511 : param->used= param->link_used= 0;
1808 511 : param->splits= share->state.state.data_file_length / block_size;
1809 :
1810 511 : for (pos= 0, page= 0;
1811 23685 : pos < share->state.state.data_file_length;
1812 22663 : pos+= block_size, page++)
1813 : {
1814 : uint row_count, real_row_count, empty_space, page_type, bitmap_pattern;
1815 22663 : LINT_INIT(row_count);
1816 22663 : LINT_INIT(empty_space);
1817 :
1818 22663 : if (*_ma_killed_ptr(param))
1819 : {
1820 0 : _ma_scan_end_block_record(info);
1821 0 : return -1;
1822 : }
1823 22663 : if ((page % share->bitmap.pages_covered) == 0)
1824 : {
1825 : /* Bitmap page */
1826 511 : if (pagecache_read(share->pagecache,
1827 : &info->s->bitmap.file,
1828 : page, 1,
1829 : bitmap_buff,
1830 : PAGECACHE_PLAIN_PAGE,
1831 : PAGECACHE_LOCK_LEFT_UNLOCKED, 0) == 0)
1832 : {
1833 0 : _ma_check_print_error(param,
1834 : "Page %9s: Got error: %d when reading datafile",
1835 : llstr(page, llbuff), my_errno);
1836 0 : goto err;
1837 : }
1838 511 : param->used+= block_size;
1839 511 : param->link_used+= block_size;
1840 511 : continue;
1841 : }
1842 : /* Skip pages marked as empty in bitmap */
1843 22152 : offset_page= (uint) ((page % share->bitmap.pages_covered) -1) * 3;
1844 22152 : offset= offset_page & 7;
1845 22152 : data= bitmap_buff + offset_page / 8;
1846 22152 : bitmap_pattern= uint2korr(data);
1847 22152 : if (!((bitmap_pattern >> offset) & 7))
1848 : {
1849 20736 : param->empty+= block_size;
1850 20736 : param->del_blocks++;
1851 20736 : continue;
1852 : }
1853 :
1854 1416 : if (pagecache_read(share->pagecache,
1855 : &info->dfile,
1856 : page, 1,
1857 : page_buff,
1858 : share->page_type,
1859 : PAGECACHE_LOCK_LEFT_UNLOCKED, 0) == 0)
1860 : {
1861 0 : _ma_check_print_error(param,
1862 : "Page %9s: Got error: %d when reading datafile",
1863 : llstr(page, llbuff), my_errno);
1864 0 : goto err;
1865 : }
1866 1416 : page_type= page_buff[PAGE_TYPE_OFFSET] & PAGE_TYPE_MASK;
1867 1416 : if (page_type == UNALLOCATED_PAGE || page_type >= MAX_PAGE_TYPE)
1868 : {
1869 0 : _ma_check_print_error(param,
1870 : "Page: %9s Found wrong page type %d",
1871 : llstr(page, llbuff), page_type);
1872 0 : if (param->err_count++ > MAXERR || !(param->testflag & T_VERBOSE))
1873 : goto err;
1874 : continue;
1875 : }
1876 1416 : switch ((enum en_page_type) page_type) {
1877 : case UNALLOCATED_PAGE:
1878 : case MAX_PAGE_TYPE:
1879 : default:
1880 0 : DBUG_ASSERT(0); /* Impossible */
1881 : break;
1882 : case HEAD_PAGE:
1883 967 : row_count= page_buff[DIR_COUNT_OFFSET];
1884 967 : empty_space= uint2korr(page_buff + EMPTY_SPACE_OFFSET);
1885 967 : param->used+= block_size - empty_space;
1886 967 : param->link_used+= (PAGE_HEADER_SIZE + PAGE_SUFFIX_SIZE +
1887 : row_count * DIR_ENTRY_SIZE);
1888 967 : if (empty_space < share->bitmap.sizes[3])
1889 169 : param->lost+= empty_space;
1890 967 : if (check_page_layout(param, info, pos, page_buff, row_count,
1891 : empty_space, &real_row_count, &free_count))
1892 967 : goto err;
1893 967 : full_dir= (row_count == MAX_ROWS_PER_PAGE &&
1894 : page_buff[DIR_FREE_OFFSET] == END_OF_DIR_FREE_LIST);
1895 967 : break;
1896 : case TAIL_PAGE:
1897 63 : row_count= page_buff[DIR_COUNT_OFFSET];
1898 63 : empty_space= uint2korr(page_buff + EMPTY_SPACE_OFFSET);
1899 63 : param->used+= block_size - empty_space;
1900 63 : param->link_used+= (PAGE_HEADER_SIZE + PAGE_SUFFIX_SIZE +
1901 : row_count * DIR_ENTRY_SIZE);
1902 63 : if (empty_space < share->bitmap.sizes[6])
1903 15 : param->lost+= empty_space;
1904 63 : if (check_page_layout(param, info, pos, page_buff, row_count,
1905 : empty_space, &real_row_count, &free_count))
1906 63 : goto err;
1907 63 : full_dir= (row_count - free_count >= MAX_ROWS_PER_PAGE -
1908 : share->base.blobs);
1909 63 : break;
1910 : case BLOB_PAGE:
1911 386 : full_page_count++;
1912 386 : full_dir= 0;
1913 386 : empty_space= block_size; /* for error reporting */
1914 386 : param->link_used+= (LSN_SIZE + PAGE_TYPE_SIZE);
1915 386 : param->used+= block_size;
1916 : break;
1917 : }
1918 1416 : if (_ma_check_bitmap_data(info, page_type, page,
1919 : full_dir ? 0 : empty_space,
1920 : &bitmap_pattern))
1921 : {
1922 0 : if (bitmap_pattern == ~(uint) 0)
1923 0 : _ma_check_print_error(param,
1924 : "Page %9s: Wrong bitmap for data on page",
1925 : llstr(page, llbuff));
1926 : else
1927 0 : _ma_check_print_error(param,
1928 : "Page %9s: Wrong data in bitmap. Page_type: "
1929 : "%d empty_space: %u Bitmap-bits: %d",
1930 : llstr(page, llbuff), page_type,
1931 : empty_space, bitmap_pattern);
1932 0 : if (param->err_count++ > MAXERR || !(param->testflag & T_VERBOSE))
1933 : goto err;
1934 : }
1935 1416 : if ((enum en_page_type) page_type == BLOB_PAGE)
1936 1030 : continue;
1937 1030 : param->empty+= empty_space;
1938 1030 : if ((enum en_page_type) page_type == TAIL_PAGE)
1939 : {
1940 63 : tail_count+= real_row_count;
1941 63 : continue;
1942 : }
1943 967 : if (check_head_page(param, info, record, extend, pos, page_buff,
1944 : row_count))
1945 22663 : goto err;
1946 : }
1947 :
1948 : /* Verify that rest of bitmap is zero */
1949 :
1950 511 : if (page % share->bitmap.pages_covered)
1951 : {
1952 : /* Not at end of bitmap */
1953 : uint bitmap_pattern;
1954 511 : offset_page= (uint) ((page % share->bitmap.pages_covered) -1) * 3;
1955 511 : offset= offset_page & 7;
1956 511 : data= bitmap_buff + offset_page / 8;
1957 511 : bitmap_pattern= uint2korr(data);
1958 511 : if (((bitmap_pattern >> offset)) ||
1959 : (data + 2 < bitmap_buff + share->bitmap.total_size &&
1960 : _ma_check_if_zero(data+2, bitmap_buff + share->bitmap.total_size -
1961 : data - 2)))
1962 : {
1963 : ulonglong bitmap_page;
1964 0 : bitmap_page= page / share->bitmap.pages_covered;
1965 0 : bitmap_page*= share->bitmap.pages_covered;
1966 :
1967 0 : _ma_check_print_error(param,
1968 : "Bitmap at page %s has pages reserved outside of "
1969 : "data file length",
1970 : llstr(bitmap_page, llbuff));
1971 0 : DBUG_EXECUTE("bitmap", _ma_print_bitmap(&share->bitmap, bitmap_buff,
1972 : bitmap_page););
1973 : }
1974 : }
1975 :
1976 511 : _ma_scan_end_block_record(info);
1977 :
1978 511 : if (full_page_count != param->full_page_count)
1979 0 : _ma_check_print_error(param, "Full page count read through records was %s "
1980 : "but we found %s pages while scanning table",
1981 : llstr(param->full_page_count, llbuff),
1982 : llstr(full_page_count, llbuff2));
1983 511 : if (tail_count != param->tail_count)
1984 0 : _ma_check_print_error(param, "Tail count read through records was %s but "
1985 : "we found %s tails while scanning table",
1986 : llstr(param->tail_count, llbuff),
1987 : llstr(tail_count, llbuff2));
1988 :
1989 511 : return param->error_printed != 0;
1990 :
1991 0 : err:
1992 0 : _ma_scan_end_block_record(info);
1993 0 : return 1;
1994 : }
1995 :
1996 :
1997 : /* Check that record-link is ok */
1998 :
1999 : int maria_chk_data_link(HA_CHECK *param, MARIA_HA *info, my_bool extend)
2000 675 : {
2001 675 : MARIA_SHARE *share= info->s;
2002 : int error;
2003 : uchar *record;
2004 : char llbuff[22],llbuff2[22],llbuff3[22];
2005 675 : DBUG_ENTER("maria_chk_data_link");
2006 :
2007 675 : if (!(param->testflag & T_SILENT))
2008 : {
2009 0 : if (extend)
2010 0 : puts("- check records and index references");
2011 : else
2012 0 : puts("- check record links");
2013 : }
2014 :
2015 675 : if (!(record= (uchar*) my_malloc(share->base.default_rec_buff_size, MYF(0))))
2016 : {
2017 0 : _ma_check_print_error(param,"Not enough memory for record");
2018 0 : DBUG_RETURN(-1);
2019 : }
2020 675 : param->records= param->del_blocks= 0;
2021 675 : param->used= param->link_used= param->splits= param->del_length= 0;
2022 675 : param->lost= 0;
2023 675 : param->tmp_record_checksum= param->glob_crc= 0;
2024 675 : param->err_count= 0;
2025 :
2026 675 : error= 0;
2027 675 : param->empty= share->pack.header_length;
2028 :
2029 675 : bzero((char*) param->tmp_key_crc,
2030 : share->base.keys * sizeof(param->tmp_key_crc[0]));
2031 :
2032 675 : switch (share->data_file_type) {
2033 : case BLOCK_RECORD:
2034 511 : error= check_block_record(param, info, extend, record);
2035 511 : break;
2036 : case STATIC_RECORD:
2037 65 : error= check_static_record(param, info, extend, record);
2038 65 : break;
2039 : case DYNAMIC_RECORD:
2040 64 : error= check_dynamic_record(param, info, extend, record);
2041 64 : break;
2042 : case COMPRESSED_RECORD:
2043 35 : error= check_compressed_record(param, info, extend, record);
2044 : break;
2045 : } /* switch */
2046 :
2047 675 : if (error)
2048 675 : goto err;
2049 :
2050 675 : if (param->testflag & T_WRITE_LOOP)
2051 : {
2052 0 : VOID(fputs(" \r",stdout)); VOID(fflush(stdout));
2053 : }
2054 675 : if (param->records != share->state.state.records)
2055 : {
2056 0 : _ma_check_print_error(param,
2057 : "Record-count is not ok; found %-10s Should be: %s",
2058 : llstr(param->records,llbuff),
2059 : llstr(share->state.state.records,llbuff2));
2060 0 : error=1;
2061 : }
2062 675 : else if (param->record_checksum &&
2063 : param->record_checksum != param->tmp_record_checksum)
2064 : {
2065 0 : _ma_check_print_error(param,
2066 : "Key pointers and record positions doesn't match");
2067 0 : error=1;
2068 : }
2069 675 : else if (param->glob_crc != share->state.state.checksum &&
2070 : (share->options &
2071 : (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)))
2072 : {
2073 0 : _ma_check_print_warning(param,
2074 : "Record checksum is not the same as checksum "
2075 : "stored in the index file");
2076 0 : error=1;
2077 : }
2078 675 : else if (!extend)
2079 : {
2080 : uint key;
2081 691 : for (key=0 ; key < share->base.keys; key++)
2082 : {
2083 493 : if (param->tmp_key_crc[key] != param->key_crc[key] &&
2084 : !(share->keyinfo[key].flag &
2085 : (HA_FULLTEXT | HA_SPATIAL | HA_RTREE_INDEX)))
2086 : {
2087 0 : _ma_check_print_error(param,"Checksum for key: %2d doesn't match "
2088 : "checksum for records",
2089 : key+1);
2090 0 : error=1;
2091 : }
2092 : }
2093 : }
2094 :
2095 675 : if (param->del_length != share->state.state.empty)
2096 : {
2097 0 : _ma_check_print_warning(param,
2098 : "Found %s deleted space. Should be %s",
2099 : llstr(param->del_length,llbuff2),
2100 : llstr(share->state.state.empty,llbuff));
2101 : }
2102 : /* Skip following checks for BLOCK RECORD as they don't make any sence */
2103 675 : if (share->data_file_type != BLOCK_RECORD)
2104 : {
2105 164 : if (param->used + param->empty + param->del_length !=
2106 : share->state.state.data_file_length)
2107 : {
2108 0 : _ma_check_print_warning(param,
2109 : "Found %s record data and %s unused data and %s "
2110 : "deleted data",
2111 : llstr(param->used, llbuff),
2112 : llstr(param->empty,llbuff2),
2113 : llstr(param->del_length,llbuff3));
2114 0 : _ma_check_print_warning(param,
2115 : "Total %s Should be: %s",
2116 : llstr((param->used+param->empty +
2117 : param->del_length), llbuff),
2118 : llstr(share->state.state.data_file_length,
2119 : llbuff2));
2120 : }
2121 164 : if (param->del_blocks != share->state.state.del)
2122 : {
2123 0 : _ma_check_print_warning(param,
2124 : "Found %10s deleted blocks. Should be: %s",
2125 : llstr(param->del_blocks,llbuff),
2126 : llstr(share->state.state.del,llbuff2));
2127 : }
2128 164 : if (param->splits != share->state.split)
2129 : {
2130 0 : _ma_check_print_warning(param,
2131 : "Found %10s parts. Should be: %s",
2132 : llstr(param->splits, llbuff),
2133 : llstr(share->state.split,llbuff2));
2134 : }
2135 : }
2136 675 : if (param->testflag & T_INFO)
2137 : {
2138 0 : if (param->warning_printed || param->error_printed)
2139 0 : puts("");
2140 0 : if (param->used != 0 && ! param->error_printed)
2141 : {
2142 0 : if (param->records)
2143 : {
2144 0 : printf("Records:%18s M.recordlength:%9lu Packed:%14.0f%%\n",
2145 : llstr(param->records,llbuff),
2146 : (long)((param->used - param->link_used)/param->records),
2147 : (share->base.blobs ? 0.0 :
2148 : (ulonglong2double((ulonglong) share->base.reclength *
2149 : param->records)-
2150 : my_off_t2double(param->used))/
2151 : ulonglong2double((ulonglong) share->base.reclength *
2152 : param->records)*100.0));
2153 0 : printf("Recordspace used:%9.0f%% Empty space:%12d%% "
2154 : "Blocks/Record: %6.2f\n",
2155 : (ulonglong2double(param->used - param->link_used)/
2156 : ulonglong2double(param->used-param->link_used+param->empty) *
2157 : 100.0),
2158 : (!param->records ? 100 :
2159 : (int) (ulonglong2double(param->del_length+param->empty)/
2160 : my_off_t2double(param->used)*100.0)),
2161 : ulonglong2double(param->splits - param->del_blocks) /
2162 : param->records);
2163 : }
2164 : else
2165 0 : printf("Records:%18s\n", "0");
2166 : }
2167 0 : printf("Record blocks:%12s Delete blocks:%10s\n",
2168 : llstr(param->splits - param->del_blocks, llbuff),
2169 : llstr(param->del_blocks, llbuff2));
2170 0 : printf("Record data: %12s Deleted data: %10s\n",
2171 : llstr(param->used - param->link_used,llbuff),
2172 : llstr(param->del_length, llbuff2));
2173 0 : printf("Empty space: %12s Linkdata: %10s\n",
2174 : llstr(param->empty, llbuff),llstr(param->link_used, llbuff2));
2175 0 : if (param->lost)
2176 0 : printf("Lost space: %12s", llstr(param->lost, llbuff));
2177 0 : if (param->max_found_trid)
2178 : {
2179 0 : printf("Max trans. id: %11s\n",
2180 : llstr(param->max_found_trid, llbuff));
2181 : }
2182 : }
2183 675 : my_free(record,MYF(0));
2184 675 : DBUG_RETURN (error);
2185 :
2186 0 : err:
2187 0 : my_free(record,MYF(0));
2188 0 : param->testflag|=T_RETRY_WITHOUT_QUICK;
2189 0 : DBUG_RETURN(1);
2190 : } /* maria_chk_data_link */
2191 :
2192 :
2193 : /**
2194 : Prepares a table for a repair or index sort: flushes pages, records durably
2195 : in the table that it is undergoing the operation (if that op crashes, that
2196 : info will serve for Recovery and the user).
2197 :
2198 : If we start overwriting the index file, and crash then, old REDOs will
2199 : be tried and fail. To prevent that, we bump skip_redo_lsn, and thus we have
2200 : to flush and sync pages so that old REDOs can be skipped.
2201 : If this is not a bulk insert, which Recovery can handle gracefully (by
2202 : truncating files, see UNDO_BULK_INSERT) we also mark the table
2203 : crashed-on-repair, so that user knows it has to re-repair. If bulk insert we
2204 : shouldn't mark it crashed-on-repair, because if we did this, the UNDO phase
2205 : would skip the table (UNDO_BULK_INSERT would not be applied),
2206 : and maria_chk would not improve that.
2207 : If this is an OPTIMIZE which merely sorts index, we need to do the same
2208 : too: old REDOs should not apply to the new index file.
2209 : Only the flush is needed when in maria_chk which is not crash-safe.
2210 :
2211 : @param info table
2212 : @param param repair parameters
2213 : @param discard_index if index pages can be thrown away
2214 : */
2215 :
2216 : static my_bool protect_against_repair_crash(MARIA_HA *info,
2217 : const HA_CHECK *param,
2218 : my_bool discard_index)
2219 110 : {
2220 110 : MARIA_SHARE *share= info->s;
2221 :
2222 : /*
2223 : There are other than recovery-related reasons to do the writes below:
2224 : - the physical size of the data file is sometimes used during repair: we
2225 : need to flush to have it exact
2226 : - we flush the state because maria_open(HA_OPEN_COPY) will want to read
2227 : it from disk.
2228 : */
2229 110 : if (_ma_flush_table_files(info, MARIA_FLUSH_DATA | MARIA_FLUSH_INDEX,
2230 : FLUSH_FORCE_WRITE,
2231 : discard_index ? FLUSH_IGNORE_CHANGED :
2232 : FLUSH_FORCE_WRITE) ||
2233 : (share->changed &&
2234 : _ma_state_info_write(share,
2235 : MA_STATE_INFO_WRITE_DONT_MOVE_OFFSET |
2236 : MA_STATE_INFO_WRITE_FULL_INFO |
2237 : MA_STATE_INFO_WRITE_LOCK)))
2238 0 : return TRUE;
2239 : /* In maria_chk this is not needed: */
2240 110 : if (maria_multi_threaded && share->base.born_transactional)
2241 : {
2242 0 : if ((param->testflag & T_NO_CREATE_RENAME_LSN) == 0)
2243 : {
2244 : /* this can be true only for a transactional table */
2245 0 : maria_mark_crashed_on_repair(info);
2246 0 : if (_ma_state_info_write(share,
2247 : MA_STATE_INFO_WRITE_DONT_MOVE_OFFSET |
2248 : MA_STATE_INFO_WRITE_LOCK))
2249 0 : return TRUE;
2250 : }
2251 0 : if (translog_status == TRANSLOG_OK &&
2252 : _ma_update_state_lsns(share, translog_get_horizon(),
2253 : share->state.create_trid, FALSE, FALSE))
2254 0 : return TRUE;
2255 0 : if (_ma_sync_table_files(info))
2256 0 : return TRUE;
2257 : }
2258 110 : return FALSE;
2259 : }
2260 :
2261 :
2262 : /**
2263 : @brief Initialize variables for repair
2264 : */
2265 :
2266 : static int initialize_variables_for_repair(HA_CHECK *param,
2267 : MARIA_SORT_INFO *sort_info,
2268 : MARIA_SORT_PARAM *sort_param,
2269 : MARIA_HA *info,
2270 : my_bool rep_quick)
2271 110 : {
2272 110 : MARIA_SHARE *share= info->s;
2273 :
2274 : /* Repair code relies on share->state.state so we have to update it here */
2275 110 : if (share->lock.update_status)
2276 0 : (*share->lock.update_status)(info);
2277 :
2278 110 : bzero((char*) sort_info, sizeof(*sort_info));
2279 110 : bzero((char*) sort_param, sizeof(*sort_param));
2280 :
2281 110 : param->testflag|= T_REP; /* for easy checking */
2282 110 : if (share->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
2283 93 : param->testflag|= T_CALC_CHECKSUM;
2284 110 : param->glob_crc= 0;
2285 110 : if (rep_quick)
2286 35 : param->testflag|= T_QUICK;
2287 : else
2288 75 : param->testflag&= ~T_QUICK;
2289 110 : param->org_key_map= share->state.key_map;
2290 :
2291 110 : sort_param->sort_info= sort_info;
2292 110 : sort_param->fix_datafile= ! rep_quick;
2293 110 : sort_param->calc_checksum= test(param->testflag & T_CALC_CHECKSUM);
2294 110 : sort_info->info= sort_info->new_info= info;
2295 110 : sort_info->param= param;
2296 110 : set_data_file_type(sort_info, info->s);
2297 110 : sort_info->org_data_file_type= share->data_file_type;
2298 :
2299 110 : bzero(&info->rec_cache, sizeof(info->rec_cache));
2300 110 : info->rec_cache.file= info->dfile.file;
2301 110 : info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
2302 :
2303 110 : if (protect_against_repair_crash(info, param, !test(param->testflag &
2304 : T_CREATE_MISSING_KEYS)))
2305 0 : return 1;
2306 :
2307 : /* calculate max_records */
2308 110 : sort_info->filelength= my_seek(info->dfile.file, 0L, MY_SEEK_END, MYF(0));
2309 145 : if ((param->testflag & T_CREATE_MISSING_KEYS) ||
2310 : sort_info->org_data_file_type == COMPRESSED_RECORD)
2311 35 : sort_info->max_records= share->state.state.records;
2312 : else
2313 : {
2314 : ulong rec_length;
2315 75 : rec_length= max(share->base.min_pack_length,
2316 : share->base.min_block_length);
2317 75 : sort_info->max_records= (ha_rows) (sort_info->filelength / rec_length);
2318 : }
2319 :
2320 : /* Set up transaction handler so that we can see all rows */
2321 110 : if (!ma_control_file_inited())
2322 0 : param->max_trid= 0; /* Give warning for first trid found */
2323 : else
2324 110 : param->max_trid= max_trid_in_system();
2325 :
2326 110 : maria_ignore_trids(info);
2327 : /* Don't write transid's during repair */
2328 110 : maria_versioning(info, 0);
2329 110 : return 0;
2330 : }
2331 :
2332 :
2333 : /**
2334 : @brief Drop all indexes
2335 :
2336 : @param[in] param check parameters
2337 : @param[in] info MARIA_HA handle
2338 : @param[in] force if to force drop all indexes
2339 :
2340 : @return status
2341 : @retval 0 OK
2342 : @retval != 0 Error
2343 :
2344 : @note
2345 : Once allocated, index blocks remain part of the key file forever.
2346 : When indexes are disabled, no block is freed. When enabling indexes,
2347 : no block is freed either. The new indexes are create from new
2348 : blocks. (Bug #4692)
2349 :
2350 : Before recreating formerly disabled indexes, the unused blocks
2351 : must be freed. There are two options to do this:
2352 : - Follow the tree of disabled indexes, add all blocks to the
2353 : deleted blocks chain. Would require a lot of random I/O.
2354 : - Drop all blocks by clearing all index root pointers and all
2355 : delete chain pointers and resetting key_file_length to the end
2356 : of the index file header. This requires to recreate all indexes,
2357 : even those that may still be intact.
2358 : The second method is probably faster in most cases.
2359 :
2360 : When disabling indexes, MySQL disables either all indexes or all
2361 : non-unique indexes. When MySQL [re-]enables disabled indexes
2362 : (T_CREATE_MISSING_KEYS), then we either have "lost" blocks in the
2363 : index file, or there are no non-unique indexes. In the latter case,
2364 : maria_repair*() would not be called as there would be no disabled
2365 : indexes.
2366 :
2367 : If there would be more unique indexes than disabled (non-unique)
2368 : indexes, we could do the first method. But this is not implemented
2369 : yet. By now we drop and recreate all indexes when repair is called.
2370 :
2371 : However, there is an exception. Sometimes MySQL disables non-unique
2372 : indexes when the table is empty (e.g. when copying a table in
2373 : mysql_alter_table()). When enabling the non-unique indexes, they
2374 : are still empty. So there is no index block that can be lost. This
2375 : optimization is implemented in this function.
2376 :
2377 : Note that in normal repair (T_CREATE_MISSING_KEYS not set) we
2378 : recreate all enabled indexes unconditonally. We do not change the
2379 : key_map. Otherwise we invert the key map temporarily (outside of
2380 : this function) and recreate the then "seemingly" enabled indexes.
2381 : When we cannot use the optimization, and drop all indexes, we
2382 : pretend that all indexes were disabled. By the inversion, we will
2383 : then recrate all indexes.
2384 : */
2385 :
2386 : static int maria_drop_all_indexes(HA_CHECK *param, MARIA_HA *info,
2387 : my_bool force)
2388 110 : {
2389 110 : MARIA_SHARE *share= info->s;
2390 110 : MARIA_STATE_INFO *state= &share->state;
2391 : uint i;
2392 110 : DBUG_ENTER("maria_drop_all_indexes");
2393 :
2394 : /*
2395 : If any of the disabled indexes has a key block assigned, we must
2396 : drop and recreate all indexes to avoid losing index blocks.
2397 :
2398 : If we want to recreate disabled indexes only _and_ all of these
2399 : indexes are empty, we don't need to recreate the existing indexes.
2400 : */
2401 110 : if (!force && (param->testflag & T_CREATE_MISSING_KEYS))
2402 : {
2403 0 : DBUG_PRINT("repair", ("creating missing indexes"));
2404 0 : for (i= 0; i < share->base.keys; i++)
2405 : {
2406 0 : DBUG_PRINT("repair", ("index #: %u key_root: 0x%lx active: %d",
2407 : i, (long) state->key_root[i],
2408 : maria_is_key_active(state->key_map, i)));
2409 0 : if ((state->key_root[i] != HA_OFFSET_ERROR) &&
2410 : !maria_is_key_active(state->key_map, i))
2411 : {
2412 : /*
2413 : This index has at least one key block and it is disabled.
2414 : We would lose its block(s) if would just recreate it.
2415 : So we need to drop and recreate all indexes.
2416 : */
2417 0 : DBUG_PRINT("repair", ("nonempty and disabled: recreate all"));
2418 0 : break;
2419 : }
2420 : }
2421 0 : if (i >= share->base.keys)
2422 0 : goto end;
2423 :
2424 : /*
2425 : We do now drop all indexes and declare them disabled. With the
2426 : T_CREATE_MISSING_KEYS flag, maria_repair*() will recreate all
2427 : disabled indexes and enable them.
2428 : */
2429 0 : maria_clear_all_keys_active(state->key_map);
2430 0 : DBUG_PRINT("repair", ("declared all indexes disabled"));
2431 : }
2432 :
2433 : /* Clear index root block pointers. */
2434 370 : for (i= 0; i < share->base.keys; i++)
2435 260 : state->key_root[i]= HA_OFFSET_ERROR;
2436 :
2437 : /* Drop the delete chain. */
2438 110 : share->state.key_del= HA_OFFSET_ERROR;
2439 :
2440 : /* Reset index file length to end of index file header. */
2441 110 : share->state.state.key_file_length= share->base.keystart;
2442 :
2443 110 : end:
2444 110 : DBUG_RETURN(0);
2445 : }
2446 :
2447 :
2448 : /*
2449 : Recover old table by reading each record and writing all keys
2450 :
2451 : NOTES
2452 : Save new datafile-name in temp_filename.
2453 : We overwrite the index file as we go (writekeys() for example), so if we
2454 : crash during this the table is unusable and user (or Recovery in the
2455 : future) must repeat the REPAIR/OPTIMIZE operation. We could use a
2456 : temporary index file in the future (drawback: more disk space).
2457 :
2458 : IMPLEMENTATION (for hard repair with block format)
2459 : - Create new, unrelated MARIA_HA of the table
2460 : - Create new datafile and associate it with new handler
2461 : - Reset all statistic information in new handler
2462 : - Copy all data to new handler with normal write operations
2463 : - Move state of new handler to old handler
2464 : - Close new handler
2465 : - Close data file in old handler
2466 : - Rename old data file to new data file.
2467 : - Reopen data file in old handler
2468 : */
2469 :
2470 : int maria_repair(HA_CHECK *param, register MARIA_HA *info,
2471 : char *name, my_bool rep_quick)
2472 25 : {
2473 : int error, got_error;
2474 : ha_rows start_records,new_header_length;
2475 : my_off_t del;
2476 : File new_file;
2477 25 : MARIA_SHARE *share= info->s;
2478 : char llbuff[22],llbuff2[22];
2479 : MARIA_SORT_INFO sort_info;
2480 : MARIA_SORT_PARAM sort_param;
2481 25 : my_bool block_record, scan_inited= 0,
2482 25 : reenable_logging= share->now_transactional;
2483 25 : enum data_file_type org_data_file_type= share->data_file_type;
2484 : myf sync_dir= ((share->now_transactional && !share->temporary) ?
2485 25 : MY_SYNC_DIR : 0);
2486 25 : DBUG_ENTER("maria_repair");
2487 :
2488 25 : got_error= 1;
2489 25 : new_file= -1;
2490 25 : start_records= share->state.state.records;
2491 25 : if (!(param->testflag & T_SILENT))
2492 : {
2493 0 : printf("- recovering (with keycache) MARIA-table '%s'\n",name);
2494 0 : printf("Data records: %s\n", llstr(start_records, llbuff));
2495 : }
2496 :
2497 25 : if (initialize_variables_for_repair(param, &sort_info, &sort_param, info,
2498 : rep_quick))
2499 25 : goto err;
2500 :
2501 25 : if (reenable_logging)
2502 0 : _ma_tmp_disable_logging_for_table(info, 0);
2503 :
2504 25 : sort_param.current_filepos= sort_param.filepos= new_header_length=
2505 : ((param->testflag & T_UNPACK) ? 0L : share->pack.header_length);
2506 :
2507 25 : if (!rep_quick)
2508 : {
2509 : /* Get real path for data file */
2510 20 : if ((new_file= my_create(fn_format(param->temp_filename,
2511 : share->data_file_name.str, "",
2512 : DATA_TMP_EXT, 2+4),
2513 : 0,param->tmpfile_createflag,
2514 : MYF(0))) < 0)
2515 : {
2516 0 : _ma_check_print_error(param,"Can't create new tempfile: '%s'",
2517 : param->temp_filename);
2518 0 : goto err;
2519 : }
2520 20 : if (new_header_length &&
2521 : maria_filecopy(param, new_file, info->dfile.file, 0L,
2522 : new_header_length, "datafile-header"))
2523 20 : goto err;
2524 20 : share->state.dellink= HA_OFFSET_ERROR;
2525 20 : info->rec_cache.file= new_file; /* For sort_delete_record */
2526 20 : if (share->data_file_type == BLOCK_RECORD ||
2527 : (param->testflag & T_UNPACK))
2528 : {
2529 11 : if (create_new_data_handle(&sort_param, new_file))
2530 11 : goto err;
2531 11 : sort_info.new_info->rec_cache.file= new_file;
2532 : }
2533 : }
2534 :
2535 25 : block_record= sort_info.new_info->s->data_file_type == BLOCK_RECORD;
2536 :
2537 25 : if (org_data_file_type != BLOCK_RECORD)
2538 : {
2539 : /* We need a read buffer to read rows in big blocks */
2540 16 : if (init_io_cache(¶m->read_cache, info->dfile.file,
2541 : (uint) param->read_buffer_length,
2542 : READ_CACHE, share->pack.header_length, 1, MYF(MY_WME)))
2543 25 : goto err;
2544 : }
2545 25 : if (sort_info.new_info->s->data_file_type != BLOCK_RECORD)
2546 : {
2547 : /* When writing to not block records, we need a write buffer */
2548 13 : if (!rep_quick)
2549 : {
2550 11 : if (init_io_cache(&sort_info.new_info->rec_cache, new_file,
2551 : (uint) param->write_buffer_length,
2552 : WRITE_CACHE, new_header_length, 1,
2553 : MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw))
2554 11 : goto err;
2555 11 : sort_info.new_info->opt_flag|=WRITE_CACHE_USED;
2556 : }
2557 : }
2558 12 : else if (block_record)
2559 : {
2560 12 : scan_inited= 1;
2561 12 : if (maria_scan_init(sort_info.info))
2562 25 : goto err;
2563 : }
2564 :
2565 25 : if (!(sort_param.record=
2566 : (uchar *) my_malloc((uint)
2567 : share->base.default_rec_buff_size, MYF(0))) ||
2568 : _ma_alloc_buffer(&sort_param.rec_buff, &sort_param.rec_buff_size,
2569 : share->base.default_rec_buff_size))
2570 : {
2571 0 : _ma_check_print_error(param, "Not enough memory for extra record");
2572 0 : goto err;
2573 : }
2574 :
2575 25 : sort_param.read_cache=param->read_cache;
2576 25 : sort_param.pos=sort_param.max_pos=share->pack.header_length;
2577 25 : param->read_cache.end_of_file= sort_info.filelength;
2578 25 : sort_param.master=1;
2579 25 : sort_info.max_records= ~(ha_rows) 0;
2580 :
2581 25 : del= share->state.state.del;
2582 25 : share->state.state.records= share->state.state.del= share->state.split= 0;
2583 25 : share->state.state.empty= 0;
2584 :
2585 25 : if (param->testflag & T_CREATE_MISSING_KEYS)
2586 0 : maria_set_all_keys_active(share->state.key_map, share->base.keys);
2587 25 : maria_drop_all_indexes(param, info, TRUE);
2588 :
2589 25 : maria_lock_memory(param); /* Everything is alloced */
2590 :
2591 : /* Re-create all keys, which are set in key_map. */
2592 500 : while (!(error=sort_get_next_record(&sort_param)))
2593 : {
2594 450 : if (block_record && _ma_sort_write_record(&sort_param))
2595 450 : goto err;
2596 :
2597 450 : if (writekeys(&sort_param))
2598 : {
2599 75 : if (my_errno != HA_ERR_FOUND_DUPP_KEY)
2600 75 : goto err;
2601 75 : DBUG_DUMP("record", sort_param.record,
2602 : share->base.default_rec_buff_size);
2603 75 : _ma_check_print_warning(param,
2604 : "Duplicate key %2d for record at %10s against "
2605 : "new record at %10s",
2606 : info->errkey+1,
2607 : llstr(sort_param.current_filepos, llbuff),
2608 : llstr(info->dup_key_pos,llbuff2));
2609 75 : if (param->testflag & T_VERBOSE)
2610 : {
2611 : MARIA_KEY tmp_key;
2612 0 : MARIA_KEYDEF *keyinfo= share->keyinfo + info->errkey;
2613 0 : (*keyinfo->make_key)(info, &tmp_key, (uint) info->errkey,
2614 : info->lastkey_buff,
2615 : sort_param.record, 0L, 0);
2616 0 : _ma_print_key(stdout, &tmp_key);
2617 : }
2618 75 : sort_info.dupp++;
2619 75 : if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
2620 : {
2621 0 : param->testflag|=T_RETRY_WITHOUT_QUICK;
2622 0 : param->error_printed=1;
2623 0 : goto err;
2624 : }
2625 : /* purecov: begin tested */
2626 75 : if (block_record)
2627 : {
2628 0 : sort_info.new_info->s->state.state.records--;
2629 0 : if ((*sort_info.new_info->s->write_record_abort)(sort_info.new_info))
2630 : {
2631 0 : _ma_check_print_error(param,"Couldn't delete duplicate row");
2632 0 : goto err;
2633 : }
2634 : }
2635 : /* purecov: end */
2636 : continue;
2637 : }
2638 375 : if (!block_record)
2639 : {
2640 195 : if (_ma_sort_write_record(&sort_param))
2641 195 : goto err;
2642 : /* Filepos is pointer to where next row will be stored */
2643 195 : sort_param.current_filepos= sort_param.filepos;
2644 : }
2645 : }
2646 25 : if (error > 0 || maria_write_data_suffix(&sort_info, !rep_quick) ||
2647 : flush_io_cache(&sort_info.new_info->rec_cache) ||
2648 : param->read_cache.error < 0)
2649 : goto err;
2650 :
2651 25 : if (param->testflag & T_WRITE_LOOP)
2652 : {
2653 0 : VOID(fputs(" \r",stdout)); VOID(fflush(stdout));
2654 : }
2655 25 : if (my_chsize(share->kfile.file, share->state.state.key_file_length, 0, MYF(0)))
2656 : {
2657 0 : _ma_check_print_warning(param,
2658 : "Can't change size of indexfile, error: %d",
2659 : my_errno);
2660 0 : goto err;
2661 : }
2662 :
2663 25 : if (rep_quick && del+sort_info.dupp != share->state.state.del)
2664 : {
2665 0 : _ma_check_print_error(param,"Couldn't fix table with quick recovery: "
2666 : "Found wrong number of deleted records");
2667 0 : _ma_check_print_error(param,"Run recovery again without -q");
2668 0 : param->retry_repair=1;
2669 0 : param->testflag|=T_RETRY_WITHOUT_QUICK;
2670 0 : goto err;
2671 : }
2672 :
2673 25 : if (param->testflag & T_SAFE_REPAIR)
2674 : {
2675 : /* Don't repair if we loosed more than one row */
2676 0 : if (sort_info.new_info->s->state.state.records+1 < start_records)
2677 : {
2678 0 : share->state.state.records= start_records;
2679 0 : goto err;
2680 : }
2681 : }
2682 :
2683 25 : VOID(end_io_cache(&sort_info.new_info->rec_cache));
2684 25 : info->opt_flag&= ~WRITE_CACHE_USED;
2685 :
2686 : /*
2687 : As we have read the data file (sort_get_next_record()) we may have
2688 : cached, non-changed blocks of it in the page cache. We must throw them
2689 : away as we are going to close their descriptor ('new_file'). We also want
2690 : to flush any index block, so that it is ready for the upcoming sync.
2691 : */
2692 25 : if (_ma_flush_table_files_before_swap(param, info))
2693 25 : goto err;
2694 :
2695 25 : if (!rep_quick)
2696 : {
2697 20 : sort_info.new_info->s->state.state.data_file_length= sort_param.filepos;
2698 20 : if (sort_info.new_info != sort_info.info)
2699 : {
2700 11 : MARIA_STATE_INFO save_state= sort_info.new_info->s->state;
2701 11 : if (maria_close(sort_info.new_info))
2702 : {
2703 0 : _ma_check_print_error(param, "Got error %d on close", my_errno);
2704 0 : goto err;
2705 : }
2706 11 : copy_data_file_state(&share->state, &save_state);
2707 11 : new_file= -1;
2708 11 : sort_info.new_info= info;
2709 : }
2710 20 : share->state.version=(ulong) time((time_t*) 0); /* Force reopen */
2711 :
2712 : /* Replace the actual file with the temporary file */
2713 20 : if (new_file >= 0)
2714 9 : my_close(new_file, MYF(MY_WME));
2715 20 : new_file= -1;
2716 20 : change_data_file_descriptor(info, -1);
2717 20 : if (maria_change_to_newfile(share->data_file_name.str, MARIA_NAME_DEXT,
2718 : DATA_TMP_EXT,
2719 : (param->testflag & T_BACKUP_DATA ?
2720 : MYF(MY_REDEL_MAKE_BACKUP): MYF(0)) |
2721 : sync_dir) ||
2722 : _ma_open_datafile(info, share, NullS, -1))
2723 : {
2724 : goto err;
2725 : }
2726 : }
2727 : else
2728 : {
2729 5 : share->state.state.data_file_length= sort_param.max_pos;
2730 : }
2731 25 : if (param->testflag & T_CALC_CHECKSUM)
2732 23 : share->state.state.checksum= param->glob_crc;
2733 :
2734 25 : if (!(param->testflag & T_SILENT))
2735 : {
2736 0 : if (start_records != share->state.state.records)
2737 0 : printf("Data records: %s\n", llstr(share->state.state.records,llbuff));
2738 : }
2739 25 : if (sort_info.dupp)
2740 5 : _ma_check_print_warning(param,
2741 : "%s records have been removed",
2742 : llstr(sort_info.dupp,llbuff));
2743 :
2744 25 : got_error= 0;
2745 : /* If invoked by external program that uses thr_lock */
2746 25 : if (&share->state.state != info->state)
2747 25 : *info->state= *info->state_start= share->state.state;
2748 :
2749 25 : err:
2750 25 : if (scan_inited)
2751 12 : maria_scan_end(sort_info.info);
2752 25 : _ma_reset_state(info);
2753 :
2754 25 : VOID(end_io_cache(¶m->read_cache));
2755 25 : VOID(end_io_cache(&sort_info.new_info->rec_cache));
2756 25 : info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
2757 25 : sort_info.new_info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
2758 : /* this below could fail, shouldn't we detect error? */
2759 25 : if (got_error)
2760 : {
2761 0 : if (! param->error_printed)
2762 0 : _ma_check_print_error(param,"%d for record at pos %s",my_errno,
2763 : llstr(sort_param.start_recpos,llbuff));
2764 0 : (void)_ma_flush_table_files_before_swap(param, info);
2765 0 : if (sort_info.new_info && sort_info.new_info != sort_info.info)
2766 : {
2767 0 : unuse_data_file_descriptor(sort_info.new_info);
2768 0 : maria_close(sort_info.new_info);
2769 : }
2770 0 : if (new_file >= 0)
2771 : {
2772 0 : VOID(my_close(new_file,MYF(0)));
2773 0 : VOID(my_delete(param->temp_filename, MYF(MY_WME)));
2774 : }
2775 0 : maria_mark_crashed_on_repair(info);
2776 : }
2777 : /* If caller had disabled logging it's not up to us to re-enable it */
2778 25 : if (reenable_logging)
2779 0 : _ma_reenable_logging_for_table(info, FALSE);
2780 :
2781 25 : my_free(sort_param.rec_buff, MYF(MY_ALLOW_ZERO_PTR));
2782 25 : my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR));
2783 25 : my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
2784 25 : if (!got_error && (param->testflag & T_UNPACK))
2785 5 : restore_data_file_type(share);
2786 25 : share->state.changed|= (STATE_NOT_OPTIMIZED_KEYS | STATE_NOT_SORTED_PAGES |
2787 : STATE_NOT_ANALYZED | STATE_NOT_ZEROFILLED);
2788 25 : if (!rep_quick)
2789 20 : share->state.changed&= ~(STATE_NOT_OPTIMIZED_ROWS | STATE_NOT_MOVABLE);
2790 25 : DBUG_RETURN(got_error);
2791 : }
2792 :
2793 :
2794 : /* Uppdate keyfile when doing repair */
2795 :
2796 : static int writekeys(MARIA_SORT_PARAM *sort_param)
2797 450 : {
2798 : uint i;
2799 450 : MARIA_HA *info= sort_param->sort_info->info;
2800 450 : MARIA_SHARE *share= info->s;
2801 450 : uchar *record= sort_param->record;
2802 : uchar *key_buff;
2803 450 : my_off_t filepos= sort_param->current_filepos;
2804 : MARIA_KEY key;
2805 450 : DBUG_ENTER("writekeys");
2806 :
2807 450 : key_buff= info->lastkey_buff+share->base.max_key_length;
2808 :
2809 825 : for (i=0 ; i < share->base.keys ; i++)
2810 : {
2811 450 : if (maria_is_key_active(share->state.key_map, i))
2812 : {
2813 450 : if (share->keyinfo[i].flag & HA_FULLTEXT )
2814 : {
2815 0 : if (_ma_ft_add(info, i, key_buff, record, filepos))
2816 : goto err;
2817 : }
2818 : else
2819 : {
2820 450 : if (!(*share->keyinfo[i].make_key)(info, &key, i, key_buff, record,
2821 : filepos, 0))
2822 450 : goto err;
2823 450 : if ((*share->keyinfo[i].ck_insert)(info, &key))
2824 375 : goto err;
2825 : }
2826 : }
2827 : }
2828 375 : DBUG_RETURN(0);
2829 :
2830 75 : err:
2831 75 : if (my_errno == HA_ERR_FOUND_DUPP_KEY)
2832 : {
2833 75 : info->errkey=(int) i; /* This key was found */
2834 150 : while ( i-- > 0 )
2835 : {
2836 0 : if (maria_is_key_active(share->state.key_map, i))
2837 : {
2838 0 : if (share->keyinfo[i].flag & HA_FULLTEXT)
2839 : {
2840 0 : if (_ma_ft_del(info,i,key_buff,record,filepos))
2841 : break;
2842 : }
2843 : else
2844 : {
2845 0 : (*share->keyinfo[i].make_key)(info, &key, i, key_buff, record,
2846 : filepos, 0);
2847 0 : if (_ma_ck_delete(info, &key))
2848 0 : break;
2849 : }
2850 : }
2851 : }
2852 : }
2853 : /* Remove checksum that was added to glob_crc in sort_get_next_record */
2854 75 : if (sort_param->calc_checksum)
2855 75 : sort_param->sort_info->param->glob_crc-= info->cur_row.checksum;
2856 75 : DBUG_PRINT("error",("errno: %d",my_errno));
2857 75 : DBUG_RETURN(-1);
2858 : } /* writekeys */
2859 :
2860 :
2861 : /* Change all key-pointers that points to a records */
2862 :
2863 : int maria_movepoint(register MARIA_HA *info, uchar *record,
2864 : MARIA_RECORD_POS oldpos, MARIA_RECORD_POS newpos,
2865 : uint prot_key)
2866 0 : {
2867 : uint i;
2868 : uchar *key_buff;
2869 0 : MARIA_SHARE *share= info->s;
2870 : MARIA_PAGE page;
2871 0 : DBUG_ENTER("maria_movepoint");
2872 :
2873 0 : key_buff= info->lastkey_buff + share->base.max_key_length;
2874 0 : for (i=0 ; i < share->base.keys; i++)
2875 : {
2876 0 : if (i != prot_key && maria_is_key_active(share->state.key_map, i))
2877 : {
2878 : MARIA_KEY key;
2879 0 : (*share->keyinfo[i].make_key)(info, &key, i, key_buff, record, oldpos,
2880 : 0);
2881 0 : if (key.keyinfo->flag & HA_NOSAME)
2882 : { /* Change pointer direct */
2883 : MARIA_KEYDEF *keyinfo;
2884 0 : keyinfo=share->keyinfo+i;
2885 0 : if (_ma_search(info, &key, (uint32) (SEARCH_SAME | SEARCH_SAVE_BUFF),
2886 : share->state.key_root[i]))
2887 0 : DBUG_RETURN(-1);
2888 0 : _ma_page_setup(&page, info, keyinfo, info->last_keypage,
2889 : info->keyread_buff);
2890 :
2891 0 : _ma_dpointer(share, info->int_keypos - page.node -
2892 : share->rec_reflength,newpos);
2893 :
2894 0 : if (_ma_write_keypage(&page, PAGECACHE_LOCK_LEFT_UNLOCKED,
2895 : DFLT_INIT_HITS))
2896 0 : DBUG_RETURN(-1);
2897 : }
2898 : else
2899 : { /* Change old key to new */
2900 0 : if (_ma_ck_delete(info, &key))
2901 0 : DBUG_RETURN(-1);
2902 0 : (*share->keyinfo[i].make_key)(info, &key, i, key_buff, record, newpos,
2903 : 0);
2904 0 : if (_ma_ck_write(info, &key))
2905 0 : DBUG_RETURN(-1);
2906 : }
2907 : }
2908 : }
2909 0 : DBUG_RETURN(0);
2910 : } /* maria_movepoint */
2911 :
2912 :
2913 : /* Tell system that we want all memory for our cache */
2914 :
2915 : void maria_lock_memory(HA_CHECK *param __attribute__((unused)))
2916 875 : {
2917 : #ifdef SUN_OS /* Key-cacheing thrases on sun 4.1 */
2918 : if (param->opt_maria_lock_memory)
2919 : {
2920 : int success = mlockall(MCL_CURRENT); /* or plock(DATLOCK); */
2921 : if (geteuid() == 0 && success != 0)
2922 : _ma_check_print_warning(param,
2923 : "Failed to lock memory. errno %d",my_errno);
2924 : }
2925 : #endif
2926 : } /* maria_lock_memory */
2927 :
2928 :
2929 : /**
2930 : Flush all changed blocks to disk.
2931 :
2932 : We release blocks as it's unlikely that they would all be needed soon.
2933 : This function needs to be called before swapping data or index files or
2934 : syncing them.
2935 :
2936 : @param param description of the repair operation
2937 : @param info table
2938 : */
2939 :
2940 : static my_bool _ma_flush_table_files_before_swap(HA_CHECK *param,
2941 : MARIA_HA *info)
2942 210 : {
2943 210 : DBUG_ENTER("_ma_flush_table_files_before_swap");
2944 210 : if (_ma_flush_table_files(info, MARIA_FLUSH_DATA | MARIA_FLUSH_INDEX,
2945 : FLUSH_RELEASE, FLUSH_RELEASE))
2946 : {
2947 0 : _ma_check_print_error(param, "%d when trying to write buffers", my_errno);
2948 0 : DBUG_RETURN(TRUE);
2949 : }
2950 210 : DBUG_RETURN(FALSE);
2951 : }
2952 :
2953 :
2954 : /* Sort index for more efficent reads */
2955 :
2956 : int maria_sort_index(HA_CHECK *param, register MARIA_HA *info, char *name)
2957 0 : {
2958 : reg2 uint key;
2959 : reg1 MARIA_KEYDEF *keyinfo;
2960 : File new_file;
2961 : my_off_t index_pos[HA_MAX_POSSIBLE_KEY];
2962 : uint r_locks,w_locks;
2963 : int old_lock;
2964 0 : MARIA_SHARE *share= info->s;
2965 : MARIA_STATE_INFO old_state;
2966 : myf sync_dir= ((share->now_transactional && !share->temporary) ?
2967 0 : MY_SYNC_DIR : 0);
2968 0 : DBUG_ENTER("maria_sort_index");
2969 :
2970 : /* cannot sort index files with R-tree indexes */
2971 0 : for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
2972 0 : key++,keyinfo++)
2973 0 : if (keyinfo->key_alg == HA_KEY_ALG_RTREE)
2974 0 : DBUG_RETURN(0);
2975 :
2976 0 : if (!(param->testflag & T_SILENT))
2977 0 : printf("- Sorting index for MARIA-table '%s'\n",name);
2978 :
2979 0 : if (protect_against_repair_crash(info, param, FALSE))
2980 0 : DBUG_RETURN(1);
2981 :
2982 : /* Get real path for index file */
2983 0 : fn_format(param->temp_filename,name,"", MARIA_NAME_IEXT,2+4+32);
2984 0 : if ((new_file=my_create(fn_format(param->temp_filename,param->temp_filename,
2985 : "", INDEX_TMP_EXT,2+4),
2986 : 0,param->tmpfile_createflag,MYF(0))) <= 0)
2987 : {
2988 0 : _ma_check_print_error(param,"Can't create new tempfile: '%s'",
2989 : param->temp_filename);
2990 0 : DBUG_RETURN(-1);
2991 : }
2992 0 : if (maria_filecopy(param, new_file, share->kfile.file, 0L,
2993 : (ulong) share->base.keystart, "headerblock"))
2994 0 : goto err;
2995 :
2996 0 : param->new_file_pos=share->base.keystart;
2997 0 : for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
2998 0 : key++,keyinfo++)
2999 : {
3000 0 : if (! maria_is_key_active(share->state.key_map, key))
3001 0 : continue;
3002 :
3003 0 : if (share->state.key_root[key] != HA_OFFSET_ERROR)
3004 : {
3005 0 : index_pos[key]=param->new_file_pos; /* Write first block here */
3006 0 : if (sort_one_index(param,info,keyinfo,share->state.key_root[key],
3007 : new_file))
3008 : goto err;
3009 : }
3010 : else
3011 0 : index_pos[key]= HA_OFFSET_ERROR; /* No blocks */
3012 : }
3013 :
3014 : /* Flush key cache for this file if we are calling this outside maria_chk */
3015 0 : flush_pagecache_blocks(share->pagecache, &share->kfile,
3016 : FLUSH_IGNORE_CHANGED);
3017 :
3018 0 : share->state.version=(ulong) time((time_t*) 0);
3019 0 : old_state= share->state; /* save state if not stored */
3020 0 : r_locks= share->r_locks;
3021 0 : w_locks= share->w_locks;
3022 0 : old_lock= info->lock_type;
3023 :
3024 : /* Put same locks as old file */
3025 0 : share->r_locks= share->w_locks= share->tot_locks= 0;
3026 0 : (void) _ma_writeinfo(info,WRITEINFO_UPDATE_KEYFILE);
3027 0 : pthread_mutex_lock(&share->intern_lock);
3028 0 : VOID(my_close(share->kfile.file, MYF(MY_WME)));
3029 0 : share->kfile.file = -1;
3030 0 : pthread_mutex_unlock(&share->intern_lock);
3031 0 : VOID(my_close(new_file,MYF(MY_WME)));
3032 0 : if (maria_change_to_newfile(share->index_file_name.str, MARIA_NAME_IEXT,
3033 : INDEX_TMP_EXT, sync_dir) ||
3034 : _ma_open_keyfile(share))
3035 : goto err2;
3036 0 : info->lock_type= F_UNLCK; /* Force maria_readinfo to lock */
3037 0 : _ma_readinfo(info,F_WRLCK,0); /* Will lock the table */
3038 0 : info->lock_type= old_lock;
3039 0 : share->r_locks= r_locks;
3040 0 : share->w_locks= w_locks;
3041 0 : share->tot_locks= r_locks+w_locks;
3042 0 : share->state= old_state; /* Restore old state */
3043 :
3044 0 : share->state.state.key_file_length=param->new_file_pos;
3045 0 : info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
3046 0 : for (key=0 ; key < share->base.keys ; key++)
3047 0 : share->state.key_root[key]=index_pos[key];
3048 0 : share->state.key_del= HA_OFFSET_ERROR;
3049 :
3050 0 : share->state.changed&= ~STATE_NOT_SORTED_PAGES;
3051 0 : DBUG_EXECUTE_IF("maria_flush_whole_log",
3052 : {
3053 : DBUG_PRINT("maria_flush_whole_log", ("now"));
3054 : translog_flush(translog_get_horizon());
3055 : });
3056 0 : DBUG_EXECUTE_IF("maria_crash_sort_index",
3057 : {
3058 : DBUG_PRINT("maria_crash_sort_index", ("now"));
3059 : DBUG_ABORT();
3060 : });
3061 0 : DBUG_RETURN(0);
3062 :
3063 0 : err:
3064 0 : VOID(my_close(new_file,MYF(MY_WME)));
3065 0 : err2:
3066 0 : VOID(my_delete(param->temp_filename,MYF(MY_WME)));
3067 0 : DBUG_RETURN(-1);
3068 : } /* maria_sort_index */
3069 :
3070 :
3071 : /**
3072 : @brief put CRC on the page
3073 :
3074 : @param buff reference on the page buffer.
3075 : @param pos position of the page in the file.
3076 : @param length length of the page
3077 : */
3078 :
3079 : static void put_crc(uchar *buff, my_off_t pos, MARIA_SHARE *share)
3080 505 : {
3081 505 : maria_page_crc_set_index(buff, (pgcache_page_no_t) (pos / share->block_size),
3082 : (uchar*) share);
3083 : }
3084 :
3085 :
3086 : /* Sort index blocks recursive using one index */
3087 :
3088 : static int sort_one_index(HA_CHECK *param, MARIA_HA *info,
3089 : MARIA_KEYDEF *keyinfo,
3090 : my_off_t pagepos, File new_file)
3091 0 : {
3092 : uint length,nod_flag;
3093 : uchar *buff,*keypos,*endpos;
3094 : my_off_t new_page_pos,next_page;
3095 0 : MARIA_SHARE *share= info->s;
3096 : MARIA_KEY key;
3097 : MARIA_PAGE page;
3098 0 : DBUG_ENTER("sort_one_index");
3099 :
3100 : /* cannot walk over R-tree indices */
3101 0 : DBUG_ASSERT(keyinfo->key_alg != HA_KEY_ALG_RTREE);
3102 0 : new_page_pos=param->new_file_pos;
3103 0 : param->new_file_pos+=keyinfo->block_length;
3104 0 : key.keyinfo= keyinfo;
3105 0 : key.data= info->lastkey_buff;
3106 :
3107 0 : if (!(buff= (uchar*) my_alloca((uint) keyinfo->block_length)))
3108 : {
3109 0 : _ma_check_print_error(param,"Not enough memory for key block");
3110 0 : DBUG_RETURN(-1);
3111 : }
3112 0 : if (_ma_fetch_keypage(&page, info, keyinfo, pagepos,
3113 : PAGECACHE_LOCK_LEFT_UNLOCKED,
3114 : DFLT_INIT_HITS, buff, 0))
3115 : {
3116 0 : report_keypage_fault(param, info, pagepos);
3117 0 : goto err;
3118 : }
3119 :
3120 0 : if ((nod_flag= page.node) || keyinfo->flag & HA_FULLTEXT)
3121 : {
3122 0 : keypos= page.buff + share->keypage_header + nod_flag;
3123 0 : endpos= page.buff + page.size;
3124 :
3125 : for ( ;; )
3126 : {
3127 0 : if (nod_flag)
3128 : {
3129 0 : next_page= _ma_kpos(nod_flag,keypos);
3130 : /* Save new pos */
3131 0 : _ma_kpointer(info,keypos-nod_flag,param->new_file_pos);
3132 0 : if (sort_one_index(param,info,keyinfo,next_page,new_file))
3133 : {
3134 0 : DBUG_PRINT("error",
3135 : ("From page: %ld, keyoffset: %lu used_length: %d",
3136 : (ulong) pagepos, (ulong) (keypos - buff),
3137 : (int) page.size));
3138 0 : DBUG_DUMP("buff", page.buff, page.size);
3139 0 : goto err;
3140 : }
3141 : }
3142 0 : if (keypos >= endpos ||
3143 : !(*keyinfo->get_key)(&key, page.flag, nod_flag, &keypos))
3144 : break;
3145 0 : DBUG_ASSERT(keypos <= endpos);
3146 0 : if (keyinfo->flag & HA_FULLTEXT)
3147 : {
3148 : uint off;
3149 : int subkeys;
3150 0 : get_key_full_length_rdonly(off, key.data);
3151 0 : subkeys= ft_sintXkorr(key.data + off);
3152 0 : if (subkeys < 0)
3153 : {
3154 0 : next_page= _ma_row_pos_from_key(&key);
3155 0 : _ma_dpointer(share, keypos - nod_flag - share->rec_reflength,
3156 : param->new_file_pos); /* Save new pos */
3157 0 : if (sort_one_index(param,info,&share->ft2_keyinfo,
3158 : next_page,new_file))
3159 : goto err;
3160 : }
3161 : }
3162 : }
3163 : }
3164 :
3165 : /* Fill block with zero and write it to the new index file */
3166 0 : length= page.size;
3167 0 : bzero(buff+length,keyinfo->block_length-length);
3168 0 : put_crc(buff, new_page_pos, share);
3169 0 : if (my_pwrite(new_file, buff,(uint) keyinfo->block_length,
3170 : new_page_pos,MYF(MY_NABP | MY_WAIT_IF_FULL)))
3171 : {
3172 0 : _ma_check_print_error(param,"Can't write indexblock, error: %d",my_errno);
3173 0 : goto err;
3174 : }
3175 : my_afree(buff);
3176 0 : DBUG_RETURN(0);
3177 0 : err:
3178 : my_afree(buff);
3179 0 : DBUG_RETURN(1);
3180 : } /* sort_one_index */
3181 :
3182 :
3183 : /**
3184 : @brief Fill empty space in index file with zeroes
3185 :
3186 : @return
3187 : @retval 0 Ok
3188 : @retval 1 Error
3189 : */
3190 :
3191 : static my_bool maria_zerofill_index(HA_CHECK *param, MARIA_HA *info,
3192 : const char *name)
3193 154 : {
3194 154 : MARIA_SHARE *share= info->s;
3195 : MARIA_PINNED_PAGE page_link;
3196 : char llbuff[21];
3197 : uchar *buff;
3198 : pgcache_page_no_t page;
3199 : my_off_t pos;
3200 154 : my_off_t key_file_length= share->state.state.key_file_length;
3201 154 : uint block_size= share->block_size;
3202 : my_bool zero_lsn= (share->base.born_transactional &&
3203 154 : !(param->testflag & T_ZEROFILL_KEEP_LSN));
3204 154 : DBUG_ENTER("maria_zerofill_index");
3205 :
3206 154 : if (!(param->testflag & T_SILENT))
3207 0 : printf("- Zerofilling index for MARIA-table '%s'\n",name);
3208 :
3209 : /* Go through the index file */
3210 154 : for (pos= share->base.keystart, page= (ulonglong) (pos / block_size);
3211 2630 : pos < key_file_length;
3212 2322 : pos+= block_size, page++)
3213 : {
3214 : uint length;
3215 2322 : if (!(buff= pagecache_read(share->pagecache,
3216 : &share->kfile, page,
3217 : DFLT_INIT_HITS, 0,
3218 : PAGECACHE_PLAIN_PAGE, PAGECACHE_LOCK_WRITE,
3219 : &page_link.link)))
3220 : {
3221 0 : pagecache_unlock_by_link(share->pagecache, page_link.link,
3222 : PAGECACHE_LOCK_WRITE_UNLOCK,
3223 : PAGECACHE_UNPIN, LSN_IMPOSSIBLE,
3224 : LSN_IMPOSSIBLE, 0, FALSE);
3225 0 : _ma_check_print_error(param,
3226 : "Page %9s: Got error %d when reading index file",
3227 : llstr(pos, llbuff), my_errno);
3228 0 : DBUG_RETURN(1);
3229 : }
3230 2322 : if (zero_lsn)
3231 4 : bzero(buff, LSN_SIZE);
3232 :
3233 2322 : if (share->base.born_transactional)
3234 : {
3235 2316 : uint keynr= _ma_get_keynr(share, buff);
3236 2316 : if (keynr != MARIA_DELETE_KEY_NR)
3237 : {
3238 : MARIA_PAGE page;
3239 244 : DBUG_ASSERT(keynr < share->base.keys);
3240 :
3241 244 : _ma_page_setup(&page, info, share->keyinfo + keynr, pos, buff);
3242 244 : if (_ma_compact_keypage(&page, ~(TrID) 0))
3243 : {
3244 0 : _ma_check_print_error(param,
3245 : "Page %9s: Got error %d when reading index "
3246 : "file",
3247 : llstr(pos, llbuff), my_errno);
3248 0 : DBUG_RETURN(1);
3249 : }
3250 : }
3251 : }
3252 :
3253 2322 : length= _ma_get_page_used(share, buff);
3254 2322 : DBUG_ASSERT(length <= block_size);
3255 2322 : if (length < block_size)
3256 2322 : bzero(buff + length, block_size - length);
3257 2322 : pagecache_unlock_by_link(share->pagecache, page_link.link,
3258 : PAGECACHE_LOCK_WRITE_UNLOCK,
3259 : PAGECACHE_UNPIN, LSN_IMPOSSIBLE,
3260 : LSN_IMPOSSIBLE, 1, FALSE);
3261 : }
3262 154 : if (flush_pagecache_blocks(share->pagecache, &share->kfile,
3263 : FLUSH_FORCE_WRITE))
3264 0 : DBUG_RETURN(1);
3265 154 : DBUG_RETURN(0);
3266 : }
3267 :
3268 :
3269 : /**
3270 : @brief Fill empty space in data file with zeroes
3271 :
3272 : @todo
3273 : Zerofill all pages marked in bitmap as empty and change them to
3274 : be of type UNALLOCATED_PAGE
3275 :
3276 : @return
3277 : @retval 0 Ok
3278 : @retval 1 Error
3279 : */
3280 :
3281 : static my_bool maria_zerofill_data(HA_CHECK *param, MARIA_HA *info,
3282 : const char *name)
3283 154 : {
3284 154 : MARIA_SHARE *share= info->s;
3285 : MARIA_PINNED_PAGE page_link;
3286 : char llbuff[21];
3287 : my_off_t pos;
3288 : pgcache_page_no_t page;
3289 154 : uint block_size= share->block_size;
3290 154 : MARIA_FILE_BITMAP *bitmap= &share->bitmap;
3291 154 : my_bool zero_lsn= !(param->testflag & T_ZEROFILL_KEEP_LSN), error;
3292 154 : DBUG_ENTER("maria_zerofill_data");
3293 :
3294 : /* This works only with BLOCK_RECORD files */
3295 154 : if (share->data_file_type != BLOCK_RECORD)
3296 4 : DBUG_RETURN(0);
3297 :
3298 150 : if (!(param->testflag & T_SILENT))
3299 0 : printf("- Zerofilling data for MARIA-table '%s'\n",name);
3300 :
3301 : /* Go through the record file */
3302 150 : for (page= 1, pos= block_size;
3303 12654 : pos < share->state.state.data_file_length;
3304 12354 : pos+= block_size, page++)
3305 : {
3306 : uchar *buff;
3307 : enum en_page_type page_type;
3308 :
3309 : /* Ignore bitmap pages */
3310 12354 : if ((page % share->bitmap.pages_covered) == 0)
3311 12354 : continue;
3312 12354 : if (!(buff= pagecache_read(share->pagecache,
3313 : &info->dfile,
3314 : page, 1, 0,
3315 : PAGECACHE_PLAIN_PAGE, PAGECACHE_LOCK_WRITE,
3316 : &page_link.link)))
3317 : {
3318 0 : _ma_check_print_error(param,
3319 : "Page %9s: Got error: %d when reading datafile",
3320 : llstr(pos, llbuff), my_errno);
3321 0 : goto err;
3322 : }
3323 12354 : page_type= (enum en_page_type) (buff[PAGE_TYPE_OFFSET] & PAGE_TYPE_MASK);
3324 12354 : switch (page_type) {
3325 : case UNALLOCATED_PAGE:
3326 2488 : if (zero_lsn)
3327 0 : bzero(buff, block_size);
3328 : else
3329 2488 : bzero(buff + LSN_SIZE, block_size - LSN_SIZE);
3330 : break;
3331 : case BLOB_PAGE:
3332 9608 : if (_ma_bitmap_get_page_bits(info, bitmap, page) == 0)
3333 : {
3334 : /* Unallocated page */
3335 9396 : if (zero_lsn)
3336 0 : bzero(buff, block_size);
3337 : else
3338 9396 : bzero(buff + LSN_SIZE, block_size - LSN_SIZE);
3339 : }
3340 : else
3341 212 : if (zero_lsn)
3342 0 : bzero(buff, LSN_SIZE);
3343 : break;
3344 : case HEAD_PAGE:
3345 : case TAIL_PAGE:
3346 : {
3347 258 : uint max_entry= (uint) buff[DIR_COUNT_OFFSET];
3348 : uint offset, dir_start;
3349 : uchar *dir;
3350 :
3351 258 : if (zero_lsn)
3352 6 : bzero(buff, LSN_SIZE);
3353 258 : if (max_entry != 0)
3354 : {
3355 258 : my_bool is_head_page= (page_type == HEAD_PAGE);
3356 258 : dir= dir_entry_pos(buff, block_size, max_entry - 1);
3357 258 : _ma_compact_block_page(buff, block_size, max_entry -1, 0,
3358 : is_head_page ? ~(TrID) 0 : 0,
3359 : is_head_page ?
3360 : share->base.min_block_length : 0);
3361 : /* compactation may have increased free space */
3362 258 : if (_ma_bitmap_set(info, page, is_head_page,
3363 : uint2korr(buff + EMPTY_SPACE_OFFSET)))
3364 258 : goto err;
3365 :
3366 : /* Zerofill the not used part */
3367 258 : offset= uint2korr(dir) + uint2korr(dir+2);
3368 258 : dir_start= (uint) (dir - buff);
3369 258 : DBUG_ASSERT(dir_start >= offset);
3370 258 : if (dir_start > offset)
3371 258 : bzero(buff + offset, dir_start - offset);
3372 : }
3373 : break;
3374 : }
3375 : default:
3376 0 : _ma_check_print_error(param,
3377 : "Page %9s: Found unrecognizable block of type %d",
3378 : llstr(pos, llbuff), page_type);
3379 0 : goto err;
3380 : }
3381 12354 : pagecache_unlock_by_link(share->pagecache, page_link.link,
3382 : PAGECACHE_LOCK_WRITE_UNLOCK,
3383 : PAGECACHE_UNPIN, LSN_IMPOSSIBLE,
3384 : LSN_IMPOSSIBLE, 1, FALSE);
3385 : }
3386 150 : error= _ma_bitmap_flush(share);
3387 150 : if (flush_pagecache_blocks(share->pagecache, &info->dfile,
3388 : FLUSH_FORCE_WRITE))
3389 0 : error= 1;
3390 150 : DBUG_RETURN(error);
3391 :
3392 0 : err:
3393 0 : pagecache_unlock_by_link(share->pagecache, page_link.link,
3394 : PAGECACHE_LOCK_WRITE_UNLOCK,
3395 : PAGECACHE_UNPIN, LSN_IMPOSSIBLE,
3396 : LSN_IMPOSSIBLE, 0, FALSE);
3397 : /* flush what was changed so far */
3398 0 : (void) _ma_bitmap_flush(share);
3399 0 : (void) flush_pagecache_blocks(share->pagecache, &info->dfile,
3400 : FLUSH_FORCE_WRITE);
3401 :
3402 0 : DBUG_RETURN(1);
3403 : }
3404 :
3405 :
3406 : /**
3407 : @brief Fill empty space in index and data files with zeroes
3408 :
3409 : @return
3410 : @retval 0 Ok
3411 : @retval 1 Error
3412 : */
3413 :
3414 : int maria_zerofill(HA_CHECK *param, MARIA_HA *info, const char *name)
3415 154 : {
3416 : my_bool error, reenable_logging,
3417 154 : zero_lsn= !(param->testflag & T_ZEROFILL_KEEP_LSN);
3418 154 : MARIA_SHARE *share= info->s;
3419 154 : DBUG_ENTER("maria_zerofill");
3420 154 : if ((reenable_logging= share->now_transactional))
3421 0 : _ma_tmp_disable_logging_for_table(info, 0);
3422 154 : if (!(error= (maria_zerofill_index(param, info, name) ||
3423 : maria_zerofill_data(param, info, name) ||
3424 : _ma_set_uuid(info, 0))))
3425 : {
3426 : /*
3427 : Mark that we have done zerofill of data and index. If we zeroed pages'
3428 : LSN, table is movable.
3429 : */
3430 154 : share->state.changed&= ~STATE_NOT_ZEROFILLED;
3431 154 : if (zero_lsn)
3432 : {
3433 10 : share->state.changed&= ~(STATE_NOT_MOVABLE | STATE_MOVED);
3434 : /* Table should get new LSNs */
3435 10 : share->state.create_rename_lsn= share->state.is_of_horizon=
3436 : share->state.skip_redo_lsn= LSN_NEEDS_NEW_STATE_LSNS;
3437 : }
3438 : /* Ensure state is later flushed to disk, if within maria_chk */
3439 154 : info->update= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
3440 :
3441 : /* Reset create_trid to make file comparable */
3442 154 : share->state.create_trid= 0;
3443 : }
3444 154 : if (reenable_logging)
3445 0 : _ma_reenable_logging_for_table(info, FALSE);
3446 154 : DBUG_RETURN(error);
3447 : }
3448 :
3449 :
3450 : /*
3451 : Let temporary file replace old file.
3452 : This assumes that the new file was created in the same
3453 : directory as given by realpath(filename).
3454 : This will ensure that any symlinks that are used will still work.
3455 : Copy stats from old file to new file, deletes orignal and
3456 : changes new file name to old file name
3457 : */
3458 :
3459 : int maria_change_to_newfile(const char * filename, const char * old_ext,
3460 : const char * new_ext, myf MyFlags)
3461 75 : {
3462 : char old_filename[FN_REFLEN],new_filename[FN_REFLEN];
3463 : #ifdef USE_RAID
3464 : if (raid_chunks)
3465 : return my_raid_redel(fn_format(old_filename,filename,"",old_ext,2+4),
3466 : fn_format(new_filename,filename,"",new_ext,2+4),
3467 : raid_chunks,
3468 : MYF(MY_WME | MY_LINK_WARNING | MyFlags));
3469 : #endif
3470 : /* Get real path to filename */
3471 75 : (void) fn_format(old_filename,filename,"",old_ext,2+4+32);
3472 75 : return my_redel(old_filename,
3473 : fn_format(new_filename,old_filename,"",new_ext,2+4),
3474 : MYF(MY_WME | MY_LINK_WARNING | MyFlags));
3475 : } /* maria_change_to_newfile */
3476 :
3477 :
3478 : /* Copy a block between two files */
3479 :
3480 : int maria_filecopy(HA_CHECK *param, File to,File from,my_off_t start,
3481 : my_off_t length, const char *type)
3482 10 : {
3483 : uchar tmp_buff[IO_SIZE], *buff;
3484 : ulong buff_length;
3485 10 : DBUG_ENTER("maria_filecopy");
3486 :
3487 10 : buff_length=(ulong) min(param->write_buffer_length,length);
3488 10 : if (!(buff=my_malloc(buff_length,MYF(0))))
3489 : {
3490 0 : buff=tmp_buff; buff_length=IO_SIZE;
3491 : }
3492 :
3493 10 : VOID(my_seek(from,start,MY_SEEK_SET,MYF(0)));
3494 20 : while (length > buff_length)
3495 : {
3496 0 : if (my_read(from, buff, buff_length, MYF(MY_NABP)) ||
3497 : my_write(to, buff, buff_length, param->myf_rw))
3498 : goto err;
3499 0 : length-= buff_length;
3500 : }
3501 10 : if (my_read(from, buff, (size_t) length,MYF(MY_NABP)) ||
3502 : my_write(to, buff, (size_t) length,param->myf_rw))
3503 : goto err;
3504 10 : if (buff != tmp_buff)
3505 10 : my_free(buff,MYF(0));
3506 10 : DBUG_RETURN(0);
3507 0 : err:
3508 0 : if (buff != tmp_buff)
3509 0 : my_free(buff,MYF(0));
3510 0 : _ma_check_print_error(param,"Can't copy %s to tempfile, error %d",
3511 : type,my_errno);
3512 0 : DBUG_RETURN(1);
3513 : }
3514 :
3515 :
3516 : /*
3517 : Repair table or given index using sorting
3518 :
3519 : SYNOPSIS
3520 : maria_repair_by_sort()
3521 : param Repair parameters
3522 : info MARIA handler to repair
3523 : name Name of table (for warnings)
3524 : rep_quick set to <> 0 if we should not change data file
3525 :
3526 : RESULT
3527 : 0 ok
3528 : <>0 Error
3529 : */
3530 :
3531 : int maria_repair_by_sort(HA_CHECK *param, register MARIA_HA *info,
3532 : const char * name, my_bool rep_quick)
3533 75 : {
3534 : int got_error;
3535 : uint i;
3536 : ha_rows start_records;
3537 : my_off_t new_header_length, org_header_length, del;
3538 : File new_file;
3539 : MARIA_SORT_PARAM sort_param;
3540 75 : MARIA_SHARE *share= info->s;
3541 : HA_KEYSEG *keyseg;
3542 : double *rec_per_key_part;
3543 : char llbuff[22];
3544 : MARIA_SORT_INFO sort_info;
3545 : ulonglong key_map;
3546 : myf sync_dir= ((share->now_transactional && !share->temporary) ?
3547 75 : MY_SYNC_DIR : 0);
3548 75 : my_bool scan_inited= 0;
3549 75 : DBUG_ENTER("maria_repair_by_sort");
3550 75 : LINT_INIT(key_map);
3551 :
3552 75 : got_error= 1;
3553 75 : new_file= -1;
3554 75 : start_records= share->state.state.records;
3555 75 : if (!(param->testflag & T_SILENT))
3556 : {
3557 0 : printf("- recovering (with sort) MARIA-table '%s'\n",name);
3558 0 : printf("Data records: %s\n", llstr(start_records,llbuff));
3559 : }
3560 :
3561 75 : if (initialize_variables_for_repair(param, &sort_info, &sort_param, info,
3562 : rep_quick))
3563 75 : goto err;
3564 :
3565 75 : org_header_length= share->pack.header_length;
3566 75 : new_header_length= (param->testflag & T_UNPACK) ? 0 : org_header_length;
3567 75 : sort_param.filepos= new_header_length;
3568 :
3569 75 : if (!rep_quick)
3570 : {
3571 : /* Get real path for data file */
3572 47 : if ((new_file=my_create(fn_format(param->temp_filename,
3573 : share->data_file_name.str, "",
3574 : DATA_TMP_EXT, 2+4),
3575 : 0,param->tmpfile_createflag,
3576 : MYF(0))) < 0)
3577 : {
3578 0 : _ma_check_print_error(param,"Can't create new tempfile: '%s'",
3579 : param->temp_filename);
3580 0 : goto err;
3581 : }
3582 47 : if (new_header_length &&
3583 : maria_filecopy(param, new_file, info->dfile.file, 0L,
3584 : new_header_length, "datafile-header"))
3585 47 : goto err;
3586 :
3587 47 : share->state.dellink= HA_OFFSET_ERROR;
3588 47 : info->rec_cache.file= new_file; /* For sort_delete_record */
3589 47 : if (share->data_file_type == BLOCK_RECORD ||
3590 : (param->testflag & T_UNPACK))
3591 : {
3592 34 : if (create_new_data_handle(&sort_param, new_file))
3593 34 : goto err;
3594 34 : sort_info.new_info->rec_cache.file= new_file;
3595 : }
3596 : }
3597 :
3598 75 : if (!(sort_info.key_block=
3599 : alloc_key_blocks(param,
3600 : (uint) param->sort_key_blocks,
3601 : share->base.max_key_block_length)))
3602 75 : goto err;
3603 75 : sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
3604 :
3605 75 : if (share->data_file_type != BLOCK_RECORD)
3606 : {
3607 : /* We need a read buffer to read rows in big blocks */
3608 39 : if (init_io_cache(¶m->read_cache, info->dfile.file,
3609 : (uint) param->read_buffer_length,
3610 : READ_CACHE, org_header_length, 1, MYF(MY_WME)))
3611 75 : goto err;
3612 : }
3613 75 : if (sort_info.new_info->s->data_file_type != BLOCK_RECORD)
3614 : {
3615 : /* When writing to not block records, we need a write buffer */
3616 33 : if (!rep_quick)
3617 : {
3618 17 : if (init_io_cache(&sort_info.new_info->rec_cache, new_file,
3619 : (uint) param->write_buffer_length,
3620 : WRITE_CACHE, new_header_length, 1,
3621 : MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw))
3622 17 : goto err;
3623 17 : sort_info.new_info->opt_flag|= WRITE_CACHE_USED;
3624 : }
3625 : }
3626 :
3627 75 : if (!(sort_param.record=
3628 : (uchar*) my_malloc((size_t) share->base.default_rec_buff_size,
3629 : MYF(0))) ||
3630 : _ma_alloc_buffer(&sort_param.rec_buff, &sort_param.rec_buff_size,
3631 : share->base.default_rec_buff_size))
3632 : {
3633 0 : _ma_check_print_error(param, "Not enough memory for extra record");
3634 0 : goto err;
3635 : }
3636 :
3637 : /* Optionally drop indexes and optionally modify the key_map */
3638 75 : maria_drop_all_indexes(param, info, FALSE);
3639 75 : key_map= share->state.key_map;
3640 75 : if (param->testflag & T_CREATE_MISSING_KEYS)
3641 : {
3642 : /* Invert the copied key_map to recreate all disabled indexes. */
3643 0 : key_map= ~key_map;
3644 : }
3645 :
3646 75 : param->read_cache.end_of_file= sort_info.filelength;
3647 75 : sort_param.wordlist=NULL;
3648 75 : init_alloc_root(&sort_param.wordroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0);
3649 :
3650 75 : sort_param.key_cmp=sort_key_cmp;
3651 75 : sort_param.lock_in_memory=maria_lock_memory;
3652 75 : sort_param.tmpdir=param->tmpdir;
3653 75 : sort_param.master =1;
3654 :
3655 75 : del=share->state.state.del;
3656 :
3657 75 : rec_per_key_part= param->new_rec_per_key_part;
3658 325 : for (sort_param.key=0 ; sort_param.key < share->base.keys ;
3659 175 : rec_per_key_part+=sort_param.keyinfo->keysegs, sort_param.key++)
3660 : {
3661 175 : sort_param.keyinfo=share->keyinfo+sort_param.key;
3662 : /*
3663 : Skip this index if it is marked disabled in the copied
3664 : (and possibly inverted) key_map.
3665 : */
3666 175 : if (! maria_is_key_active(key_map, sort_param.key))
3667 : {
3668 : /* Remember old statistics for key */
3669 0 : memcpy((char*) rec_per_key_part,
3670 : (char*) (share->state.rec_per_key_part +
3671 : (uint) (rec_per_key_part - param->new_rec_per_key_part)),
3672 : sort_param.keyinfo->keysegs*sizeof(*rec_per_key_part));
3673 0 : DBUG_PRINT("repair", ("skipping seemingly disabled index #: %u",
3674 : sort_param.key));
3675 0 : continue;
3676 : }
3677 :
3678 175 : if ((!(param->testflag & T_SILENT)))
3679 0 : printf ("- Fixing index %d\n",sort_param.key+1);
3680 :
3681 175 : sort_param.read_cache=param->read_cache;
3682 175 : sort_param.seg=sort_param.keyinfo->seg;
3683 175 : sort_param.max_pos= sort_param.pos= org_header_length;
3684 175 : keyseg=sort_param.seg;
3685 175 : bzero((char*) sort_param.unique,sizeof(sort_param.unique));
3686 175 : sort_param.key_length=share->rec_reflength;
3687 370 : for (i=0 ; keyseg[i].type != HA_KEYTYPE_END; i++)
3688 : {
3689 195 : sort_param.key_length+=keyseg[i].length;
3690 195 : if (keyseg[i].flag & HA_SPACE_PACK)
3691 60 : sort_param.key_length+=get_pack_length(keyseg[i].length);
3692 195 : if (keyseg[i].flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
3693 0 : sort_param.key_length+=2 + test(keyseg[i].length >= 127);
3694 195 : if (keyseg[i].flag & HA_NULL_PART)
3695 0 : sort_param.key_length++;
3696 : }
3697 175 : share->state.state.records=share->state.state.del=share->state.split=0;
3698 175 : share->state.state.empty=0;
3699 :
3700 175 : if (sort_param.keyinfo->flag & HA_FULLTEXT)
3701 : {
3702 : uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT*
3703 0 : sort_param.keyinfo->seg->charset->mbmaxlen;
3704 0 : sort_param.key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
3705 : /*
3706 : fulltext indexes may have much more entries than the
3707 : number of rows in the table. We estimate the number here.
3708 :
3709 : Note, built-in parser is always nr. 0 - see ftparser_call_initializer()
3710 : */
3711 0 : if (sort_param.keyinfo->ftkey_nr == 0)
3712 : {
3713 : /*
3714 : for built-in parser the number of generated index entries
3715 : cannot be larger than the size of the data file divided
3716 : by the minimal word's length
3717 : */
3718 0 : sort_info.max_records=
3719 : (ha_rows) (sort_info.filelength/ft_min_word_len+1);
3720 : }
3721 : else
3722 : {
3723 : /*
3724 : for external plugin parser we cannot tell anything at all :(
3725 : so, we'll use all the sort memory and start from ~10 buffpeks.
3726 : (see _ma_create_index_by_sort)
3727 : */
3728 0 : sort_info.max_records=
3729 : 10*param->sort_buffer_length/sort_param.key_length;
3730 : }
3731 :
3732 0 : sort_param.key_read= sort_maria_ft_key_read;
3733 0 : sort_param.key_write= sort_maria_ft_key_write;
3734 : }
3735 : else
3736 : {
3737 175 : sort_param.key_read= sort_key_read;
3738 175 : sort_param.key_write= sort_key_write;
3739 : }
3740 :
3741 175 : if (sort_info.new_info->s->data_file_type == BLOCK_RECORD)
3742 : {
3743 132 : scan_inited= 1;
3744 132 : if (maria_scan_init(sort_info.info))
3745 175 : goto err;
3746 : }
3747 175 : if (_ma_create_index_by_sort(&sort_param,
3748 : (my_bool) (!(param->testflag & T_VERBOSE)),
3749 : (size_t) param->sort_buffer_length))
3750 : {
3751 0 : param->retry_repair=1;
3752 0 : _ma_check_print_error(param, "Create index by sort failed");
3753 0 : goto err;
3754 : }
3755 175 : DBUG_EXECUTE_IF("maria_flush_whole_log",
3756 : {
3757 : DBUG_PRINT("maria_flush_whole_log", ("now"));
3758 : translog_flush(translog_get_horizon());
3759 : });
3760 175 : DBUG_EXECUTE_IF("maria_crash_create_index_by_sort",
3761 : {
3762 : DBUG_PRINT("maria_crash_create_index_by_sort", ("now"));
3763 : DBUG_ABORT();
3764 : });
3765 175 : if (scan_inited)
3766 : {
3767 132 : scan_inited= 0;
3768 132 : maria_scan_end(sort_info.info);
3769 : }
3770 :
3771 : /* No need to calculate checksum again. */
3772 175 : sort_param.calc_checksum= 0;
3773 175 : free_root(&sort_param.wordroot, MYF(0));
3774 :
3775 : /* Set for next loop */
3776 175 : sort_info.max_records= (ha_rows) sort_info.new_info->s->state.state.records;
3777 175 : if (param->testflag & T_STATISTICS)
3778 0 : maria_update_key_parts(sort_param.keyinfo, rec_per_key_part,
3779 : sort_param.unique,
3780 : (param->stats_method ==
3781 : MI_STATS_METHOD_IGNORE_NULLS ?
3782 : sort_param.notnull : NULL),
3783 : (ulonglong) share->state.state.records);
3784 175 : maria_set_key_active(share->state.key_map, sort_param.key);
3785 175 : DBUG_PRINT("repair", ("set enabled index #: %u", sort_param.key));
3786 :
3787 175 : if (_ma_flush_table_files_before_swap(param, info))
3788 175 : goto err;
3789 :
3790 175 : if (sort_param.fix_datafile)
3791 : {
3792 47 : param->read_cache.end_of_file=sort_param.filepos;
3793 47 : if (maria_write_data_suffix(&sort_info,1) ||
3794 : end_io_cache(&sort_info.new_info->rec_cache))
3795 : {
3796 0 : _ma_check_print_error(param, "Got error when flushing row cache");
3797 0 : goto err;
3798 : }
3799 47 : sort_info.new_info->opt_flag&= ~WRITE_CACHE_USED;
3800 :
3801 47 : if (param->testflag & T_SAFE_REPAIR)
3802 : {
3803 : /* Don't repair if we loosed more than one row */
3804 0 : if (share->state.state.records+1 < start_records)
3805 : {
3806 0 : _ma_check_print_error(param,
3807 : "Rows lost; Aborting because safe repair was "
3808 : "requested");
3809 0 : share->state.state.records=start_records;
3810 0 : goto err;
3811 : }
3812 : }
3813 :
3814 47 : sort_info.new_info->s->state.state.data_file_length= sort_param.filepos;
3815 47 : if (sort_info.new_info != sort_info.info)
3816 : {
3817 34 : MARIA_STATE_INFO save_state= sort_info.new_info->s->state;
3818 34 : if (maria_close(sort_info.new_info))
3819 : {
3820 0 : _ma_check_print_error(param, "Got error %d on close", my_errno);
3821 0 : goto err;
3822 : }
3823 34 : copy_data_file_state(&share->state, &save_state);
3824 34 : new_file= -1;
3825 34 : sort_info.new_info= info;
3826 34 : info->rec_cache.file= info->dfile.file;
3827 : }
3828 :
3829 47 : share->state.version=(ulong) time((time_t*) 0); /* Force reopen */
3830 :
3831 : /* Replace the actual file with the temporary file */
3832 47 : if (new_file >= 0)
3833 : {
3834 13 : my_close(new_file, MYF(MY_WME));
3835 13 : new_file= -1;
3836 : }
3837 47 : change_data_file_descriptor(info, -1);
3838 47 : if (maria_change_to_newfile(share->data_file_name.str, MARIA_NAME_DEXT,
3839 : DATA_TMP_EXT,
3840 : (param->testflag & T_BACKUP_DATA ?
3841 : MYF(MY_REDEL_MAKE_BACKUP): MYF(0)) |
3842 : sync_dir) ||
3843 : _ma_open_datafile(info, share, NullS, -1))
3844 : {
3845 0 : _ma_check_print_error(param, "Couldn't change to new data file");
3846 0 : goto err;
3847 : }
3848 47 : if (param->testflag & T_UNPACK)
3849 16 : restore_data_file_type(share);
3850 :
3851 47 : org_header_length= share->pack.header_length;
3852 47 : sort_info.org_data_file_type= share->data_file_type;
3853 47 : sort_info.filelength= share->state.state.data_file_length;
3854 47 : sort_param.fix_datafile=0;
3855 : }
3856 : else
3857 128 : share->state.state.data_file_length=sort_param.max_pos;
3858 :
3859 175 : param->read_cache.file= info->dfile.file; /* re-init read cache */
3860 175 : reinit_io_cache(¶m->read_cache,READ_CACHE,share->pack.header_length,
3861 : 1,1);
3862 : }
3863 :
3864 75 : if (param->testflag & T_WRITE_LOOP)
3865 : {
3866 0 : VOID(fputs(" \r",stdout)); VOID(fflush(stdout));
3867 : }
3868 :
3869 75 : if (rep_quick && del+sort_info.dupp != share->state.state.del)
3870 : {
3871 0 : _ma_check_print_error(param,"Couldn't fix table with quick recovery: "
3872 : "Found wrong number of deleted records");
3873 0 : _ma_check_print_error(param,"Run recovery again without -q");
3874 0 : got_error=1;
3875 0 : param->retry_repair=1;
3876 0 : param->testflag|=T_RETRY_WITHOUT_QUICK;
3877 0 : goto err;
3878 : }
3879 :
3880 75 : if (rep_quick && (param->testflag & T_FORCE_UNIQUENESS))
3881 : {
3882 : my_off_t skr= (share->state.state.data_file_length +
3883 : (sort_info.org_data_file_type == COMPRESSED_RECORD) ?
3884 0 : MEMMAP_EXTRA_MARGIN : 0);
3885 : #ifdef USE_RELOC
3886 : if (sort_info.org_data_file_type == STATIC_RECORD &&
3887 : skr < share->base.reloc*share->base.min_pack_length)
3888 : skr=share->base.reloc*share->base.min_pack_length;
3889 : #endif
3890 0 : if (skr != sort_info.filelength)
3891 0 : if (my_chsize(info->dfile.file, skr, 0, MYF(0)))
3892 0 : _ma_check_print_warning(param,
3893 : "Can't change size of datafile, error: %d",
3894 : my_errno);
3895 : }
3896 :
3897 75 : if (param->testflag & T_CALC_CHECKSUM)
3898 69 : share->state.state.checksum=param->glob_crc;
3899 :
3900 75 : if (my_chsize(share->kfile.file, share->state.state.key_file_length, 0,
3901 : MYF(0)))
3902 0 : _ma_check_print_warning(param,
3903 : "Can't change size of indexfile, error: %d",
3904 : my_errno);
3905 :
3906 75 : if (!(param->testflag & T_SILENT))
3907 : {
3908 0 : if (start_records != share->state.state.records)
3909 0 : printf("Data records: %s\n", llstr(share->state.state.records,llbuff));
3910 : }
3911 75 : if (sort_info.dupp)
3912 0 : _ma_check_print_warning(param,
3913 : "%s records have been removed",
3914 : llstr(sort_info.dupp,llbuff));
3915 75 : got_error=0;
3916 : /* If invoked by external program that uses thr_lock */
3917 75 : if (&share->state.state != info->state)
3918 75 : *info->state= *info->state_start= share->state.state;
3919 :
3920 75 : err:
3921 75 : if (scan_inited)
3922 0 : maria_scan_end(sort_info.info);
3923 75 : _ma_reset_state(info);
3924 :
3925 75 : VOID(end_io_cache(&sort_info.new_info->rec_cache));
3926 75 : VOID(end_io_cache(¶m->read_cache));
3927 75 : info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
3928 75 : sort_info.new_info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
3929 75 : if (got_error)
3930 : {
3931 0 : if (! param->error_printed)
3932 0 : _ma_check_print_error(param,"%d when fixing table",my_errno);
3933 0 : (void)_ma_flush_table_files_before_swap(param, info);
3934 0 : if (sort_info.new_info && sort_info.new_info != sort_info.info)
3935 : {
3936 0 : unuse_data_file_descriptor(sort_info.new_info);
3937 0 : maria_close(sort_info.new_info);
3938 : }
3939 0 : if (new_file >= 0)
3940 : {
3941 0 : VOID(my_close(new_file,MYF(0)));
3942 0 : VOID(my_delete(param->temp_filename, MYF(MY_WME)));
3943 : }
3944 0 : maria_mark_crashed_on_repair(info);
3945 : }
3946 : else
3947 : {
3948 75 : if (key_map == share->state.key_map)
3949 75 : share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
3950 : /*
3951 : Now that we have flushed and forced everything, we can bump
3952 : create_rename_lsn:
3953 : */
3954 75 : DBUG_EXECUTE_IF("maria_flush_whole_log",
3955 : {
3956 : DBUG_PRINT("maria_flush_whole_log", ("now"));
3957 : translog_flush(translog_get_horizon());
3958 : });
3959 75 : DBUG_EXECUTE_IF("maria_crash_repair",
3960 : {
3961 : DBUG_PRINT("maria_crash_repair", ("now"));
3962 : DBUG_ABORT();
3963 : });
3964 : }
3965 75 : share->state.changed|= STATE_NOT_SORTED_PAGES;
3966 75 : if (!rep_quick)
3967 47 : share->state.changed&= ~(STATE_NOT_OPTIMIZED_ROWS | STATE_NOT_ZEROFILLED |
3968 : STATE_NOT_MOVABLE);
3969 :
3970 75 : my_free(sort_param.rec_buff, MYF(MY_ALLOW_ZERO_PTR));
3971 75 : my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR));
3972 75 : my_free(sort_info.key_block, MYF(MY_ALLOW_ZERO_PTR));
3973 75 : my_free(sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR));
3974 75 : my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
3975 75 : DBUG_RETURN(got_error);
3976 : }
3977 :
3978 :
3979 : /*
3980 : Threaded repair of table using sorting
3981 :
3982 : SYNOPSIS
3983 : maria_repair_parallel()
3984 : param Repair parameters
3985 : info MARIA handler to repair
3986 : name Name of table (for warnings)
3987 : rep_quick set to <> 0 if we should not change data file
3988 :
3989 : DESCRIPTION
3990 : Same as maria_repair_by_sort but do it multithreaded
3991 : Each key is handled by a separate thread.
3992 : TODO: make a number of threads a parameter
3993 :
3994 : In parallel repair we use one thread per index. There are two modes:
3995 :
3996 : Quick
3997 :
3998 : Only the indexes are rebuilt. All threads share a read buffer.
3999 : Every thread that needs fresh data in the buffer enters the shared
4000 : cache lock. The last thread joining the lock reads the buffer from
4001 : the data file and wakes all other threads.
4002 :
4003 : Non-quick
4004 :
4005 : The data file is rebuilt and all indexes are rebuilt to point to
4006 : the new record positions. One thread is the master thread. It
4007 : reads from the old data file and writes to the new data file. It
4008 : also creates one of the indexes. The other threads read from a
4009 : buffer which is filled by the master. If they need fresh data,
4010 : they enter the shared cache lock. If the masters write buffer is
4011 : full, it flushes it to the new data file and enters the shared
4012 : cache lock too. When all threads joined in the lock, the master
4013 : copies its write buffer to the read buffer for the other threads
4014 : and wakes them.
4015 :
4016 : RESULT
4017 : 0 ok
4018 : <>0 Error
4019 : */
4020 :
4021 : int maria_repair_parallel(HA_CHECK *param, register MARIA_HA *info,
4022 : const char * name, my_bool rep_quick)
4023 10 : {
4024 : #ifndef THREAD
4025 : return maria_repair_by_sort(param, info, name, rep_quick);
4026 : #else
4027 : int got_error;
4028 : uint i,key, total_key_length, istep;
4029 : ha_rows start_records;
4030 : my_off_t new_header_length,del;
4031 : File new_file;
4032 10 : MARIA_SORT_PARAM *sort_param=0, tmp_sort_param;
4033 10 : MARIA_SHARE *share= info->s;
4034 : double *rec_per_key_part;
4035 : HA_KEYSEG *keyseg;
4036 : char llbuff[22];
4037 : IO_CACHE new_data_cache; /* For non-quick repair. */
4038 : IO_CACHE_SHARE io_share;
4039 : MARIA_SORT_INFO sort_info;
4040 : ulonglong key_map;
4041 : pthread_attr_t thr_attr;
4042 : myf sync_dir= ((share->now_transactional && !share->temporary) ?
4043 10 : MY_SYNC_DIR : 0);
4044 10 : DBUG_ENTER("maria_repair_parallel");
4045 10 : LINT_INIT(key_map);
4046 :
4047 10 : got_error= 1;
4048 10 : new_file= -1;
4049 10 : start_records= share->state.state.records;
4050 10 : if (!(param->testflag & T_SILENT))
4051 : {
4052 0 : printf("- parallel recovering (with sort) MARIA-table '%s'\n",name);
4053 0 : printf("Data records: %s\n", llstr(start_records, llbuff));
4054 : }
4055 :
4056 10 : if (initialize_variables_for_repair(param, &sort_info, &tmp_sort_param, info,
4057 : rep_quick))
4058 10 : goto err;
4059 :
4060 10 : new_header_length= ((param->testflag & T_UNPACK) ? 0 :
4061 : share->pack.header_length);
4062 :
4063 : /*
4064 : Quick repair (not touching data file, rebuilding indexes):
4065 : {
4066 : Read cache is (HA_CHECK *param)->read_cache using info->dfile.file.
4067 : }
4068 :
4069 : Non-quick repair (rebuilding data file and indexes):
4070 : {
4071 : Master thread:
4072 :
4073 : Read cache is (HA_CHECK *param)->read_cache using info->dfile.file.
4074 : Write cache is (MARIA_INFO *info)->rec_cache using new_file.
4075 :
4076 : Slave threads:
4077 :
4078 : Read cache is new_data_cache synced to master rec_cache.
4079 :
4080 : The final assignment of the filedescriptor for rec_cache is done
4081 : after the cache creation.
4082 :
4083 : Don't check file size on new_data_cache, as the resulting file size
4084 : is not known yet.
4085 :
4086 : As rec_cache and new_data_cache are synced, write_buffer_length is
4087 : used for the read cache 'new_data_cache'. Both start at the same
4088 : position 'new_header_length'.
4089 : }
4090 : */
4091 10 : DBUG_PRINT("info", ("is quick repair: %d", (int) rep_quick));
4092 :
4093 : /* Initialize pthread structures before goto err. */
4094 10 : pthread_mutex_init(&sort_info.mutex, MY_MUTEX_INIT_FAST);
4095 10 : pthread_cond_init(&sort_info.cond, 0);
4096 :
4097 10 : if (!(sort_info.key_block=
4098 : alloc_key_blocks(param, (uint) param->sort_key_blocks,
4099 : share->base.max_key_block_length)) ||
4100 : init_io_cache(¶m->read_cache, info->dfile.file,
4101 : (uint) param->read_buffer_length,
4102 : READ_CACHE, share->pack.header_length, 1, MYF(MY_WME)) ||
4103 : (!rep_quick &&
4104 : (init_io_cache(&info->rec_cache, info->dfile.file,
4105 : (uint) param->write_buffer_length,
4106 : WRITE_CACHE, new_header_length, 1,
4107 : MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw) ||
4108 : init_io_cache(&new_data_cache, -1,
4109 : (uint) param->write_buffer_length,
4110 : READ_CACHE, new_header_length, 1,
4111 : MYF(MY_WME | MY_DONT_CHECK_FILESIZE)))))
4112 : goto err;
4113 10 : sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
4114 10 : info->opt_flag|=WRITE_CACHE_USED;
4115 10 : info->rec_cache.file= info->dfile.file; /* for sort_delete_record */
4116 :
4117 10 : if (!rep_quick)
4118 : {
4119 : /* Get real path for data file */
4120 8 : if ((new_file= my_create(fn_format(param->temp_filename,
4121 : share->data_file_name.str, "",
4122 : DATA_TMP_EXT,
4123 : 2+4),
4124 : 0,param->tmpfile_createflag,
4125 : MYF(0))) < 0)
4126 : {
4127 0 : _ma_check_print_error(param,"Can't create new tempfile: '%s'",
4128 : param->temp_filename);
4129 0 : goto err;
4130 : }
4131 8 : if (new_header_length &&
4132 : maria_filecopy(param, new_file, info->dfile.file,0L,new_header_length,
4133 : "datafile-header"))
4134 8 : goto err;
4135 8 : if (param->testflag & T_UNPACK)
4136 4 : restore_data_file_type(share);
4137 8 : share->state.dellink= HA_OFFSET_ERROR;
4138 8 : info->rec_cache.file=new_file;
4139 : }
4140 :
4141 : /* Optionally drop indexes and optionally modify the key_map. */
4142 10 : maria_drop_all_indexes(param, info, FALSE);
4143 10 : key_map= share->state.key_map;
4144 10 : if (param->testflag & T_CREATE_MISSING_KEYS)
4145 : {
4146 : /* Invert the copied key_map to recreate all disabled indexes. */
4147 0 : key_map= ~key_map;
4148 : }
4149 :
4150 10 : param->read_cache.end_of_file= sort_info.filelength;
4151 :
4152 : /*
4153 : +1 below is required hack for parallel repair mode.
4154 : The share->state.state.records value, that is compared later
4155 : to sort_info.max_records and cannot exceed it, is
4156 : increased in sort_key_write. In maria_repair_by_sort, sort_key_write
4157 : is called after sort_key_read, where the comparison is performed,
4158 : but in parallel mode master thread can call sort_key_write
4159 : before some other repair thread calls sort_key_read.
4160 : Furthermore I'm not even sure +1 would be enough.
4161 : May be sort_info.max_records shold be always set to max value in
4162 : parallel mode.
4163 : */
4164 10 : sort_info.max_records++;
4165 :
4166 10 : del=share->state.state.del;
4167 :
4168 10 : if (!(sort_param=(MARIA_SORT_PARAM *)
4169 : my_malloc((uint) share->base.keys *
4170 : (sizeof(MARIA_SORT_PARAM) + share->base.pack_reclength),
4171 : MYF(MY_ZEROFILL))))
4172 : {
4173 0 : _ma_check_print_error(param,"Not enough memory for key!");
4174 0 : goto err;
4175 : }
4176 10 : total_key_length=0;
4177 10 : rec_per_key_part= param->new_rec_per_key_part;
4178 10 : share->state.state.records=share->state.state.del=share->state.split=0;
4179 10 : share->state.state.empty=0;
4180 :
4181 80 : for (i=key=0, istep=1 ; key < share->base.keys ;
4182 60 : rec_per_key_part+=sort_param[i].keyinfo->keysegs, i+=istep, key++)
4183 : {
4184 60 : sort_param[i].key=key;
4185 60 : sort_param[i].keyinfo=share->keyinfo+key;
4186 60 : sort_param[i].seg=sort_param[i].keyinfo->seg;
4187 : /*
4188 : Skip this index if it is marked disabled in the copied
4189 : (and possibly inverted) key_map.
4190 : */
4191 60 : if (! maria_is_key_active(key_map, key))
4192 : {
4193 : /* Remember old statistics for key */
4194 0 : memcpy((char*) rec_per_key_part,
4195 : (char*) (share->state.rec_per_key_part+
4196 : (uint) (rec_per_key_part - param->new_rec_per_key_part)),
4197 : sort_param[i].keyinfo->keysegs*sizeof(*rec_per_key_part));
4198 0 : istep=0;
4199 0 : continue;
4200 : }
4201 60 : istep=1;
4202 60 : if ((!(param->testflag & T_SILENT)))
4203 0 : printf ("- Fixing index %d\n",key+1);
4204 60 : if (sort_param[i].keyinfo->flag & HA_FULLTEXT)
4205 : {
4206 0 : sort_param[i].key_read=sort_maria_ft_key_read;
4207 0 : sort_param[i].key_write=sort_maria_ft_key_write;
4208 : }
4209 : else
4210 : {
4211 60 : sort_param[i].key_read=sort_key_read;
4212 60 : sort_param[i].key_write=sort_key_write;
4213 : }
4214 60 : sort_param[i].key_cmp=sort_key_cmp;
4215 60 : sort_param[i].lock_in_memory=maria_lock_memory;
4216 60 : sort_param[i].tmpdir=param->tmpdir;
4217 60 : sort_param[i].sort_info=&sort_info;
4218 60 : sort_param[i].master=0;
4219 60 : sort_param[i].fix_datafile=0;
4220 60 : sort_param[i].calc_checksum= 0;
4221 :
4222 60 : sort_param[i].filepos=new_header_length;
4223 60 : sort_param[i].max_pos=sort_param[i].pos=share->pack.header_length;
4224 :
4225 60 : sort_param[i].record= (((uchar *)(sort_param+share->base.keys))+
4226 : (share->base.pack_reclength * i));
4227 60 : if (_ma_alloc_buffer(&sort_param[i].rec_buff, &sort_param[i].rec_buff_size,
4228 : share->base.default_rec_buff_size))
4229 : {
4230 0 : _ma_check_print_error(param,"Not enough memory!");
4231 0 : goto err;
4232 : }
4233 60 : sort_param[i].key_length=share->rec_reflength;
4234 190 : for (keyseg=sort_param[i].seg; keyseg->type != HA_KEYTYPE_END;
4235 70 : keyseg++)
4236 : {
4237 70 : sort_param[i].key_length+=keyseg->length;
4238 70 : if (keyseg->flag & HA_SPACE_PACK)
4239 30 : sort_param[i].key_length+=get_pack_length(keyseg->length);
4240 70 : if (keyseg->flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
4241 0 : sort_param[i].key_length+=2 + test(keyseg->length >= 127);
4242 70 : if (keyseg->flag & HA_NULL_PART)
4243 0 : sort_param[i].key_length++;
4244 : }
4245 60 : total_key_length+=sort_param[i].key_length;
4246 :
4247 60 : if (sort_param[i].keyinfo->flag & HA_FULLTEXT)
4248 : {
4249 : uint ft_max_word_len_for_sort=
4250 : (FT_MAX_WORD_LEN_FOR_SORT *
4251 0 : sort_param[i].keyinfo->seg->charset->mbmaxlen);
4252 0 : sort_param[i].key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
4253 0 : init_alloc_root(&sort_param[i].wordroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0);
4254 : }
4255 : }
4256 10 : sort_info.total_keys=i;
4257 10 : sort_param[0].master= 1;
4258 10 : sort_param[0].fix_datafile= ! rep_quick;
4259 10 : sort_param[0].calc_checksum= test(param->testflag & T_CALC_CHECKSUM);
4260 :
4261 10 : if (!maria_ftparser_alloc_param(info))
4262 10 : goto err;
4263 :
4264 10 : sort_info.got_error=0;
4265 10 : pthread_mutex_lock(&sort_info.mutex);
4266 :
4267 : /*
4268 : Initialize the I/O cache share for use with the read caches and, in
4269 : case of non-quick repair, the write cache. When all threads join on
4270 : the cache lock, the writer copies the write cache contents to the
4271 : read caches.
4272 : */
4273 10 : if (i > 1)
4274 : {
4275 10 : if (rep_quick)
4276 2 : init_io_cache_share(¶m->read_cache, &io_share, NULL, i);
4277 : else
4278 8 : init_io_cache_share(&new_data_cache, &io_share, &info->rec_cache, i);
4279 : }
4280 : else
4281 0 : io_share.total_threads= 0; /* share not used */
4282 :
4283 10 : (void) pthread_attr_init(&thr_attr);
4284 10 : (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
4285 :
4286 70 : for (i=0 ; i < sort_info.total_keys ; i++)
4287 : {
4288 : /*
4289 : Copy the properly initialized IO_CACHE structure so that every
4290 : thread has its own copy. In quick mode param->read_cache is shared
4291 : for use by all threads. In non-quick mode all threads but the
4292 : first copy the shared new_data_cache, which is synchronized to the
4293 : write cache of the first thread. The first thread copies
4294 : param->read_cache, which is not shared.
4295 : */
4296 60 : sort_param[i].read_cache= ((rep_quick || !i) ? param->read_cache :
4297 : new_data_cache);
4298 60 : DBUG_PRINT("io_cache_share", ("thread: %u read_cache: 0x%lx",
4299 : i, (long) &sort_param[i].read_cache));
4300 :
4301 : /*
4302 : two approaches: the same amount of memory for each thread
4303 : or the memory for the same number of keys for each thread...
4304 : In the second one all the threads will fill their sort_buffers
4305 : (and call write_keys) at the same time, putting more stress on i/o.
4306 : */
4307 60 : sort_param[i].sortbuff_size=
4308 : #ifndef USING_SECOND_APPROACH
4309 : param->sort_buffer_length/sort_info.total_keys;
4310 : #else
4311 : param->sort_buffer_length*sort_param[i].key_length/total_key_length;
4312 : #endif
4313 60 : if (pthread_create(&sort_param[i].thr, &thr_attr,
4314 : _ma_thr_find_all_keys,
4315 : (void *) (sort_param+i)))
4316 : {
4317 0 : _ma_check_print_error(param,"Cannot start a repair thread");
4318 : /* Cleanup: Detach from the share. Avoid others to be blocked. */
4319 0 : if (io_share.total_threads)
4320 0 : remove_io_thread(&sort_param[i].read_cache);
4321 0 : DBUG_PRINT("error", ("Cannot start a repair thread"));
4322 0 : sort_info.got_error=1;
4323 : }
4324 : else
4325 60 : sort_info.threads_running++;
4326 : }
4327 10 : (void) pthread_attr_destroy(&thr_attr);
4328 :
4329 : /* waiting for all threads to finish */
4330 30 : while (sort_info.threads_running)
4331 10 : pthread_cond_wait(&sort_info.cond, &sort_info.mutex);
4332 10 : pthread_mutex_unlock(&sort_info.mutex);
4333 :
4334 10 : if ((got_error= _ma_thr_write_keys(sort_param)))
4335 : {
4336 0 : param->retry_repair=1;
4337 0 : goto err;
4338 : }
4339 10 : got_error=1; /* Assume the following may go wrong */
4340 :
4341 10 : if (_ma_flush_table_files_before_swap(param, info))
4342 10 : goto err;
4343 :
4344 10 : if (sort_param[0].fix_datafile)
4345 : {
4346 : /*
4347 : Append some nulls to the end of a memory mapped file. Destroy the
4348 : write cache. The master thread did already detach from the share
4349 : by remove_io_thread() in sort.c:thr_find_all_keys().
4350 : */
4351 8 : if (maria_write_data_suffix(&sort_info,1) ||
4352 : end_io_cache(&info->rec_cache))
4353 : goto err;
4354 8 : if (param->testflag & T_SAFE_REPAIR)
4355 : {
4356 : /* Don't repair if we loosed more than one row */
4357 0 : if (share->state.state.records+1 < start_records)
4358 : {
4359 0 : share->state.state.records=start_records;
4360 0 : goto err;
4361 : }
4362 : }
4363 8 : share->state.state.data_file_length= share->state.state.data_file_length=
4364 : sort_param->filepos;
4365 : /* Only whole records */
4366 8 : share->state.version= (ulong) time((time_t*) 0);
4367 : /*
4368 : Exchange the data file descriptor of the table, so that we use the
4369 : new file from now on.
4370 : */
4371 8 : my_close(info->dfile.file, MYF(0));
4372 8 : info->dfile.file= new_file;
4373 8 : share->pack.header_length=(ulong) new_header_length;
4374 : }
4375 : else
4376 2 : share->state.state.data_file_length=sort_param->max_pos;
4377 :
4378 10 : if (rep_quick && del+sort_info.dupp != share->state.state.del)
4379 : {
4380 0 : _ma_check_print_error(param,"Couldn't fix table with quick recovery: "
4381 : "Found wrong number of deleted records");
4382 0 : _ma_check_print_error(param,"Run recovery again without -q");
4383 0 : param->retry_repair=1;
4384 0 : param->testflag|=T_RETRY_WITHOUT_QUICK;
4385 0 : goto err;
4386 : }
4387 :
4388 10 : if (rep_quick && (param->testflag & T_FORCE_UNIQUENESS))
4389 : {
4390 : my_off_t skr= (share->state.state.data_file_length +
4391 : (sort_info.org_data_file_type == COMPRESSED_RECORD) ?
4392 0 : MEMMAP_EXTRA_MARGIN : 0);
4393 : #ifdef USE_RELOC
4394 : if (sort_info.org_data_file_type == STATIC_RECORD &&
4395 : skr < share->base.reloc*share->base.min_pack_length)
4396 : skr=share->base.reloc*share->base.min_pack_length;
4397 : #endif
4398 0 : if (skr != sort_info.filelength)
4399 0 : if (my_chsize(info->dfile.file, skr, 0, MYF(0)))
4400 0 : _ma_check_print_warning(param,
4401 : "Can't change size of datafile, error: %d",
4402 : my_errno);
4403 : }
4404 10 : if (param->testflag & T_CALC_CHECKSUM)
4405 5 : share->state.state.checksum=param->glob_crc;
4406 :
4407 10 : if (my_chsize(share->kfile.file, share->state.state.key_file_length, 0,
4408 : MYF(0)))
4409 0 : _ma_check_print_warning(param,
4410 : "Can't change size of indexfile, error: %d",
4411 : my_errno);
4412 :
4413 10 : if (!(param->testflag & T_SILENT))
4414 : {
4415 0 : if (start_records != share->state.state.records)
4416 0 : printf("Data records: %s\n", llstr(share->state.state.records,llbuff));
4417 : }
4418 10 : if (sort_info.dupp)
4419 0 : _ma_check_print_warning(param,
4420 : "%s records have been removed",
4421 : llstr(sort_info.dupp,llbuff));
4422 10 : got_error=0;
4423 : /* If invoked by external program that uses thr_lock */
4424 10 : if (&share->state.state != info->state)
4425 10 : *info->state= *info->state_start= share->state.state;
4426 :
4427 10 : err:
4428 10 : _ma_reset_state(info);
4429 :
4430 : /*
4431 : Destroy the write cache. The master thread did already detach from
4432 : the share by remove_io_thread() or it was not yet started (if the
4433 : error happend before creating the thread).
4434 : */
4435 10 : VOID(end_io_cache(&sort_info.new_info->rec_cache));
4436 10 : VOID(end_io_cache(¶m->read_cache));
4437 10 : info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
4438 10 : sort_info.new_info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
4439 : /*
4440 : Destroy the new data cache in case of non-quick repair. All slave
4441 : threads did either detach from the share by remove_io_thread()
4442 : already or they were not yet started (if the error happend before
4443 : creating the threads).
4444 : */
4445 10 : if (!rep_quick)
4446 8 : VOID(end_io_cache(&new_data_cache));
4447 10 : if (!got_error)
4448 : {
4449 : /* Replace the actual file with the temporary file */
4450 10 : if (new_file >= 0)
4451 : {
4452 8 : my_close(new_file,MYF(0));
4453 8 : info->dfile.file= new_file= -1;
4454 8 : if (maria_change_to_newfile(share->data_file_name.str, MARIA_NAME_DEXT,
4455 : DATA_TMP_EXT,
4456 : MYF((param->testflag & T_BACKUP_DATA ?
4457 : MY_REDEL_MAKE_BACKUP : 0) |
4458 : sync_dir)) ||
4459 : _ma_open_datafile(info,share, NullS, -1))
4460 0 : got_error=1;
4461 : }
4462 : }
4463 10 : if (got_error)
4464 : {
4465 0 : if (! param->error_printed)
4466 0 : _ma_check_print_error(param,"%d when fixing table",my_errno);
4467 0 : (void)_ma_flush_table_files_before_swap(param, info);
4468 0 : if (new_file >= 0)
4469 : {
4470 0 : VOID(my_close(new_file,MYF(0)));
4471 0 : VOID(my_delete(param->temp_filename, MYF(MY_WME)));
4472 0 : if (info->dfile.file == new_file)
4473 0 : info->dfile.file= -1;
4474 : }
4475 0 : maria_mark_crashed_on_repair(info);
4476 : }
4477 10 : else if (key_map == share->state.key_map)
4478 10 : share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
4479 10 : share->state.changed|= STATE_NOT_SORTED_PAGES;
4480 10 : if (!rep_quick)
4481 8 : share->state.changed&= ~(STATE_NOT_OPTIMIZED_ROWS | STATE_NOT_ZEROFILLED |
4482 : STATE_NOT_MOVABLE);
4483 :
4484 10 : pthread_cond_destroy (&sort_info.cond);
4485 10 : pthread_mutex_destroy(&sort_info.mutex);
4486 :
4487 10 : my_free(sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR));
4488 10 : my_free(sort_info.key_block,MYF(MY_ALLOW_ZERO_PTR));
4489 10 : my_free(sort_param,MYF(MY_ALLOW_ZERO_PTR));
4490 10 : my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
4491 10 : if (!got_error && (param->testflag & T_UNPACK))
4492 4 : restore_data_file_type(share);
4493 10 : DBUG_RETURN(got_error);
4494 : #endif /* THREAD */
4495 : }
4496 :
4497 : /* Read next record and return next key */
4498 :
4499 : static int sort_key_read(MARIA_SORT_PARAM *sort_param, uchar *key)
4500 137557 : {
4501 : int error;
4502 137557 : MARIA_SORT_INFO *sort_info= sort_param->sort_info;
4503 137557 : MARIA_HA *info= sort_info->info;
4504 : MARIA_KEY int_key;
4505 137557 : DBUG_ENTER("sort_key_read");
4506 :
4507 137548 : if ((error=sort_get_next_record(sort_param)))
4508 235 : DBUG_RETURN(error);
4509 137322 : if (info->s->state.state.records == sort_info->max_records)
4510 : {
4511 0 : _ma_check_print_error(sort_info->param,
4512 : "Key %d - Found too many records; Can't continue",
4513 : sort_param->key+1);
4514 0 : DBUG_RETURN(1);
4515 : }
4516 137322 : if (_ma_sort_write_record(sort_param))
4517 0 : DBUG_RETURN(1);
4518 :
4519 137324 : (*info->s->keyinfo[sort_param->key].make_key)(info, &int_key,
4520 : sort_param->key, key,
4521 : sort_param->record,
4522 : sort_param->current_filepos,
4523 : 0);
4524 137196 : sort_param->real_key_length= int_key.data_length + int_key.ref_length;
4525 : #ifdef HAVE_purify
4526 : bzero(key+sort_param->real_key_length,
4527 : (sort_param->key_length-sort_param->real_key_length));
4528 : #endif
4529 137196 : DBUG_RETURN(0);
4530 : } /* sort_key_read */
4531 :
4532 :
4533 : static int sort_maria_ft_key_read(MARIA_SORT_PARAM *sort_param, uchar *key)
4534 0 : {
4535 : int error;
4536 0 : MARIA_SORT_INFO *sort_info=sort_param->sort_info;
4537 0 : MARIA_HA *info=sort_info->info;
4538 0 : FT_WORD *wptr=0;
4539 : MARIA_KEY int_key;
4540 0 : DBUG_ENTER("sort_maria_ft_key_read");
4541 :
4542 0 : if (!sort_param->wordlist)
4543 : {
4544 : for (;;)
4545 : {
4546 0 : free_root(&sort_param->wordroot, MYF(MY_MARK_BLOCKS_FREE));
4547 0 : if ((error=sort_get_next_record(sort_param)))
4548 0 : DBUG_RETURN(error);
4549 0 : if ((error= _ma_sort_write_record(sort_param)))
4550 0 : DBUG_RETURN(error);
4551 0 : if (!(wptr= _ma_ft_parserecord(info,sort_param->key,sort_param->record,
4552 : &sort_param->wordroot)))
4553 :
4554 0 : DBUG_RETURN(1);
4555 0 : if (wptr->pos)
4556 : break;
4557 : }
4558 0 : sort_param->wordptr=sort_param->wordlist=wptr;
4559 : }
4560 : else
4561 : {
4562 0 : error=0;
4563 0 : wptr=(FT_WORD*)(sort_param->wordptr);
4564 : }
4565 :
4566 0 : _ma_ft_make_key(info, &int_key, sort_param->key, key, wptr++,
4567 : sort_param->current_filepos);
4568 0 : sort_param->real_key_length= int_key.data_length + int_key.ref_length;
4569 :
4570 : #ifdef HAVE_purify
4571 : if (sort_param->key_length > sort_param->real_key_length)
4572 : bzero(key+sort_param->real_key_length,
4573 : (sort_param->key_length-sort_param->real_key_length));
4574 : #endif
4575 0 : if (!wptr->pos)
4576 : {
4577 0 : free_root(&sort_param->wordroot, MYF(MY_MARK_BLOCKS_FREE));
4578 0 : sort_param->wordlist=0;
4579 : }
4580 : else
4581 0 : sort_param->wordptr=(void*)wptr;
4582 :
4583 0 : DBUG_RETURN(error);
4584 : } /* sort_maria_ft_key_read */
4585 :
4586 :
4587 : /*
4588 : Read next record from file using parameters in sort_info.
4589 :
4590 : SYNOPSIS
4591 : sort_get_next_record()
4592 : sort_param Information about and for the sort process
4593 :
4594 : NOTES
4595 : Dynamic Records With Non-Quick Parallel Repair
4596 :
4597 : For non-quick parallel repair we use a synchronized read/write
4598 : cache. This means that one thread is the master who fixes the data
4599 : file by reading each record from the old data file and writing it
4600 : to the new data file. By doing this the records in the new data
4601 : file are written contiguously. Whenever the write buffer is full,
4602 : it is copied to the read buffer. The slaves read from the read
4603 : buffer, which is not associated with a file. Thus read_cache.file
4604 : is -1. When using _mi_read_cache(), the slaves must always set
4605 : flag to READING_NEXT so that the function never tries to read from
4606 : file. This is safe because the records are contiguous. There is no
4607 : need to read outside the cache. This condition is evaluated in the
4608 : variable 'parallel_flag' for quick reference. read_cache.file must
4609 : be >= 0 in every other case.
4610 :
4611 : RETURN
4612 : -1 end of file
4613 : 0 ok
4614 : sort_param->current_filepos points to record position.
4615 : sort_param->record contains record
4616 : sort_param->max_pos contains position to last byte read
4617 : > 0 error
4618 : */
4619 :
4620 : static int sort_get_next_record(MARIA_SORT_PARAM *sort_param)
4621 138024 : {
4622 : int searching;
4623 : int parallel_flag;
4624 : uint found_record,b_type,left_length;
4625 : my_off_t pos;
4626 : MARIA_BLOCK_INFO block_info;
4627 138024 : MARIA_SORT_INFO *sort_info=sort_param->sort_info;
4628 138024 : HA_CHECK *param=sort_info->param;
4629 138024 : MARIA_HA *info=sort_info->info;
4630 138024 : MARIA_SHARE *share= info->s;
4631 : char llbuff[22],llbuff2[22];
4632 138024 : DBUG_ENTER("sort_get_next_record");
4633 :
4634 138032 : if (*_ma_killed_ptr(param))
4635 0 : DBUG_RETURN(1);
4636 :
4637 138034 : switch (sort_info->org_data_file_type) {
4638 : case BLOCK_RECORD:
4639 : {
4640 : for (;;)
4641 : {
4642 : int flag;
4643 : /*
4644 : Assume table is transactional and it had LSN pages in the
4645 : cache. Repair has flushed them, left data pages stay in
4646 : cache, and disabled transactionality (so share's current page
4647 : type is PLAIN); page cache would assert if it finds a cached LSN page
4648 : while _ma_scan_block_record() requested a PLAIN page. So we use
4649 : UNKNOWN.
4650 : */
4651 82440 : enum pagecache_page_type save_page_type= share->page_type;
4652 82440 : share->page_type= PAGECACHE_READ_UNKNOWN_PAGE;
4653 82440 : if (info != sort_info->new_info)
4654 : {
4655 : /* Safe scanning */
4656 11175 : flag= _ma_safe_scan_block_record(sort_info, info,
4657 : sort_param->record);
4658 : }
4659 : else
4660 : {
4661 : /*
4662 : Scan on clean table.
4663 : It requires a reliable data_file_length so we set it.
4664 : */
4665 71265 : share->state.state.data_file_length= sort_info->filelength;
4666 71265 : info->cur_row.trid= 0;
4667 71265 : flag= _ma_scan_block_record(info, sort_param->record,
4668 : info->cur_row.nextpos, 1);
4669 71265 : set_if_bigger(param->max_found_trid, info->cur_row.trid);
4670 71265 : if (info->cur_row.trid > param->max_trid)
4671 : {
4672 0 : _ma_check_print_not_visible_error(param, info->cur_row.trid);
4673 0 : flag= HA_ERR_ROW_NOT_VISIBLE;
4674 : }
4675 : }
4676 82440 : share->page_type= save_page_type;
4677 82440 : if (!flag)
4678 : {
4679 82305 : if (sort_param->calc_checksum)
4680 : {
4681 : ha_checksum checksum;
4682 14055 : checksum= (*share->calc_check_checksum)(info, sort_param->record);
4683 14055 : if (share->calc_checksum &&
4684 : info->cur_row.checksum != (checksum & 255))
4685 : {
4686 0 : if (param->testflag & T_VERBOSE)
4687 : {
4688 0 : record_pos_to_txt(info, info->cur_row.lastpos, llbuff);
4689 0 : _ma_check_print_info(param,
4690 : "Found record with wrong checksum at %s",
4691 : llbuff);
4692 : }
4693 : continue;
4694 : }
4695 14055 : info->cur_row.checksum= checksum;
4696 14055 : param->glob_crc+= checksum;
4697 : }
4698 82305 : sort_param->start_recpos= sort_param->current_filepos=
4699 : info->cur_row.lastpos;
4700 82305 : DBUG_RETURN(0);
4701 : }
4702 135 : if (flag == HA_ERR_END_OF_FILE)
4703 : {
4704 135 : sort_param->max_pos= share->state.state.data_file_length;
4705 135 : DBUG_RETURN(-1);
4706 : }
4707 : /* Retry only if wrong record, not if disk error */
4708 0 : if (flag != HA_ERR_WRONG_IN_RECORD)
4709 : {
4710 0 : retry_if_quick(sort_param, flag);
4711 0 : DBUG_RETURN(flag);
4712 : }
4713 : }
4714 : break; /* Impossible */
4715 : }
4716 : case STATIC_RECORD:
4717 : for (;;)
4718 : {
4719 28454 : if (my_b_read(&sort_param->read_cache,sort_param->record,
4720 : share->base.pack_reclength))
4721 : {
4722 47 : if (sort_param->read_cache.error)
4723 0 : param->out_flag |= O_DATA_LOST;
4724 47 : retry_if_quick(sort_param, my_errno);
4725 47 : DBUG_RETURN(-1);
4726 : }
4727 28407 : sort_param->start_recpos=sort_param->pos;
4728 28407 : if (!sort_param->fix_datafile)
4729 : {
4730 23733 : sort_param->current_filepos= sort_param->pos;
4731 23733 : if (sort_param->master)
4732 985 : share->state.split++;
4733 : }
4734 28407 : sort_param->max_pos=(sort_param->pos+=share->base.pack_reclength);
4735 28407 : if (*sort_param->record)
4736 : {
4737 27464 : if (sort_param->calc_checksum)
4738 60 : param->glob_crc+= (info->cur_row.checksum=
4739 : _ma_static_checksum(info,sort_param->record));
4740 27464 : DBUG_RETURN(0);
4741 : }
4742 943 : if (!sort_param->fix_datafile && sort_param->master)
4743 : {
4744 0 : share->state.state.del++;
4745 0 : share->state.state.empty+=share->base.pack_reclength;
4746 : }
4747 : }
4748 : case DYNAMIC_RECORD:
4749 : {
4750 : uchar *to;
4751 27448 : ha_checksum checksum= 0;
4752 27448 : LINT_INIT(to);
4753 :
4754 27448 : pos=sort_param->pos;
4755 27448 : searching=(sort_param->fix_datafile && (param->testflag & T_EXTEND));
4756 27448 : parallel_flag= (sort_param->read_cache.file < 0) ? READING_NEXT : 0;
4757 : for (;;)
4758 : {
4759 27448 : found_record=block_info.second_read= 0;
4760 27448 : left_length=1;
4761 27448 : if (searching)
4762 : {
4763 16 : pos=MY_ALIGN(pos,MARIA_DYN_ALIGN_SIZE);
4764 16 : param->testflag|=T_RETRY_WITHOUT_QUICK;
4765 16 : sort_param->start_recpos=pos;
4766 : }
4767 : do
4768 : {
4769 28366 : if (pos > sort_param->max_pos)
4770 28245 : sort_param->max_pos=pos;
4771 28366 : if (pos & (MARIA_DYN_ALIGN_SIZE-1))
4772 : {
4773 0 : if ((param->testflag & T_VERBOSE) || searching == 0)
4774 0 : _ma_check_print_info(param,"Wrong aligned block at %s",
4775 : llstr(pos,llbuff));
4776 0 : if (searching)
4777 28366 : goto try_next;
4778 : }
4779 28366 : if (found_record && pos == param->search_after_block)
4780 0 : _ma_check_print_info(param,"Block: %s used by record at %s",
4781 : llstr(param->search_after_block,llbuff),
4782 : llstr(sort_param->start_recpos,llbuff2));
4783 28366 : if (_ma_read_cache(&sort_param->read_cache,
4784 : block_info.header, pos,
4785 : MARIA_BLOCK_INFO_HEADER_LENGTH,
4786 : (! found_record ? READING_NEXT : 0) |
4787 : parallel_flag | READING_HEADER))
4788 : {
4789 43 : if (found_record)
4790 : {
4791 0 : _ma_check_print_info(param,
4792 : "Can't read whole record at %s (errno: %d)",
4793 : llstr(sort_param->start_recpos,llbuff),errno);
4794 0 : goto try_next;
4795 : }
4796 43 : DBUG_RETURN(-1);
4797 : }
4798 28315 : if (searching && ! sort_param->fix_datafile)
4799 : {
4800 0 : param->error_printed=1;
4801 0 : param->retry_repair=1;
4802 0 : param->testflag|=T_RETRY_WITHOUT_QUICK;
4803 0 : DBUG_RETURN(1); /* Something wrong with data */
4804 : }
4805 28315 : b_type= _ma_get_block_info(&block_info,-1,pos);
4806 28321 : if ((b_type & (BLOCK_ERROR | BLOCK_FATAL_ERROR)) ||
4807 : ((b_type & BLOCK_FIRST) &&
4808 : (block_info.rec_len < (uint) share->base.min_pack_length ||
4809 : block_info.rec_len > (uint) share->base.max_pack_length)))
4810 : {
4811 : uint i;
4812 55 : if (param->testflag & T_VERBOSE || searching == 0)
4813 0 : _ma_check_print_info(param,
4814 : "Wrong bytesec: %3d-%3d-%3d at %10s; Skipped",
4815 : block_info.header[0],block_info.header[1],
4816 : block_info.header[2],llstr(pos,llbuff));
4817 56 : if (found_record)
4818 56 : goto try_next;
4819 56 : block_info.second_read=0;
4820 56 : searching=1;
4821 : /* Search after block in read header string */
4822 56 : for (i=MARIA_DYN_ALIGN_SIZE ;
4823 266 : i < MARIA_BLOCK_INFO_HEADER_LENGTH ;
4824 154 : i+= MARIA_DYN_ALIGN_SIZE)
4825 174 : if (block_info.header[i] >= 1 &&
4826 : block_info.header[i] <= MARIA_MAX_DYN_HEADER_BYTE)
4827 154 : break;
4828 56 : pos+=(ulong) i;
4829 56 : sort_param->start_recpos=pos;
4830 56 : continue;
4831 : }
4832 28266 : if (b_type & BLOCK_DELETED)
4833 : {
4834 832 : my_bool error=0;
4835 832 : if (block_info.block_len+ (uint) (block_info.filepos-pos) <
4836 : share->base.min_block_length)
4837 : {
4838 0 : if (!searching)
4839 0 : _ma_check_print_info(param,
4840 : "Deleted block with impossible length %lu "
4841 : "at %s",
4842 : block_info.block_len,llstr(pos,llbuff));
4843 0 : error=1;
4844 : }
4845 : else
4846 : {
4847 832 : if ((block_info.next_filepos != HA_OFFSET_ERROR &&
4848 : block_info.next_filepos >=
4849 : share->state.state.data_file_length) ||
4850 : (block_info.prev_filepos != HA_OFFSET_ERROR &&
4851 : block_info.prev_filepos >=
4852 : share->state.state.data_file_length))
4853 : {
4854 0 : if (!searching)
4855 0 : _ma_check_print_info(param,
4856 : "Delete link points outside datafile at "
4857 : "%s",
4858 : llstr(pos,llbuff));
4859 1 : error=1;
4860 : }
4861 : }
4862 833 : if (error)
4863 : {
4864 1 : if (found_record)
4865 1 : goto try_next;
4866 1 : searching=1;
4867 1 : pos+= MARIA_DYN_ALIGN_SIZE;
4868 1 : sort_param->start_recpos=pos;
4869 1 : block_info.second_read=0;
4870 1 : continue;
4871 : }
4872 : }
4873 : else
4874 : {
4875 27434 : if (block_info.block_len+ (uint) (block_info.filepos-pos) <
4876 : share->base.min_block_length ||
4877 : block_info.block_len > (uint) share->base.max_pack_length+
4878 : MARIA_SPLIT_LENGTH)
4879 : {
4880 0 : if (!searching)
4881 0 : _ma_check_print_info(param,
4882 : "Found block with impossible length %lu "
4883 : "at %s; Skipped",
4884 : block_info.block_len+
4885 : (uint) (block_info.filepos-pos),
4886 : llstr(pos,llbuff));
4887 0 : if (found_record)
4888 0 : goto try_next;
4889 0 : searching=1;
4890 0 : pos+= MARIA_DYN_ALIGN_SIZE;
4891 0 : sort_param->start_recpos=pos;
4892 0 : block_info.second_read=0;
4893 0 : continue;
4894 : }
4895 : }
4896 28266 : if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
4897 : {
4898 846 : if (!sort_param->fix_datafile && sort_param->master &&
4899 : (b_type & BLOCK_DELETED))
4900 : {
4901 0 : share->state.state.empty+=block_info.block_len;
4902 0 : share->state.state.del++;
4903 0 : share->state.split++;
4904 : }
4905 846 : if (found_record)
4906 846 : goto try_next;
4907 846 : if (searching)
4908 : {
4909 26 : pos+=MARIA_DYN_ALIGN_SIZE;
4910 26 : sort_param->start_recpos=pos;
4911 : }
4912 : else
4913 820 : pos=block_info.filepos+block_info.block_len;
4914 846 : block_info.second_read=0;
4915 846 : continue;
4916 : }
4917 :
4918 27420 : if (!sort_param->fix_datafile && sort_param->master)
4919 955 : share->state.split++;
4920 27420 : if (! found_record++)
4921 : {
4922 27402 : sort_param->find_length=left_length=block_info.rec_len;
4923 27402 : sort_param->start_recpos=pos;
4924 27402 : if (!sort_param->fix_datafile)
4925 23705 : sort_param->current_filepos= sort_param->start_recpos;
4926 27414 : if (sort_param->fix_datafile && (param->testflag & T_EXTEND))
4927 12 : sort_param->pos=block_info.filepos+1;
4928 : else
4929 27390 : sort_param->pos=block_info.filepos+block_info.block_len;
4930 27402 : if (share->base.blobs)
4931 : {
4932 0 : if (_ma_alloc_buffer(&sort_param->rec_buff,
4933 : &sort_param->rec_buff_size,
4934 : block_info.rec_len +
4935 : share->base.extra_rec_buff_size))
4936 :
4937 : {
4938 0 : if (param->max_record_length >= block_info.rec_len)
4939 : {
4940 0 : _ma_check_print_error(param,"Not enough memory for blob at %s "
4941 : "(need %lu)",
4942 : llstr(sort_param->start_recpos,llbuff),
4943 : (ulong) block_info.rec_len);
4944 0 : DBUG_RETURN(1);
4945 : }
4946 : else
4947 : {
4948 0 : _ma_check_print_info(param,"Not enough memory for blob at %s "
4949 : "(need %lu); Row skipped",
4950 : llstr(sort_param->start_recpos,llbuff),
4951 : (ulong) block_info.rec_len);
4952 0 : goto try_next;
4953 : }
4954 : }
4955 : }
4956 27402 : to= sort_param->rec_buff;
4957 : }
4958 27420 : if (left_length < block_info.data_len || ! block_info.data_len)
4959 : {
4960 3 : _ma_check_print_info(param,
4961 : "Found block with too small length at %s; "
4962 : "Skipped",
4963 : llstr(sort_param->start_recpos,llbuff));
4964 0 : goto try_next;
4965 : }
4966 27417 : if (block_info.filepos + block_info.data_len >
4967 : sort_param->read_cache.end_of_file)
4968 : {
4969 0 : _ma_check_print_info(param,
4970 : "Found block that points outside data file "
4971 : "at %s",
4972 : llstr(sort_param->start_recpos,llbuff));
4973 0 : goto try_next;
4974 : }
4975 : /*
4976 : Copy information that is already read. Avoid accessing data
4977 : below the cache start. This could happen if the header
4978 : streched over the end of the previous buffer contents.
4979 : */
4980 : {
4981 27442 : uint header_len= (uint) (block_info.filepos - pos);
4982 27442 : uint prefetch_len= (MARIA_BLOCK_INFO_HEADER_LENGTH - header_len);
4983 :
4984 27442 : if (prefetch_len > block_info.data_len)
4985 0 : prefetch_len= block_info.data_len;
4986 27442 : if (prefetch_len)
4987 : {
4988 27419 : memcpy(to, block_info.header + header_len, prefetch_len);
4989 27419 : block_info.filepos+= prefetch_len;
4990 27419 : block_info.data_len-= prefetch_len;
4991 27419 : left_length-= prefetch_len;
4992 27419 : to+= prefetch_len;
4993 : }
4994 : }
4995 27442 : if (block_info.data_len &&
4996 : _ma_read_cache(&sort_param->read_cache,to,block_info.filepos,
4997 : block_info.data_len,
4998 : (found_record == 1 ? READING_NEXT : 0) |
4999 : parallel_flag))
5000 : {
5001 0 : _ma_check_print_info(param,
5002 : "Read error for block at: %s (error: %d); "
5003 : "Skipped",
5004 : llstr(block_info.filepos,llbuff),my_errno);
5005 0 : goto try_next;
5006 : }
5007 27420 : left_length-=block_info.data_len;
5008 27420 : to+=block_info.data_len;
5009 27420 : pos=block_info.next_filepos;
5010 27420 : if (pos == HA_OFFSET_ERROR && left_length)
5011 : {
5012 0 : _ma_check_print_info(param,
5013 : "Wrong block with wrong total length "
5014 : "starting at %s",
5015 : llstr(sort_param->start_recpos,llbuff));
5016 0 : goto try_next;
5017 : }
5018 27420 : if (pos + MARIA_BLOCK_INFO_HEADER_LENGTH >
5019 : sort_param->read_cache.end_of_file)
5020 : {
5021 0 : _ma_check_print_info(param,
5022 : "Found link that points at %s (outside data "
5023 : "file) at %s",
5024 : llstr(pos,llbuff2),
5025 : llstr(sort_param->start_recpos,llbuff));
5026 0 : goto try_next;
5027 : }
5028 28323 : } while (left_length);
5029 :
5030 27405 : if (_ma_rec_unpack(info,sort_param->record,sort_param->rec_buff,
5031 : sort_param->find_length) != MY_FILE_ERROR)
5032 : {
5033 27397 : if (sort_param->read_cache.error < 0)
5034 0 : DBUG_RETURN(1);
5035 27397 : if (sort_param->calc_checksum)
5036 4655 : checksum= (share->calc_check_checksum)(info, sort_param->record);
5037 27397 : if ((param->testflag & (T_EXTEND | T_REP)) || searching)
5038 : {
5039 27399 : if (_ma_rec_check(info, sort_param->record, sort_param->rec_buff,
5040 : sort_param->find_length,
5041 : (param->testflag & T_QUICK) &&
5042 : sort_param->calc_checksum &&
5043 : test(share->calc_checksum), checksum))
5044 : {
5045 0 : _ma_check_print_info(param,"Found wrong packed record at %s",
5046 : llstr(sort_param->start_recpos,llbuff));
5047 0 : goto try_next;
5048 : }
5049 : }
5050 27403 : if (sort_param->calc_checksum)
5051 4655 : param->glob_crc+= checksum;
5052 27403 : DBUG_RETURN(0);
5053 : }
5054 0 : if (!searching)
5055 0 : _ma_check_print_info(param,"Key %d - Found wrong stored record at %s",
5056 : sort_param->key+1,
5057 : llstr(sort_param->start_recpos,llbuff));
5058 0 : try_next:
5059 0 : pos=(sort_param->start_recpos+=MARIA_DYN_ALIGN_SIZE);
5060 0 : searching=1;
5061 0 : }
5062 : }
5063 : case COMPRESSED_RECORD:
5064 635 : for (searching=0 ;; searching=1, sort_param->pos++)
5065 : {
5066 635 : if (_ma_read_cache(&sort_param->read_cache, block_info.header,
5067 : sort_param->pos,
5068 : share->pack.ref_length,READING_NEXT))
5069 0 : DBUG_RETURN(-1);
5070 635 : if (searching && ! sort_param->fix_datafile)
5071 : {
5072 0 : param->error_printed=1;
5073 0 : param->retry_repair=1;
5074 0 : param->testflag|=T_RETRY_WITHOUT_QUICK;
5075 0 : DBUG_RETURN(1); /* Something wrong with data */
5076 : }
5077 635 : sort_param->start_recpos=sort_param->pos;
5078 635 : if (_ma_pack_get_block_info(info, &sort_param->bit_buff, &block_info,
5079 : &sort_param->rec_buff,
5080 : &sort_param->rec_buff_size, -1,
5081 : sort_param->pos))
5082 0 : DBUG_RETURN(-1);
5083 635 : if (!block_info.rec_len &&
5084 : sort_param->pos + MEMMAP_EXTRA_MARGIN ==
5085 : sort_param->read_cache.end_of_file)
5086 35 : DBUG_RETURN(-1);
5087 600 : if (block_info.rec_len < (uint) share->min_pack_length ||
5088 : block_info.rec_len > (uint) share->max_pack_length)
5089 : {
5090 0 : if (! searching)
5091 0 : _ma_check_print_info(param,
5092 : "Found block with wrong recordlength: %lu "
5093 : "at %s\n",
5094 : block_info.rec_len,
5095 : llstr(sort_param->pos,llbuff));
5096 : continue;
5097 : }
5098 600 : if (_ma_read_cache(&sort_param->read_cache, sort_param->rec_buff,
5099 : block_info.filepos, block_info.rec_len,
5100 : READING_NEXT))
5101 : {
5102 0 : if (! searching)
5103 0 : _ma_check_print_info(param,"Couldn't read whole record from %s",
5104 : llstr(sort_param->pos,llbuff));
5105 : continue;
5106 : }
5107 : #ifdef HAVE_purify
5108 : bzero(sort_param->rec_buff + block_info.rec_len,
5109 : share->base.extra_rec_buff_size);
5110 : #endif
5111 600 : if (_ma_pack_rec_unpack(info, &sort_param->bit_buff, sort_param->record,
5112 : sort_param->rec_buff, block_info.rec_len))
5113 : {
5114 0 : if (! searching)
5115 0 : _ma_check_print_info(param,"Found wrong record at %s",
5116 : llstr(sort_param->pos,llbuff));
5117 : continue;
5118 : }
5119 600 : if (!sort_param->fix_datafile)
5120 : {
5121 150 : sort_param->current_filepos= sort_param->pos;
5122 150 : if (sort_param->master)
5123 150 : share->state.split++;
5124 : }
5125 600 : sort_param->max_pos= (sort_param->pos=block_info.filepos+
5126 : block_info.rec_len);
5127 600 : info->packed_length=block_info.rec_len;
5128 :
5129 600 : if (sort_param->calc_checksum)
5130 : {
5131 600 : info->cur_row.checksum= (*share->calc_check_checksum)(info,
5132 : sort_param->
5133 : record);
5134 600 : param->glob_crc+= info->cur_row.checksum;
5135 : }
5136 600 : DBUG_RETURN(0);
5137 0 : }
5138 : }
5139 0 : DBUG_RETURN(1); /* Impossible */
5140 : }
5141 :
5142 :
5143 : /**
5144 : @brief Write record to new file.
5145 :
5146 : @fn _ma_sort_write_record()
5147 : @param sort_param Sort parameters.
5148 :
5149 : @note
5150 : This is only called by a master thread if parallel repair is used.
5151 :
5152 : @return
5153 : @retval 0 OK
5154 : sort_param->current_filepos points to inserted record for
5155 : block_records and to the place for the next record for
5156 : other row types.
5157 : sort_param->filepos points to end of file
5158 : @retval 1 Error
5159 : */
5160 :
5161 : int _ma_sort_write_record(MARIA_SORT_PARAM *sort_param)
5162 137699 : {
5163 : int flag;
5164 : uint length;
5165 : ulong block_length,reclength;
5166 : uchar *from;
5167 : uchar block_buff[8];
5168 137699 : MARIA_SORT_INFO *sort_info=sort_param->sort_info;
5169 137699 : HA_CHECK *param= sort_info->param;
5170 137699 : MARIA_HA *info= sort_info->new_info;
5171 137699 : MARIA_SHARE *share= info->s;
5172 137699 : DBUG_ENTER("_ma_sort_write_record");
5173 :
5174 137700 : if (sort_param->fix_datafile)
5175 : {
5176 18950 : sort_param->current_filepos= sort_param->filepos;
5177 18950 : switch (sort_info->new_data_file_type) {
5178 : case BLOCK_RECORD:
5179 11280 : if ((sort_param->current_filepos=
5180 : (*share->write_record_init)(info, sort_param->record)) ==
5181 : HA_OFFSET_ERROR)
5182 0 : DBUG_RETURN(1);
5183 : /* Pointer to end of file */
5184 11280 : sort_param->filepos= share->state.state.data_file_length;
5185 11280 : break;
5186 : case STATIC_RECORD:
5187 3790 : if (my_b_write(&info->rec_cache,sort_param->record,
5188 : share->base.pack_reclength))
5189 : {
5190 0 : _ma_check_print_error(param,"%d when writing to datafile",my_errno);
5191 0 : DBUG_RETURN(1);
5192 : }
5193 3790 : sort_param->filepos+=share->base.pack_reclength;
5194 3790 : share->state.split++;
5195 3790 : break;
5196 : case DYNAMIC_RECORD:
5197 3730 : if (! info->blobs)
5198 0 : from=sort_param->rec_buff;
5199 : else
5200 : {
5201 : /* must be sure that local buffer is big enough */
5202 3730 : reclength=share->base.pack_reclength+
5203 : _ma_calc_total_blob_length(info,sort_param->record)+
5204 : ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER)+MARIA_SPLIT_LENGTH+
5205 : MARIA_DYN_DELETE_BLOCK_HEADER;
5206 3730 : if (sort_info->buff_length < reclength)
5207 : {
5208 10 : if (!(sort_info->buff=my_realloc(sort_info->buff, (uint) reclength,
5209 : MYF(MY_FREE_ON_ERROR |
5210 : MY_ALLOW_ZERO_PTR))))
5211 0 : DBUG_RETURN(1);
5212 10 : sort_info->buff_length=reclength;
5213 : }
5214 3730 : from= (uchar *) sort_info->buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER);
5215 : }
5216 : /* We can use info->checksum here as only one thread calls this */
5217 3730 : info->cur_row.checksum= (*share->calc_check_checksum)(info,
5218 : sort_param->
5219 : record);
5220 3730 : reclength= _ma_rec_pack(info,from,sort_param->record);
5221 3730 : flag=0;
5222 :
5223 : do
5224 : {
5225 3730 : block_length=reclength+ 3 + test(reclength >= (65520-3));
5226 3730 : if (block_length < share->base.min_block_length)
5227 0 : block_length=share->base.min_block_length;
5228 3730 : info->update|=HA_STATE_WRITE_AT_END;
5229 3730 : block_length=MY_ALIGN(block_length,MARIA_DYN_ALIGN_SIZE);
5230 3730 : if (block_length > MARIA_MAX_BLOCK_LENGTH)
5231 0 : block_length=MARIA_MAX_BLOCK_LENGTH;
5232 3730 : if (_ma_write_part_record(info,0L,block_length,
5233 : sort_param->filepos+block_length,
5234 : &from,&reclength,&flag))
5235 : {
5236 0 : _ma_check_print_error(param,"%d when writing to datafile",my_errno);
5237 0 : DBUG_RETURN(1);
5238 : }
5239 3730 : sort_param->filepos+=block_length;
5240 3730 : share->state.split++;
5241 3730 : } while (reclength);
5242 : break;
5243 : case COMPRESSED_RECORD:
5244 150 : reclength=info->packed_length;
5245 150 : length= _ma_save_pack_length((uint) share->pack.version, block_buff,
5246 : reclength);
5247 150 : if (share->base.blobs)
5248 0 : length+= _ma_save_pack_length((uint) share->pack.version,
5249 : block_buff + length, info->blob_length);
5250 150 : if (my_b_write(&info->rec_cache,block_buff,length) ||
5251 : my_b_write(&info->rec_cache, sort_param->rec_buff, reclength))
5252 : {
5253 0 : _ma_check_print_error(param,"%d when writing to datafile",my_errno);
5254 0 : DBUG_RETURN(1);
5255 : }
5256 150 : sort_param->filepos+=reclength+length;
5257 150 : share->state.split++;
5258 : break;
5259 : }
5260 : }
5261 137700 : if (sort_param->master)
5262 : {
5263 92200 : share->state.state.records++;
5264 92200 : if ((param->testflag & T_WRITE_LOOP) &&
5265 : (share->state.state.records % WRITE_COUNT) == 0)
5266 : {
5267 : char llbuff[22];
5268 0 : printf("%s\r", llstr(share->state.state.records,llbuff));
5269 0 : VOID(fflush(stdout));
5270 : }
5271 : }
5272 137700 : DBUG_RETURN(0);
5273 : } /* _ma_sort_write_record */
5274 :
5275 :
5276 : /* Compare two keys from _ma_create_index_by_sort */
5277 :
5278 : static int sort_key_cmp(MARIA_SORT_PARAM *sort_param, const void *a,
5279 : const void *b)
5280 1424760 : {
5281 : uint not_used[2];
5282 1424760 : return (ha_key_cmp(sort_param->seg, *((uchar* const *) a),
5283 : *((uchar* const *) b),
5284 : USE_WHOLE_KEY, SEARCH_SAME, not_used));
5285 : } /* sort_key_cmp */
5286 :
5287 :
5288 : static int sort_key_write(MARIA_SORT_PARAM *sort_param, const uchar *a)
5289 137325 : {
5290 : uint diff_pos[2];
5291 : char llbuff[22],llbuff2[22];
5292 137325 : MARIA_SORT_INFO *sort_info=sort_param->sort_info;
5293 137325 : HA_CHECK *param= sort_info->param;
5294 : int cmp;
5295 :
5296 137325 : if (sort_info->key_block->inited)
5297 : {
5298 137120 : cmp= ha_key_cmp(sort_param->seg, sort_info->key_block->lastkey,
5299 : a, USE_WHOLE_KEY,
5300 : SEARCH_FIND | SEARCH_UPDATE | SEARCH_INSERT,
5301 : diff_pos);
5302 137120 : if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL)
5303 137120 : ha_key_cmp(sort_param->seg, sort_info->key_block->lastkey,
5304 : a, USE_WHOLE_KEY,
5305 : SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diff_pos);
5306 0 : else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
5307 : {
5308 0 : diff_pos[0]= maria_collect_stats_nonulls_next(sort_param->seg,
5309 : sort_param->notnull,
5310 : sort_info->key_block->lastkey,
5311 : a);
5312 : }
5313 137120 : sort_param->unique[diff_pos[0]-1]++;
5314 : }
5315 : else
5316 : {
5317 205 : cmp= -1;
5318 205 : if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
5319 0 : maria_collect_stats_nonulls_first(sort_param->seg, sort_param->notnull,
5320 : a);
5321 : }
5322 137325 : if ((sort_param->keyinfo->flag & HA_NOSAME) && cmp == 0)
5323 : {
5324 0 : sort_info->dupp++;
5325 0 : sort_info->info->cur_row.lastpos= get_record_for_key(sort_param->keyinfo,
5326 : a);
5327 0 : _ma_check_print_warning(param,
5328 : "Duplicate key %2u for record at %10s against "
5329 : "record at %10s",
5330 : sort_param->key + 1,
5331 : llstr(sort_info->info->cur_row.lastpos, llbuff),
5332 : llstr(get_record_for_key(sort_param->keyinfo,
5333 : sort_info->key_block->
5334 : lastkey),
5335 : llbuff2));
5336 0 : param->testflag|=T_RETRY_WITHOUT_QUICK;
5337 0 : if (sort_info->param->testflag & T_VERBOSE)
5338 0 : _ma_print_keydata(stdout,sort_param->seg, a, USE_WHOLE_KEY);
5339 0 : return (sort_delete_record(sort_param));
5340 : }
5341 : #ifndef DBUG_OFF
5342 137325 : if (cmp > 0)
5343 : {
5344 0 : _ma_check_print_error(param,
5345 : "Internal error: Keys are not in order from sort");
5346 0 : return(1);
5347 : }
5348 : #endif
5349 137325 : return (sort_insert_key(sort_param, sort_info->key_block,
5350 : a, HA_OFFSET_ERROR));
5351 : } /* sort_key_write */
5352 :
5353 :
5354 : int _ma_sort_ft_buf_flush(MARIA_SORT_PARAM *sort_param)
5355 0 : {
5356 0 : MARIA_SORT_INFO *sort_info=sort_param->sort_info;
5357 0 : SORT_KEY_BLOCKS *key_block=sort_info->key_block;
5358 0 : MARIA_SHARE *share=sort_info->info->s;
5359 : uint val_off, val_len;
5360 : int error;
5361 0 : SORT_FT_BUF *maria_ft_buf=sort_info->ft_buf;
5362 : uchar *from, *to;
5363 :
5364 0 : val_len=share->ft2_keyinfo.keylength;
5365 0 : get_key_full_length_rdonly(val_off, maria_ft_buf->lastkey);
5366 0 : to= maria_ft_buf->lastkey+val_off;
5367 :
5368 0 : if (maria_ft_buf->buf)
5369 : {
5370 : /* flushing first-level tree */
5371 0 : error= sort_insert_key(sort_param,key_block,maria_ft_buf->lastkey,
5372 : HA_OFFSET_ERROR);
5373 0 : for (from=to+val_len;
5374 0 : !error && from < maria_ft_buf->buf;
5375 0 : from+= val_len)
5376 : {
5377 0 : memcpy(to, from, val_len);
5378 0 : error= sort_insert_key(sort_param,key_block,maria_ft_buf->lastkey,
5379 : HA_OFFSET_ERROR);
5380 : }
5381 0 : return error;
5382 : }
5383 : /* flushing second-level tree keyblocks */
5384 0 : error=_ma_flush_pending_blocks(sort_param);
5385 : /* updating lastkey with second-level tree info */
5386 0 : ft_intXstore(maria_ft_buf->lastkey+val_off, -maria_ft_buf->count);
5387 0 : _ma_dpointer(sort_info->info->s, maria_ft_buf->lastkey+val_off+HA_FT_WLEN,
5388 : share->state.key_root[sort_param->key]);
5389 : /* restoring first level tree data in sort_info/sort_param */
5390 0 : sort_info->key_block=sort_info->key_block_end- sort_info->param->sort_key_blocks;
5391 0 : sort_param->keyinfo=share->keyinfo+sort_param->key;
5392 0 : share->state.key_root[sort_param->key]=HA_OFFSET_ERROR;
5393 : /* writing lastkey in first-level tree */
5394 0 : return error ? error :
5395 : sort_insert_key(sort_param,sort_info->key_block,
5396 : maria_ft_buf->lastkey,HA_OFFSET_ERROR);
5397 : }
5398 :
5399 :
5400 : static int sort_maria_ft_key_write(MARIA_SORT_PARAM *sort_param,
5401 : const uchar *a)
5402 0 : {
5403 : uint a_len, val_off, val_len, error;
5404 0 : MARIA_SORT_INFO *sort_info= sort_param->sort_info;
5405 0 : SORT_FT_BUF *ft_buf= sort_info->ft_buf;
5406 0 : SORT_KEY_BLOCKS *key_block= sort_info->key_block;
5407 0 : MARIA_SHARE *share= sort_info->info->s;
5408 :
5409 0 : val_len=HA_FT_WLEN+share->base.rec_reflength;
5410 0 : get_key_full_length_rdonly(a_len, a);
5411 :
5412 0 : if (!ft_buf)
5413 : {
5414 : /*
5415 : use two-level tree only if key_reflength fits in rec_reflength place
5416 : and row format is NOT static - for _ma_dpointer not to garble offsets
5417 : */
5418 0 : if ((share->base.key_reflength <=
5419 : share->base.rec_reflength) &&
5420 : (share->options &
5421 : (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
5422 0 : ft_buf= (SORT_FT_BUF *)my_malloc(sort_param->keyinfo->block_length +
5423 : sizeof(SORT_FT_BUF), MYF(MY_WME));
5424 :
5425 0 : if (!ft_buf)
5426 : {
5427 0 : sort_param->key_write=sort_key_write;
5428 0 : return sort_key_write(sort_param, a);
5429 : }
5430 0 : sort_info->ft_buf= ft_buf;
5431 0 : goto word_init_ft_buf; /* no need to duplicate the code */
5432 : }
5433 0 : get_key_full_length_rdonly(val_off, ft_buf->lastkey);
5434 :
5435 0 : if (ha_compare_text(sort_param->seg->charset,
5436 : a+1,a_len-1,
5437 : ft_buf->lastkey+1,val_off-1, 0, 0)==0)
5438 : {
5439 : uchar *p;
5440 0 : if (!ft_buf->buf) /* store in second-level tree */
5441 : {
5442 0 : ft_buf->count++;
5443 0 : return sort_insert_key(sort_param,key_block,
5444 : a + a_len, HA_OFFSET_ERROR);
5445 : }
5446 :
5447 : /* storing the key in the buffer. */
5448 0 : memcpy (ft_buf->buf, (const char *)a+a_len, val_len);
5449 0 : ft_buf->buf+=val_len;
5450 0 : if (ft_buf->buf < ft_buf->end)
5451 0 : return 0;
5452 :
5453 : /* converting to two-level tree */
5454 0 : p=ft_buf->lastkey+val_off;
5455 :
5456 0 : while (key_block->inited)
5457 0 : key_block++;
5458 0 : sort_info->key_block=key_block;
5459 0 : sort_param->keyinfo= &share->ft2_keyinfo;
5460 0 : ft_buf->count=(ft_buf->buf - p)/val_len;
5461 :
5462 : /* flushing buffer to second-level tree */
5463 0 : for (error=0; !error && p < ft_buf->buf; p+= val_len)
5464 0 : error=sort_insert_key(sort_param,key_block,p,HA_OFFSET_ERROR);
5465 0 : ft_buf->buf=0;
5466 0 : return error;
5467 : }
5468 :
5469 : /* flushing buffer */
5470 0 : if ((error=_ma_sort_ft_buf_flush(sort_param)))
5471 0 : return error;
5472 :
5473 0 : word_init_ft_buf:
5474 0 : a_len+=val_len;
5475 0 : memcpy(ft_buf->lastkey, a, a_len);
5476 0 : ft_buf->buf=ft_buf->lastkey+a_len;
5477 : /*
5478 : 32 is just a safety margin here
5479 : (at least max(val_len, sizeof(nod_flag)) should be there).
5480 : May be better performance could be achieved if we'd put
5481 : (sort_info->keyinfo->block_length-32)/XXX
5482 : instead.
5483 : TODO: benchmark the best value for XXX.
5484 : */
5485 0 : ft_buf->end= ft_buf->lastkey+ (sort_param->keyinfo->block_length-32);
5486 0 : return 0;
5487 : } /* sort_maria_ft_key_write */
5488 :
5489 :
5490 : /* get pointer to record from a key */
5491 :
5492 : static my_off_t get_record_for_key(MARIA_KEYDEF *keyinfo,
5493 : const uchar *key_data)
5494 0 : {
5495 : MARIA_KEY key;
5496 0 : key.keyinfo= keyinfo;
5497 0 : key.data= (uchar*) key_data;
5498 0 : key.data_length= _ma_keylength(keyinfo, key_data);
5499 0 : return _ma_row_pos_from_key(&key);
5500 : } /* get_record_for_key */
5501 :
5502 :
5503 : /* Insert a key in sort-key-blocks */
5504 :
5505 : static int sort_insert_key(MARIA_SORT_PARAM *sort_param,
5506 : register SORT_KEY_BLOCKS *key_block,
5507 : const uchar *key,
5508 : my_off_t prev_block)
5509 137775 : {
5510 : uint a_length,t_length,nod_flag;
5511 : my_off_t filepos,key_file_length;
5512 : uchar *anc_buff,*lastkey;
5513 : MARIA_KEY_PARAM s_temp;
5514 137775 : MARIA_KEYDEF *keyinfo=sort_param->keyinfo;
5515 137775 : MARIA_SORT_INFO *sort_info= sort_param->sort_info;
5516 137775 : HA_CHECK *param=sort_info->param;
5517 137775 : MARIA_PINNED_PAGE tmp_page_link, *page_link= &tmp_page_link;
5518 : MARIA_KEY tmp_key;
5519 137775 : MARIA_HA *info= sort_info->info;
5520 137775 : MARIA_SHARE *share= info->s;
5521 137775 : DBUG_ENTER("sort_insert_key");
5522 :
5523 137775 : anc_buff= key_block->buff;
5524 137775 : lastkey=key_block->lastkey;
5525 137775 : nod_flag= (key_block == sort_info->key_block ? 0 :
5526 : share->base.key_reflength);
5527 :
5528 137775 : if (!key_block->inited)
5529 : {
5530 505 : key_block->inited=1;
5531 505 : if (key_block == sort_info->key_block_end)
5532 : {
5533 0 : _ma_check_print_error(param,
5534 : "To many key-block-levels; "
5535 : "Try increasing sort_key_blocks");
5536 0 : DBUG_RETURN(1);
5537 : }
5538 505 : a_length= share->keypage_header + nod_flag;
5539 505 : key_block->end_pos= anc_buff + share->keypage_header;
5540 505 : bzero(anc_buff, share->keypage_header);
5541 505 : _ma_store_keynr(share, anc_buff, (uint) (sort_param->keyinfo -
5542 : share->keyinfo));
5543 505 : lastkey=0; /* No previous key in block */
5544 : }
5545 : else
5546 137270 : a_length= _ma_get_page_used(share, anc_buff);
5547 :
5548 : /* Save pointer to previous block */
5549 137775 : if (nod_flag)
5550 : {
5551 225 : _ma_store_keypage_flag(share, anc_buff, KEYPAGE_FLAG_ISNOD);
5552 225 : _ma_kpointer(info,key_block->end_pos,prev_block);
5553 : }
5554 :
5555 137775 : tmp_key.keyinfo= keyinfo;
5556 137775 : tmp_key.data= (uchar*) key;
5557 137775 : tmp_key.data_length= _ma_keylength(keyinfo, key) - share->base.rec_reflength;
5558 137775 : tmp_key.ref_length= share->base.rec_reflength;
5559 :
5560 137775 : t_length= (*keyinfo->pack_key)(&tmp_key, nod_flag,
5561 : (uchar*) 0, lastkey, lastkey, &s_temp);
5562 137775 : (*keyinfo->store_key)(keyinfo, key_block->end_pos+nod_flag,&s_temp);
5563 137775 : a_length+=t_length;
5564 137775 : _ma_store_page_used(share, anc_buff, a_length);
5565 137775 : key_block->end_pos+=t_length;
5566 137775 : if (a_length <= (uint) (keyinfo->block_length - KEYPAGE_CHECKSUM_SIZE))
5567 : {
5568 : MARIA_KEY tmp_key2;
5569 137550 : tmp_key2.data= key_block->lastkey;
5570 137550 : _ma_copy_key(&tmp_key2, &tmp_key);
5571 137550 : key_block->last_length=a_length-t_length;
5572 137550 : DBUG_RETURN(0);
5573 : }
5574 :
5575 : /* Fill block with end-zero and write filled block */
5576 225 : _ma_store_page_used(share, anc_buff, key_block->last_length);
5577 225 : bzero(anc_buff+key_block->last_length,
5578 : keyinfo->block_length- key_block->last_length);
5579 225 : key_file_length=share->state.state.key_file_length;
5580 225 : if ((filepos= _ma_new(info, DFLT_INIT_HITS, &page_link)) == HA_OFFSET_ERROR)
5581 0 : DBUG_RETURN(1);
5582 225 : _ma_fast_unlock_key_del(info);
5583 :
5584 : /* If we read the page from the key cache, we have to write it back to it */
5585 225 : if (page_link->changed)
5586 : {
5587 : MARIA_PAGE page;
5588 0 : pop_dynamic(&info->pinned_pages);
5589 0 : _ma_page_setup(&page, info, keyinfo, filepos, anc_buff);
5590 0 : if (_ma_write_keypage(&page, PAGECACHE_LOCK_WRITE_UNLOCK, DFLT_INIT_HITS))
5591 0 : DBUG_RETURN(1);
5592 : }
5593 : else
5594 : {
5595 225 : put_crc(anc_buff, filepos, share);
5596 225 : if (my_pwrite(share->kfile.file, anc_buff,
5597 : (uint) keyinfo->block_length, filepos, param->myf_rw))
5598 0 : DBUG_RETURN(1);
5599 : }
5600 225 : DBUG_DUMP("buff", anc_buff, _ma_get_page_used(share, anc_buff));
5601 :
5602 : /* Write separator-key to block in next level */
5603 225 : if (sort_insert_key(sort_param,key_block+1,key_block->lastkey,filepos))
5604 0 : DBUG_RETURN(1);
5605 :
5606 : /* clear old block and write new key in it */
5607 225 : key_block->inited=0;
5608 225 : DBUG_RETURN(sort_insert_key(sort_param, key_block,key,prev_block));
5609 : } /* sort_insert_key */
5610 :
5611 :
5612 : /* Delete record when we found a duplicated key */
5613 :
5614 : static int sort_delete_record(MARIA_SORT_PARAM *sort_param)
5615 0 : {
5616 : uint i;
5617 : int old_file,error;
5618 : uchar *key;
5619 0 : MARIA_SORT_INFO *sort_info=sort_param->sort_info;
5620 0 : HA_CHECK *param=sort_info->param;
5621 0 : MARIA_HA *row_info= sort_info->new_info, *key_info= sort_info->info;
5622 0 : DBUG_ENTER("sort_delete_record");
5623 :
5624 0 : if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
5625 : {
5626 0 : _ma_check_print_error(param,
5627 : "Quick-recover aborted; Run recovery without switch "
5628 : "-q or with switch -qq");
5629 0 : DBUG_RETURN(1);
5630 : }
5631 0 : if (key_info->s->options & HA_OPTION_COMPRESS_RECORD)
5632 : {
5633 0 : _ma_check_print_error(param,
5634 : "Recover aborted; Can't run standard recovery on "
5635 : "compressed tables with errors in data-file. "
5636 : "Use 'maria_chk --safe-recover' to fix it");
5637 0 : DBUG_RETURN(1);
5638 : }
5639 :
5640 0 : old_file= row_info->dfile.file;
5641 : /* This only affects static and dynamic row formats */
5642 0 : row_info->dfile.file= row_info->rec_cache.file;
5643 0 : if (flush_io_cache(&row_info->rec_cache))
5644 0 : DBUG_RETURN(1);
5645 :
5646 0 : key= key_info->lastkey_buff + key_info->s->base.max_key_length;
5647 0 : if ((error=(*row_info->s->read_record)(row_info, sort_param->record,
5648 : key_info->cur_row.lastpos)) &&
5649 : error != HA_ERR_RECORD_DELETED)
5650 : {
5651 0 : _ma_check_print_error(param,"Can't read record to be removed");
5652 0 : row_info->dfile.file= old_file;
5653 0 : DBUG_RETURN(1);
5654 : }
5655 0 : row_info->cur_row.lastpos= key_info->cur_row.lastpos;
5656 :
5657 0 : for (i=0 ; i < sort_info->current_key ; i++)
5658 : {
5659 : MARIA_KEY tmp_key;
5660 0 : (*key_info->s->keyinfo[i].make_key)(key_info, &tmp_key, i, key,
5661 : sort_param->record,
5662 : key_info->cur_row.lastpos, 0);
5663 0 : if (_ma_ck_delete(key_info, &tmp_key))
5664 : {
5665 0 : _ma_check_print_error(param,
5666 : "Can't delete key %d from record to be removed",
5667 : i+1);
5668 0 : row_info->dfile.file= old_file;
5669 0 : DBUG_RETURN(1);
5670 : }
5671 : }
5672 0 : if (sort_param->calc_checksum)
5673 0 : param->glob_crc-=(*key_info->s->calc_check_checksum)(key_info,
5674 : sort_param->record);
5675 0 : error= (*row_info->s->delete_record)(row_info, sort_param->record);
5676 0 : if (error)
5677 0 : _ma_check_print_error(param,"Got error %d when deleting record",
5678 : my_errno);
5679 0 : row_info->dfile.file= old_file; /* restore actual value */
5680 0 : row_info->s->state.state.records--;
5681 0 : DBUG_RETURN(error);
5682 : } /* sort_delete_record */
5683 :
5684 :
5685 : /* Fix all pending blocks and flush everything to disk */
5686 :
5687 : int _ma_flush_pending_blocks(MARIA_SORT_PARAM *sort_param)
5688 235 : {
5689 : uint nod_flag,length;
5690 : my_off_t filepos,key_file_length;
5691 : SORT_KEY_BLOCKS *key_block;
5692 235 : MARIA_SORT_INFO *sort_info= sort_param->sort_info;
5693 235 : myf myf_rw=sort_info->param->myf_rw;
5694 235 : MARIA_HA *info=sort_info->info;
5695 235 : MARIA_KEYDEF *keyinfo=sort_param->keyinfo;
5696 235 : MARIA_PINNED_PAGE tmp_page_link, *page_link= &tmp_page_link;
5697 235 : DBUG_ENTER("_ma_flush_pending_blocks");
5698 :
5699 235 : filepos= HA_OFFSET_ERROR; /* if empty file */
5700 235 : nod_flag=0;
5701 515 : for (key_block=sort_info->key_block ; key_block->inited ; key_block++)
5702 : {
5703 280 : key_block->inited=0;
5704 280 : length= _ma_get_page_used(info->s, key_block->buff);
5705 280 : if (nod_flag)
5706 75 : _ma_kpointer(info,key_block->end_pos,filepos);
5707 280 : key_file_length= info->s->state.state.key_file_length;
5708 280 : bzero(key_block->buff+length, keyinfo->block_length-length);
5709 280 : if ((filepos= _ma_new(info, DFLT_INIT_HITS, &page_link)) ==
5710 : HA_OFFSET_ERROR)
5711 280 : goto err;
5712 :
5713 : /* If we read the page from the key cache, we have to write it back */
5714 280 : if (page_link->changed)
5715 : {
5716 : MARIA_PAGE page;
5717 0 : pop_dynamic(&info->pinned_pages);
5718 :
5719 0 : _ma_page_setup(&page, info, keyinfo, filepos, key_block->buff);
5720 0 : if (_ma_write_keypage(&page, PAGECACHE_LOCK_WRITE_UNLOCK,
5721 : DFLT_INIT_HITS))
5722 : goto err;
5723 : }
5724 : else
5725 : {
5726 280 : put_crc(key_block->buff, filepos, info->s);
5727 280 : if (my_pwrite(info->s->kfile.file, key_block->buff,
5728 : (uint) keyinfo->block_length,filepos, myf_rw))
5729 280 : goto err;
5730 : }
5731 280 : DBUG_DUMP("buff",key_block->buff,length);
5732 280 : nod_flag=1;
5733 : }
5734 235 : info->s->state.key_root[sort_param->key]=filepos; /* Last is root for tree */
5735 235 : _ma_fast_unlock_key_del(info);
5736 235 : DBUG_RETURN(0);
5737 :
5738 0 : err:
5739 0 : _ma_fast_unlock_key_del(info);
5740 0 : DBUG_RETURN(1);
5741 : } /* _ma_flush_pending_blocks */
5742 :
5743 : /* alloc space and pointers for key_blocks */
5744 :
5745 : static SORT_KEY_BLOCKS *alloc_key_blocks(HA_CHECK *param, uint blocks,
5746 : uint buffer_length)
5747 85 : {
5748 : reg1 uint i;
5749 : SORT_KEY_BLOCKS *block;
5750 85 : DBUG_ENTER("alloc_key_blocks");
5751 :
5752 85 : if (!(block= (SORT_KEY_BLOCKS*) my_malloc((sizeof(SORT_KEY_BLOCKS)+
5753 : buffer_length+IO_SIZE)*blocks,
5754 : MYF(0))))
5755 : {
5756 0 : _ma_check_print_error(param,"Not enough memory for sort-key-blocks");
5757 0 : return(0);
5758 : }
5759 1445 : for (i=0 ; i < blocks ; i++)
5760 : {
5761 1360 : block[i].inited=0;
5762 1360 : block[i].buff= (uchar*) (block+blocks)+(buffer_length+IO_SIZE)*i;
5763 : }
5764 85 : DBUG_RETURN(block);
5765 : } /* alloc_key_blocks */
5766 :
5767 :
5768 : /* Check if file is almost full */
5769 :
5770 : int maria_test_if_almost_full(MARIA_HA *info)
5771 80 : {
5772 80 : MARIA_SHARE *share= info->s;
5773 :
5774 80 : if (share->options & HA_OPTION_COMPRESS_RECORD)
5775 5 : return 0;
5776 75 : return my_seek(share->kfile.file, 0L, MY_SEEK_END,
5777 : MYF(MY_THREADSAFE))/10*9 >
5778 : (my_off_t) share->base.max_key_file_length ||
5779 : my_seek(info->dfile.file, 0L, MY_SEEK_END, MYF(0)) / 10 * 9 >
5780 : (my_off_t) share->base.max_data_file_length;
5781 : }
5782 :
5783 :
5784 : /* Recreate table with bigger more alloced record-data */
5785 :
5786 : int maria_recreate_table(HA_CHECK *param, MARIA_HA **org_info, char *filename)
5787 30 : {
5788 : int error;
5789 : MARIA_HA info;
5790 : MARIA_SHARE share;
5791 : MARIA_KEYDEF *keyinfo,*key,*key_end;
5792 : HA_KEYSEG *keysegs,*keyseg;
5793 : MARIA_COLUMNDEF *columndef,*column,*end;
5794 : MARIA_UNIQUEDEF *uniquedef,*u_ptr,*u_end;
5795 : MARIA_STATUS_INFO status_info;
5796 : uint unpack,key_parts;
5797 : ha_rows max_records;
5798 : ulonglong file_length,tmp_length;
5799 : MARIA_CREATE_INFO create_info;
5800 30 : DBUG_ENTER("maria_recreate_table");
5801 :
5802 30 : error=1; /* Default error */
5803 30 : info= **org_info;
5804 30 : status_info= (*org_info)->state[0];
5805 30 : info.state= &status_info;
5806 30 : share= *(*org_info)->s;
5807 30 : unpack= ((share.data_file_type == COMPRESSED_RECORD) &&
5808 : (param->testflag & T_UNPACK));
5809 30 : if (!(keyinfo=(MARIA_KEYDEF*) my_alloca(sizeof(MARIA_KEYDEF) *
5810 : share.base.keys)))
5811 0 : DBUG_RETURN(0);
5812 30 : memcpy((uchar*) keyinfo,(uchar*) share.keyinfo,
5813 : (size_t) (sizeof(MARIA_KEYDEF)*share.base.keys));
5814 :
5815 30 : key_parts= share.base.all_key_parts;
5816 30 : if (!(keysegs=(HA_KEYSEG*) my_alloca(sizeof(HA_KEYSEG)*
5817 : (key_parts+share.base.keys))))
5818 : {
5819 : my_afree(keyinfo);
5820 0 : DBUG_RETURN(1);
5821 : }
5822 30 : if (!(columndef=(MARIA_COLUMNDEF*)
5823 : my_alloca(sizeof(MARIA_COLUMNDEF)*(share.base.fields+1))))
5824 : {
5825 : my_afree(keyinfo);
5826 : my_afree(keysegs);
5827 0 : DBUG_RETURN(1);
5828 : }
5829 30 : if (!(uniquedef=(MARIA_UNIQUEDEF*)
5830 : my_alloca(sizeof(MARIA_UNIQUEDEF)*(share.state.header.uniques+1))))
5831 : {
5832 : my_afree(columndef);
5833 : my_afree(keyinfo);
5834 : my_afree(keysegs);
5835 0 : DBUG_RETURN(1);
5836 : }
5837 :
5838 : /* Copy the column definitions in their original order */
5839 30 : for (column= share.columndef, end= share.columndef+share.base.fields;
5840 120 : column != end ;
5841 60 : column++)
5842 60 : columndef[column->column_nr]= *column;
5843 :
5844 : /* Change the new key to point at the saved key segments */
5845 30 : memcpy((uchar*) keysegs,(uchar*) share.keyparts,
5846 : (size_t) (sizeof(HA_KEYSEG)*(key_parts+share.base.keys+
5847 : share.state.header.uniques)));
5848 30 : keyseg=keysegs;
5849 60 : for (key=keyinfo,key_end=keyinfo+share.base.keys; key != key_end ; key++)
5850 : {
5851 30 : key->seg=keyseg;
5852 60 : for (; keyseg->type ; keyseg++)
5853 : {
5854 30 : if (param->language)
5855 0 : keyseg->language=param->language; /* change language */
5856 : }
5857 30 : keyseg++; /* Skip end pointer */
5858 : }
5859 :
5860 : /*
5861 : Copy the unique definitions and change them to point at the new key
5862 : segments
5863 : */
5864 30 : memcpy((uchar*) uniquedef,(uchar*) share.uniqueinfo,
5865 : (size_t) (sizeof(MARIA_UNIQUEDEF)*(share.state.header.uniques)));
5866 30 : for (u_ptr=uniquedef,u_end=uniquedef+share.state.header.uniques;
5867 30 : u_ptr != u_end ; u_ptr++)
5868 : {
5869 0 : u_ptr->seg=keyseg;
5870 0 : keyseg+=u_ptr->keysegs+1;
5871 : }
5872 :
5873 30 : file_length=(ulonglong) my_seek(info.dfile.file, 0L, MY_SEEK_END, MYF(0));
5874 30 : if (share.options & HA_OPTION_COMPRESS_RECORD)
5875 30 : share.base.records=max_records=info.state->records;
5876 0 : else if (share.base.min_pack_length)
5877 0 : max_records=(ha_rows) (file_length / share.base.min_pack_length);
5878 : else
5879 0 : max_records=0;
5880 30 : share.options&= ~HA_OPTION_TEMP_COMPRESS_RECORD;
5881 :
5882 30 : tmp_length= file_length+file_length/10;
5883 30 : set_if_bigger(file_length,param->max_data_file_length);
5884 30 : set_if_bigger(file_length,tmp_length);
5885 30 : set_if_bigger(file_length,(ulonglong) share.base.max_data_file_length);
5886 :
5887 30 : VOID(maria_close(*org_info));
5888 :
5889 30 : bzero((char*) &create_info,sizeof(create_info));
5890 30 : create_info.max_rows=max(max_records,share.base.records);
5891 30 : create_info.reloc_rows=share.base.reloc;
5892 30 : create_info.old_options=(share.options |
5893 : (unpack ? HA_OPTION_TEMP_COMPRESS_RECORD : 0));
5894 :
5895 30 : create_info.data_file_length=file_length;
5896 30 : create_info.auto_increment=share.state.auto_increment;
5897 30 : create_info.language = (param->language ? param->language :
5898 : share.state.header.language);
5899 30 : create_info.key_file_length= status_info.key_file_length;
5900 30 : create_info.org_data_file_type= ((enum data_file_type)
5901 : share.state.header.org_data_file_type);
5902 :
5903 : /*
5904 : Allow for creating an auto_increment key. This has an effect only if
5905 : an auto_increment key exists in the original table.
5906 : */
5907 30 : create_info.with_auto_increment= TRUE;
5908 30 : create_info.null_bytes= share.base.null_bytes;
5909 30 : create_info.transactional= share.base.born_transactional;
5910 :
5911 : /*
5912 : We don't have to handle symlinks here because we are using
5913 : HA_DONT_TOUCH_DATA
5914 : */
5915 30 : if (maria_create(filename, share.data_file_type,
5916 : share.base.keys - share.state.header.uniques,
5917 : keyinfo, share.base.fields, columndef,
5918 : share.state.header.uniques, uniquedef,
5919 : &create_info,
5920 : HA_DONT_TOUCH_DATA))
5921 : {
5922 0 : _ma_check_print_error(param,
5923 : "Got error %d when trying to recreate indexfile",
5924 : my_errno);
5925 0 : goto end;
5926 : }
5927 30 : *org_info= maria_open(filename,O_RDWR,
5928 : (HA_OPEN_FOR_REPAIR |
5929 : ((param->testflag & T_WAIT_FOREVER) ?
5930 : HA_OPEN_WAIT_IF_LOCKED :
5931 : (param->testflag & T_DESCRIPT) ?
5932 : HA_OPEN_IGNORE_IF_LOCKED :
5933 : HA_OPEN_ABORT_IF_LOCKED)));
5934 30 : if (!*org_info)
5935 : {
5936 0 : _ma_check_print_error(param,
5937 : "Got error %d when trying to open re-created "
5938 : "indexfile", my_errno);
5939 0 : goto end;
5940 : }
5941 : /* We are modifing */
5942 30 : (*org_info)->s->options&= ~HA_OPTION_READ_ONLY_DATA;
5943 30 : VOID(_ma_readinfo(*org_info,F_WRLCK,0));
5944 30 : (*org_info)->s->state.state.records= info.state->records;
5945 30 : if (share.state.create_time)
5946 30 : (*org_info)->s->state.create_time=share.state.create_time;
5947 : #ifdef EXTERNAL_LOCKING
5948 : (*org_info)->s->state.unique= (*org_info)->this_unique= share.state.unique;
5949 : #endif
5950 30 : (*org_info)->s->state.state.checksum= info.state->checksum;
5951 30 : (*org_info)->s->state.state.del= info.state->del;
5952 30 : (*org_info)->s->state.dellink= share.state.dellink;
5953 30 : (*org_info)->s->state.state.empty= info.state->empty;
5954 30 : (*org_info)->s->state.state.data_file_length= info.state->data_file_length;
5955 30 : *(*org_info)->state= (*org_info)->s->state.state;
5956 30 : if (maria_update_state_info(param,*org_info,UPDATE_TIME | UPDATE_STAT |
5957 : UPDATE_OPEN_COUNT))
5958 30 : goto end;
5959 30 : error=0;
5960 30 : end:
5961 : my_afree(uniquedef);
5962 : my_afree(keyinfo);
5963 : my_afree(columndef);
5964 : my_afree(keysegs);
5965 30 : DBUG_RETURN(error);
5966 : }
5967 :
5968 :
5969 : /* write suffix to data file if neaded */
5970 :
5971 : int maria_write_data_suffix(MARIA_SORT_INFO *sort_info, my_bool fix_datafile)
5972 80 : {
5973 80 : MARIA_HA *info=sort_info->new_info;
5974 :
5975 80 : if (info->s->data_file_type == COMPRESSED_RECORD && fix_datafile)
5976 : {
5977 : uchar buff[MEMMAP_EXTRA_MARGIN];
5978 10 : bzero(buff,sizeof(buff));
5979 10 : if (my_b_write(&info->rec_cache,buff,sizeof(buff)))
5980 : {
5981 0 : _ma_check_print_error(sort_info->param,
5982 : "%d when writing to datafile",my_errno);
5983 0 : return 1;
5984 : }
5985 10 : sort_info->param->read_cache.end_of_file+=sizeof(buff);
5986 : }
5987 80 : return 0;
5988 : }
5989 :
5990 :
5991 : /* Update state and maria_chk time of indexfile */
5992 :
5993 : int maria_update_state_info(HA_CHECK *param, MARIA_HA *info,uint update)
5994 294 : {
5995 294 : MARIA_SHARE *share= info->s;
5996 294 : DBUG_ENTER("maria_update_state_info");
5997 :
5998 294 : if (update & UPDATE_OPEN_COUNT)
5999 : {
6000 294 : share->state.open_count=0;
6001 294 : share->global_changed=0;
6002 : }
6003 294 : if (update & UPDATE_STAT)
6004 : {
6005 115 : uint i, key_parts= mi_uint2korr(share->state.header.key_parts);
6006 115 : share->state.records_at_analyze= share->state.state.records;
6007 115 : share->state.changed&= ~STATE_NOT_ANALYZED;
6008 115 : if (share->state.state.records)
6009 : {
6010 370 : for (i=0; i<key_parts; i++)
6011 : {
6012 260 : if (!(share->state.rec_per_key_part[i]=param->new_rec_per_key_part[i]))
6013 260 : share->state.changed|= STATE_NOT_ANALYZED;
6014 : }
6015 : }
6016 : }
6017 294 : if (update & (UPDATE_STAT | UPDATE_SORT | UPDATE_TIME | UPDATE_AUTO_INC))
6018 : {
6019 140 : if (update & UPDATE_TIME)
6020 : {
6021 140 : share->state.check_time= (long) time((time_t*) 0);
6022 140 : if (!share->state.create_time)
6023 0 : share->state.create_time= share->state.check_time;
6024 : }
6025 140 : if (_ma_state_info_write(share,
6026 : MA_STATE_INFO_WRITE_DONT_MOVE_OFFSET |
6027 : MA_STATE_INFO_WRITE_FULL_INFO))
6028 140 : goto err;
6029 140 : share->changed=0;
6030 : }
6031 : { /* Force update of status */
6032 : int error;
6033 294 : uint r_locks=share->r_locks,w_locks=share->w_locks;
6034 294 : share->r_locks= share->w_locks= share->tot_locks= 0;
6035 294 : error= _ma_writeinfo(info,WRITEINFO_NO_UNLOCK);
6036 294 : share->r_locks=r_locks;
6037 294 : share->w_locks=w_locks;
6038 294 : share->tot_locks=r_locks+w_locks;
6039 294 : if (!error)
6040 294 : DBUG_RETURN(0);
6041 : }
6042 0 : err:
6043 0 : _ma_check_print_error(param,"%d when updating keyfile",my_errno);
6044 0 : DBUG_RETURN(1);
6045 : }
6046 :
6047 : /*
6048 : Update auto increment value for a table
6049 : When setting the 'repair_only' flag we only want to change the
6050 : old auto_increment value if its wrong (smaller than some given key).
6051 : The reason is that we shouldn't change the auto_increment value
6052 : for a table without good reason when only doing a repair; If the
6053 : user have inserted and deleted rows, the auto_increment value
6054 : may be bigger than the biggest current row and this is ok.
6055 :
6056 : If repair_only is not set, we will update the flag to the value in
6057 : param->auto_increment is bigger than the biggest key.
6058 : */
6059 :
6060 : void _ma_update_auto_increment_key(HA_CHECK *param, MARIA_HA *info,
6061 : my_bool repair_only)
6062 0 : {
6063 0 : MARIA_SHARE *share= info->s;
6064 : uchar *record;
6065 0 : DBUG_ENTER("update_auto_increment_key");
6066 :
6067 0 : if (!share->base.auto_key ||
6068 : ! maria_is_key_active(share->state.key_map, share->base.auto_key - 1))
6069 : {
6070 0 : if (!(param->testflag & T_VERY_SILENT))
6071 0 : _ma_check_print_info(param,
6072 : "Table: %s doesn't have an auto increment key\n",
6073 : param->isam_file_name);
6074 0 : DBUG_VOID_RETURN;
6075 : }
6076 0 : if (!(param->testflag & T_SILENT) &&
6077 : !(param->testflag & T_REP))
6078 0 : printf("Updating MARIA file: %s\n", param->isam_file_name);
6079 : /*
6080 : We have to use an allocated buffer instead of info->rec_buff as
6081 : _ma_put_key_in_record() may use info->rec_buff
6082 : */
6083 0 : if (!(record= (uchar*) my_malloc((size_t) share->base.default_rec_buff_size,
6084 : MYF(0))))
6085 : {
6086 0 : _ma_check_print_error(param,"Not enough memory for extra record");
6087 0 : DBUG_VOID_RETURN;
6088 : }
6089 :
6090 0 : maria_extra(info,HA_EXTRA_KEYREAD,0);
6091 0 : if (maria_rlast(info, record, share->base.auto_key-1))
6092 : {
6093 0 : if (my_errno != HA_ERR_END_OF_FILE)
6094 : {
6095 0 : maria_extra(info,HA_EXTRA_NO_KEYREAD,0);
6096 0 : my_free((char*) record, MYF(0));
6097 0 : _ma_check_print_error(param,"%d when reading last record",my_errno);
6098 0 : DBUG_VOID_RETURN;
6099 : }
6100 0 : if (!repair_only)
6101 0 : share->state.auto_increment=param->auto_increment_value;
6102 : }
6103 : else
6104 : {
6105 0 : const HA_KEYSEG *keyseg= share->keyinfo[share->base.auto_key-1].seg;
6106 : ulonglong auto_increment=
6107 0 : ma_retrieve_auto_increment(record + keyseg->start, keyseg->type);
6108 0 : set_if_bigger(share->state.auto_increment,auto_increment);
6109 0 : if (!repair_only)
6110 0 : set_if_bigger(share->state.auto_increment, param->auto_increment_value);
6111 : }
6112 0 : maria_extra(info,HA_EXTRA_NO_KEYREAD,0);
6113 0 : my_free((char*) record, MYF(0));
6114 0 : maria_update_state_info(param, info, UPDATE_AUTO_INC);
6115 0 : DBUG_VOID_RETURN;
6116 : }
6117 :
6118 :
6119 : /*
6120 : Update statistics for each part of an index
6121 :
6122 : SYNOPSIS
6123 : maria_update_key_parts()
6124 : keyinfo IN Index information (only key->keysegs used)
6125 : rec_per_key_part OUT Store statistics here
6126 : unique IN Array of (#distinct tuples)
6127 : notnull_tuples IN Array of (#tuples), or NULL
6128 : records Number of records in the table
6129 :
6130 : DESCRIPTION
6131 : This function is called produce index statistics values from unique and
6132 : notnull_tuples arrays after these arrays were produced with sequential
6133 : index scan (the scan is done in two places: chk_index() and
6134 : sort_key_write()).
6135 :
6136 : This function handles all 3 index statistics collection methods.
6137 :
6138 : Unique is an array:
6139 : unique[0]= (#different values of {keypart1}) - 1
6140 : unique[1]= (#different values of {keypart1,keypart2} tuple)-unique[0]-1
6141 : ...
6142 :
6143 : For MI_STATS_METHOD_IGNORE_NULLS method, notnull_tuples is an array too:
6144 : notnull_tuples[0]= (#of {keypart1} tuples such that keypart1 is not NULL)
6145 : notnull_tuples[1]= (#of {keypart1,keypart2} tuples such that all
6146 : keypart{i} are not NULL)
6147 : ...
6148 : For all other statistics collection methods notnull_tuples==NULL.
6149 :
6150 : Output is an array:
6151 : rec_per_key_part[k] =
6152 : = E(#records in the table such that keypart_1=c_1 AND ... AND
6153 : keypart_k=c_k for arbitrary constants c_1 ... c_k)
6154 :
6155 : = {assuming that values have uniform distribution and index contains all
6156 : tuples from the domain (or that {c_1, ..., c_k} tuple is choosen from
6157 : index tuples}
6158 :
6159 : = #tuples-in-the-index / #distinct-tuples-in-the-index.
6160 :
6161 : The #tuples-in-the-index and #distinct-tuples-in-the-index have different
6162 : meaning depending on which statistics collection method is used:
6163 :
6164 : MI_STATS_METHOD_* how are nulls compared? which tuples are counted?
6165 : NULLS_EQUAL NULL == NULL all tuples in table
6166 : NULLS_NOT_EQUAL NULL != NULL all tuples in table
6167 : IGNORE_NULLS n/a tuples that don't have NULLs
6168 : */
6169 :
6170 : void maria_update_key_parts(MARIA_KEYDEF *keyinfo, double *rec_per_key_part,
6171 : ulonglong *unique, ulonglong *notnull,
6172 : ulonglong records)
6173 0 : {
6174 0 : ulonglong count=0, unique_tuples;
6175 0 : ulonglong tuples= records;
6176 : uint parts;
6177 : double tmp;
6178 0 : for (parts=0 ; parts < keyinfo->keysegs ; parts++)
6179 : {
6180 0 : count+=unique[parts];
6181 0 : unique_tuples= count + 1;
6182 0 : if (notnull)
6183 : {
6184 0 : tuples= notnull[parts];
6185 : /*
6186 : #(unique_tuples not counting tuples with NULLs) =
6187 : #(unique_tuples counting tuples with NULLs as different) -
6188 : #(tuples with NULLs)
6189 : */
6190 0 : unique_tuples -= (records - notnull[parts]);
6191 : }
6192 :
6193 0 : if (unique_tuples == 0)
6194 0 : tmp= 1;
6195 0 : else if (count == 0)
6196 0 : tmp= ulonglong2double(tuples); /* 1 unique tuple */
6197 : else
6198 0 : tmp= ulonglong2double(tuples) / ulonglong2double(unique_tuples);
6199 :
6200 : /*
6201 : for some weird keys (e.g. FULLTEXT) tmp can be <1 here.
6202 : let's ensure it is not
6203 : */
6204 0 : set_if_bigger(tmp,1);
6205 :
6206 0 : *rec_per_key_part++= tmp;
6207 : }
6208 : }
6209 :
6210 :
6211 : static ha_checksum maria_byte_checksum(const uchar *buf, uint length)
6212 199035 : {
6213 : ha_checksum crc;
6214 199035 : const uchar *end=buf+length;
6215 4206855 : for (crc=0; buf != end; buf++)
6216 4007820 : crc=((crc << 1) + *buf) +
6217 : test(crc & (((ha_checksum) 1) << (8*sizeof(ha_checksum)-1)));
6218 199035 : return crc;
6219 : }
6220 :
6221 : static my_bool maria_too_big_key_for_sort(MARIA_KEYDEF *key, ha_rows rows)
6222 235 : {
6223 235 : uint key_maxlength=key->maxlength;
6224 235 : if (key->flag & HA_FULLTEXT)
6225 : {
6226 : uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT*
6227 0 : key->seg->charset->mbmaxlen;
6228 0 : key_maxlength+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
6229 : }
6230 235 : return (key->flag & HA_SPATIAL) ||
6231 : (key->flag & (HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY | HA_FULLTEXT) &&
6232 : ((ulonglong) rows * key_maxlength >
6233 : (ulonglong) maria_max_temp_length));
6234 : }
6235 :
6236 : /*
6237 : Deactivate all not unique index that can be recreated fast
6238 : These include packed keys on which sorting will use more temporary
6239 : space than the max allowed file length or for which the unpacked keys
6240 : will take much more space than packed keys.
6241 : Note that 'rows' may be zero for the case when we don't know how many
6242 : rows we will put into the file.
6243 : */
6244 :
6245 : void maria_disable_non_unique_index(MARIA_HA *info, ha_rows rows)
6246 0 : {
6247 0 : MARIA_SHARE *share= info->s;
6248 0 : MARIA_KEYDEF *key=share->keyinfo;
6249 : uint i;
6250 :
6251 0 : DBUG_ASSERT(share->state.state.records == 0 &&
6252 : (!rows || rows >= MARIA_MIN_ROWS_TO_DISABLE_INDEXES));
6253 0 : for (i=0 ; i < share->base.keys ; i++,key++)
6254 : {
6255 0 : if (!(key->flag &
6256 : (HA_NOSAME | HA_SPATIAL | HA_AUTO_KEY | HA_RTREE_INDEX)) &&
6257 : ! maria_too_big_key_for_sort(key,rows) && share->base.auto_key != i+1)
6258 : {
6259 0 : maria_clear_key_active(share->state.key_map, i);
6260 0 : info->update|= HA_STATE_CHANGED;
6261 : }
6262 : }
6263 : }
6264 :
6265 :
6266 : /*
6267 : Return TRUE if we can use repair by sorting
6268 : One can set the force argument to force to use sorting
6269 : even if the temporary file would be quite big!
6270 : */
6271 :
6272 : my_bool maria_test_if_sort_rep(MARIA_HA *info, ha_rows rows,
6273 : ulonglong key_map, my_bool force)
6274 85 : {
6275 85 : MARIA_SHARE *share= info->s;
6276 85 : MARIA_KEYDEF *key=share->keyinfo;
6277 : uint i;
6278 :
6279 : /*
6280 : maria_repair_by_sort only works if we have at least one key. If we don't
6281 : have any keys, we should use the normal repair.
6282 : */
6283 85 : if (! maria_is_any_key_active(key_map))
6284 0 : return FALSE; /* Can't use sort */
6285 320 : for (i=0 ; i < share->base.keys ; i++,key++)
6286 : {
6287 235 : if (!force && maria_too_big_key_for_sort(key,rows))
6288 0 : return FALSE;
6289 : }
6290 85 : return TRUE;
6291 : }
6292 :
6293 :
6294 : /**
6295 : @brief Create a new handle for manipulation the new record file
6296 :
6297 : @note
6298 : It's ok for Recovery to have two MARIA_SHARE on the same index file
6299 : because the one we create here is not transactional
6300 : */
6301 :
6302 : static my_bool create_new_data_handle(MARIA_SORT_PARAM *param, File new_file)
6303 45 : {
6304 :
6305 45 : MARIA_SORT_INFO *sort_info= param->sort_info;
6306 45 : MARIA_HA *info= sort_info->info;
6307 : MARIA_HA *new_info;
6308 45 : DBUG_ENTER("create_new_data_handle");
6309 :
6310 45 : if (!(sort_info->new_info= maria_open(info->s->open_file_name.str, O_RDWR,
6311 : HA_OPEN_COPY | HA_OPEN_FOR_REPAIR)))
6312 0 : DBUG_RETURN(1);
6313 :
6314 45 : new_info= sort_info->new_info;
6315 45 : _ma_bitmap_set_pagecache_callbacks(&new_info->s->bitmap.file,
6316 : new_info->s);
6317 45 : _ma_set_data_pagecache_callbacks(&new_info->dfile, new_info->s);
6318 45 : change_data_file_descriptor(new_info, new_file);
6319 45 : maria_lock_database(new_info, F_EXTRA_LCK);
6320 45 : if ((sort_info->param->testflag & T_UNPACK) &&
6321 : info->s->data_file_type == COMPRESSED_RECORD)
6322 : {
6323 15 : (*new_info->s->once_end)(new_info->s);
6324 15 : (*new_info->s->end)(new_info);
6325 15 : restore_data_file_type(new_info->s);
6326 15 : _ma_setup_functions(new_info->s);
6327 15 : if ((*new_info->s->once_init)(new_info->s, new_file) ||
6328 : (*new_info->s->init)(new_info))
6329 0 : DBUG_RETURN(1);
6330 : }
6331 45 : _ma_reset_status(new_info);
6332 45 : if (_ma_initialize_data_file(new_info->s, new_file))
6333 0 : DBUG_RETURN(1);
6334 :
6335 : /* Take into account any bitmap page created above: */
6336 45 : param->filepos= new_info->s->state.state.data_file_length;
6337 :
6338 : /* Use new virtual functions for key generation */
6339 45 : info->s->keypos_to_recpos= new_info->s->keypos_to_recpos;
6340 45 : info->s->recpos_to_keypos= new_info->s->recpos_to_keypos;
6341 45 : DBUG_RETURN(0);
6342 : }
6343 :
6344 :
6345 : static void
6346 : set_data_file_type(MARIA_SORT_INFO *sort_info, MARIA_SHARE *share)
6347 110 : {
6348 110 : if ((sort_info->new_data_file_type=share->data_file_type) ==
6349 : COMPRESSED_RECORD && sort_info->param->testflag & T_UNPACK)
6350 : {
6351 : MARIA_SHARE tmp;
6352 15 : sort_info->new_data_file_type= share->state.header.org_data_file_type;
6353 : /* Set delete_function for sort_delete_record() */
6354 15 : tmp= *share;
6355 15 : tmp.state.header.data_file_type= tmp.state.header.org_data_file_type;
6356 15 : tmp.options= ~HA_OPTION_COMPRESS_RECORD;
6357 15 : _ma_setup_functions(&tmp);
6358 15 : share->delete_record=tmp.delete_record;
6359 : }
6360 : }
6361 :
6362 : static void restore_data_file_type(MARIA_SHARE *share)
6363 44 : {
6364 : MARIA_SHARE tmp_share;
6365 44 : share->options&= ~HA_OPTION_COMPRESS_RECORD;
6366 44 : mi_int2store(share->state.header.options,share->options);
6367 44 : share->state.header.data_file_type=
6368 : share->state.header.org_data_file_type;
6369 44 : share->data_file_type= share->state.header.data_file_type;
6370 44 : share->pack.header_length= 0;
6371 :
6372 : /* Use new virtual functions for key generation */
6373 44 : tmp_share= *share;
6374 44 : _ma_setup_functions(&tmp_share);
6375 44 : share->keypos_to_recpos= tmp_share.keypos_to_recpos;
6376 44 : share->recpos_to_keypos= tmp_share.recpos_to_keypos;
6377 : }
6378 :
6379 :
6380 : static void change_data_file_descriptor(MARIA_HA *info, File new_file)
6381 112 : {
6382 112 : my_close(info->dfile.file, MYF(MY_WME));
6383 112 : info->dfile.file= info->s->bitmap.file.file= new_file;
6384 112 : _ma_bitmap_reset_cache(info->s);
6385 : }
6386 :
6387 :
6388 : /**
6389 : @brief Mark the data file to not be used
6390 :
6391 : @note
6392 : This is used in repair when we want to ensure the handler will not
6393 : write anything to the data file anymore
6394 : */
6395 :
6396 : static void unuse_data_file_descriptor(MARIA_HA *info)
6397 0 : {
6398 0 : info->dfile.file= info->s->bitmap.file.file= -1;
6399 0 : _ma_bitmap_reset_cache(info->s);
6400 : }
6401 :
6402 :
6403 : /*
6404 : Copy all states that has to do with the data file
6405 :
6406 : NOTES
6407 : This is done to copy the state from the data file generated from
6408 : repair to the original handler
6409 : */
6410 :
6411 : static void copy_data_file_state(MARIA_STATE_INFO *to,
6412 : MARIA_STATE_INFO *from)
6413 45 : {
6414 45 : to->state.records= from->state.records;
6415 45 : to->state.del= from->state.del;
6416 45 : to->state.empty= from->state.empty;
6417 45 : to->state.data_file_length= from->state.data_file_length;
6418 45 : to->split= from->split;
6419 45 : to->dellink= from->dellink;
6420 45 : to->first_bitmap_with_space= from->first_bitmap_with_space;
6421 : }
6422 :
6423 :
6424 : /*
6425 : Read 'safely' next record while scanning table.
6426 :
6427 : SYNOPSIS
6428 : _ma_safe_scan_block_record()
6429 : info Maria handler
6430 : record Store found here
6431 :
6432 : NOTES
6433 : - One must have called mi_scan() before this
6434 :
6435 : Differences compared to _ma_scan_block_records() are:
6436 : - We read all blocks, not only blocks marked by the bitmap to be safe
6437 : - In case of errors, next read will read next record.
6438 : - More sanity checks
6439 :
6440 : RETURN
6441 : 0 ok
6442 : HA_ERR_END_OF_FILE End of file
6443 : # error number
6444 : */
6445 :
6446 :
6447 : static int _ma_safe_scan_block_record(MARIA_SORT_INFO *sort_info,
6448 : MARIA_HA *info, uchar *record)
6449 11175 : {
6450 11175 : MARIA_SHARE *share= info->s;
6451 11175 : MARIA_RECORD_POS record_pos= info->cur_row.nextpos;
6452 11175 : pgcache_page_no_t page= sort_info->page;
6453 11175 : DBUG_ENTER("_ma_safe_scan_block_record");
6454 :
6455 : for (;;)
6456 : {
6457 : /* Find next row in current page */
6458 11310 : if (likely(record_pos < info->scan.number_of_rows))
6459 : {
6460 : uint length, offset;
6461 : uchar *data, *end_of_data;
6462 : char llbuff[22];
6463 :
6464 11151 : while (!(offset= uint2korr(info->scan.dir)))
6465 : {
6466 6 : info->scan.dir-= DIR_ENTRY_SIZE;
6467 6 : record_pos++;
6468 6 : if (info->scan.dir < info->scan.dir_end)
6469 : {
6470 0 : _ma_check_print_info(sort_info->param,
6471 : "Wrong directory on page %s",
6472 : llstr(page, llbuff));
6473 0 : goto read_next_page;
6474 : }
6475 : }
6476 : /* found row */
6477 11145 : info->cur_row.lastpos= info->scan.row_base_page + record_pos;
6478 11145 : info->cur_row.nextpos= record_pos + 1;
6479 11145 : data= info->scan.page_buff + offset;
6480 11145 : length= uint2korr(info->scan.dir + 2);
6481 11145 : end_of_data= data + length;
6482 11145 : info->scan.dir-= DIR_ENTRY_SIZE; /* Point to previous row */
6483 :
6484 11145 : if (end_of_data > info->scan.dir_end ||
6485 : offset < PAGE_HEADER_SIZE || length < share->base.min_block_length)
6486 : {
6487 0 : _ma_check_print_info(sort_info->param,
6488 : "Wrong directory entry %3u at page %s",
6489 : (uint) record_pos, llstr(page, llbuff));
6490 0 : record_pos++;
6491 0 : continue;
6492 : }
6493 : else
6494 : {
6495 11145 : DBUG_PRINT("info", ("rowid: %lu", (ulong) info->cur_row.lastpos));
6496 11145 : DBUG_RETURN(_ma_read_block_record2(info, record, data, end_of_data));
6497 : }
6498 : }
6499 :
6500 30 : read_next_page:
6501 : /* Read until we find next head page */
6502 : for (;;)
6503 : {
6504 : uint page_type;
6505 : char llbuff[22];
6506 :
6507 195 : sort_info->page++; /* In case of errors */
6508 195 : page++;
6509 195 : if (!(page % share->bitmap.pages_covered))
6510 : {
6511 : /* Skip bitmap */
6512 0 : page++;
6513 0 : sort_info->page++;
6514 : }
6515 195 : if ((my_off_t) (page + 1) * share->block_size > sort_info->filelength)
6516 30 : DBUG_RETURN(HA_ERR_END_OF_FILE);
6517 165 : if (!(pagecache_read(share->pagecache,
6518 : &info->dfile,
6519 : page, 0, info->scan.page_buff,
6520 : PAGECACHE_READ_UNKNOWN_PAGE,
6521 : PAGECACHE_LOCK_LEFT_UNLOCKED, 0)))
6522 : {
6523 0 : if (my_errno == HA_ERR_WRONG_CRC)
6524 : {
6525 0 : _ma_check_print_info(sort_info->param,
6526 : "Wrong CRC on datapage at %s",
6527 : llstr(page, llbuff));
6528 0 : continue;
6529 : }
6530 0 : DBUG_RETURN(my_errno);
6531 : }
6532 165 : page_type= (info->scan.page_buff[PAGE_TYPE_OFFSET] &
6533 : PAGE_TYPE_MASK);
6534 165 : if (page_type == HEAD_PAGE)
6535 : {
6536 135 : if ((info->scan.number_of_rows=
6537 : (uint) (uchar) info->scan.page_buff[DIR_COUNT_OFFSET]) != 0)
6538 0 : break;
6539 0 : _ma_check_print_info(sort_info->param,
6540 : "Wrong head page at page %s",
6541 : llstr(page, llbuff));
6542 : }
6543 30 : else if (page_type >= MAX_PAGE_TYPE)
6544 : {
6545 0 : _ma_check_print_info(sort_info->param,
6546 : "Found wrong page type: %d at page %s",
6547 : page_type, llstr(page, llbuff));
6548 : }
6549 : }
6550 :
6551 : /* New head page */
6552 135 : info->scan.dir= (info->scan.page_buff + share->block_size -
6553 : PAGE_SUFFIX_SIZE - DIR_ENTRY_SIZE);
6554 135 : info->scan.dir_end= (info->scan.dir -
6555 : (info->scan.number_of_rows - 1) *
6556 : DIR_ENTRY_SIZE);
6557 135 : info->scan.row_base_page= ma_recordpos(page, 0);
6558 135 : record_pos= 0;
6559 : }
6560 : }
6561 :
6562 :
6563 : /**
6564 : @brief Writes a LOGREC_REPAIR_TABLE record and updates create_rename_lsn
6565 : if needed (so that maria_read_log does not redo the repair).
6566 :
6567 : @param param description of the REPAIR operation
6568 : @param info table
6569 :
6570 : @return Operation status
6571 : @retval 0 ok
6572 : @retval 1 error (disk problem)
6573 : */
6574 :
6575 : my_bool write_log_record_for_repair(const HA_CHECK *param, MARIA_HA *info)
6576 5 : {
6577 5 : MARIA_SHARE *share= info->s;
6578 : /* in case this is maria_chk or recovery... */
6579 5 : if (translog_status == TRANSLOG_OK && !maria_in_recovery &&
6580 : share->base.born_transactional)
6581 : {
6582 2 : my_bool save_now_transactional= share->now_transactional;
6583 :
6584 : /*
6585 : For now this record is only informative. It could serve when applying
6586 : logs to a backup, but that needs more thought. Assume table became
6587 : corrupted. It is repaired, then some writes happen to it.
6588 : Later we restore an old backup, and want to apply this REDO_REPAIR_TABLE
6589 : record. For it to give the same result as originally, the table should
6590 : be corrupted the same way, so applying previous REDOs should produce the
6591 : same corruption; that's really not guaranteed (different execution paths
6592 : in execution of REDOs vs runtime code so not same bugs hit, temporary
6593 : hardware issues not repeatable etc). Corruption may not be repeatable.
6594 : A reasonable solution is to execute the REDO_REPAIR_TABLE record and
6595 : check if the checksum of the resulting table matches what it was at the
6596 : end of the original repair (should be stored in log record); or execute
6597 : the REDO_REPAIR_TABLE if the checksum of the table-before-repair matches
6598 : was it was at the start of the original repair (should be stored in log
6599 : record).
6600 : */
6601 : LEX_CUSTRING log_array[TRANSLOG_INTERNAL_PARTS + 1];
6602 : uchar log_data[FILEID_STORE_SIZE + 8 + 8];
6603 : LSN lsn;
6604 :
6605 : /*
6606 : testflag gives an idea of what REPAIR did (in particular T_QUICK
6607 : or not: did it touch the data file or not?).
6608 : */
6609 2 : int8store(log_data + FILEID_STORE_SIZE, param->testflag);
6610 : /* org_key_map is used when recreating index after a load data infile */
6611 2 : int8store(log_data + FILEID_STORE_SIZE + 8, param->org_key_map);
6612 :
6613 2 : log_array[TRANSLOG_INTERNAL_PARTS + 0].str= log_data;
6614 2 : log_array[TRANSLOG_INTERNAL_PARTS + 0].length= sizeof(log_data);
6615 :
6616 2 : share->now_transactional= 1;
6617 2 : if (unlikely(translog_write_record(&lsn, LOGREC_REDO_REPAIR_TABLE,
6618 : &dummy_transaction_object, info,
6619 : (translog_size_t) sizeof(log_data),
6620 : sizeof(log_array)/sizeof(log_array[0]),
6621 : log_array, log_data, NULL) ||
6622 : translog_flush(lsn)))
6623 0 : return TRUE;
6624 : /*
6625 : The table's existence was made durable earlier (MY_SYNC_DIR passed to
6626 : maria_change_to_newfile()). All pages have been flushed, state too, we
6627 : need to force it to disk. Old REDOs should not be applied to the table,
6628 : which is already enforced as skip_redos_lsn was increased in
6629 : protect_against_repair_crash(). But if this is an explicit repair,
6630 : even UNDO phase should ignore this table: create_rename_lsn should be
6631 : increased, and this also serves for the REDO_REPAIR to be ignored by
6632 : maria_read_log.
6633 : The fully correct order would be: sync data and index file, remove crash
6634 : mark and update LSNs then write state and sync index file. But at this
6635 : point state (without crash mark) is already written.
6636 : */
6637 2 : if ((!(param->testflag & T_NO_CREATE_RENAME_LSN) &&
6638 : _ma_update_state_lsns(share, lsn, share->state.create_trid, FALSE,
6639 : FALSE)) ||
6640 : _ma_sync_table_files(info))
6641 0 : return TRUE;
6642 2 : share->now_transactional= save_now_transactional;
6643 : }
6644 5 : return FALSE;
6645 : }
6646 :
6647 :
6648 : /**
6649 : Writes an UNDO record which if executed in UNDO phase, will empty the
6650 : table. Such record is thus logged only in certain cases of bulk insert
6651 : (table needs to be empty etc).
6652 : */
6653 : my_bool write_log_record_for_bulk_insert(MARIA_HA *info)
6654 0 : {
6655 : LEX_CUSTRING log_array[TRANSLOG_INTERNAL_PARTS + 1];
6656 : uchar log_data[LSN_STORE_SIZE + FILEID_STORE_SIZE];
6657 : LSN lsn;
6658 0 : lsn_store(log_data, info->trn->undo_lsn);
6659 0 : log_array[TRANSLOG_INTERNAL_PARTS + 0].str= log_data;
6660 0 : log_array[TRANSLOG_INTERNAL_PARTS + 0].length= sizeof(log_data);
6661 0 : return translog_write_record(&lsn, LOGREC_UNDO_BULK_INSERT,
6662 : info->trn, info,
6663 : (translog_size_t)
6664 : log_array[TRANSLOG_INTERNAL_PARTS +
6665 : 0].length,
6666 : TRANSLOG_INTERNAL_PARTS + 1, log_array,
6667 : log_data + LSN_STORE_SIZE, NULL) ||
6668 : translog_flush(lsn); /* WAL */
6669 : }
6670 :
6671 :
6672 : /* Give error message why reading of key page failed */
6673 :
6674 : static void report_keypage_fault(HA_CHECK *param, MARIA_HA *info,
6675 : my_off_t position)
6676 0 : {
6677 : char buff[11];
6678 0 : uint32 block_size= info->s->block_size;
6679 :
6680 0 : if (my_errno == HA_ERR_CRASHED)
6681 0 : _ma_check_print_error(param,
6682 : "Wrong base information on indexpage at page: %s",
6683 : llstr(position / block_size, buff));
6684 : else
6685 0 : _ma_check_print_error(param,
6686 : "Can't read indexpage from page: %s, "
6687 : "error: %d",
6688 : llstr(position / block_size, buff), my_errno);
6689 : }
6690 :
6691 :
6692 : /**
6693 : When we want to check a table, we verify that the transaction ids of rows
6694 : and keys are not bigger than the biggest id generated by Maria so far, which
6695 : is returned by the function below.
6696 :
6697 : @note If control file is not open, 0 may be returned; to not confuse
6698 : this with a valid max trid of 0, the caller should notice that it failed to
6699 : open the control file (ma_control_file_inited() can serve for that).
6700 : */
6701 :
6702 : static TrID max_trid_in_system(void)
6703 775 : {
6704 775 : TrID id= trnman_get_max_trid(); /* 0 if transac manager not initialized */
6705 : /* 'id' may be far bigger, if last shutdown is old */
6706 775 : return max(id, max_trid_in_control_file);
6707 : }
6708 :
6709 :
6710 : static void _ma_check_print_not_visible_error(HA_CHECK *param, TrID used_trid)
6711 0 : {
6712 : char buff[22], buff2[22];
6713 0 : if (!param->not_visible_rows_found++)
6714 : {
6715 0 : if (!ma_control_file_inited())
6716 : {
6717 0 : _ma_check_print_warning(param,
6718 : "Found row with transaction id %s but no "
6719 : "maria_control_file was specified. "
6720 : "The table may be corrupted",
6721 : llstr(used_trid, buff));
6722 : }
6723 : else
6724 : {
6725 0 : _ma_check_print_error(param,
6726 : "Found row with transaction id %s when max "
6727 : "transaction id according to maria_control_file "
6728 : "is %s",
6729 : llstr(used_trid, buff),
6730 : llstr(param->max_trid, buff2));
6731 : }
6732 : }
6733 : }
6734 :
6735 :
6736 : /**
6737 : Mark that we can retry normal repair if we used quick repair
6738 :
6739 : We shouldn't do this in case of disk error as in this case we are likely
6740 : to loose much more than expected.
6741 : */
6742 :
6743 : void retry_if_quick(MARIA_SORT_PARAM *sort_param, int error)
6744 47 : {
6745 47 : HA_CHECK *param=sort_param->sort_info->param;
6746 :
6747 47 : if (!sort_param->fix_datafile && error >= HA_ERR_FIRST)
6748 : {
6749 0 : param->retry_repair=1;
6750 0 : param->testflag|=T_RETRY_WITHOUT_QUICK;
6751 : }
6752 : }
|