1 /* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
15
16 /* Describe, check and repair of MyISAM 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 byte 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 byte 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 "ftdefs.h"
44 #include <m_ctype.h>
45 #include <my_getopt.h>
46 #ifdef HAVE_SYS_VADVISE_H
47 #include <sys/vadvise.h>
48 #endif
49 #include "rt_index.h"
50 #include <mysqld_error.h>
51
52 /* Functions defined in this file */
53
54 static int check_k_link(HA_CHECK *param, MI_INFO *info,uint nr);
55 static int chk_index(HA_CHECK *, MI_INFO *, MI_KEYDEF *, my_off_t, uchar *,
56 ha_rows *, ha_checksum *, uint);
57 static uint isam_key_length(MI_INFO *info,MI_KEYDEF *keyinfo);
58 static ha_checksum calc_checksum(ha_rows count);
59 static int writekeys(MI_SORT_PARAM *sort_param);
60 static int sort_one_index(HA_CHECK *, MI_INFO *, MI_KEYDEF *, my_off_t, File);
61 static int sort_key_read(MI_SORT_PARAM *sort_param,void *key);
62 static int sort_ft_key_read(MI_SORT_PARAM *sort_param,void *key);
63 static int sort_get_next_record(MI_SORT_PARAM *sort_param);
64 static int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,const void *b);
DX10Controller()65 static int sort_ft_key_write(MI_SORT_PARAM *sort_param, const void *a);
66 static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a);
67 static my_off_t get_record_for_key(MI_INFO *, MI_KEYDEF *, uchar *);
68 static int sort_insert_key(MI_SORT_PARAM *, SORT_KEY_BLOCKS *, uchar *, my_off_t);
69 static int sort_delete_record(MI_SORT_PARAM *sort_param);
70 /*static int flush_pending_blocks(HA_CHECK *param);*/
~DX10Controller()71 static SORT_KEY_BLOCKS *alloc_key_blocks(HA_CHECK *, uint, uint);
72 static ha_checksum mi_byte_checksum(const uchar *buf, uint length);
73 static void set_data_file_type(MI_SORT_INFO *sort_info, MYISAM_SHARE *share);
74 static int replace_data_file(HA_CHECK *param, MI_INFO *info, File new_file);
75
initialize(FUnknown * context)76 void myisamchk_init(HA_CHECK *param)
77 {
78 bzero((uchar*) param,sizeof(*param));
79 /* Set all params that are not 0 */
80 param->opt_follow_links=1;
81 param->keys_in_use= ~(ulonglong) 0;
82 param->search_after_block=HA_OFFSET_ERROR;
83 param->use_buffers= KEY_BUFFER_INIT;
84 param->read_buffer_length=READ_BUFFER_INIT;
85 param->write_buffer_length=READ_BUFFER_INIT;
86 param->sort_buffer_length=SORT_BUFFER_INIT;
87 param->sort_key_blocks=BUFFERS_WHEN_SORTING;
88 param->tmpfile_createflag=O_RDWR | O_TRUNC | O_EXCL;
89 param->myf_rw=MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL);
90 param->max_record_length= LONGLONG_MAX;
91 param->key_cache_block_size= KEY_CACHE_BLOCK_SIZE;
92 param->stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
93 param->need_print_msg_lock= 0;
94 }
95
96 /* Check the status flags for the table */
97
98 int chk_status(HA_CHECK *param, register MI_INFO *info)
99 {
100 MYISAM_SHARE *share=info->s;
101
102 if (mi_is_crashed_on_repair(info))
103 mi_check_print_warning(param,
104 "Table is marked as crashed and last repair failed");
105 else if (mi_is_crashed(info))
106 mi_check_print_warning(param,
107 "Table is marked as crashed");
108 if (share->state.open_count != (uint) (info->s->global_changed ? 1 : 0))
109 {
110 /* Don't count this as a real warning, as check can correct this ! */
111 my_bool save=param->warning_printed;
112 mi_check_print_warning(param,
113 share->state.open_count==1 ?
114 "%d client is using or hasn't closed the table properly" :
115 "%d clients are using or haven't closed the table properly",
terminate()116 share->state.open_count);
117 /* If this will be fixed by the check, forget the warning */
118 if (param->testflag & T_UPDATE_STATE)
119 param->warning_printed=save;
120 }
121 return 0;
setParamNormalized(ParamID tag,ParamValue value)122 }
123
124 /* Check delete links */
125
126 int chk_del(HA_CHECK *param, register MI_INFO *info, ulonglong test_flag)
127 {
128 reg2 ha_rows i;
129 uint delete_link_length;
130 my_off_t empty,next_link,UNINIT_VAR(old_link);
131 char buff[22],buff2[22];
132 DBUG_ENTER("chk_del");
133
134 param->record_checksum=0;
135 delete_link_length=((info->s->options & HA_OPTION_PACK_RECORD) ? 20 :
136 info->s->rec_reflength+1);
137
138 if (!(test_flag & T_SILENT))
139 puts("- check record delete-chain");
140
141 next_link=info->s->state.dellink;
142 if (info->state->del == 0)
143 {
144 if (test_flag & T_VERBOSE)
145 {
146 puts("No recordlinks");
147 }
148 }
149 else
150 {
151 if (test_flag & T_VERBOSE)
152 printf("Recordlinks: ");
153 empty=0;
154 for (i= info->state->del ; i > 0L && next_link != HA_OFFSET_ERROR ; i--)
155 {
156 if (killed_ptr(param))
157 DBUG_RETURN(1);
158 if (test_flag & T_VERBOSE)
159 printf(" %9s",llstr(next_link,buff));
160 if (next_link >= info->state->data_file_length)
161 goto wrong;
162 if (mysql_file_pread(info->dfile, (uchar*) buff, delete_link_length,
163 next_link, MYF(MY_NABP)))
164 {
165 if (test_flag & T_VERBOSE) puts("");
166 mi_check_print_error(param,"Can't read delete-link at filepos: %s",
167 llstr(next_link,buff));
168 DBUG_RETURN(1);
169 }
170 if (*buff != '\0')
171 {
172 if (test_flag & T_VERBOSE) puts("");
173 mi_check_print_error(param,"Record at pos: %s is not remove-marked",
174 llstr(next_link,buff));
175 goto wrong;
176 }
177 if (info->s->options & HA_OPTION_PACK_RECORD)
178 {
179 my_off_t prev_link=mi_sizekorr(buff+12);
180 if (empty && prev_link != old_link)
181 {
182 if (test_flag & T_VERBOSE) puts("");
183 mi_check_print_error(param,"Deleted block at %s doesn't point back at previous delete link",llstr(next_link,buff2));
184 goto wrong;
185 }
186 old_link=next_link;
187 next_link=mi_sizekorr(buff+4);
188 empty+=mi_uint3korr(buff+1);
189 }
190 else
191 {
192 param->record_checksum+=(ha_checksum) next_link;
193 next_link=_mi_rec_pos(info->s,(uchar*) buff+1);
194 empty+=info->s->base.pack_reclength;
195 }
196 }
197 if (test_flag & T_VERBOSE)
198 puts("\n");
199 if (empty != info->state->empty)
200 {
201 mi_check_print_warning(param,
202 "Found %s deleted space in delete link chain. Should be %s",
203 llstr(empty,buff2),
204 llstr(info->state->empty,buff));
205 }
206 if (next_link != HA_OFFSET_ERROR)
207 {
208 mi_check_print_error(param,
209 "Found more than the expected %s deleted rows in delete link chain",
210 llstr(info->state->del, buff));
211 goto wrong;
212 }
213 if (i != 0)
214 {
215 mi_check_print_error(param,
216 "Found %s deleted rows in delete link chain. Should be %s",
217 llstr(info->state->del - i, buff2),
218 llstr(info->state->del, buff));
219 goto wrong;
220 }
221 }
222 DBUG_RETURN(0);
223
224 wrong:
225 param->testflag|=T_RETRY_WITHOUT_QUICK;
226 if (test_flag & T_VERBOSE) puts("");
227 mi_check_print_error(param,"record delete-link-chain corrupted");
228 DBUG_RETURN(1);
229 } /* chk_del */
230
231
232 /* Check delete links in index file */
233
234 static int check_k_link(HA_CHECK *param, register MI_INFO *info, uint nr)
235 {
236 my_off_t next_link;
237 uint block_size=(nr+1)*MI_MIN_KEY_BLOCK_LENGTH;
238 ha_rows records;
239 char llbuff[21], llbuff2[21];
240 uchar *buff;
241 DBUG_ENTER("check_k_link");
242 DBUG_PRINT("enter", ("block_size: %u", block_size));
243
244 if (param->testflag & T_VERBOSE)
245 printf("block_size %4u:", block_size); /* purecov: tested */
246
247 next_link=info->s->state.key_del[nr];
248 records= (ha_rows) (info->state->key_file_length / block_size);
249 while (next_link != HA_OFFSET_ERROR && records > 0)
250 {
251 if (killed_ptr(param))
252 DBUG_RETURN(1);
253 if (param->testflag & T_VERBOSE)
254 printf("%16s",llstr(next_link,llbuff));
255
256 /* Key blocks must lay within the key file length entirely. */
257 if (next_link + block_size > info->state->key_file_length)
258 {
259 /* purecov: begin tested */
260 mi_check_print_error(param, "Invalid key block position: %s "
261 "key block size: %u file_length: %s",
262 llstr(next_link, llbuff), block_size,
263 llstr(info->state->key_file_length, llbuff2));
264 DBUG_RETURN(1);
265 /* purecov: end */
266 }
267
268 /* Key blocks must be aligned at MI_MIN_KEY_BLOCK_LENGTH. */
269 if (next_link & (MI_MIN_KEY_BLOCK_LENGTH - 1))
270 {
271 /* purecov: begin tested */
272 mi_check_print_error(param, "Mis-aligned key block: %s "
273 "minimum key block length: %u",
274 llstr(next_link, llbuff), MI_MIN_KEY_BLOCK_LENGTH);
275 DBUG_RETURN(1);
276 /* purecov: end */
277 }
278
279 /*
280 Read the key block with MI_MIN_KEY_BLOCK_LENGTH to find next link.
281 If the key cache block size is smaller than block_size, we can so
282 avoid unnecessary eviction of cache block.
283 */
284 if (!(buff=key_cache_read(info->s->key_cache,
285 info->s->kfile, next_link, DFLT_INIT_HITS,
286 (uchar*) info->buff, MI_MIN_KEY_BLOCK_LENGTH,
287 MI_MIN_KEY_BLOCK_LENGTH, 1)))
288 {
289 /* purecov: begin tested */
290 mi_check_print_error(param, "key cache read error for block: %s",
291 llstr(next_link,llbuff));
292 DBUG_RETURN(1);
293 /* purecov: end */
294 }
295 next_link=mi_sizekorr(buff);
296 records--;
297 param->key_file_blocks+=block_size;
298 }
299 if (param->testflag & T_VERBOSE)
300 {
301 if (next_link != HA_OFFSET_ERROR)
302 printf("%16s\n",llstr(next_link,llbuff));
303 else
304 puts("");
305 }
306 DBUG_RETURN (next_link != HA_OFFSET_ERROR);
307 } /* check_k_link */
308
309
310 /* Check sizes of files */
311
312 int chk_size(HA_CHECK *param, register MI_INFO *info)
313 {
314 int error=0;
315 register my_off_t skr,size;
316 char buff[22],buff2[22];
317 DBUG_ENTER("chk_size");
318
319 if (!(param->testflag & T_SILENT)) puts("- check file-size");
320
321 /* The following is needed if called externally (not from myisamchk) */
322 flush_key_blocks(info->s->key_cache,
323 info->s->kfile, &info->s->dirty_part_map,
324 FLUSH_FORCE_WRITE);
325
326 size= mysql_file_seek(info->s->kfile, 0L, MY_SEEK_END, MYF(MY_THREADSAFE));
327 if ((skr=(my_off_t) info->state->key_file_length) != size)
328 {
329 /* Don't give error if file generated by myisampack */
330 if (skr > size && mi_is_any_key_active(info->s->state.key_map))
331 {
332 error=1;
333 mi_check_print_error(param,
334 "Size of indexfile is: %-8s Should be: %s",
335 llstr(size,buff), llstr(skr,buff2));
336 }
337 else
338 mi_check_print_warning(param,
339 "Size of indexfile is: %-8s Should be: %s",
340 llstr(size,buff), llstr(skr,buff2));
341 }
342 if (!(param->testflag & T_VERY_SILENT) &&
343 ! (info->s->options & HA_OPTION_COMPRESS_RECORD) &&
344 ulonglong2double(info->state->key_file_length) >
345 ulonglong2double(info->s->base.margin_key_file_length)*0.9)
346 mi_check_print_warning(param,"Keyfile is almost full, %10s of %10s used",
347 llstr(info->state->key_file_length,buff),
348 llstr(info->s->base.max_key_file_length-1,buff));
349
350 size= mysql_file_seek(info->dfile, 0L, MY_SEEK_END, MYF(0));
351 skr=(my_off_t) info->state->data_file_length;
352 if (info->s->options & HA_OPTION_COMPRESS_RECORD)
353 skr+= MEMMAP_EXTRA_MARGIN;
354 #ifdef USE_RELOC
355 if (info->data_file_type == STATIC_RECORD &&
356 skr < (my_off_t) info->s->base.reloc*info->s->base.min_pack_length)
357 skr=(my_off_t) info->s->base.reloc*info->s->base.min_pack_length;
358 #endif
359 if (skr != size)
360 {
361 info->state->data_file_length=size; /* Skip other errors */
362 if (skr > size && skr != size + MEMMAP_EXTRA_MARGIN)
363 {
364 error=1;
365 mi_check_print_error(param,"Size of datafile is: %-9s Should be: %s",
366 llstr(size,buff), llstr(skr,buff2));
367 param->testflag|=T_RETRY_WITHOUT_QUICK;
368 }
369 else
370 {
371 mi_check_print_warning(param,
372 "Size of datafile is: %-9s Should be: %s",
373 llstr(size,buff), llstr(skr,buff2));
374 }
375 }
376 if (!(param->testflag & T_VERY_SILENT) &&
377 !(info->s->options & HA_OPTION_COMPRESS_RECORD) &&
378 ulonglong2double(info->state->data_file_length) >
379 (ulonglong2double(info->s->base.max_data_file_length)*0.9))
380 mi_check_print_warning(param, "Datafile is almost full, %10s of %10s used",
381 llstr(info->state->data_file_length,buff),
382 llstr(info->s->base.max_data_file_length-1,buff2));
383 DBUG_RETURN(error);
384 } /* chk_size */
385
386
387 /* Check keys */
388
389 int chk_key(HA_CHECK *param, register MI_INFO *info)
390 {
391 uint key,found_keys=0,full_text_keys=0,result=0;
392 ha_rows keys;
393 ha_checksum old_record_checksum,init_checksum;
394 my_off_t all_keydata,all_totaldata,key_totlength,length;
395 ulong *rec_per_key_part;
396 MYISAM_SHARE *share=info->s;
397 MI_KEYDEF *keyinfo;
398 char buff[22],buff2[22];
399 DBUG_ENTER("chk_key");
400
401 if (!(param->testflag & T_SILENT))
402 puts("- check key delete-chain");
403
404 param->key_file_blocks=info->s->base.keystart;
405 for (key=0 ; key < info->s->state.header.max_block_size_index ; key++)
406 if (check_k_link(param,info,key))
407 {
408 if (param->testflag & T_VERBOSE) puts("");
409 mi_check_print_error(param,"key delete-link-chain corrupted");
410 DBUG_RETURN(-1);
411 }
412
413 if (!(param->testflag & T_SILENT)) puts("- check index reference");
414
415 all_keydata=all_totaldata=key_totlength=0;
416 old_record_checksum=0;
417 init_checksum=param->record_checksum;
418 if (!(share->options &
419 (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
420 old_record_checksum=calc_checksum(info->state->records+info->state->del-1)*
421 share->base.pack_reclength;
422 rec_per_key_part= param->rec_per_key_part;
423 for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
424 rec_per_key_part+=keyinfo->keysegs, key++, keyinfo++)
425 {
426 param->key_crc[key]=0;
427 if (! mi_is_key_active(share->state.key_map, key))
428 {
429 /* Remember old statistics for key */
430 memcpy((char*) rec_per_key_part,
431 (char*) (share->state.rec_per_key_part +
432 (uint) (rec_per_key_part - param->rec_per_key_part)),
433 keyinfo->keysegs*sizeof(*rec_per_key_part));
434 continue;
435 }
436 found_keys++;
437
438 param->record_checksum=init_checksum;
439
440 bzero((char*) ¶m->unique_count,sizeof(param->unique_count));
441 bzero((char*) ¶m->notnull_count,sizeof(param->notnull_count));
442
443 if ((!(param->testflag & T_SILENT)))
444 printf ("- check data record references index: %d\n",key+1);
445 if (keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL))
446 full_text_keys++;
447 if (share->state.key_root[key] == HA_OFFSET_ERROR &&
448 (info->state->records == 0 || keyinfo->flag & HA_FULLTEXT))
449 goto do_stat;
450 if (!_mi_fetch_keypage(info,keyinfo,share->state.key_root[key],
451 DFLT_INIT_HITS,info->buff,0))
452 {
453 mi_check_print_error(param,"Can't read indexpage from filepos: %s",
454 llstr(share->state.key_root[key],buff));
455 if (!(param->testflag & T_INFO))
456 DBUG_RETURN(-1);
457 result= -1;
458 continue;
459 }
460 param->key_file_blocks+=keyinfo->block_length;
461 keys=0;
462 param->keydata=param->totaldata=0;
463 param->key_blocks=0;
464 param->max_level=0;
465 if (chk_index(param,info,keyinfo,share->state.key_root[key],info->buff,
466 &keys, param->key_crc+key,1))
467 DBUG_RETURN(-1);
468 if(!(keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL)))
469 {
470 if (keys != info->state->records)
471 {
472 mi_check_print_error(param,"Found %s keys of %s",llstr(keys,buff),
473 llstr(info->state->records,buff2));
474 if (!(param->testflag & T_INFO))
475 DBUG_RETURN(-1);
476 result= -1;
477 continue;
478 }
479 if (found_keys - full_text_keys == 1 &&
480 ((share->options &
481 (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
482 (param->testflag & T_DONT_CHECK_CHECKSUM)))
483 old_record_checksum=param->record_checksum;
484 else if (old_record_checksum != param->record_checksum)
485 {
486 if (key)
487 mi_check_print_error(param,"Key %u doesn't point at same records that key 1",
488 key+1);
489 else
490 mi_check_print_error(param,"Key 1 doesn't point at all records");
491 if (!(param->testflag & T_INFO))
492 DBUG_RETURN(-1);
493 result= -1;
494 continue;
495 }
496 }
497 if ((uint) share->base.auto_key -1 == key)
498 {
499 /* Check that auto_increment key is bigger than max key value */
500 ulonglong auto_increment;
501 info->lastinx=key;
502 _mi_read_key_record(info, 0L, info->rec_buff);
503 auto_increment= retrieve_auto_increment(info, info->rec_buff);
504 if (auto_increment > info->s->state.auto_increment)
505 {
506 mi_check_print_warning(param, "Auto-increment value: %s is smaller "
507 "than max used value: %s",
508 llstr(info->s->state.auto_increment,buff2),
509 llstr(auto_increment, buff));
510 }
511 if (param->testflag & T_AUTO_INC)
512 {
513 set_if_bigger(info->s->state.auto_increment,
514 auto_increment);
515 set_if_bigger(info->s->state.auto_increment,
516 param->auto_increment_value);
517 }
518
519 /* Check that there isn't a row with auto_increment = 0 in the table */
520 mi_extra(info,HA_EXTRA_KEYREAD,0);
521 bzero(info->lastkey,keyinfo->seg->length);
522 if (!mi_rkey(info, info->rec_buff, key, (const uchar*) info->lastkey,
523 (key_part_map)1, HA_READ_KEY_EXACT))
524 {
525 /* Don't count this as a real warning, as myisamchk can't correct it */
526 my_bool save=param->warning_printed;
527 mi_check_print_warning(param, "Found row where the auto_increment "
528 "column has the value 0");
529 param->warning_printed=save;
530 }
531 mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
532 }
533
534 length=(my_off_t) isam_key_length(info,keyinfo)*keys + param->key_blocks*2;
535 if (param->testflag & T_INFO && param->totaldata != 0L && keys != 0L)
536 printf("Key: %2d: Keyblocks used: %3d%% Packed: %4d%% Max levels: %2d\n",
537 key+1,
538 (int) (my_off_t2double(param->keydata)*100.0/my_off_t2double(param->totaldata)),
539 (int) ((my_off_t2double(length) - my_off_t2double(param->keydata))*100.0/
540 my_off_t2double(length)),
541 param->max_level);
542 all_keydata+=param->keydata; all_totaldata+=param->totaldata; key_totlength+=length;
543
544 do_stat:
545 if (param->testflag & T_STATISTICS)
546 update_key_parts(keyinfo, rec_per_key_part, param->unique_count,
547 param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
548 param->notnull_count: NULL,
549 (ulonglong)info->state->records);
550 }
551 if (param->testflag & T_INFO)
552 {
553 if (all_totaldata != 0L && found_keys > 0)
554 printf("Total: Keyblocks used: %3d%% Packed: %4d%%\n\n",
555 (int) (my_off_t2double(all_keydata)*100.0/
556 my_off_t2double(all_totaldata)),
557 (int) ((my_off_t2double(key_totlength) -
558 my_off_t2double(all_keydata))*100.0/
559 my_off_t2double(key_totlength)));
560 else if (all_totaldata != 0L && mi_is_any_key_active(share->state.key_map))
561 puts("");
562 }
563 if (param->key_file_blocks != info->state->key_file_length &&
564 param->keys_in_use != ~(ulonglong) 0)
565 mi_check_print_warning(param, "Some data are unreferenced in keyfile");
566 if (found_keys != full_text_keys)
567 param->record_checksum=old_record_checksum-init_checksum; /* Remove delete links */
568 else
569 param->record_checksum=0;
570 DBUG_RETURN(result);
571 } /* chk_key */
572
573
574 static int chk_index_down(HA_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
575 my_off_t page, uchar *buff, ha_rows *keys,
576 ha_checksum *key_checksum, uint level)
577 {
578 char llbuff[22],llbuff2[22];
579 DBUG_ENTER("chk_index_down");
580
581 /* Key blocks must lay within the key file length entirely. */
582 if (page + keyinfo->block_length > info->state->key_file_length)
583 {
584 /* purecov: begin tested */
585 /* Give it a chance to fit in the real file size. */
586 my_off_t max_length= mysql_file_seek(info->s->kfile, 0L, MY_SEEK_END,
587 MYF(MY_THREADSAFE));
588 mi_check_print_error(param, "Invalid key block position: %s "
589 "key block size: %u file_length: %s",
590 llstr(page, llbuff), keyinfo->block_length,
591 llstr(info->state->key_file_length, llbuff2));
592 if (page + keyinfo->block_length > max_length)
593 goto err;
594 /* Fix the remebered key file length. */
595 info->state->key_file_length= (max_length &
596 ~ (my_off_t) (keyinfo->block_length - 1));
597 /* purecov: end */
598 }
599
600 /* Key blocks must be aligned at MI_MIN_KEY_BLOCK_LENGTH. */
601 if (page & (MI_MIN_KEY_BLOCK_LENGTH - 1))
602 {
603 /* purecov: begin tested */
604 mi_check_print_error(param, "Mis-aligned key block: %s "
605 "minimum key block length: %u",
606 llstr(page, llbuff), MI_MIN_KEY_BLOCK_LENGTH);
607 goto err;
608 /* purecov: end */
609 }
610
611 if (!_mi_fetch_keypage(info,keyinfo,page, DFLT_INIT_HITS,buff,0))
612 {
613 mi_check_print_error(param,"Can't read key from filepos: %s",
614 llstr(page,llbuff));
615 goto err;
616 }
617 param->key_file_blocks+=keyinfo->block_length;
618 if (chk_index(param,info,keyinfo,page,buff,keys,key_checksum,level))
619 goto err;
620
621 DBUG_RETURN(0);
622
623 /* purecov: begin tested */
624 err:
625 DBUG_RETURN(1);
626 /* purecov: end */
627 }
628
629
630 /*
631 "Ignore NULLs" statistics collection method: process first index tuple.
632
633 SYNOPSIS
634 mi_collect_stats_nonulls_first()
635 keyseg IN Array of key part descriptions
636 notnull INOUT Array, notnull[i] = (number of {keypart1...keypart_i}
637 tuples that don't contain NULLs)
638 key IN Key values tuple
639
640 DESCRIPTION
641 Process the first index tuple - find out which prefix tuples don't
642 contain NULLs, and update the array of notnull counters accordingly.
643 */
644
645 static
646 void mi_collect_stats_nonulls_first(HA_KEYSEG *keyseg, ulonglong *notnull,
647 uchar *key)
648 {
649 uint first_null, kp;
650 first_null= (uint) (ha_find_null(keyseg, key) - keyseg);
651 /*
652 All prefix tuples that don't include keypart_{first_null} are not-null
653 tuples (and all others aren't), increment counters for them.
654 */
655 for (kp= 0; kp < first_null; kp++)
656 notnull[kp]++;
657 }
658
659
660 /*
661 "Ignore NULLs" statistics collection method: process next index tuple.
662
663 SYNOPSIS
664 mi_collect_stats_nonulls_next()
665 keyseg IN Array of key part descriptions
666 notnull INOUT Array, notnull[i] = (number of {keypart1...keypart_i}
667 tuples that don't contain NULLs)
668 prev_key IN Previous key values tuple
669 last_key IN Next key values tuple
670
671 DESCRIPTION
672 Process the next index tuple:
673 1. Find out which prefix tuples of last_key don't contain NULLs, and
674 update the array of notnull counters accordingly.
675 2. Find the first keypart number where the prev_key and last_key tuples
676 are different(A), or last_key has NULL value(B), and return it, so the
677 caller can count number of unique tuples for each key prefix. We don't
678 need (B) to be counted, and that is compensated back in
679 update_key_parts().
680
681 RETURN
682 1 + number of first keypart where values differ or last_key tuple has NULL
683 */
684
685 static
686 int mi_collect_stats_nonulls_next(HA_KEYSEG *keyseg, ulonglong *notnull,
687 uchar *prev_key, uchar *last_key)
688 {
689 uint diffs[2];
690 uint first_null_seg, kp;
691 HA_KEYSEG *seg;
692
693 /*
694 Find the first keypart where values are different or either of them is
695 NULL. We get results in diffs array:
696 diffs[0]= 1 + number of first different keypart
697 diffs[1]=offset: (last_key + diffs[1]) points to first value in
698 last_key that is NULL or different from corresponding
699 value in prev_key.
700 */
701 ha_key_cmp(keyseg, prev_key, last_key, USE_WHOLE_KEY,
702 SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diffs);
703 seg= keyseg + diffs[0] - 1;
704
705 /* Find first NULL in last_key */
706 first_null_seg= (uint) (ha_find_null(seg, last_key + diffs[1]) - keyseg);
707 for (kp= 0; kp < first_null_seg; kp++)
708 notnull[kp]++;
709
710 /*
711 Return 1+ number of first key part where values differ. Don't care if
712 these were NULLs and not .... We compensate for that in
713 update_key_parts.
714 */
715 return diffs[0];
716 }
717
718
719 /* Check if index is ok */
720
721 static int chk_index(HA_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
722 my_off_t page, uchar *buff, ha_rows *keys,
723 ha_checksum *key_checksum, uint level)
724 {
725 int flag;
726 uint used_length,comp_flag,nod_flag,key_length=0;
727 uchar key[HA_MAX_POSSIBLE_KEY_BUFF],*temp_buff,*keypos,*old_keypos,*endpos;
728 my_off_t next_page,record;
729 char llbuff[22];
730 uint diff_pos[2];
731 DBUG_ENTER("chk_index");
732 DBUG_DUMP("buff",(uchar*) buff,mi_getint(buff));
733
734 /* TODO: implement appropriate check for RTree keys */
735 if (keyinfo->flag & HA_SPATIAL)
736 DBUG_RETURN(0);
737
738 if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
739 {
740 mi_check_print_error(param,"Not enough memory for keyblock");
741 DBUG_RETURN(-1);
742 }
743
744 if (keyinfo->flag & HA_NOSAME)
745 {
746 /* Not real duplicates */
747 comp_flag= SEARCH_FIND | SEARCH_UPDATE | SEARCH_INSERT;
748 }
749 else
750 comp_flag=SEARCH_SAME; /* Keys in positionorder */
751 nod_flag=mi_test_if_nod(buff);
752 used_length=mi_getint(buff);
753 keypos=buff+2+nod_flag;
754 endpos=buff+used_length;
755
756 param->keydata+=used_length; param->totaldata+=keyinfo->block_length; /* INFO */
757 param->key_blocks++;
758 if (level > param->max_level)
759 param->max_level=level;
760
761 if (used_length > keyinfo->block_length)
762 {
763 mi_check_print_error(param,"Wrong pageinfo at page: %s",
764 llstr(page,llbuff));
765 goto err;
766 }
767 for ( ;; )
768 {
769 if (killed_ptr(param))
770 goto err;
771 memcpy((char*) info->lastkey,(char*) key,key_length);
772 info->lastkey_length=key_length;
773 if (nod_flag)
774 {
775 next_page=_mi_kpos(nod_flag,keypos);
776 if (chk_index_down(param,info,keyinfo,next_page,
777 temp_buff,keys,key_checksum,level+1))
778 goto err;
779 }
780 old_keypos=keypos;
781 if (keypos >= endpos ||
782 (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0)
783 break;
784 if (keypos > endpos)
785 {
786 mi_check_print_error(param,"Wrong key block length at page: %s",llstr(page,llbuff));
787 goto err;
788 }
789 if ((*keys)++ &&
790 (flag=ha_key_cmp(keyinfo->seg,info->lastkey,key,key_length,
791 comp_flag, diff_pos)) >=0)
792 {
793 DBUG_DUMP("old",info->lastkey, info->lastkey_length);
794 DBUG_DUMP("new",key, key_length);
795 DBUG_DUMP("new_in_page",old_keypos,(uint) (keypos-old_keypos));
796
797 if (comp_flag & SEARCH_FIND && flag == 0)
798 mi_check_print_error(param,"Found duplicated key at page %s",llstr(page,llbuff));
799 else
800 mi_check_print_error(param,"Key in wrong position at page %s",llstr(page,llbuff));
801 goto err;
802 }
803 if (param->testflag & T_STATISTICS)
804 {
805 if (*keys != 1L) /* not first_key */
806 {
807 if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL)
808 ha_key_cmp(keyinfo->seg,info->lastkey,key,USE_WHOLE_KEY,
809 SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL,
810 diff_pos);
811 else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
812 {
813 diff_pos[0]= mi_collect_stats_nonulls_next(keyinfo->seg,
814 param->notnull_count,
815 info->lastkey, key);
816 }
817 param->unique_count[diff_pos[0]-1]++;
818 }
819 else
820 {
821 if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
822 mi_collect_stats_nonulls_first(keyinfo->seg, param->notnull_count,
823 key);
824 }
825 }
826 (*key_checksum)+= mi_byte_checksum((uchar*) key,
827 key_length- info->s->rec_reflength);
828 record= _mi_dpos(info,0,key+key_length);
829 if (keyinfo->flag & HA_FULLTEXT) /* special handling for ft2 */
830 {
831 uint off;
832 int subkeys;
833 get_key_full_length_rdonly(off, key);
834 subkeys=ft_sintXkorr(key+off);
835 if (subkeys < 0)
836 {
837 ha_rows tmp_keys=0;
838 if (chk_index_down(param,info,&info->s->ft2_keyinfo,record,
839 temp_buff,&tmp_keys,key_checksum,1))
840 goto err;
841 if (tmp_keys + subkeys)
842 {
843 mi_check_print_error(param,
844 "Number of words in the 2nd level tree "
845 "does not match the number in the header. "
846 "Parent word in on the page %s, offset %u",
847 llstr(page,llbuff), (uint) (old_keypos-buff));
848 goto err;
849 }
850 (*keys)+=tmp_keys-1;
851 continue;
852 }
853 /* fall through */
854 }
855 if (record >= info->state->data_file_length)
856 {
857 #ifndef DBUG_OFF
858 char llbuff2[22], llbuff3[22];
859 #endif
860 mi_check_print_error(param,"Found key at page %s that points to record outside datafile",llstr(page,llbuff));
861 DBUG_PRINT("test",("page: %s record: %s filelength: %s",
862 llstr(page,llbuff),llstr(record,llbuff2),
863 llstr(info->state->data_file_length,llbuff3)));
864 DBUG_DUMP("key",key,key_length);
865 DBUG_DUMP("new_in_page",old_keypos,(uint) (keypos-old_keypos));
866 goto err;
867 }
868 param->record_checksum+=(ha_checksum) record;
869 }
870 if (keypos != endpos)
871 {
872 mi_check_print_error(param,"Keyblock size at page %s is not correct. Block length: %d key length: %d",
873 llstr(page,llbuff), used_length, (keypos - buff));
874 goto err;
875 }
876 my_afree((uchar*) temp_buff);
877 DBUG_RETURN(0);
878 err:
879 my_afree((uchar*) temp_buff);
880 DBUG_RETURN(1);
881 } /* chk_index */
882
883
884 /* Calculate a checksum of 1+2+3+4...N = N*(N+1)/2 without overflow */
885
886 static ha_checksum calc_checksum(ha_rows count)
887 {
888 ulonglong sum,a,b;
889 DBUG_ENTER("calc_checksum");
890
891 sum=0;
892 a=count; b=count+1;
893 if (a & 1)
894 b>>=1;
895 else
896 a>>=1;
897 while (b)
898 {
899 if (b & 1)
900 sum+=a;
901 a<<=1; b>>=1;
902 }
903 DBUG_PRINT("exit",("sum: %lx",(ulong) sum));
904 DBUG_RETURN((ha_checksum) sum);
905 } /* calc_checksum */
906
907
908 /* Calc length of key in normal isam */
909
910 static uint isam_key_length(MI_INFO *info, register MI_KEYDEF *keyinfo)
911 {
912 uint length;
913 HA_KEYSEG *keyseg;
914 DBUG_ENTER("isam_key_length");
915
916 length= info->s->rec_reflength;
917 for (keyseg=keyinfo->seg ; keyseg->type ; keyseg++)
918 length+= keyseg->length;
919
920 DBUG_PRINT("exit",("length: %d",length));
921 DBUG_RETURN(length);
922 } /* key_length */
923
924
925 /* Check that record-link is ok */
926
927 int chk_data_link(HA_CHECK *param, MI_INFO *info, my_bool extend)
928 {
929 int error,got_error,flag;
930 uint key, UNINIT_VAR(left_length), b_type;
931 ha_rows records,del_blocks;
932 my_off_t used,empty,pos,splits,UNINIT_VAR(start_recpos),
933 del_length,link_used,start_block;
934 uchar *record= 0, *UNINIT_VAR(to);
935 char llbuff[22],llbuff2[22],llbuff3[22];
936 ha_checksum intern_record_checksum;
937 ha_checksum key_checksum[HA_MAX_POSSIBLE_KEY];
938 MI_KEYDEF *keyinfo;
939 MI_BLOCK_INFO block_info;
940 DBUG_ENTER("chk_data_link");
941
942 if (!(param->testflag & T_SILENT))
943 {
944 if (extend)
945 puts("- check records and index references");
946 else
947 puts("- check record links");
948 }
949
950 if (!mi_alloc_rec_buff(info, -1, &record))
951 {
952 mi_check_print_error(param,"Not enough memory for record");
953 DBUG_RETURN(-1);
954 }
955 records=del_blocks=0;
956 used=link_used=splits=del_length=0;
957 intern_record_checksum=param->glob_crc=0;
958 got_error=error=0;
959 empty=info->s->pack.header_length;
960
961 pos=my_b_tell(¶m->read_cache);
962 bzero((char*) key_checksum, info->s->base.keys * sizeof(key_checksum[0]));
963 while (pos < info->state->data_file_length)
964 {
965 if (killed_ptr(param))
966 goto err2;
967 switch (info->s->data_file_type) {
968 case STATIC_RECORD:
969 if (my_b_read(¶m->read_cache,(uchar*) record,
970 info->s->base.pack_reclength))
971 goto err;
972 start_recpos=pos;
973 pos+=info->s->base.pack_reclength;
974 splits++;
975 if (*record == '\0')
976 {
977 del_blocks++;
978 del_length+=info->s->base.pack_reclength;
979 continue; /* Record removed */
980 }
981 param->glob_crc+= (*info->s->calc_check_checksum)(info,record);
982 used+=info->s->base.pack_reclength;
983 break;
984 case DYNAMIC_RECORD:
985 flag=block_info.second_read=0;
986 block_info.next_filepos=pos;
987 do
988 {
989 if (_mi_read_cache(¶m->read_cache,(uchar*) block_info.header,
990 (start_block=block_info.next_filepos),
991 sizeof(block_info.header),
992 (flag ? 0 : READING_NEXT) | READING_HEADER))
993 goto err;
994 if (start_block & (MI_DYN_ALIGN_SIZE-1))
995 {
996 mi_check_print_error(param,"Wrong aligned block at %s",
997 llstr(start_block,llbuff));
998 goto err2;
999 }
1000 b_type=_mi_get_block_info(&block_info,-1,start_block);
1001 if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
1002 BLOCK_FATAL_ERROR))
1003 {
1004 if (b_type & BLOCK_SYNC_ERROR)
1005 {
1006 if (flag)
1007 {
1008 mi_check_print_error(param,"Unexpected byte: %d at link: %s",
1009 (int) block_info.header[0],
1010 llstr(start_block,llbuff));
1011 goto err2;
1012 }
1013 pos=block_info.filepos+block_info.block_len;
1014 goto next;
1015 }
1016 if (b_type & BLOCK_DELETED)
1017 {
1018 if (block_info.block_len < info->s->base.min_block_length)
1019 {
1020 mi_check_print_error(param,
1021 "Deleted block with impossible length %lu at %s",
1022 block_info.block_len,llstr(pos,llbuff));
1023 goto err2;
1024 }
1025 if ((block_info.next_filepos != HA_OFFSET_ERROR &&
1026 block_info.next_filepos >= info->state->data_file_length) ||
1027 (block_info.prev_filepos != HA_OFFSET_ERROR &&
1028 block_info.prev_filepos >= info->state->data_file_length))
1029 {
1030 mi_check_print_error(param,"Delete link points outside datafile at %s",
1031 llstr(pos,llbuff));
1032 goto err2;
1033 }
1034 del_blocks++;
1035 del_length+=block_info.block_len;
1036 pos=block_info.filepos+block_info.block_len;
1037 splits++;
1038 goto next;
1039 }
1040 mi_check_print_error(param,"Wrong bytesec: %d-%d-%d at linkstart: %s",
1041 block_info.header[0],block_info.header[1],
1042 block_info.header[2],
1043 llstr(start_block,llbuff));
1044 goto err2;
1045 }
1046 if (info->state->data_file_length < block_info.filepos+
1047 block_info.block_len)
1048 {
1049 mi_check_print_error(param,
1050 "Recordlink that points outside datafile at %s",
1051 llstr(pos,llbuff));
1052 got_error=1;
1053 break;
1054 }
1055 splits++;
1056 if (!flag++) /* First block */
1057 {
1058 start_recpos=pos;
1059 pos=block_info.filepos+block_info.block_len;
1060 if (block_info.rec_len > (uint) info->s->base.max_pack_length)
1061 {
1062 mi_check_print_error(param,"Found too long record (%lu) at %s",
1063 (ulong) block_info.rec_len,
1064 llstr(start_recpos,llbuff));
1065 got_error=1;
1066 break;
1067 }
1068 if (info->s->base.blobs)
1069 {
1070 if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
1071 &info->rec_buff)))
1072 {
1073 mi_check_print_error(param,
1074 "Not enough memory (%lu) for blob at %s",
1075 (ulong) block_info.rec_len,
1076 llstr(start_recpos,llbuff));
1077 got_error=1;
1078 break;
1079 }
1080 }
1081 else
1082 to= info->rec_buff;
1083 left_length=block_info.rec_len;
1084 }
1085 if (left_length < block_info.data_len)
1086 {
1087 mi_check_print_error(param,"Found too long record (%lu) at %s",
1088 (ulong) block_info.data_len,
1089 llstr(start_recpos,llbuff));
1090 got_error=1;
1091 break;
1092 }
1093 if (_mi_read_cache(¶m->read_cache,(uchar*) to,block_info.filepos,
1094 (uint) block_info.data_len,
1095 flag == 1 ? READING_NEXT : 0))
1096 goto err;
1097 to+=block_info.data_len;
1098 link_used+= block_info.filepos-start_block;
1099 used+= block_info.filepos - start_block + block_info.data_len;
1100 empty+=block_info.block_len-block_info.data_len;
1101 left_length-=block_info.data_len;
1102 if (left_length)
1103 {
1104 if (b_type & BLOCK_LAST)
1105 {
1106 mi_check_print_error(param,
1107 "Wrong record length %s of %s at %s",
1108 llstr(block_info.rec_len-left_length,llbuff),
1109 llstr(block_info.rec_len, llbuff2),
1110 llstr(start_recpos,llbuff3));
1111 got_error=1;
1112 break;
1113 }
1114 if (info->state->data_file_length < block_info.next_filepos)
1115 {
1116 mi_check_print_error(param,
1117 "Found next-recordlink that points outside datafile at %s",
1118 llstr(block_info.filepos,llbuff));
1119 got_error=1;
1120 break;
1121 }
1122 }
1123 } while (left_length);
1124 if (! got_error)
1125 {
1126 if (_mi_rec_unpack(info,record,info->rec_buff,block_info.rec_len) ==
1127 MY_FILE_ERROR)
1128 {
1129 mi_check_print_error(param,"Found wrong record at %s",
1130 llstr(start_recpos,llbuff));
1131 got_error=1;
1132 }
1133 else
1134 {
1135 info->checksum= (*info->s->calc_check_checksum)(info,record);
1136 if (param->testflag & (T_EXTEND | T_MEDIUM | T_VERBOSE))
1137 {
1138 if (_mi_rec_check(info,record, info->rec_buff,block_info.rec_len,
1139 MY_TEST(info->s->calc_checksum)))
1140 {
1141 mi_check_print_error(param,"Found wrong packed record at %s",
1142 llstr(start_recpos,llbuff));
1143 got_error=1;
1144 }
1145 }
1146 if (!got_error)
1147 param->glob_crc+= info->checksum;
1148 }
1149 }
1150 else if (!flag)
1151 pos=block_info.filepos+block_info.block_len;
1152 break;
1153 case COMPRESSED_RECORD:
1154 if (_mi_read_cache(¶m->read_cache,(uchar*) block_info.header, pos,
1155 info->s->pack.ref_length, READING_NEXT))
1156 goto err;
1157 start_recpos=pos;
1158 splits++;
1159 (void) _mi_pack_get_block_info(info, &info->bit_buff, &block_info,
1160 &info->rec_buff, -1, start_recpos);
1161 pos=block_info.filepos+block_info.rec_len;
1162 if (block_info.rec_len < (uint) info->s->min_pack_length ||
1163 block_info.rec_len > (uint) info->s->max_pack_length)
1164 {
1165 mi_check_print_error(param,
1166 "Found block with wrong recordlength: %d at %s",
1167 block_info.rec_len, llstr(start_recpos,llbuff));
1168 got_error=1;
1169 break;
1170 }
1171 if (_mi_read_cache(¶m->read_cache,(uchar*) info->rec_buff,
1172 block_info.filepos, block_info.rec_len, READING_NEXT))
1173 goto err;
1174 if (_mi_pack_rec_unpack(info, &info->bit_buff, record,
1175 info->rec_buff, block_info.rec_len))
1176 {
1177 mi_check_print_error(param,"Found wrong record at %s",
1178 llstr(start_recpos,llbuff));
1179 got_error=1;
1180 }
1181 param->glob_crc+= (*info->s->calc_check_checksum)(info,record);
1182 link_used+= (block_info.filepos - start_recpos);
1183 used+= (pos-start_recpos);
1184 break;
1185 default:
1186 DBUG_ASSERT(0); /* Impossible */
1187 break;
1188 } /* switch */
1189 if (param->fix_record)
1190 param->fix_record(info, record, -1);
1191 if (! got_error)
1192 {
1193 intern_record_checksum+=(ha_checksum) start_recpos;
1194 records++;
1195 if (param->testflag & T_WRITE_LOOP && records % WRITE_COUNT == 0)
1196 {
1197 printf("%s\r", llstr(records,llbuff)); (void) fflush(stdout);
1198 }
1199
1200 /* Check if keys match the record */
1201
1202 for (key=0,keyinfo= info->s->keyinfo; key < info->s->base.keys;
1203 key++,keyinfo++)
1204 {
1205 if (mi_is_key_active(info->s->state.key_map, key))
1206 {
1207 if(!(keyinfo->flag & HA_FULLTEXT))
1208 {
1209 uint key_length=_mi_make_key(info,key,info->lastkey,record,
1210 start_recpos);
1211 if (extend)
1212 {
1213 /* We don't need to lock the key tree here as we don't allow
1214 concurrent threads when running myisamchk
1215 */
1216 int search_result=
1217 #ifdef HAVE_RTREE_KEYS
1218 (keyinfo->flag & HA_SPATIAL) ?
1219 rtree_find_first(info, key, info->lastkey, key_length,
1220 MBR_EQUAL | MBR_DATA) :
1221 #endif
1222 _mi_search(info,keyinfo,info->lastkey,key_length,
1223 SEARCH_SAME, info->s->state.key_root[key]);
1224 if (search_result)
1225 {
1226 mi_check_print_error(param,"Record at: %10s "
1227 "Can't find key for index: %2d",
1228 llstr(start_recpos,llbuff),key+1);
1229 if (error++ > MAXERR || !(param->testflag & T_VERBOSE))
1230 goto err2;
1231 }
1232 }
1233 else
1234 key_checksum[key]+=mi_byte_checksum((uchar*) info->lastkey,
1235 key_length);
1236 }
1237 }
1238 }
1239 }
1240 else
1241 {
1242 got_error=0;
1243 if (error++ > MAXERR || !(param->testflag & T_VERBOSE))
1244 goto err2;
1245 }
1246 next:; /* Next record */
1247 }
1248 if (param->testflag & T_WRITE_LOOP)
1249 {
1250 (void) fputs(" \r",stdout); (void) fflush(stdout);
1251 }
1252 if (records != info->state->records)
1253 {
1254 mi_check_print_error(param,"Record-count is not ok; is %-10s Should be: %s",
1255 llstr(records,llbuff), llstr(info->state->records,llbuff2));
1256 error=1;
1257 }
1258 else if (param->record_checksum &&
1259 param->record_checksum != intern_record_checksum)
1260 {
1261 mi_check_print_error(param,
1262 "Keypointers and record positions doesn't match");
1263 error=1;
1264 }
1265 else if (param->glob_crc != info->state->checksum &&
1266 (info->s->options &
1267 (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)))
1268 {
1269 mi_check_print_warning(param,
1270 "Record checksum is not the same as checksum stored in the index file\n");
1271 error=1;
1272 }
1273 else if (!extend)
1274 {
1275 for (key=0 ; key < info->s->base.keys; key++)
1276 {
1277 if (key_checksum[key] != param->key_crc[key] &&
1278 !(info->s->keyinfo[key].flag & (HA_FULLTEXT | HA_SPATIAL)))
1279 {
1280 mi_check_print_error(param,"Checksum for key: %2d doesn't match checksum for records",
1281 key+1);
1282 error=1;
1283 }
1284 }
1285 }
1286
1287 if (del_length != info->state->empty)
1288 {
1289 mi_check_print_warning(param,
1290 "Found %s deleted space. Should be %s",
1291 llstr(del_length,llbuff2),
1292 llstr(info->state->empty,llbuff));
1293 }
1294 if (used+empty+del_length != info->state->data_file_length)
1295 {
1296 mi_check_print_warning(param,
1297 "Found %s record-data and %s unused data and %s deleted-data",
1298 llstr(used,llbuff),llstr(empty,llbuff2),
1299 llstr(del_length,llbuff3));
1300 mi_check_print_warning(param,
1301 "Total %s, Should be: %s",
1302 llstr((used+empty+del_length),llbuff),
1303 llstr(info->state->data_file_length,llbuff2));
1304 }
1305 if (del_blocks != info->state->del)
1306 {
1307 mi_check_print_warning(param,
1308 "Found %10s deleted blocks Should be: %s",
1309 llstr(del_blocks,llbuff),
1310 llstr(info->state->del,llbuff2));
1311 }
1312 if (splits != info->s->state.split)
1313 {
1314 mi_check_print_warning(param,
1315 "Found %10s key parts. Should be: %s",
1316 llstr(splits,llbuff),
1317 llstr(info->s->state.split,llbuff2));
1318 }
1319 if (param->testflag & T_INFO)
1320 {
1321 if (param->warning_printed || param->error_printed || param->note_printed)
1322 puts("");
1323 if (used != 0 && ! param->error_printed)
1324 {
1325 printf("Records:%18s M.recordlength:%9lu Packed:%14.0f%%\n",
1326 llstr(records,llbuff), (long)((used-link_used)/records),
1327 (info->s->base.blobs ? 0.0 :
1328 (ulonglong2double((ulonglong) info->s->base.reclength*records)-
1329 my_off_t2double(used))/
1330 ulonglong2double((ulonglong) info->s->base.reclength*records)*100.0));
1331 printf("Recordspace used:%9.0f%% Empty space:%12d%% Blocks/Record: %6.2f\n",
1332 (ulonglong2double(used-link_used)/ulonglong2double(used-link_used+empty)*100.0),
1333 (!records ? 100 : (int) (ulonglong2double(del_length+empty)/
1334 my_off_t2double(used)*100.0)),
1335 ulonglong2double(splits - del_blocks) / records);
1336 }
1337 printf("Record blocks:%12s Delete blocks:%10s\n",
1338 llstr(splits-del_blocks,llbuff),llstr(del_blocks,llbuff2));
1339 printf("Record data: %12s Deleted data: %10s\n",
1340 llstr(used-link_used,llbuff),llstr(del_length,llbuff2));
1341 printf("Lost space: %12s Linkdata: %10s\n",
1342 llstr(empty,llbuff),llstr(link_used,llbuff2));
1343 }
1344 my_free(mi_get_rec_buff_ptr(info, record));
1345 DBUG_RETURN (error);
1346 err:
1347 mi_check_print_error(param,"got error: %d when reading datafile at record: %s",my_errno, llstr(records,llbuff));
1348 err2:
1349 my_free(mi_get_rec_buff_ptr(info, record));
1350 param->testflag|=T_RETRY_WITHOUT_QUICK;
1351 DBUG_RETURN(1);
1352 } /* chk_data_link */
1353
1354
1355 /**
1356 @brief Drop all indexes
1357
1358 @param[in] param check parameters
1359 @param[in] info MI_INFO handle
1360 @param[in] force if to force drop all indexes
1361
1362 @return status
1363 @retval 0 OK
1364 @retval != 0 Error
1365
1366 @note
1367 Once allocated, index blocks remain part of the key file forever.
1368 When indexes are disabled, no block is freed. When enabling indexes,
1369 no block is freed either. The new indexes are create from new
1370 blocks. (Bug #4692)
1371
1372 Before recreating formerly disabled indexes, the unused blocks
1373 must be freed. There are two options to do this:
1374 - Follow the tree of disabled indexes, add all blocks to the
1375 deleted blocks chain. Would require a lot of random I/O.
1376 - Drop all blocks by clearing all index root pointers and all
1377 delete chain pointers and resetting key_file_length to the end
1378 of the index file header. This requires to recreate all indexes,
1379 even those that may still be intact.
1380 The second method is probably faster in most cases.
1381
1382 When disabling indexes, MySQL disables either all indexes or all
1383 non-unique indexes. When MySQL [re-]enables disabled indexes
1384 (T_CREATE_MISSING_KEYS), then we either have "lost" blocks in the
1385 index file, or there are no non-unique indexes. In the latter case,
1386 mi_repair*() would not be called as there would be no disabled
1387 indexes.
1388
1389 If there would be more unique indexes than disabled (non-unique)
1390 indexes, we could do the first method. But this is not implemented
1391 yet. By now we drop and recreate all indexes when repair is called.
1392
1393 However, there is an exception. Sometimes MySQL disables non-unique
1394 indexes when the table is empty (e.g. when copying a table in
1395 mysql_alter_table()). When enabling the non-unique indexes, they
1396 are still empty. So there is no index block that can be lost. This
1397 optimization is implemented in this function.
1398
1399 Note that in normal repair (T_CREATE_MISSING_KEYS not set) we
1400 recreate all enabled indexes unconditonally. We do not change the
1401 key_map. Otherwise we invert the key map temporarily (outside of
1402 this function) and recreate the then "seemingly" enabled indexes.
1403 When we cannot use the optimization, and drop all indexes, we
1404 pretend that all indexes were disabled. By the inversion, we will
1405 then recrate all indexes.
1406 */
1407
1408 static int mi_drop_all_indexes(HA_CHECK *param, MI_INFO *info, my_bool force)
1409 {
1410 MYISAM_SHARE *share= info->s;
1411 MI_STATE_INFO *state= &share->state;
1412 uint i;
1413 int error;
1414 DBUG_ENTER("mi_drop_all_indexes");
1415
1416 /*
1417 If any of the disabled indexes has a key block assigned, we must
1418 drop and recreate all indexes to avoid losing index blocks.
1419
1420 If we want to recreate disabled indexes only _and_ all of these
1421 indexes are empty, we don't need to recreate the existing indexes.
1422 */
1423 if (!force && (param->testflag & T_CREATE_MISSING_KEYS))
1424 {
1425 DBUG_PRINT("repair", ("creating missing indexes"));
1426 for (i= 0; i < share->base.keys; i++)
1427 {
1428 DBUG_PRINT("repair", ("index #: %u key_root: 0x%lx active: %d",
1429 i, (long) state->key_root[i],
1430 mi_is_key_active(state->key_map, i)));
1431 if ((state->key_root[i] != HA_OFFSET_ERROR) &&
1432 !mi_is_key_active(state->key_map, i))
1433 {
1434 /*
1435 This index has at least one key block and it is disabled.
1436 We would lose its block(s) if would just recreate it.
1437 So we need to drop and recreate all indexes.
1438 */
1439 DBUG_PRINT("repair", ("nonempty and disabled: recreate all"));
1440 break;
1441 }
1442 }
1443 if (i >= share->base.keys)
1444 {
1445 /*
1446 All of the disabled indexes are empty. We can just recreate them.
1447 Flush dirty blocks of this index file from key cache and remove
1448 all blocks of this index file from key cache.
1449 */
1450 DBUG_PRINT("repair", ("all disabled are empty: create missing"));
1451 error= flush_key_blocks(share->key_cache, share->kfile,
1452 &share->dirty_part_map,
1453 FLUSH_FORCE_WRITE);
1454 goto end;
1455 }
1456 /*
1457 We do now drop all indexes and declare them disabled. With the
1458 T_CREATE_MISSING_KEYS flag, mi_repair*() will recreate all
1459 disabled indexes and enable them.
1460 */
1461 mi_clear_all_keys_active(state->key_map);
1462 DBUG_PRINT("repair", ("declared all indexes disabled"));
1463 }
1464
1465 /* Remove all key blocks of this index file from key cache. */
1466 if ((error= flush_key_blocks(share->key_cache, share->kfile,
1467 &share->dirty_part_map,
1468 FLUSH_IGNORE_CHANGED)))
1469 goto end; /* purecov: inspected */
1470
1471 /* Clear index root block pointers. */
1472 for (i= 0; i < share->base.keys; i++)
1473 state->key_root[i]= HA_OFFSET_ERROR;
1474
1475 /* Clear the delete chains. */
1476 for (i= 0; i < state->header.max_block_size_index; i++)
1477 state->key_del[i]= HA_OFFSET_ERROR;
1478
1479 /* Reset index file length to end of index file header. */
1480 info->state->key_file_length= share->base.keystart;
1481
1482 DBUG_PRINT("repair", ("dropped all indexes"));
1483 /* error= 0; set by last (error= flush_key_bocks()). */
1484
1485 end:
1486 DBUG_RETURN(error);
1487 }
1488
1489
1490 /* Recover old table by reading each record and writing all keys */
1491 /* Save new datafile-name in temp_filename */
1492
1493 int mi_repair(HA_CHECK *param, register MI_INFO *info,
1494 char * name, int rep_quick)
1495 {
1496 int error,got_error;
1497 ha_rows start_records,new_header_length;
1498 my_off_t del;
1499 File new_file;
1500 MYISAM_SHARE *share=info->s;
1501 char llbuff[22],llbuff2[22];
1502 MI_SORT_INFO sort_info;
1503 MI_SORT_PARAM sort_param;
1504 DBUG_ENTER("mi_repair");
1505
1506 bzero((char *)&sort_info, sizeof(sort_info));
1507 bzero((char *)&sort_param, sizeof(sort_param));
1508 start_records=info->state->records;
1509 new_header_length=(param->testflag & T_UNPACK) ? 0L :
1510 share->pack.header_length;
1511 got_error=1;
1512 new_file= -1;
1513 sort_param.sort_info=&sort_info;
1514 param->retry_repair= 0;
1515 param->warning_printed= param->note_printed= 0;
1516 param->error_printed= 0;
1517
1518 if (!(param->testflag & T_SILENT))
1519 {
1520 printf("- recovering (with keycache) MyISAM-table '%s'\n",name);
1521 printf("Data records: %s\n", llstr(info->state->records,llbuff));
1522 }
1523 param->testflag|=T_REP; /* for easy checking */
1524
1525 if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
1526 param->testflag|=T_CALC_CHECKSUM;
1527
1528 DBUG_ASSERT(param->use_buffers < SIZE_T_MAX);
1529
1530 if (!param->using_global_keycache)
1531 (void) init_key_cache(dflt_key_cache, param->key_cache_block_size,
1532 (size_t) param->use_buffers, 0, 0, 0, 0);
1533
1534 if (init_io_cache(¶m->read_cache,info->dfile,
1535 (uint) param->read_buffer_length,
1536 READ_CACHE,share->pack.header_length,1,MYF(MY_WME)))
1537 {
1538 bzero(&info->rec_cache,sizeof(info->rec_cache));
1539 goto err;
1540 }
1541 info->opt_flag|=WRITE_CACHE_USED;
1542 if (!mi_alloc_rec_buff(info, -1, &sort_param.record) ||
1543 !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff))
1544 {
1545 mi_check_print_error(param, "Not enough memory for extra record");
1546 goto err;
1547 }
1548
1549 if (!rep_quick)
1550 {
1551 /* Get real path for data file */
1552 if ((new_file= mysql_file_create(mi_key_file_datatmp,
1553 fn_format(param->temp_filename,
1554 share->data_file_name, "",
1555 DATA_TMP_EXT, 2+4),
1556 0, param->tmpfile_createflag,
1557 MYF(0))) < 0)
1558 {
1559 mi_check_print_error(param,"Can't create new tempfile: '%s'",
1560 param->temp_filename);
1561 goto err;
1562 }
1563 if (new_header_length &&
1564 filecopy(param,new_file,info->dfile,0L,new_header_length,
1565 "datafile-header"))
1566 goto err;
1567 info->s->state.dellink= HA_OFFSET_ERROR;
1568 if (param->testflag & T_UNPACK)
1569 {
1570 share->options&= ~HA_OPTION_COMPRESS_RECORD;
1571 mi_int2store(share->state.header.options,share->options);
1572 }
1573 if (init_io_cache(&info->rec_cache, new_file,
1574 (uint) param->write_buffer_length,
1575 WRITE_CACHE, new_header_length, 1,
1576 MYF(MY_WME | MY_WAIT_IF_FULL)))
1577 goto err;
1578 }
1579 sort_info.info=info;
1580 sort_info.param = param;
1581 sort_param.read_cache=param->read_cache;
1582 sort_param.pos=sort_param.max_pos=share->pack.header_length;
1583 sort_param.filepos=new_header_length;
1584 param->read_cache.end_of_file=sort_info.filelength=
1585 mysql_file_seek(info->dfile, 0L, MY_SEEK_END, MYF(0));
1586 if (info->state->data_file_length == 0)
1587 info->state->data_file_length= sort_info.filelength;
1588 sort_info.dupp=0;
1589 sort_param.fix_datafile= (my_bool) (! rep_quick);
1590 sort_param.master=1;
1591 sort_info.max_records= ~(ha_rows) 0;
1592
1593 set_data_file_type(&sort_info, share);
1594 del=info->state->del;
1595 info->state->records=info->state->del=share->state.split=0;
1596 info->state->empty=0;
1597 param->glob_crc=0;
1598 if (param->testflag & T_CALC_CHECKSUM)
1599 sort_param.calc_checksum= 1;
1600
1601 info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
1602
1603 /* This function always recreates all enabled indexes. */
1604 if (param->testflag & T_CREATE_MISSING_KEYS)
1605 mi_set_all_keys_active(share->state.key_map, share->base.keys);
1606 mi_drop_all_indexes(param, info, TRUE);
1607
1608 lock_memory(param); /* Everything is alloced */
1609
1610 /* Re-create all keys, which are set in key_map. */
1611 while (!(error=sort_get_next_record(&sort_param)))
1612 {
1613 if (writekeys(&sort_param))
1614 {
1615 if (my_errno != HA_ERR_FOUND_DUPP_KEY)
1616 goto err;
1617 DBUG_DUMP("record",(uchar*) sort_param.record,share->base.pack_reclength);
1618 mi_check_print_info(param,"Duplicate key %2d for record at %10s against new record at %10s",
1619 info->errkey+1,
1620 llstr(sort_param.start_recpos,llbuff),
1621 llstr(info->dupp_key_pos,llbuff2));
1622 if (param->testflag & T_VERBOSE)
1623 {
1624 (void) _mi_make_key(info,(uint) info->errkey,info->lastkey,
1625 sort_param.record,0L);
1626 _mi_print_key(stdout,share->keyinfo[info->errkey].seg,info->lastkey,
1627 USE_WHOLE_KEY);
1628 }
1629 sort_info.dupp++;
1630 if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
1631 {
1632 param->testflag|=T_RETRY_WITHOUT_QUICK;
1633 param->error_printed=1;
1634 goto err;
1635 }
1636 continue;
1637 }
1638 if (sort_write_record(&sort_param))
1639 goto err;
1640 }
1641 if (error > 0 || write_data_suffix(&sort_info, (my_bool)!rep_quick) ||
1642 flush_io_cache(&info->rec_cache) || param->read_cache.error < 0)
1643 goto err;
1644
1645 if (param->testflag & T_WRITE_LOOP)
1646 {
1647 (void) fputs(" \r",stdout); (void) fflush(stdout);
1648 }
1649 if (mysql_file_chsize(share->kfile, info->state->key_file_length, 0, MYF(0)))
1650 {
1651 mi_check_print_warning(param,
1652 "Can't change size of indexfile, error: %d",
1653 my_errno);
1654 goto err;
1655 }
1656
1657 if (rep_quick && del+sort_info.dupp != info->state->del)
1658 {
1659 mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
1660 mi_check_print_error(param,"Run recovery again without --quick");
1661 got_error=1;
1662 param->retry_repair=1;
1663 param->testflag|=T_RETRY_WITHOUT_QUICK;
1664 goto err;
1665 }
1666 if (param->testflag & T_SAFE_REPAIR)
1667 {
1668 /* Don't repair if we loosed more than one row */
1669 if (info->state->records+1 < start_records)
1670 {
1671 info->state->records=start_records;
1672 got_error=1;
1673 goto err;
1674 }
1675 }
1676
1677 if (!rep_quick)
1678 {
1679 mysql_file_close(info->dfile, MYF(0));
1680 info->dfile=new_file;
1681 info->state->data_file_length=sort_param.filepos;
1682 share->state.version=(ulong) time((time_t*) 0); /* Force reopen */
1683 }
1684 else
1685 {
1686 info->state->data_file_length=sort_param.max_pos;
1687 }
1688 if (param->testflag & T_CALC_CHECKSUM)
1689 info->state->checksum=param->glob_crc;
1690
1691 if (!(param->testflag & T_SILENT))
1692 {
1693 if (start_records != info->state->records)
1694 printf("Data records: %s\n", llstr(info->state->records,llbuff));
1695 if (sort_info.dupp)
1696 mi_check_print_warning(param,
1697 "%s records have been removed",
1698 llstr(sort_info.dupp,llbuff));
1699 }
1700
1701 got_error=0;
1702 /* If invoked by external program that uses thr_lock */
1703 if (&share->state.state != info->state)
1704 memcpy( &share->state.state, info->state, sizeof(*info->state));
1705
1706 err:
1707 if (!got_error)
1708 {
1709 /* Replace the actual file with the temporary file */
1710 if (new_file >= 0)
1711 {
1712 got_error= replace_data_file(param, info, new_file);
1713 new_file= -1;
1714 param->retry_repair= 0;
1715 }
1716 }
1717 if (got_error)
1718 {
1719 if (! param->error_printed)
1720 mi_check_print_error(param,"%d for record at pos %s",my_errno,
1721 llstr(sort_param.start_recpos,llbuff));
1722 if (new_file >= 0)
1723 {
1724 (void) mysql_file_close(new_file, MYF(0));
1725 (void) mysql_file_delete(mi_key_file_datatmp,
1726 param->temp_filename, MYF(MY_WME));
1727 info->rec_cache.file=-1; /* don't flush data to new_file, it's closed */
1728 }
1729 mi_mark_crashed_on_repair(info);
1730 }
1731 my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff));
1732 my_free(mi_get_rec_buff_ptr(info, sort_param.record));
1733 my_free(sort_info.buff);
1734 (void) end_io_cache(¶m->read_cache);
1735 info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
1736 (void) end_io_cache(&info->rec_cache);
1737 got_error|=flush_blocks(param, share->key_cache, share->kfile,
1738 &share->dirty_part_map);
1739 if (!got_error && param->testflag & T_UNPACK)
1740 {
1741 share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
1742 share->pack.header_length=0;
1743 share->data_file_type=sort_info.new_data_file_type;
1744 }
1745 share->state.changed|= (STATE_NOT_OPTIMIZED_KEYS | STATE_NOT_SORTED_PAGES |
1746 STATE_NOT_ANALYZED);
1747 DBUG_RETURN(got_error);
1748 }
1749
1750
1751 /* Uppate keyfile when doing repair */
1752
1753 static int writekeys(MI_SORT_PARAM *sort_param)
1754 {
1755 register uint i;
1756 uchar *key;
1757 MI_INFO *info= sort_param->sort_info->info;
1758 uchar *buff= sort_param->record;
1759 my_off_t filepos= sort_param->filepos;
1760 DBUG_ENTER("writekeys");
1761
1762 key=info->lastkey+info->s->base.max_key_length;
1763 for (i=0 ; i < info->s->base.keys ; i++)
1764 {
1765 if (mi_is_key_active(info->s->state.key_map, i))
1766 {
1767 if (info->s->keyinfo[i].flag & HA_FULLTEXT )
1768 {
1769 if (_mi_ft_add(info, i, key, buff, filepos))
1770 goto err;
1771 }
1772 #ifdef HAVE_SPATIAL
1773 else if (info->s->keyinfo[i].flag & HA_SPATIAL)
1774 {
1775 uint key_length=_mi_make_key(info,i,key,buff,filepos);
1776 if (rtree_insert(info, i, key, key_length))
1777 goto err;
1778 }
1779 #endif /*HAVE_SPATIAL*/
1780 else
1781 {
1782 uint key_length=_mi_make_key(info,i,key,buff,filepos);
1783 if (_mi_ck_write(info,i,key,key_length))
1784 goto err;
1785 }
1786 }
1787 }
1788 DBUG_RETURN(0);
1789
1790 err:
1791 if (my_errno == HA_ERR_FOUND_DUPP_KEY)
1792 {
1793 info->errkey=(int) i; /* This key was found */
1794 while ( i-- > 0 )
1795 {
1796 if (mi_is_key_active(info->s->state.key_map, i))
1797 {
1798 if (info->s->keyinfo[i].flag & HA_FULLTEXT)
1799 {
1800 if (_mi_ft_del(info,i, key,buff,filepos))
1801 break;
1802 }
1803 else
1804 {
1805 uint key_length=_mi_make_key(info,i,key,buff,filepos);
1806 if (_mi_ck_delete(info,i,key,key_length))
1807 break;
1808 }
1809 }
1810 }
1811 }
1812 /* Remove checksum that was added to glob_crc in sort_get_next_record */
1813 if (sort_param->calc_checksum)
1814 sort_param->sort_info->param->glob_crc-= info->checksum;
1815 DBUG_PRINT("error",("errno: %d",my_errno));
1816 DBUG_RETURN(-1);
1817 } /* writekeys */
1818
1819
1820 /* Change all key-pointers that points to a records */
1821
1822 int movepoint(register MI_INFO *info, uchar *record, my_off_t oldpos,
1823 my_off_t newpos, uint prot_key)
1824 {
1825 register uint i;
1826 uchar *key;
1827 uint key_length;
1828 DBUG_ENTER("movepoint");
1829
1830 key=info->lastkey+info->s->base.max_key_length;
1831 for (i=0 ; i < info->s->base.keys; i++)
1832 {
1833 if (i != prot_key && mi_is_key_active(info->s->state.key_map, i))
1834 {
1835 key_length=_mi_make_key(info,i,key,record,oldpos);
1836 if (info->s->keyinfo[i].flag & HA_NOSAME)
1837 { /* Change pointer direct */
1838 uint nod_flag;
1839 MI_KEYDEF *keyinfo;
1840 keyinfo=info->s->keyinfo+i;
1841 if (_mi_search(info,keyinfo,key,USE_WHOLE_KEY,
1842 (uint) (SEARCH_SAME | SEARCH_SAVE_BUFF),
1843 info->s->state.key_root[i]))
1844 DBUG_RETURN(-1);
1845 nod_flag=mi_test_if_nod(info->buff);
1846 _mi_dpointer(info,info->int_keypos-nod_flag-
1847 info->s->rec_reflength,newpos);
1848 if (_mi_write_keypage(info,keyinfo,info->last_keypage,
1849 DFLT_INIT_HITS,info->buff))
1850 DBUG_RETURN(-1);
1851 }
1852 else
1853 { /* Change old key to new */
1854 if (_mi_ck_delete(info,i,key,key_length))
1855 DBUG_RETURN(-1);
1856 key_length=_mi_make_key(info,i,key,record,newpos);
1857 if (_mi_ck_write(info,i,key,key_length))
1858 DBUG_RETURN(-1);
1859 }
1860 }
1861 }
1862 DBUG_RETURN(0);
1863 } /* movepoint */
1864
1865
1866 /* Tell system that we want all memory for our cache */
1867
1868 void lock_memory(HA_CHECK *param __attribute__((unused)))
1869 {
1870 #ifdef SUN_OS /* Key-cacheing thrases on sun 4.1 */
1871 if (param->opt_lock_memory)
1872 {
1873 int success = mlockall(MCL_CURRENT); /* or plock(DATLOCK); */
1874 if (geteuid() == 0 && success != 0)
1875 mi_check_print_warning(param,
1876 "Failed to lock memory. errno %d",my_errno);
1877 }
1878 #endif
1879 } /* lock_memory */
1880
1881
1882 /* Flush all changed blocks to disk */
1883
1884 int flush_blocks(HA_CHECK *param, KEY_CACHE *key_cache, File file,
1885 ulonglong *dirty_part_map)
1886 {
1887 if (flush_key_blocks(key_cache, file, dirty_part_map, FLUSH_RELEASE))
1888 {
1889 mi_check_print_error(param,"%d when trying to write buffers",my_errno);
1890 return(1);
1891 }
1892 if (!param->using_global_keycache)
1893 end_key_cache(key_cache,1);
1894 return 0;
1895 } /* flush_blocks */
1896
1897
1898 /* Sort index for more efficient reads */
1899
1900 int mi_sort_index(HA_CHECK *param, register MI_INFO *info, char * name)
1901 {
1902 reg2 uint key;
1903 reg1 MI_KEYDEF *keyinfo;
1904 File new_file;
1905 my_off_t index_pos[HA_MAX_POSSIBLE_KEY];
1906 uint r_locks,w_locks;
1907 int old_lock;
1908 MYISAM_SHARE *share=info->s;
1909 MI_STATE_INFO old_state;
1910 DBUG_ENTER("mi_sort_index");
1911
1912 /* cannot sort index files with R-tree indexes */
1913 for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
1914 key++,keyinfo++)
1915 if (keyinfo->key_alg == HA_KEY_ALG_RTREE)
1916 DBUG_RETURN(0);
1917
1918 if (!(param->testflag & T_SILENT))
1919 printf("- Sorting index for MyISAM-table '%s'\n",name);
1920
1921 /* Get real path for index file */
1922 fn_format(param->temp_filename,name,"", MI_NAME_IEXT,2+4+32);
1923 if ((new_file= mysql_file_create(mi_key_file_datatmp,
1924 fn_format(param->temp_filename,
1925 param->temp_filename,
1926 "", INDEX_TMP_EXT, 2+4),
1927 0, param->tmpfile_createflag, MYF(0))) < 0)
1928 {
1929 mi_check_print_error(param,"Can't create new tempfile: '%s'",
1930 param->temp_filename);
1931 DBUG_RETURN(-1);
1932 }
1933 if (filecopy(param, new_file,share->kfile,0L,
1934 (ulong) share->base.keystart, "headerblock"))
1935 goto err;
1936
1937 param->new_file_pos=share->base.keystart;
1938 for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
1939 key++,keyinfo++)
1940 {
1941 if (mi_is_key_active(info->s->state.key_map, key) &&
1942 share->state.key_root[key] != HA_OFFSET_ERROR)
1943 {
1944 index_pos[key]=param->new_file_pos; /* Write first block here */
1945 if (sort_one_index(param,info,keyinfo,share->state.key_root[key],
1946 new_file))
1947 goto err;
1948 }
1949 else
1950 index_pos[key]= HA_OFFSET_ERROR; /* No blocks */
1951 }
1952
1953 /* Flush key cache for this file if we are calling this outside myisamchk */
1954 flush_key_blocks(share->key_cache, share->kfile, &share->dirty_part_map,
1955 FLUSH_IGNORE_CHANGED);
1956
1957 share->state.version=(ulong) time((time_t*) 0);
1958 old_state= share->state; /* save state if not stored */
1959 r_locks= share->r_locks;
1960 w_locks= share->w_locks;
1961 old_lock= info->lock_type;
1962
1963 /* Put same locks as old file */
1964 share->r_locks= share->w_locks= share->tot_locks= 0;
1965 (void) _mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE);
1966 (void) mysql_file_close(share->kfile, MYF(MY_WME));
1967 share->kfile = -1;
1968 (void) mysql_file_close(new_file, MYF(MY_WME));
1969 if (change_to_newfile(share->index_file_name,MI_NAME_IEXT,INDEX_TMP_EXT,
1970 0, MYF(0)) ||
1971 mi_open_keyfile(share))
1972 goto err2;
1973 info->lock_type= F_UNLCK; /* Force mi_readinfo to lock */
1974 _mi_readinfo(info,F_WRLCK,0); /* Will lock the table */
1975 info->lock_type= old_lock;
1976 share->r_locks= r_locks;
1977 share->w_locks= w_locks;
1978 share->tot_locks= r_locks+w_locks;
1979 share->state= old_state; /* Restore old state */
1980
1981 info->state->key_file_length=param->new_file_pos;
1982 info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
1983 for (key=0 ; key < info->s->base.keys ; key++)
1984 info->s->state.key_root[key]=index_pos[key];
1985 for (key=0 ; key < info->s->state.header.max_block_size_index ; key++)
1986 info->s->state.key_del[key]= HA_OFFSET_ERROR;
1987
1988 info->s->state.changed&= ~STATE_NOT_SORTED_PAGES;
1989 DBUG_RETURN(0);
1990
1991 err:
1992 (void) mysql_file_close(new_file, MYF(MY_WME));
1993 err2:
1994 (void) mysql_file_delete(mi_key_file_datatmp,
1995 param->temp_filename, MYF(MY_WME));
1996 DBUG_RETURN(-1);
1997 } /* mi_sort_index */
1998
1999
2000 /* Sort records recursive using one index */
2001
2002 static int sort_one_index(HA_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
2003 my_off_t pagepos, File new_file)
2004 {
2005 uint length,nod_flag,used_length, key_length;
2006 uchar *buff,*keypos,*endpos;
2007 uchar key[HA_MAX_POSSIBLE_KEY_BUFF];
2008 my_off_t new_page_pos,next_page;
2009 char llbuff[22];
2010 DBUG_ENTER("sort_one_index");
2011
2012 /* cannot walk over R-tree indices */
2013 DBUG_ASSERT(keyinfo->key_alg != HA_KEY_ALG_RTREE);
2014 new_page_pos=param->new_file_pos;
2015 param->new_file_pos+=keyinfo->block_length;
2016
2017 if (!(buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
2018 {
2019 mi_check_print_error(param,"Not enough memory for key block");
2020 DBUG_RETURN(-1);
2021 }
2022 if (!_mi_fetch_keypage(info,keyinfo,pagepos,DFLT_INIT_HITS,buff,0))
2023 {
2024 mi_check_print_error(param,"Can't read key block from filepos: %s",
2025 llstr(pagepos,llbuff));
2026 goto err;
2027 }
2028 if ((nod_flag=mi_test_if_nod(buff)) || keyinfo->flag & HA_FULLTEXT)
2029 {
2030 used_length=mi_getint(buff);
2031 keypos=buff+2+nod_flag;
2032 endpos=buff+used_length;
2033 for ( ;; )
2034 {
2035 if (nod_flag)
2036 {
2037 next_page=_mi_kpos(nod_flag,keypos);
2038 _mi_kpointer(info,keypos-nod_flag,param->new_file_pos); /* Save new pos */
2039 if (sort_one_index(param,info,keyinfo,next_page,new_file))
2040 {
2041 DBUG_PRINT("error",
2042 ("From page: %ld, keyoffset: %lu used_length: %d",
2043 (ulong) pagepos, (ulong) (keypos - buff),
2044 (int) used_length));
2045 DBUG_DUMP("buff",(uchar*) buff,used_length);
2046 goto err;
2047 }
2048 }
2049 if (keypos >= endpos ||
2050 (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0)
2051 break;
2052 DBUG_ASSERT(keypos <= endpos);
2053 if (keyinfo->flag & HA_FULLTEXT)
2054 {
2055 uint off;
2056 int subkeys;
2057 get_key_full_length_rdonly(off, key);
2058 subkeys=ft_sintXkorr(key+off);
2059 if (subkeys < 0)
2060 {
2061 next_page= _mi_dpos(info,0,key+key_length);
2062 _mi_dpointer(info,keypos-nod_flag-info->s->rec_reflength,
2063 param->new_file_pos); /* Save new pos */
2064 if (sort_one_index(param,info,&info->s->ft2_keyinfo,
2065 next_page,new_file))
2066 goto err;
2067 }
2068 }
2069 }
2070 }
2071
2072 /* Fill block with zero and write it to the new index file */
2073 length=mi_getint(buff);
2074 bzero((uchar*) buff+length,keyinfo->block_length-length);
2075 if (mysql_file_pwrite(new_file, (uchar*) buff, (uint) keyinfo->block_length,
2076 new_page_pos, MYF(MY_NABP | MY_WAIT_IF_FULL)))
2077 {
2078 mi_check_print_error(param,"Can't write indexblock, error: %d",my_errno);
2079 goto err;
2080 }
2081 my_afree((uchar*) buff);
2082 DBUG_RETURN(0);
2083 err:
2084 my_afree((uchar*) buff);
2085 DBUG_RETURN(1);
2086 } /* sort_one_index */
2087
2088
2089 /*
2090 Let temporary file replace old file.
2091 This assumes that the new file was created in the same
2092 directory as given by realpath(filename).
2093 This will ensure that any symlinks that are used will still work.
2094 Copy stats from old file to new file, deletes orignal and
2095 changes new file name to old file name
2096 */
2097
2098 int change_to_newfile(const char * filename, const char * old_ext,
2099 const char * new_ext,
2100 time_t backup_time,
2101 myf MyFlags)
2102 {
2103 char old_filename[FN_REFLEN],new_filename[FN_REFLEN];
2104 /* Get real path to filename */
2105 (void) fn_format(old_filename,filename,"",old_ext,2+4+32);
2106 return my_redel(old_filename,
2107 fn_format(new_filename,old_filename,"",new_ext,2+4),
2108 backup_time, MYF(MY_WME | MY_LINK_WARNING | MyFlags));
2109 } /* change_to_newfile */
2110
2111
2112 /* Locks a whole file */
2113 /* Gives an error-message if file can't be locked */
2114
2115 int lock_file(HA_CHECK *param, File file, my_off_t start, int lock_type,
2116 const char *filetype, const char *filename)
2117 {
2118 if (my_lock(file,lock_type,start,F_TO_EOF,
2119 param->testflag & T_WAIT_FOREVER ? MYF(MY_SEEK_NOT_DONE) :
2120 MYF(MY_SEEK_NOT_DONE | MY_SHORT_WAIT)))
2121 {
2122 mi_check_print_error(param," %d when locking %s '%s'",my_errno,filetype,filename);
2123 param->error_printed=2; /* Don't give that data is crashed */
2124 return 1;
2125 }
2126 return 0;
2127 } /* lock_file */
2128
2129
2130 /* Copy a block between two files */
2131
2132 int filecopy(HA_CHECK *param, File to,File from,my_off_t start,
2133 my_off_t length, const char *type)
2134 {
2135 char tmp_buff[IO_SIZE],*buff;
2136 ulong buff_length;
2137 DBUG_ENTER("filecopy");
2138
2139 buff_length=(ulong) MY_MIN(param->write_buffer_length,length);
2140 if (!(buff=my_malloc(buff_length,MYF(0))))
2141 {
2142 buff=tmp_buff; buff_length=IO_SIZE;
2143 }
2144
2145 mysql_file_seek(from, start, MY_SEEK_SET, MYF(0));
2146 while (length > buff_length)
2147 {
2148 if (mysql_file_read(from, (uchar*) buff, buff_length, MYF(MY_NABP)) ||
2149 mysql_file_write(to, (uchar*) buff, buff_length, param->myf_rw))
2150 goto err;
2151 length-= buff_length;
2152 }
2153 if (mysql_file_read(from, (uchar*) buff, (uint) length, MYF(MY_NABP)) ||
2154 mysql_file_write(to, (uchar*) buff, (uint) length, param->myf_rw))
2155 goto err;
2156 if (buff != tmp_buff)
2157 my_free(buff);
2158 DBUG_RETURN(0);
2159 err:
2160 if (buff != tmp_buff)
2161 my_free(buff);
2162 mi_check_print_error(param,"Can't copy %s to tempfile, error %d",
2163 type,my_errno);
2164 DBUG_RETURN(1);
2165 }
2166
2167
2168 /*
2169 Repair table or given index using sorting
2170
2171 SYNOPSIS
2172 mi_repair_by_sort()
2173 param Repair parameters
2174 info MyISAM handler to repair
2175 name Name of table (for warnings)
2176 rep_quick set to <> 0 if we should not change data file
2177
2178 RESULT
2179 0 ok
2180 <>0 Error
2181 */
2182
2183 int mi_repair_by_sort(HA_CHECK *param, register MI_INFO *info,
2184 const char * name, int rep_quick)
2185 {
2186 int got_error;
2187 uint i;
2188 ulong length;
2189 ha_rows start_records;
2190 my_off_t new_header_length,del;
2191 File new_file;
2192 MI_SORT_PARAM sort_param;
2193 MYISAM_SHARE *share=info->s;
2194 HA_KEYSEG *keyseg;
2195 ulong *rec_per_key_part;
2196 char llbuff[22], llbuff2[22];
2197 MI_SORT_INFO sort_info;
2198 ulonglong UNINIT_VAR(key_map);
2199 DBUG_ENTER("mi_repair_by_sort");
2200
2201 start_records=info->state->records;
2202 got_error=1;
2203 new_file= -1;
2204 new_header_length=(param->testflag & T_UNPACK) ? 0 :
2205 share->pack.header_length;
2206 if (!(param->testflag & T_SILENT))
2207 {
2208 printf("- recovering (with sort) MyISAM-table '%s'\n",name);
2209 printf("Data records: %s\n", llstr(start_records,llbuff));
2210 }
2211 param->testflag|=T_REP_BY_SORT; /* for easy checking */
2212 param->retry_repair= 0;
2213 param->warning_printed= param->note_printed= 0;
2214 param->error_printed= 0;
2215
2216 if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
2217 param->testflag|=T_CALC_CHECKSUM;
2218
2219 bzero((char*)&sort_info,sizeof(sort_info));
2220 bzero((char *)&sort_param, sizeof(sort_param));
2221
2222 if (!(sort_info.key_block=
2223 alloc_key_blocks(param,
2224 (uint) param->sort_key_blocks,
2225 share->base.max_key_block_length)))
2226 goto err;
2227
2228 if (init_io_cache(¶m->read_cache,info->dfile,
2229 (uint) param->read_buffer_length,
2230 READ_CACHE,share->pack.header_length,1,MYF(MY_WME)))
2231 goto err;
2232
2233 sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
2234 info->opt_flag|=WRITE_CACHE_USED;
2235
2236 if (!mi_alloc_rec_buff(info, -1, &sort_param.record) ||
2237 !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff))
2238 {
2239 mi_check_print_error(param, "Not enough memory for extra record");
2240 goto err;
2241 }
2242 if (!rep_quick)
2243 {
2244 /* Get real path for data file */
2245 if ((new_file= mysql_file_create(mi_key_file_datatmp,
2246 fn_format(param->temp_filename,
2247 share->data_file_name, "",
2248 DATA_TMP_EXT, 2+4),
2249 0, param->tmpfile_createflag,
2250 MYF(0))) < 0)
2251 {
2252 mi_check_print_error(param,"Can't create new tempfile: '%s'",
2253 param->temp_filename);
2254 goto err;
2255 }
2256 if (new_header_length &&
2257 filecopy(param, new_file,info->dfile,0L,new_header_length,
2258 "datafile-header"))
2259 goto err;
2260 if (param->testflag & T_UNPACK)
2261 {
2262 share->options&= ~HA_OPTION_COMPRESS_RECORD;
2263 mi_int2store(share->state.header.options,share->options);
2264 }
2265 share->state.dellink= HA_OFFSET_ERROR;
2266 if (init_io_cache(&info->rec_cache, new_file,
2267 (uint) param->write_buffer_length,
2268 WRITE_CACHE, new_header_length, 1,
2269 MYF((param->myf_rw & MY_WAIT_IF_FULL) | MY_WME)))
2270 goto err;
2271 }
2272
2273 info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
2274
2275 /* Optionally drop indexes and optionally modify the key_map. */
2276 mi_drop_all_indexes(param, info, FALSE);
2277 key_map= share->state.key_map;
2278 if (param->testflag & T_CREATE_MISSING_KEYS)
2279 {
2280 /* Invert the copied key_map to recreate all disabled indexes. */
2281 key_map= ~key_map;
2282 }
2283
2284 sort_info.info=info;
2285 sort_info.param = param;
2286
2287 set_data_file_type(&sort_info, share);
2288 sort_param.filepos=new_header_length;
2289 sort_info.dupp=0;
2290 sort_info.buff=0;
2291 param->read_cache.end_of_file=sort_info.filelength=
2292 mysql_file_seek(param->read_cache.file, 0L, MY_SEEK_END, MYF(0));
2293 if (info->state->data_file_length == 0)
2294 info->state->data_file_length= sort_info.filelength;
2295
2296 sort_param.wordlist=NULL;
2297 init_alloc_root(&sort_param.wordroot, "sort", FTPARSER_MEMROOT_ALLOC_SIZE, 0,
2298 MYF(param->malloc_flags));
2299
2300 if (share->data_file_type == DYNAMIC_RECORD)
2301 length=MY_MAX(share->base.min_pack_length+1,share->base.min_block_length);
2302 else if (share->data_file_type == COMPRESSED_RECORD)
2303 length=share->base.min_block_length;
2304 else
2305 length=share->base.pack_reclength;
2306 sort_info.max_records=
2307 ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records :
2308 (ha_rows) (sort_info.filelength/length+1));
2309 sort_param.key_cmp=sort_key_cmp;
2310 sort_param.lock_in_memory=lock_memory;
2311 sort_param.tmpdir=param->tmpdir;
2312 sort_param.sort_info=&sort_info;
2313 sort_param.fix_datafile= (my_bool) (! rep_quick);
2314 sort_param.master =1;
2315
2316 del=info->state->del;
2317 param->glob_crc=0;
2318 if (param->testflag & T_CALC_CHECKSUM)
2319 sort_param.calc_checksum= 1;
2320
2321 rec_per_key_part= param->rec_per_key_part;
2322 for (sort_param.key=0 ; sort_param.key < share->base.keys ;
2323 rec_per_key_part+=sort_param.keyinfo->keysegs, sort_param.key++)
2324 {
2325 sort_param.read_cache=param->read_cache;
2326 sort_param.keyinfo=share->keyinfo+sort_param.key;
2327 sort_param.seg=sort_param.keyinfo->seg;
2328 /*
2329 Skip this index if it is marked disabled in the copied
2330 (and possibly inverted) key_map.
2331 */
2332 if (! mi_is_key_active(key_map, sort_param.key))
2333 {
2334 /* Remember old statistics for key */
2335 memcpy((char*) rec_per_key_part,
2336 (char*) (share->state.rec_per_key_part +
2337 (uint) (rec_per_key_part - param->rec_per_key_part)),
2338 sort_param.keyinfo->keysegs*sizeof(*rec_per_key_part));
2339 DBUG_PRINT("repair", ("skipping seemingly disabled index #: %u",
2340 sort_param.key));
2341 continue;
2342 }
2343
2344 if ((!(param->testflag & T_SILENT)))
2345 printf ("- Fixing index %d\n",sort_param.key+1);
2346 sort_param.max_pos=sort_param.pos=share->pack.header_length;
2347 keyseg=sort_param.seg;
2348 bzero((char*) sort_param.unique,sizeof(sort_param.unique));
2349 sort_param.key_length=share->rec_reflength;
2350 for (i=0 ; keyseg[i].type != HA_KEYTYPE_END; i++)
2351 {
2352 sort_param.key_length+=keyseg[i].length;
2353 if (keyseg[i].flag & HA_SPACE_PACK)
2354 sort_param.key_length+=get_pack_length(keyseg[i].length);
2355 if (keyseg[i].flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
2356 sort_param.key_length+= 2 + MY_TEST(keyseg[i].length >= 127);
2357 if (keyseg[i].flag & HA_NULL_PART)
2358 sort_param.key_length++;
2359 }
2360 info->state->records=info->state->del=share->state.split=0;
2361 info->state->empty=0;
2362
2363 if (sort_param.keyinfo->flag & HA_FULLTEXT)
2364 {
2365 uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT*
2366 sort_param.keyinfo->seg->charset->mbmaxlen;
2367 sort_param.key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
2368 /*
2369 fulltext indexes may have much more entries than the
2370 number of rows in the table. We estimate the number here.
2371 */
2372 if (sort_param.keyinfo->parser == &ft_default_parser)
2373 {
2374 /*
2375 for built-in parser the number of generated index entries
2376 cannot be larger than the size of the data file divided
2377 by the minimal word's length
2378 */
2379 sort_info.max_records=
2380 (ha_rows) (sort_info.filelength/ft_min_word_len+1);
2381 }
2382 else
2383 {
2384 /*
2385 for external plugin parser we cannot tell anything at all :(
2386 so, we'll use all the sort memory and start from ~10 buffpeks.
2387 (see _create_index_by_sort)
2388 */
2389 sort_info.max_records= 10 *
2390 MY_MAX(param->sort_buffer_length, MIN_SORT_BUFFER) /
2391 sort_param.key_length;
2392 }
2393
2394 sort_param.key_read=sort_ft_key_read;
2395 sort_param.key_write=sort_ft_key_write;
2396 }
2397 else
2398 {
2399 sort_param.key_read=sort_key_read;
2400 sort_param.key_write=sort_key_write;
2401 }
2402
2403 if (_create_index_by_sort(&sort_param,
2404 (my_bool) (!(param->testflag & T_VERBOSE)),
2405 param->sort_buffer_length))
2406 {
2407 if ((param->testflag & T_CREATE_UNIQUE_BY_SORT) && sort_param.sort_info->dupp)
2408 share->state.dupp_key= sort_param.key;
2409 else
2410 param->retry_repair= 1;
2411 if (! param->error_printed)
2412 mi_check_print_error(param, "Couldn't fix table with create_index_by_sort(). Error: %d",
2413 my_errno);
2414 goto err;
2415 }
2416 /* No need to calculate checksum again. */
2417 sort_param.calc_checksum= 0;
2418 free_root(&sort_param.wordroot, MYF(0));
2419
2420 /* Set for next loop */
2421 sort_info.max_records= (ha_rows) info->state->records;
2422
2423 if (param->testflag & T_STATISTICS)
2424 update_key_parts(sort_param.keyinfo, rec_per_key_part, sort_param.unique,
2425 param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
2426 sort_param.notnull: NULL,
2427 (ulonglong) info->state->records);
2428 /* Enable this index in the permanent (not the copied) key_map. */
2429 mi_set_key_active(share->state.key_map, sort_param.key);
2430 DBUG_PRINT("repair", ("set enabled index #: %u", sort_param.key));
2431
2432 if (sort_param.fix_datafile)
2433 {
2434 param->read_cache.end_of_file=sort_param.filepos;
2435 if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache))
2436 goto err;
2437 if (param->testflag & T_SAFE_REPAIR)
2438 {
2439 /* Don't repair if we loosed more than one row */
2440 if (info->state->records+1 < start_records)
2441 {
2442 mi_check_print_error(param,
2443 "Couldn't fix table as SAFE_REPAIR was requested and we would loose too many rows. %s -> %s",
2444 llstr(start_records, llbuff), llstr(info->state->records, llbuff2));
2445 info->state->records= start_records;
2446 goto err;
2447 }
2448 }
2449 share->state.state.data_file_length = info->state->data_file_length=
2450 sort_param.filepos;
2451 /* Only whole records */
2452 share->state.version=(ulong) time((time_t*) 0);
2453 mysql_file_close(info->dfile, MYF(0));
2454 info->dfile=new_file;
2455 share->data_file_type=sort_info.new_data_file_type;
2456 share->pack.header_length=(ulong) new_header_length;
2457 sort_param.fix_datafile=0;
2458 }
2459 else
2460 info->state->data_file_length=sort_param.max_pos;
2461
2462 param->read_cache.file=info->dfile; /* re-init read cache */
2463 reinit_io_cache(¶m->read_cache,READ_CACHE,share->pack.header_length,
2464 1,1);
2465 }
2466
2467 if (param->testflag & T_WRITE_LOOP)
2468 {
2469 (void) fputs(" \r",stdout); (void) fflush(stdout);
2470 }
2471
2472 if (rep_quick && del+sort_info.dupp != info->state->del)
2473 {
2474 mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
2475 mi_check_print_error(param,"Run recovery again without --quick");
2476 got_error=1;
2477 param->retry_repair=1;
2478 param->testflag|=T_RETRY_WITHOUT_QUICK;
2479 goto err;
2480 }
2481
2482 if (rep_quick && (param->testflag & T_FORCE_UNIQUENESS))
2483 {
2484 my_off_t skr=info->state->data_file_length+
2485 (share->options & HA_OPTION_COMPRESS_RECORD ?
2486 MEMMAP_EXTRA_MARGIN : 0);
2487 #ifdef USE_RELOC
2488 if (share->data_file_type == STATIC_RECORD &&
2489 skr < share->base.reloc*share->base.min_pack_length)
2490 skr=share->base.reloc*share->base.min_pack_length;
2491 #endif
2492 if (skr != sort_info.filelength)
2493 if (mysql_file_chsize(info->dfile, skr, 0, MYF(0)))
2494 mi_check_print_warning(param,
2495 "Can't change size of datafile, error: %d",
2496 my_errno);
2497 }
2498 if (param->testflag & T_CALC_CHECKSUM)
2499 info->state->checksum=param->glob_crc;
2500
2501 if (mysql_file_chsize(share->kfile, info->state->key_file_length, 0, MYF(0)))
2502 mi_check_print_warning(param,
2503 "Can't change size of indexfile, error: %d",
2504 my_errno);
2505
2506 if (!(param->testflag & T_SILENT))
2507 {
2508 if (start_records != info->state->records)
2509 printf("Data records: %s\n", llstr(info->state->records,llbuff));
2510 if (sort_info.dupp)
2511 mi_check_print_warning(param,
2512 "%s records have been removed",
2513 llstr(sort_info.dupp,llbuff));
2514 }
2515 got_error=0;
2516
2517 if (&share->state.state != info->state)
2518 memcpy( &share->state.state, info->state, sizeof(*info->state));
2519
2520 err:
2521 got_error|= flush_blocks(param, share->key_cache, share->kfile,
2522 &share->dirty_part_map);
2523 (void) end_io_cache(&info->rec_cache);
2524 if (!got_error)
2525 {
2526 /* Replace the actual file with the temporary file */
2527 if (new_file >= 0)
2528 {
2529 got_error= replace_data_file(param, info, new_file);
2530 new_file= -1;
2531 }
2532 }
2533 if (got_error)
2534 {
2535 if (! param->error_printed)
2536 mi_check_print_error(param,"%d when fixing table",my_errno);
2537 if (new_file >= 0)
2538 {
2539 (void) mysql_file_close(new_file, MYF(0));
2540 (void) mysql_file_delete(mi_key_file_datatmp,
2541 param->temp_filename, MYF(MY_WME));
2542 if (info->dfile == new_file) /* Retry with key cache */
2543 if (unlikely(mi_open_datafile(info, share)))
2544 param->retry_repair= 0; /* Safety */
2545 }
2546 mi_mark_crashed_on_repair(info);
2547 if (killed_ptr(param))
2548 param->retry_repair= 0; /* No use to retry repair */
2549 }
2550 else if (key_map == share->state.key_map)
2551 share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
2552 share->state.changed|=STATE_NOT_SORTED_PAGES;
2553
2554 my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff));
2555 my_free(mi_get_rec_buff_ptr(info, sort_param.record));
2556 my_free(sort_info.key_block);
2557 my_free(sort_info.ft_buf);
2558 my_free(sort_info.buff);
2559 (void) end_io_cache(¶m->read_cache);
2560 info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
2561 if (!got_error && (param->testflag & T_UNPACK))
2562 {
2563 share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
2564 share->pack.header_length=0;
2565 }
2566 DBUG_RETURN(got_error);
2567 }
2568
2569 /*
2570 Threaded repair of table using sorting
2571
2572 SYNOPSIS
2573 mi_repair_parallel()
2574 param Repair parameters
2575 info MyISAM handler to repair
2576 name Name of table (for warnings)
2577 rep_quick set to <> 0 if we should not change data file
2578
2579 DESCRIPTION
2580 Same as mi_repair_by_sort but do it multithreaded
2581 Each key is handled by a separate thread.
2582 TODO: make a number of threads a parameter
2583
2584 In parallel repair we use one thread per index. There are two modes:
2585
2586 Quick
2587
2588 Only the indexes are rebuilt. All threads share a read buffer.
2589 Every thread that needs fresh data in the buffer enters the shared
2590 cache lock. The last thread joining the lock reads the buffer from
2591 the data file and wakes all other threads.
2592
2593 Non-quick
2594
2595 The data file is rebuilt and all indexes are rebuilt to point to
2596 the new record positions. One thread is the master thread. It
2597 reads from the old data file and writes to the new data file. It
2598 also creates one of the indexes. The other threads read from a
2599 buffer which is filled by the master. If they need fresh data,
2600 they enter the shared cache lock. If the masters write buffer is
2601 full, it flushes it to the new data file and enters the shared
2602 cache lock too. When all threads joined in the lock, the master
2603 copies its write buffer to the read buffer for the other threads
2604 and wakes them.
2605
2606 RESULT
2607 0 ok
2608 <>0 Error
2609 */
2610
2611 int mi_repair_parallel(HA_CHECK *param, register MI_INFO *info,
2612 const char * name, int rep_quick)
2613 {
2614 int got_error;
2615 uint i,key, istep;
2616 ulong rec_length;
2617 ha_rows start_records;
2618 my_off_t new_header_length,del;
2619 File new_file;
2620 MI_SORT_PARAM *sort_param=0;
2621 MYISAM_SHARE *share=info->s;
2622 ulong *rec_per_key_part;
2623 HA_KEYSEG *keyseg;
2624 char llbuff[22];
2625 IO_CACHE new_data_cache; /* For non-quick repair. */
2626 IO_CACHE_SHARE io_share;
2627 MI_SORT_INFO sort_info;
2628 ulonglong UNINIT_VAR(key_map);
2629 pthread_attr_t thr_attr;
2630 ulong max_pack_reclength;
2631 int error;
2632 DBUG_ENTER("mi_repair_parallel");
2633
2634 start_records=info->state->records;
2635 got_error=1;
2636 new_file= -1;
2637 new_header_length=(param->testflag & T_UNPACK) ? 0 :
2638 share->pack.header_length;
2639 if (!(param->testflag & T_SILENT))
2640 {
2641 printf("- parallel recovering (with sort) MyISAM-table '%s'\n",name);
2642 printf("Data records: %s\n", llstr(start_records,llbuff));
2643 }
2644 param->testflag|=T_REP_PARALLEL; /* for easy checking */
2645 param->retry_repair= 0;
2646 param->warning_printed= 0;
2647 param->error_printed= 0;
2648
2649 if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
2650 param->testflag|=T_CALC_CHECKSUM;
2651
2652 /*
2653 Quick repair (not touching data file, rebuilding indexes):
2654 {
2655 Read cache is (HA_CHECK *param)->read_cache using info->dfile.
2656 }
2657
2658 Non-quick repair (rebuilding data file and indexes):
2659 {
2660 Master thread:
2661
2662 Read cache is (HA_CHECK *param)->read_cache using info->dfile.
2663 Write cache is (MI_INFO *info)->rec_cache using new_file.
2664
2665 Slave threads:
2666
2667 Read cache is new_data_cache synced to master rec_cache.
2668
2669 The final assignment of the filedescriptor for rec_cache is done
2670 after the cache creation.
2671
2672 Don't check file size on new_data_cache, as the resulting file size
2673 is not known yet.
2674
2675 As rec_cache and new_data_cache are synced, write_buffer_length is
2676 used for the read cache 'new_data_cache'. Both start at the same
2677 position 'new_header_length'.
2678 }
2679 */
2680 DBUG_PRINT("info", ("is quick repair: %d", rep_quick));
2681 bzero((char*)&sort_info,sizeof(sort_info));
2682 if (!rep_quick)
2683 my_b_clear(&new_data_cache);
2684 /* Initialize pthread structures before goto err. */
2685 mysql_mutex_init(mi_key_mutex_MI_SORT_INFO_mutex,
2686 &sort_info.mutex, MY_MUTEX_INIT_FAST);
2687 mysql_cond_init(mi_key_cond_MI_SORT_INFO_cond, &sort_info.cond, 0);
2688 mysql_mutex_init(mi_key_mutex_MI_CHECK_print_msg,
2689 ¶m->print_msg_mutex, MY_MUTEX_INIT_FAST);
2690 param->need_print_msg_lock= 1;
2691
2692 if (!(sort_info.key_block=
2693 alloc_key_blocks(param, (uint) param->sort_key_blocks,
2694 share->base.max_key_block_length)))
2695 goto err;
2696
2697 if (init_io_cache(¶m->read_cache, info->dfile,
2698 (uint) param->read_buffer_length,
2699 READ_CACHE, share->pack.header_length, 1, MYF(MY_WME)))
2700 goto err;
2701
2702 sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
2703 info->opt_flag|=WRITE_CACHE_USED;
2704
2705 if (!rep_quick)
2706 {
2707 /* Get real path for data file */
2708 if ((new_file= mysql_file_create(mi_key_file_datatmp,
2709 fn_format(param->temp_filename,
2710 share->data_file_name, "",
2711 DATA_TMP_EXT, 2+4),
2712 0, param->tmpfile_createflag,
2713 MYF(0))) < 0)
2714 {
2715 mi_check_print_error(param,"Can't create new tempfile: '%s'",
2716 param->temp_filename);
2717 goto err;
2718 }
2719 if (new_header_length &&
2720 filecopy(param, new_file,info->dfile,0L,new_header_length,
2721 "datafile-header"))
2722 goto err;
2723 if (param->testflag & T_UNPACK)
2724 {
2725 share->options&= ~HA_OPTION_COMPRESS_RECORD;
2726 mi_int2store(share->state.header.options,share->options);
2727 }
2728 share->state.dellink= HA_OFFSET_ERROR;
2729
2730 if (init_io_cache(&info->rec_cache, new_file,
2731 (uint) param->write_buffer_length,
2732 WRITE_CACHE, new_header_length, 1,
2733 MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw))
2734 goto err;
2735
2736 if (init_io_cache(&new_data_cache, -1,
2737 (uint) param->write_buffer_length,
2738 READ_CACHE, new_header_length, 1,
2739 MYF(MY_WME | MY_DONT_CHECK_FILESIZE)))
2740 goto err;
2741 }
2742
2743 info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
2744
2745 /* Optionally drop indexes and optionally modify the key_map. */
2746 mi_drop_all_indexes(param, info, FALSE);
2747 key_map= share->state.key_map;
2748 if (param->testflag & T_CREATE_MISSING_KEYS)
2749 {
2750 /* Invert the copied key_map to recreate all disabled indexes. */
2751 key_map= ~key_map;
2752 }
2753
2754 sort_info.info=info;
2755 sort_info.param = param;
2756
2757 set_data_file_type(&sort_info, share);
2758 sort_info.dupp=0;
2759 sort_info.buff=0;
2760 param->read_cache.end_of_file=sort_info.filelength=
2761 mysql_file_seek(param->read_cache.file, 0L, MY_SEEK_END, MYF(0));
2762 if (info->state->data_file_length == 0)
2763 info->state->data_file_length= sort_info.filelength;
2764
2765 if (share->data_file_type == DYNAMIC_RECORD)
2766 rec_length=MY_MAX(share->base.min_pack_length+1,share->base.min_block_length);
2767 else if (share->data_file_type == COMPRESSED_RECORD)
2768 rec_length=share->base.min_block_length;
2769 else
2770 rec_length=share->base.pack_reclength;
2771 /*
2772 +1 below is required hack for parallel repair mode.
2773 The info->state->records value, that is compared later
2774 to sort_info.max_records and cannot exceed it, is
2775 increased in sort_key_write. In mi_repair_by_sort, sort_key_write
2776 is called after sort_key_read, where the comparison is performed,
2777 but in parallel mode master thread can call sort_key_write
2778 before some other repair thread calls sort_key_read.
2779 Furthermore I'm not even sure +1 would be enough.
2780 May be sort_info.max_records shold be always set to max value in
2781 parallel mode.
2782 */
2783 sort_info.max_records=
2784 ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records + 1:
2785 (ha_rows) (sort_info.filelength/rec_length+1));
2786
2787 del=info->state->del;
2788 param->glob_crc=0;
2789 /* for compressed tables */
2790 max_pack_reclength= MY_MAX(share->base.pack_reclength, share->vreclength);
2791 if (share->options & HA_OPTION_COMPRESS_RECORD)
2792 set_if_bigger(max_pack_reclength, share->max_pack_length);
2793 if (!(sort_param=(MI_SORT_PARAM *)
2794 my_malloc((uint) share->base.keys *
2795 (sizeof(MI_SORT_PARAM) + max_pack_reclength),
2796 MYF(MY_ZEROFILL))))
2797 {
2798 mi_check_print_error(param,"Not enough memory for key!");
2799 goto err;
2800 }
2801 #ifdef USING_SECOND_APPROACH
2802 uint total_key_length=0;
2803 #endif
2804 rec_per_key_part= param->rec_per_key_part;
2805 info->state->records=info->state->del=share->state.split=0;
2806 info->state->empty=0;
2807
2808 for (i=key=0, istep=1 ; key < share->base.keys ;
2809 rec_per_key_part+=sort_param[i].keyinfo->keysegs, i+=istep, key++)
2810 {
2811 sort_param[i].key=key;
2812 sort_param[i].keyinfo=share->keyinfo+key;
2813 sort_param[i].seg=sort_param[i].keyinfo->seg;
2814 /*
2815 Skip this index if it is marked disabled in the copied
2816 (and possibly inverted) key_map.
2817 */
2818 if (! mi_is_key_active(key_map, key))
2819 {
2820 /* Remember old statistics for key */
2821 memcpy((char*) rec_per_key_part,
2822 (char*) (share->state.rec_per_key_part+
2823 (uint) (rec_per_key_part - param->rec_per_key_part)),
2824 sort_param[i].keyinfo->keysegs*sizeof(*rec_per_key_part));
2825 istep=0;
2826 continue;
2827 }
2828 istep=1;
2829 if ((!(param->testflag & T_SILENT)))
2830 printf ("- Fixing index %d\n",key+1);
2831 if (sort_param[i].keyinfo->flag & HA_FULLTEXT)
2832 {
2833 sort_param[i].key_read=sort_ft_key_read;
2834 sort_param[i].key_write=sort_ft_key_write;
2835 }
2836 else
2837 {
2838 sort_param[i].key_read=sort_key_read;
2839 sort_param[i].key_write=sort_key_write;
2840 }
2841 sort_param[i].key_cmp=sort_key_cmp;
2842 sort_param[i].lock_in_memory=lock_memory;
2843 sort_param[i].tmpdir=param->tmpdir;
2844 sort_param[i].sort_info=&sort_info;
2845 sort_param[i].master=0;
2846 sort_param[i].fix_datafile=0;
2847 sort_param[i].calc_checksum= 0;
2848
2849 sort_param[i].filepos=new_header_length;
2850 sort_param[i].max_pos=sort_param[i].pos=share->pack.header_length;
2851
2852 sort_param[i].record= (((uchar *)(sort_param+share->base.keys))+
2853 (max_pack_reclength * i));
2854 if (!mi_alloc_rec_buff(info, -1, &sort_param[i].rec_buff))
2855 {
2856 mi_check_print_error(param,"Not enough memory!");
2857 goto err;
2858 }
2859
2860 sort_param[i].key_length=share->rec_reflength;
2861 for (keyseg=sort_param[i].seg; keyseg->type != HA_KEYTYPE_END;
2862 keyseg++)
2863 {
2864 sort_param[i].key_length+=keyseg->length;
2865 if (keyseg->flag & HA_SPACE_PACK)
2866 sort_param[i].key_length+=get_pack_length(keyseg->length);
2867 if (keyseg->flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
2868 sort_param[i].key_length+= 2 + MY_TEST(keyseg->length >= 127);
2869 if (keyseg->flag & HA_NULL_PART)
2870 sort_param[i].key_length++;
2871 }
2872 #ifdef USING_SECOND_APPROACH
2873 total_key_length+=sort_param[i].key_length;
2874 #endif
2875
2876 if (sort_param[i].keyinfo->flag & HA_FULLTEXT)
2877 {
2878 uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT*
2879 sort_param[i].keyinfo->seg->charset->mbmaxlen;
2880 sort_param[i].key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
2881 init_alloc_root(&sort_param[i].wordroot, "sort",
2882 FTPARSER_MEMROOT_ALLOC_SIZE, 0,
2883 MYF(param->malloc_flags));
2884 }
2885 }
2886 sort_info.total_keys=i;
2887 sort_param[0].master= 1;
2888 sort_param[0].fix_datafile= (my_bool)(! rep_quick);
2889 sort_param[0].calc_checksum= MY_TEST(param->testflag & T_CALC_CHECKSUM);
2890
2891 if (!ftparser_alloc_param(info))
2892 goto err;
2893
2894 sort_info.got_error=0;
2895 mysql_mutex_lock(&sort_info.mutex);
2896
2897 /*
2898 Initialize the I/O cache share for use with the read caches and, in
2899 case of non-quick repair, the write cache. When all threads join on
2900 the cache lock, the writer copies the write cache contents to the
2901 read caches.
2902 */
2903 if (i > 1)
2904 {
2905 if (rep_quick)
2906 init_io_cache_share(¶m->read_cache, &io_share, NULL, i);
2907 else
2908 init_io_cache_share(&new_data_cache, &io_share, &info->rec_cache, i);
2909 }
2910 else
2911 io_share.total_threads= 0; /* share not used */
2912
2913 (void) pthread_attr_init(&thr_attr);
2914 (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
2915
2916 for (i=0 ; i < sort_info.total_keys ; i++)
2917 {
2918 /*
2919 Copy the properly initialized IO_CACHE structure so that every
2920 thread has its own copy. In quick mode param->read_cache is shared
2921 for use by all threads. In non-quick mode all threads but the
2922 first copy the shared new_data_cache, which is synchronized to the
2923 write cache of the first thread. The first thread copies
2924 param->read_cache, which is not shared.
2925 */
2926 sort_param[i].read_cache= ((rep_quick || !i) ? param->read_cache :
2927 new_data_cache);
2928 DBUG_PRINT("io_cache_share", ("thread: %u read_cache: %p",
2929 i, &sort_param[i].read_cache));
2930
2931 /*
2932 two approaches: the same amount of memory for each thread
2933 or the memory for the same number of keys for each thread...
2934 In the second one all the threads will fill their sort_buffers
2935 (and call write_keys) at the same time, putting more stress on i/o.
2936 */
2937 sort_param[i].sortbuff_size=
2938 #ifndef USING_SECOND_APPROACH
2939 param->sort_buffer_length/sort_info.total_keys;
2940 #else
2941 param->sort_buffer_length*sort_param[i].key_length/total_key_length;
2942 #endif
2943 if ((error= mysql_thread_create(mi_key_thread_find_all_keys,
2944 &sort_param[i].thr, &thr_attr,
2945 thr_find_all_keys,
2946 (void *) (sort_param+i))))
2947 {
2948 mi_check_print_error(param,"Cannot start a repair thread (errno= %d)",
2949 error);
2950 /* Cleanup: Detach from the share. Avoid others to be blocked. */
2951 if (io_share.total_threads)
2952 remove_io_thread(&sort_param[i].read_cache);
2953 DBUG_PRINT("error", ("Cannot start a repair thread"));
2954 sort_info.got_error=1;
2955 }
2956 else
2957 sort_info.threads_running++;
2958 }
2959 (void) pthread_attr_destroy(&thr_attr);
2960
2961 /* waiting for all threads to finish */
2962 while (sort_info.threads_running)
2963 mysql_cond_wait(&sort_info.cond, &sort_info.mutex);
2964 mysql_mutex_unlock(&sort_info.mutex);
2965
2966 if ((got_error= thr_write_keys(sort_param)))
2967 {
2968 param->retry_repair=1;
2969 goto err;
2970 }
2971 got_error=1; /* Assume the following may go wrong */
2972
2973 if (sort_param[0].fix_datafile)
2974 {
2975 /*
2976 Append some nuls to the end of a memory mapped file. Destroy the
2977 write cache. The master thread did already detach from the share
2978 by remove_io_thread() in sort.c:thr_find_all_keys().
2979 */
2980 if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache))
2981 goto err;
2982 if (param->testflag & T_SAFE_REPAIR)
2983 {
2984 /* Don't repair if we loosed more than one row */
2985 if (info->state->records+1 < start_records)
2986 {
2987 info->state->records=start_records;
2988 goto err;
2989 }
2990 }
2991 share->state.state.data_file_length= info->state->data_file_length=
2992 sort_param->filepos;
2993 /* Only whole records */
2994 share->state.version=(ulong) time((time_t*) 0);
2995
2996 /*
2997 Exchange the data file descriptor of the table, so that we use the
2998 new file from now on.
2999 */
3000 mysql_file_close(info->dfile, MYF(0));
3001 info->dfile=new_file;
3002
3003 share->data_file_type=sort_info.new_data_file_type;
3004 share->pack.header_length=(ulong) new_header_length;
3005 }
3006 else
3007 info->state->data_file_length=sort_param->max_pos;
3008
3009 if (rep_quick && del+sort_info.dupp != info->state->del)
3010 {
3011 mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
3012 mi_check_print_error(param,"Run recovery again without -q");
3013 param->retry_repair=1;
3014 param->testflag|=T_RETRY_WITHOUT_QUICK;
3015 goto err;
3016 }
3017
3018 if (rep_quick && (param->testflag & T_FORCE_UNIQUENESS))
3019 {
3020 my_off_t skr=info->state->data_file_length+
3021 (share->options & HA_OPTION_COMPRESS_RECORD ?
3022 MEMMAP_EXTRA_MARGIN : 0);
3023 #ifdef USE_RELOC
3024 if (share->data_file_type == STATIC_RECORD &&
3025 skr < share->base.reloc*share->base.min_pack_length)
3026 skr=share->base.reloc*share->base.min_pack_length;
3027 #endif
3028 if (skr != sort_info.filelength)
3029 if (mysql_file_chsize(info->dfile, skr, 0, MYF(0)))
3030 mi_check_print_warning(param,
3031 "Can't change size of datafile, error: %d",
3032 my_errno);
3033 }
3034 if (param->testflag & T_CALC_CHECKSUM)
3035 info->state->checksum=param->glob_crc;
3036
3037 if (mysql_file_chsize(share->kfile, info->state->key_file_length, 0, MYF(0)))
3038 mi_check_print_warning(param,
3039 "Can't change size of indexfile, error: %d", my_errno);
3040
3041 if (!(param->testflag & T_SILENT))
3042 {
3043 if (start_records != info->state->records)
3044 printf("Data records: %s\n", llstr(info->state->records,llbuff));
3045 if (sort_info.dupp)
3046 mi_check_print_warning(param,
3047 "%s records have been removed",
3048 llstr(sort_info.dupp,llbuff));
3049 }
3050 got_error=0;
3051
3052 if (&share->state.state != info->state)
3053 memcpy(&share->state.state, info->state, sizeof(*info->state));
3054
3055 err:
3056 got_error|= flush_blocks(param, share->key_cache, share->kfile,
3057 &share->dirty_part_map);
3058 /*
3059 Destroy the write cache. The master thread did already detach from
3060 the share by remove_io_thread() or it was not yet started (if the
3061 error happened before creating the thread).
3062 */
3063 (void) end_io_cache(&info->rec_cache);
3064 /*
3065 Destroy the new data cache in case of non-quick repair. All slave
3066 threads did either detach from the share by remove_io_thread()
3067 already or they were not yet started (if the error happened before
3068 creating the threads).
3069 */
3070 if (!rep_quick && my_b_inited(&new_data_cache))
3071 (void) end_io_cache(&new_data_cache);
3072 if (!got_error)
3073 {
3074 /* Replace the actual file with the temporary file */
3075 if (new_file >= 0)
3076 {
3077 got_error= replace_data_file(param, info, new_file);
3078 new_file= -1;
3079 }
3080 }
3081 if (got_error)
3082 {
3083 if (! param->error_printed)
3084 mi_check_print_error(param,"%d when fixing table",my_errno);
3085 if (new_file >= 0)
3086 {
3087 (void) mysql_file_close(new_file, MYF(0));
3088 (void) mysql_file_delete(mi_key_file_datatmp,
3089 param->temp_filename, MYF(MY_WME));
3090 if (info->dfile == new_file) /* Retry with key cache */
3091 if (unlikely(mi_open_datafile(info, share)))
3092 param->retry_repair= 0; /* Safety */
3093 }
3094 mi_mark_crashed_on_repair(info);
3095 if (killed_ptr(param))
3096 param->retry_repair= 0;
3097 }
3098 else if (key_map == share->state.key_map)
3099 share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
3100 share->state.changed|=STATE_NOT_SORTED_PAGES;
3101
3102 mysql_cond_destroy(&sort_info.cond);
3103 mysql_mutex_destroy(&sort_info.mutex);
3104 mysql_mutex_destroy(¶m->print_msg_mutex);
3105 param->need_print_msg_lock= 0;
3106
3107 my_free(sort_info.ft_buf);
3108 my_free(sort_info.key_block);
3109 my_free(sort_param);
3110 my_free(sort_info.buff);
3111 (void) end_io_cache(¶m->read_cache);
3112 info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
3113 if (!got_error && (param->testflag & T_UNPACK))
3114 {
3115 share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
3116 share->pack.header_length=0;
3117 }
3118 DBUG_RETURN(got_error);
3119 }
3120
3121 /* Read next record and return next key */
3122
3123 static int sort_key_read(MI_SORT_PARAM *sort_param, void *key)
3124 {
3125 int error;
3126 MI_SORT_INFO *sort_info=sort_param->sort_info;
3127 MI_INFO *info=sort_info->info;
3128 DBUG_ENTER("sort_key_read");
3129
3130 if ((error=sort_get_next_record(sort_param)))
3131 {
3132 DBUG_ASSERT(error < 0 ||
3133 sort_info->param->error_printed ||
3134 sort_info->param->warning_printed ||
3135 sort_info->param->note_printed);
3136 DBUG_RETURN(error);
3137 }
3138 if (info->state->records == sort_info->max_records)
3139 {
3140 my_errno= HA_ERR_WRONG_IN_RECORD;
3141 mi_check_print_error(sort_info->param,
3142 "Key %d - Found too many records; Can't continue",
3143 sort_param->key+1);
3144 DBUG_RETURN(1);
3145 }
3146 sort_param->real_key_length=
3147 (info->s->rec_reflength+
3148 _mi_make_key(info, sort_param->key, (uchar*) key,
3149 sort_param->record, sort_param->filepos));
3150 #ifdef HAVE_valgrind
3151 bzero(key+sort_param->real_key_length,
3152 (sort_param->key_length-sort_param->real_key_length));
3153 #endif
3154 DBUG_RETURN(sort_write_record(sort_param));
3155 } /* sort_key_read */
3156
3157 static int sort_ft_key_read(MI_SORT_PARAM *sort_param, void *key)
3158 {
3159 int error;
3160 MI_SORT_INFO *sort_info=sort_param->sort_info;
3161 MI_INFO *info=sort_info->info;
3162 FT_WORD *wptr=0;
3163 DBUG_ENTER("sort_ft_key_read");
3164
3165 if (!sort_param->wordlist)
3166 {
3167 for (;;)
3168 {
3169 free_root(&sort_param->wordroot, MYF(MY_MARK_BLOCKS_FREE));
3170 if ((error=sort_get_next_record(sort_param)))
3171 DBUG_RETURN(error);
3172 if (!(wptr=_mi_ft_parserecord(info,sort_param->key,sort_param->record,
3173 &sort_param->wordroot)))
3174 DBUG_RETURN(1);
3175 if (wptr->pos)
3176 break;
3177 error=sort_write_record(sort_param);
3178 }
3179 sort_param->wordptr=sort_param->wordlist=wptr;
3180 }
3181 else
3182 {
3183 error=0;
3184 wptr=(FT_WORD*)(sort_param->wordptr);
3185 }
3186
3187 sort_param->real_key_length=(info->s->rec_reflength+
3188 _ft_make_key(info, sort_param->key,
3189 key, wptr++, sort_param->filepos));
3190 #ifdef HAVE_valgrind
3191 if (sort_param->key_length > sort_param->real_key_length)
3192 bzero(key+sort_param->real_key_length,
3193 (sort_param->key_length-sort_param->real_key_length));
3194 #endif
3195 if (!wptr->pos)
3196 {
3197 free_root(&sort_param->wordroot, MYF(MY_MARK_BLOCKS_FREE));
3198 sort_param->wordlist=0;
3199 error=sort_write_record(sort_param);
3200 }
3201 else
3202 sort_param->wordptr=(void*)wptr;
3203
3204 DBUG_RETURN(error);
3205 } /* sort_ft_key_read */
3206
3207
3208 /*
3209 Read next record from file using parameters in sort_info.
3210
3211 SYNOPSIS
3212 sort_get_next_record()
3213 sort_param Information about and for the sort process
3214
3215 NOTE
3216
3217 Dynamic Records With Non-Quick Parallel Repair
3218
3219 For non-quick parallel repair we use a synchronized read/write
3220 cache. This means that one thread is the master who fixes the data
3221 file by reading each record from the old data file and writing it
3222 to the new data file. By doing this the records in the new data
3223 file are written contiguously. Whenever the write buffer is full,
3224 it is copied to the read buffer. The slaves read from the read
3225 buffer, which is not associated with a file. Thus read_cache.file
3226 is -1. When using _mi_read_cache(), the slaves must always set
3227 flag to READING_NEXT so that the function never tries to read from
3228 file. This is safe because the records are contiguous. There is no
3229 need to read outside the cache. This condition is evaluated in the
3230 variable 'parallel_flag' for quick reference. read_cache.file must
3231 be >= 0 in every other case.
3232
3233 RETURN
3234 -1 end of file
3235 0 ok
3236 > 0 error
3237 */
3238
3239 static int sort_get_next_record(MI_SORT_PARAM *sort_param)
3240 {
3241 int searching;
3242 int parallel_flag;
3243 uint found_record,b_type,left_length;
3244 my_off_t pos;
3245 uchar *UNINIT_VAR(to);
3246 MI_BLOCK_INFO block_info;
3247 MI_SORT_INFO *sort_info=sort_param->sort_info;
3248 HA_CHECK *param=sort_info->param;
3249 MI_INFO *info=sort_info->info;
3250 MYISAM_SHARE *share=info->s;
3251 char llbuff[22],llbuff2[22];
3252 DBUG_ENTER("sort_get_next_record");
3253
3254 if (killed_ptr(param))
3255 {
3256 mi_check_print_error(param, "Repair killed by user with cause: %d",
3257 (int) killed_ptr(param));
3258 param->retry_repair= 0;
3259 DBUG_RETURN(1);
3260 }
3261
3262 switch (share->data_file_type) {
3263 case STATIC_RECORD:
3264 for (;;)
3265 {
3266 if (my_b_read(&sort_param->read_cache,sort_param->record,
3267 share->base.pack_reclength))
3268 {
3269 if (sort_param->read_cache.error)
3270 param->out_flag |= O_DATA_LOST;
3271 param->retry_repair=1;
3272 param->testflag|=T_RETRY_WITHOUT_QUICK;
3273 DBUG_RETURN(-1);
3274 }
3275 sort_param->start_recpos=sort_param->pos;
3276 if (!sort_param->fix_datafile)
3277 {
3278 sort_param->filepos=sort_param->pos;
3279 if (sort_param->master)
3280 share->state.split++;
3281 }
3282 sort_param->max_pos=(sort_param->pos+=share->base.pack_reclength);
3283 if (*sort_param->record)
3284 {
3285 if (sort_param->calc_checksum)
3286 info->checksum= (*info->s->calc_check_checksum)(info, sort_param->record);
3287 goto finish;
3288 }
3289 if (!sort_param->fix_datafile && sort_param->master)
3290 {
3291 info->state->del++;
3292 info->state->empty+=share->base.pack_reclength;
3293 }
3294 }
3295 case DYNAMIC_RECORD:
3296 pos=sort_param->pos;
3297 searching=(sort_param->fix_datafile && (param->testflag & T_EXTEND));
3298 parallel_flag= (sort_param->read_cache.file < 0) ? READING_NEXT : 0;
3299 for (;;)
3300 {
3301 found_record=block_info.second_read= 0;
3302 left_length=1;
3303 if (searching)
3304 {
3305 pos=MY_ALIGN(pos,MI_DYN_ALIGN_SIZE);
3306 param->testflag|=T_RETRY_WITHOUT_QUICK;
3307 sort_param->start_recpos=pos;
3308 }
3309 do
3310 {
3311 if (pos > sort_param->max_pos)
3312 sort_param->max_pos=pos;
3313 if (pos & (MI_DYN_ALIGN_SIZE-1))
3314 {
3315 if ((param->testflag & T_VERBOSE) || searching == 0)
3316 mi_check_print_info(param,"Wrong aligned block at %s",
3317 llstr(pos,llbuff));
3318 if (searching)
3319 goto try_next;
3320 }
3321 if (found_record && pos == param->search_after_block)
3322 mi_check_print_info(param,"Block: %s used by record at %s",
3323 llstr(param->search_after_block,llbuff),
3324 llstr(sort_param->start_recpos,llbuff2));
3325 if (_mi_read_cache(&sort_param->read_cache,
3326 (uchar*) block_info.header,pos,
3327 MI_BLOCK_INFO_HEADER_LENGTH,
3328 (! found_record ? READING_NEXT : 0) |
3329 parallel_flag | READING_HEADER))
3330 {
3331 if (found_record)
3332 {
3333 mi_check_print_info(param,
3334 "Can't read whole record at %s (errno: %d)",
3335 llstr(sort_param->start_recpos,llbuff),errno);
3336 goto try_next;
3337 }
3338 DBUG_RETURN(-1);
3339 }
3340 if (searching && ! sort_param->fix_datafile)
3341 {
3342 mi_check_print_info(param,
3343 "Datafile is corrupted; Restart repair with option to copy datafile");
3344 param->error_printed=1;
3345 param->retry_repair=1;
3346 param->testflag|=T_RETRY_WITHOUT_QUICK;
3347 my_errno= HA_ERR_WRONG_IN_RECORD;
3348 DBUG_RETURN(1); /* Something wrong with data */
3349 }
3350 b_type=_mi_get_block_info(&block_info,-1,pos);
3351 if ((b_type & (BLOCK_ERROR | BLOCK_FATAL_ERROR)) ||
3352 ((b_type & BLOCK_FIRST) &&
3353 (block_info.rec_len < (uint) share->base.min_pack_length ||
3354 block_info.rec_len > (uint) share->base.max_pack_length)))
3355 {
3356 uint i;
3357 if (param->testflag & T_VERBOSE || searching == 0)
3358 mi_check_print_info(param,
3359 "Wrong bytesec: %3d-%3d-%3d at %10s; Skipped",
3360 block_info.header[0],block_info.header[1],
3361 block_info.header[2],llstr(pos,llbuff));
3362 if (found_record)
3363 goto try_next;
3364 block_info.second_read=0;
3365 searching=1;
3366 /* Search after block in read header string */
3367 for (i=MI_DYN_ALIGN_SIZE ;
3368 i < MI_BLOCK_INFO_HEADER_LENGTH ;
3369 i+= MI_DYN_ALIGN_SIZE)
3370 if (block_info.header[i] >= 1 &&
3371 block_info.header[i] <= MI_MAX_DYN_HEADER_BYTE)
3372 break;
3373 pos+=(ulong) i;
3374 sort_param->start_recpos=pos;
3375 continue;
3376 }
3377 if (b_type & BLOCK_DELETED)
3378 {
3379 my_bool error=0;
3380 if (block_info.block_len+ (uint) (block_info.filepos-pos) <
3381 share->base.min_block_length)
3382 {
3383 if (!searching)
3384 mi_check_print_info(param,
3385 "Deleted block with impossible length %u at %s",
3386 block_info.block_len,llstr(pos,llbuff));
3387 error=1;
3388 }
3389 else
3390 {
3391 if ((block_info.next_filepos != HA_OFFSET_ERROR &&
3392 block_info.next_filepos >=
3393 info->state->data_file_length) ||
3394 (block_info.prev_filepos != HA_OFFSET_ERROR &&
3395 block_info.prev_filepos >= info->state->data_file_length))
3396 {
3397 if (!searching)
3398 mi_check_print_info(param,
3399 "Delete link points outside datafile at %s",
3400 llstr(pos,llbuff));
3401 error=1;
3402 }
3403 }
3404 if (error)
3405 {
3406 DBUG_ASSERT(param->note_printed);
3407 if (found_record)
3408 goto try_next;
3409 searching=1;
3410 pos+= MI_DYN_ALIGN_SIZE;
3411 sort_param->start_recpos=pos;
3412 block_info.second_read=0;
3413 continue;
3414 }
3415 }
3416 else
3417 {
3418 if (block_info.block_len+ (uint) (block_info.filepos-pos) <
3419 share->base.min_block_length ||
3420 block_info.block_len > (uint) share->base.max_pack_length+
3421 MI_SPLIT_LENGTH)
3422 {
3423 if (!searching)
3424 mi_check_print_info(param,
3425 "Found block with impossible length %u at %s; Skipped",
3426 block_info.block_len+ (uint) (block_info.filepos-pos),
3427 llstr(pos,llbuff));
3428 if (found_record)
3429 goto try_next;
3430 searching=1;
3431 pos+= MI_DYN_ALIGN_SIZE;
3432 sort_param->start_recpos=pos;
3433 block_info.second_read=0;
3434 continue;
3435 }
3436 }
3437 if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
3438 {
3439 if (!sort_param->fix_datafile && sort_param->master &&
3440 (b_type & BLOCK_DELETED))
3441 {
3442 info->state->empty+=block_info.block_len;
3443 info->state->del++;
3444 share->state.split++;
3445 }
3446 if (found_record)
3447 {
3448 mi_check_print_info(param,
3449 "Found row block followed by deleted block");
3450 goto try_next;
3451 }
3452 if (searching)
3453 {
3454 pos+=MI_DYN_ALIGN_SIZE;
3455 sort_param->start_recpos=pos;
3456 }
3457 else
3458 pos=block_info.filepos+block_info.block_len;
3459 block_info.second_read=0;
3460 continue;
3461 }
3462
3463 if (!sort_param->fix_datafile && sort_param->master)
3464 share->state.split++;
3465 if (! found_record++)
3466 {
3467 sort_param->find_length=left_length=block_info.rec_len;
3468 sort_param->start_recpos=pos;
3469 if (!sort_param->fix_datafile)
3470 sort_param->filepos=sort_param->start_recpos;
3471 if (sort_param->fix_datafile && (param->testflag & T_EXTEND))
3472 sort_param->pos=block_info.filepos+1;
3473 else
3474 sort_param->pos=block_info.filepos+block_info.block_len;
3475 if (share->base.blobs)
3476 {
3477 if (!(to=mi_alloc_rec_buff(info,block_info.rec_len,
3478 &(sort_param->rec_buff))))
3479 {
3480 if (param->max_record_length >= block_info.rec_len)
3481 {
3482 mi_check_print_error(param,"Not enough memory for blob at %s (need %lu)",
3483 llstr(sort_param->start_recpos,llbuff),
3484 (ulong) block_info.rec_len);
3485 DBUG_ASSERT(param->error_printed);
3486 DBUG_RETURN(1);
3487 }
3488 else
3489 {
3490 mi_check_print_info(param,"Not enough memory for blob at %s (need %lu); Row skipped",
3491 llstr(sort_param->start_recpos,llbuff),
3492 (ulong) block_info.rec_len);
3493 goto try_next;
3494 }
3495 }
3496 }
3497 else
3498 to= sort_param->rec_buff;
3499 }
3500 if (left_length < block_info.data_len || ! block_info.data_len)
3501 {
3502 mi_check_print_info(param,
3503 "Found block with too small length at %s; Skipped",
3504 llstr(sort_param->start_recpos,llbuff));
3505 goto try_next;
3506 }
3507 if (block_info.filepos + block_info.data_len >
3508 sort_param->read_cache.end_of_file)
3509 {
3510 mi_check_print_info(param,
3511 "Found block that points outside data file at %s",
3512 llstr(sort_param->start_recpos,llbuff));
3513 goto try_next;
3514 }
3515 /*
3516 Copy information that is already read. Avoid accessing data
3517 below the cache start. This could happen if the header
3518 streched over the end of the previous buffer contents.
3519 */
3520 {
3521 uint header_len= (uint) (block_info.filepos - pos);
3522 uint prefetch_len= (MI_BLOCK_INFO_HEADER_LENGTH - header_len);
3523
3524 if (prefetch_len > block_info.data_len)
3525 prefetch_len= block_info.data_len;
3526 if (prefetch_len)
3527 {
3528 memcpy(to, block_info.header + header_len, prefetch_len);
3529 block_info.filepos+= prefetch_len;
3530 block_info.data_len-= prefetch_len;
3531 left_length-= prefetch_len;
3532 to+= prefetch_len;
3533 }
3534 }
3535 if (block_info.data_len &&
3536 _mi_read_cache(&sort_param->read_cache,to,block_info.filepos,
3537 block_info.data_len,
3538 (found_record == 1 ? READING_NEXT : 0) |
3539 parallel_flag))
3540 {
3541 mi_check_print_info(param,
3542 "Read error for block at: %s (error: %d); Skipped",
3543 llstr(block_info.filepos,llbuff),my_errno);
3544 goto try_next;
3545 }
3546 left_length-=block_info.data_len;
3547 to+=block_info.data_len;
3548 pos=block_info.next_filepos;
3549 if (pos == HA_OFFSET_ERROR && left_length)
3550 {
3551 mi_check_print_info(param,"Wrong block with wrong total length starting at %s",
3552 llstr(sort_param->start_recpos,llbuff));
3553 goto try_next;
3554 }
3555 if (pos + MI_BLOCK_INFO_HEADER_LENGTH > sort_param->read_cache.end_of_file)
3556 {
3557 mi_check_print_info(param,"Found link that points at %s (outside data file) at %s",
3558 llstr(pos,llbuff2),
3559 llstr(sort_param->start_recpos,llbuff));
3560 goto try_next;
3561 }
3562 } while (left_length);
3563
3564 if (_mi_rec_unpack(info,sort_param->record,sort_param->rec_buff,
3565 sort_param->find_length) != MY_FILE_ERROR)
3566 {
3567 if (sort_param->calc_checksum)
3568 info->checksum= (*info->s->calc_check_checksum)(info,
3569 sort_param->record);
3570 if ((param->testflag & (T_EXTEND | T_REP_ANY)) || searching)
3571 {
3572 if (_mi_rec_check(info, sort_param->record, sort_param->rec_buff,
3573 sort_param->find_length,
3574 (param->testflag & T_QUICK) &&
3575 sort_param->calc_checksum &&
3576 MY_TEST(info->s->calc_checksum)))
3577 {
3578 mi_check_print_info(param,"Found wrong packed record at %s",
3579 llstr(sort_param->start_recpos,llbuff));
3580 goto try_next;
3581 }
3582 }
3583 goto finish;
3584 }
3585 if (!searching)
3586 mi_check_print_info(param,"Key %d - Found wrong stored record at %s",
3587 sort_param->key+1,
3588 llstr(sort_param->start_recpos,llbuff));
3589 try_next:
3590 DBUG_ASSERT(param->error_printed || param->note_printed);
3591 pos=(sort_param->start_recpos+=MI_DYN_ALIGN_SIZE);
3592 searching=1;
3593 }
3594 case COMPRESSED_RECORD:
3595 for (searching=0 ;; searching=1, sort_param->pos++)
3596 {
3597 if (_mi_read_cache(&sort_param->read_cache,(uchar*) block_info.header,
3598 sort_param->pos,
3599 share->pack.ref_length,READING_NEXT))
3600 DBUG_RETURN(-1);
3601 if (searching && ! sort_param->fix_datafile)
3602 {
3603 param->error_printed=1;
3604 param->retry_repair=1;
3605 param->testflag|=T_RETRY_WITHOUT_QUICK;
3606 my_errno= HA_ERR_WRONG_IN_RECORD;
3607 DBUG_RETURN(1); /* Something wrong with data */
3608 }
3609 sort_param->start_recpos=sort_param->pos;
3610 if (_mi_pack_get_block_info(info, &sort_param->bit_buff, &block_info,
3611 &sort_param->rec_buff, -1, sort_param->pos))
3612 DBUG_RETURN(-1);
3613 if (!block_info.rec_len &&
3614 sort_param->pos + MEMMAP_EXTRA_MARGIN ==
3615 sort_param->read_cache.end_of_file)
3616 DBUG_RETURN(-1);
3617 if (block_info.rec_len < (uint) share->min_pack_length ||
3618 block_info.rec_len > (uint) share->max_pack_length)
3619 {
3620 if (! searching)
3621 mi_check_print_info(param,"Found block with wrong recordlength: %d at %s\n",
3622 block_info.rec_len,
3623 llstr(sort_param->pos,llbuff));
3624 continue;
3625 }
3626 if (_mi_read_cache(&sort_param->read_cache,(uchar*) sort_param->rec_buff,
3627 block_info.filepos, block_info.rec_len,
3628 READING_NEXT))
3629 {
3630 if (! searching)
3631 mi_check_print_info(param,"Couldn't read whole record from %s",
3632 llstr(sort_param->pos,llbuff));
3633 continue;
3634 }
3635 if (_mi_pack_rec_unpack(info, &sort_param->bit_buff, sort_param->record,
3636 sort_param->rec_buff, block_info.rec_len))
3637 {
3638 if (! searching)
3639 mi_check_print_info(param,"Found wrong record at %s",
3640 llstr(sort_param->pos,llbuff));
3641 continue;
3642 }
3643 if (!sort_param->fix_datafile)
3644 {
3645 sort_param->filepos=sort_param->pos;
3646 if (sort_param->master)
3647 share->state.split++;
3648 }
3649 sort_param->max_pos=(sort_param->pos=block_info.filepos+
3650 block_info.rec_len);
3651 info->packed_length=block_info.rec_len;
3652 if (sort_param->calc_checksum)
3653 info->checksum= (*info->s->calc_check_checksum)(info, sort_param->record);
3654 goto finish;
3655 }
3656 default:
3657 DBUG_ASSERT(0); /* Impossible */
3658 break;
3659 }
3660 DBUG_ASSERT(0); /* Impossible */
3661 DBUG_RETURN(1); /* Impossible */
3662 finish:
3663 if (sort_param->calc_checksum)
3664 param->glob_crc+= info->checksum;
3665 if (param->fix_record)
3666 param->fix_record(info, sort_param->record,
3667 param->testflag & T_REP_BY_SORT ? (int)sort_param->key
3668 : -1);
3669 DBUG_RETURN(0);
3670 }
3671
3672
3673 /*
3674 Write record to new file.
3675
3676 SYNOPSIS
3677 sort_write_record()
3678 sort_param Sort parameters.
3679
3680 NOTE
3681 This is only called by a master thread if parallel repair is used.
3682
3683 RETURN
3684 0 OK
3685 1 Error
3686 */
3687
3688 int sort_write_record(MI_SORT_PARAM *sort_param)
3689 {
3690 int flag;
3691 uint length;
3692 ulong block_length,reclength;
3693 uchar *from;
3694 uchar block_buff[8];
3695 MI_SORT_INFO *sort_info=sort_param->sort_info;
3696 HA_CHECK *param=sort_info->param;
3697 MI_INFO *info=sort_info->info;
3698 MYISAM_SHARE *share=info->s;
3699 DBUG_ENTER("sort_write_record");
3700
3701 if (sort_param->fix_datafile)
3702 {
3703 switch (sort_info->new_data_file_type) {
3704 case STATIC_RECORD:
3705 if (my_b_write(&info->rec_cache,sort_param->record,
3706 share->base.pack_reclength))
3707 {
3708 mi_check_print_error(param,"%d when writing to datafile",my_errno);
3709 DBUG_RETURN(1);
3710 }
3711 sort_param->filepos+=share->base.pack_reclength;
3712 info->s->state.split++;
3713 break;
3714 case DYNAMIC_RECORD:
3715 if (! info->blobs)
3716 from=sort_param->rec_buff;
3717 else
3718 {
3719 /* must be sure that local buffer is big enough */
3720 reclength=info->s->base.pack_reclength+
3721 _mi_calc_total_blob_length(info,sort_param->record)+
3722 ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
3723 MI_DYN_DELETE_BLOCK_HEADER;
3724 if (sort_info->buff_length < reclength)
3725 {
3726 if (!(sort_info->buff=my_realloc(sort_info->buff, (uint) reclength,
3727 MYF(MY_FREE_ON_ERROR | MY_WME |
3728 MY_ALLOW_ZERO_PTR))))
3729 DBUG_RETURN(1);
3730 sort_info->buff_length=reclength;
3731 }
3732 from= sort_info->buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER);
3733 }
3734 /* We can use info->checksum here as only one thread calls this. */
3735 info->checksum= (*info->s->calc_check_checksum)(info,sort_param->record);
3736 reclength=_mi_rec_pack(info,from,sort_param->record);
3737 flag=0;
3738
3739 do
3740 {
3741 block_length= reclength + 3 + MY_TEST(reclength >= (65520 - 3));
3742 if (block_length < share->base.min_block_length)
3743 block_length=share->base.min_block_length;
3744 info->update|=HA_STATE_WRITE_AT_END;
3745 block_length=MY_ALIGN(block_length,MI_DYN_ALIGN_SIZE);
3746 if (block_length > MI_MAX_BLOCK_LENGTH)
3747 block_length=MI_MAX_BLOCK_LENGTH;
3748 if (_mi_write_part_record(info,0L,block_length,
3749 sort_param->filepos+block_length,
3750 &from,&reclength,&flag))
3751 {
3752 mi_check_print_error(param,"%d when writing to datafile",my_errno);
3753 DBUG_RETURN(1);
3754 }
3755 sort_param->filepos+=block_length;
3756 info->s->state.split++;
3757 } while (reclength);
3758 /* sort_info->param->glob_crc+=info->checksum; */
3759 break;
3760 case COMPRESSED_RECORD:
3761 reclength=info->packed_length;
3762 length= save_pack_length((uint) share->pack.version, block_buff,
3763 reclength);
3764 if (info->s->base.blobs)
3765 length+= save_pack_length((uint) share->pack.version,
3766 block_buff + length, info->blob_length);
3767 if (my_b_write(&info->rec_cache,block_buff,length) ||
3768 my_b_write(&info->rec_cache,(uchar*) sort_param->rec_buff,reclength))
3769 {
3770 mi_check_print_error(param,"%d when writing to datafile",my_errno);
3771 DBUG_RETURN(1);
3772 }
3773 /* sort_info->param->glob_crc+=info->checksum; */
3774 sort_param->filepos+=reclength+length;
3775 info->s->state.split++;
3776 break;
3777 default:
3778 DBUG_ASSERT(0); /* Impossible */
3779 break;
3780 }
3781 }
3782 if (sort_param->master)
3783 {
3784 info->state->records++;
3785 if ((param->testflag & T_WRITE_LOOP) &&
3786 (info->state->records % WRITE_COUNT) == 0)
3787 {
3788 char llbuff[22];
3789 printf("%s\r", llstr(info->state->records,llbuff));
3790 (void) fflush(stdout);
3791 }
3792 }
3793 DBUG_RETURN(0);
3794 } /* sort_write_record */
3795
3796
3797 /* Compare two keys from _create_index_by_sort */
3798
3799 static int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,
3800 const void *b)
3801 {
3802 uint not_used[2];
3803 return (ha_key_cmp(sort_param->seg, *((uchar**) a), *((uchar**) b),
3804 USE_WHOLE_KEY, SEARCH_SAME, not_used));
3805 } /* sort_key_cmp */
3806
3807
3808 static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a)
3809 {
3810 uint diff_pos[2];
3811 char llbuff[22],llbuff2[22];
3812 MI_SORT_INFO *sort_info=sort_param->sort_info;
3813 HA_CHECK *param= sort_info->param;
3814 int cmp;
3815
3816 if (sort_info->key_block->inited)
3817 {
3818 cmp=ha_key_cmp(sort_param->seg, (uchar*) sort_info->key_block->lastkey,
3819 (uchar*) a, USE_WHOLE_KEY,
3820 SEARCH_FIND | SEARCH_UPDATE | SEARCH_INSERT,
3821 diff_pos);
3822 if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL)
3823 ha_key_cmp(sort_param->seg, (uchar*) sort_info->key_block->lastkey,
3824 (uchar*) a, USE_WHOLE_KEY,
3825 SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diff_pos);
3826 else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
3827 {
3828 diff_pos[0]= mi_collect_stats_nonulls_next(sort_param->seg,
3829 sort_param->notnull,
3830 (uchar*) sort_info->
3831 key_block->lastkey,
3832 (uchar*)a);
3833 }
3834 sort_param->unique[diff_pos[0]-1]++;
3835 }
3836 else
3837 {
3838 cmp= -1;
3839 if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
3840 mi_collect_stats_nonulls_first(sort_param->seg, sort_param->notnull,
3841 (uchar*)a);
3842 }
3843 if ((sort_param->keyinfo->flag & HA_NOSAME) && cmp == 0)
3844 {
3845 sort_info->dupp++;
3846 sort_info->info->lastpos=get_record_for_key(sort_info->info,
3847 sort_param->keyinfo,
3848 (uchar*) a);
3849 if ((param->testflag & (T_CREATE_UNIQUE_BY_SORT | T_SUPPRESS_ERR_HANDLING))
3850 == T_CREATE_UNIQUE_BY_SORT)
3851 param->testflag|= T_SUPPRESS_ERR_HANDLING;
3852 mi_check_print_warning(param,
3853 "Duplicate key for record at %10s against record at %10s",
3854 llstr(sort_info->info->lastpos,llbuff),
3855 llstr(get_record_for_key(sort_info->info,
3856 sort_param->keyinfo,
3857 (uchar*) sort_info->
3858 key_block->lastkey),
3859 llbuff2));
3860 param->testflag|=T_RETRY_WITHOUT_QUICK;
3861 if (sort_info->param->testflag & T_VERBOSE)
3862 _mi_print_key(stdout,sort_param->seg,(uchar*) a, USE_WHOLE_KEY);
3863 return (sort_delete_record(sort_param));
3864 }
3865 #ifndef DBUG_OFF
3866 if (cmp > 0)
3867 {
3868 mi_check_print_error(param,
3869 "Internal error: Keys are not in order from sort");
3870 return(1);
3871 }
3872 #endif
3873 return (sort_insert_key(sort_param,sort_info->key_block,
3874 (uchar*) a, HA_OFFSET_ERROR));
3875 } /* sort_key_write */
3876
3877 int sort_ft_buf_flush(MI_SORT_PARAM *sort_param)
3878 {
3879 MI_SORT_INFO *sort_info=sort_param->sort_info;
3880 SORT_KEY_BLOCKS *key_block=sort_info->key_block;
3881 MYISAM_SHARE *share=sort_info->info->s;
3882 uint val_off, val_len;
3883 int error;
3884 SORT_FT_BUF *ft_buf=sort_info->ft_buf;
3885 uchar *from, *to;
3886
3887 val_len=share->ft2_keyinfo.keylength;
3888 get_key_full_length_rdonly(val_off, ft_buf->lastkey);
3889 to= (uchar*) ft_buf->lastkey+val_off;
3890
3891 if (ft_buf->buf)
3892 {
3893 /* flushing first-level tree */
3894 error=sort_insert_key(sort_param,key_block, (uchar*) ft_buf->lastkey,
3895 HA_OFFSET_ERROR);
3896 for (from=to+val_len;
3897 !error && from < (uchar*) ft_buf->buf;
3898 from+= val_len)
3899 {
3900 memcpy(to, from, val_len);
3901 error=sort_insert_key(sort_param,key_block, (uchar*) ft_buf->lastkey,
3902 HA_OFFSET_ERROR);
3903 }
3904 return error;
3905 }
3906 /* flushing second-level tree keyblocks */
3907 error=flush_pending_blocks(sort_param);
3908 /* updating lastkey with second-level tree info */
3909 ft_intXstore(ft_buf->lastkey+val_off, -ft_buf->count);
3910 _mi_dpointer(sort_info->info, (uchar*) ft_buf->lastkey+val_off+HA_FT_WLEN,
3911 share->state.key_root[sort_param->key]);
3912 /* restoring first level tree data in sort_info/sort_param */
3913 sort_info->key_block=sort_info->key_block_end- sort_info->param->sort_key_blocks;
3914 sort_param->keyinfo=share->keyinfo+sort_param->key;
3915 share->state.key_root[sort_param->key]=HA_OFFSET_ERROR;
3916 /* writing lastkey in first-level tree */
3917 return error ? error :
3918 sort_insert_key(sort_param,sort_info->key_block,
3919 (uchar*) ft_buf->lastkey,HA_OFFSET_ERROR);
3920 }
3921
3922 static int sort_ft_key_write(MI_SORT_PARAM *sort_param, const void *a)
3923 {
3924 uint a_len, val_off, val_len, error;
3925 uchar *p;
3926 MI_SORT_INFO *sort_info=sort_param->sort_info;
3927 SORT_FT_BUF *ft_buf=sort_info->ft_buf;
3928 SORT_KEY_BLOCKS *key_block=sort_info->key_block;
3929
3930 val_len= HA_FT_WLEN + sort_info->info->s->rec_reflength;
3931 get_key_full_length_rdonly(a_len, (uchar *)a);
3932
3933 if (!ft_buf)
3934 {
3935 /*
3936 use two-level tree only if key_reflength fits in rec_reflength place
3937 and row format is NOT static - for _mi_dpointer not to garble offsets
3938 */
3939 if ((sort_info->info->s->base.key_reflength <=
3940 sort_info->info->s->rec_reflength) &&
3941 (sort_info->info->s->options &
3942 (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
3943 ft_buf=(SORT_FT_BUF *)my_malloc(sort_param->keyinfo->block_length +
3944 sizeof(SORT_FT_BUF), MYF(MY_WME));
3945
3946 if (!ft_buf)
3947 {
3948 sort_param->key_write=sort_key_write;
3949 return sort_key_write(sort_param, a);
3950 }
3951 sort_info->ft_buf=ft_buf;
3952 goto word_init_ft_buf; /* no need to duplicate the code */
3953 }
3954 get_key_full_length_rdonly(val_off, ft_buf->lastkey);
3955
3956 if (ha_compare_text(sort_param->seg->charset,
3957 ((uchar *)a)+1,a_len-1,
3958 (uchar*) ft_buf->lastkey+1,val_off-1, 0)==0)
3959 {
3960 if (!ft_buf->buf) /* store in second-level tree */
3961 {
3962 ft_buf->count++;
3963 return sort_insert_key(sort_param,key_block,
3964 ((uchar *)a)+a_len, HA_OFFSET_ERROR);
3965 }
3966
3967 /* storing the key in the buffer. */
3968 memcpy (ft_buf->buf, (char *)a+a_len, val_len);
3969 ft_buf->buf+=val_len;
3970 if (ft_buf->buf < ft_buf->end)
3971 return 0;
3972
3973 /* converting to two-level tree */
3974 p= (uchar*) ft_buf->lastkey+val_off;
3975
3976 while (key_block->inited)
3977 key_block++;
3978 sort_info->key_block=key_block;
3979 sort_param->keyinfo=& sort_info->info->s->ft2_keyinfo;
3980 ft_buf->count=(int)((uchar*) ft_buf->buf - p)/val_len;
3981
3982 /* flushing buffer to second-level tree */
3983 for (error=0; !error && p < (uchar*) ft_buf->buf; p+= val_len)
3984 error=sort_insert_key(sort_param,key_block,p,HA_OFFSET_ERROR);
3985 ft_buf->buf=0;
3986 return error;
3987 }
3988
3989 /* flushing buffer */
3990 if ((error=sort_ft_buf_flush(sort_param)))
3991 return error;
3992
3993 word_init_ft_buf:
3994 a_len+=val_len;
3995 memcpy(ft_buf->lastkey, a, a_len);
3996 ft_buf->buf=ft_buf->lastkey+a_len;
3997 /*
3998 32 is just a safety margin here
3999 (at least MY_MAX(val_len, sizeof(nod_flag)) should be there).
4000 May be better performance could be achieved if we'd put
4001 (sort_info->keyinfo->block_length-32)/XXX
4002 instead.
4003 TODO: benchmark the best value for XXX.
4004 */
4005 ft_buf->end=ft_buf->lastkey+ (sort_param->keyinfo->block_length-32);
4006 return 0;
4007 } /* sort_ft_key_write */
4008
4009
4010 /* get pointer to record from a key */
4011
4012 static my_off_t get_record_for_key(MI_INFO *info, MI_KEYDEF *keyinfo,
4013 uchar *key)
4014 {
4015 return _mi_dpos(info,0,key+_mi_keylength(keyinfo,key));
4016 } /* get_record_for_key */
4017
4018
4019 /* Insert a key in sort-key-blocks */
4020
4021 static int sort_insert_key(MI_SORT_PARAM *sort_param,
4022 register SORT_KEY_BLOCKS *key_block, uchar *key,
4023 my_off_t prev_block)
4024 {
4025 uint a_length,t_length,nod_flag;
4026 my_off_t filepos,key_file_length;
4027 uchar *anc_buff,*lastkey;
4028 MI_KEY_PARAM s_temp;
4029 MI_INFO *info;
4030 MI_KEYDEF *keyinfo=sort_param->keyinfo;
4031 MI_SORT_INFO *sort_info= sort_param->sort_info;
4032 HA_CHECK *param=sort_info->param;
4033 DBUG_ENTER("sort_insert_key");
4034
4035 anc_buff= (uchar*) key_block->buff;
4036 info=sort_info->info;
4037 lastkey= (uchar*) key_block->lastkey;
4038 nod_flag= (key_block == sort_info->key_block ? 0 :
4039 info->s->base.key_reflength);
4040
4041 if (!key_block->inited)
4042 {
4043 key_block->inited=1;
4044 if (key_block == sort_info->key_block_end)
4045 {
4046 mi_check_print_error(param,"To many key-block-levels; Try increasing sort_key_blocks");
4047 DBUG_RETURN(1);
4048 }
4049 a_length=2+nod_flag;
4050 key_block->end_pos= anc_buff+2;
4051 lastkey=0; /* No previous key in block */
4052 }
4053 else
4054 a_length=mi_getint(anc_buff);
4055
4056 /* Save pointer to previous block */
4057 if (nod_flag)
4058 _mi_kpointer(info,(uchar*) key_block->end_pos,prev_block);
4059
4060 t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,
4061 (uchar*) 0,lastkey,lastkey,key,
4062 &s_temp);
4063 (*keyinfo->store_key)(keyinfo, (uchar*) key_block->end_pos+nod_flag,&s_temp);
4064 a_length+=t_length;
4065 mi_putint(anc_buff,a_length,nod_flag);
4066 key_block->end_pos+=t_length;
4067 if (a_length <= keyinfo->block_length)
4068 {
4069 (void) _mi_move_key(keyinfo,key_block->lastkey,key);
4070 key_block->last_length=a_length-t_length;
4071 DBUG_RETURN(0);
4072 }
4073
4074 /* Fill block with end-zero and write filled block */
4075 mi_putint(anc_buff,key_block->last_length,nod_flag);
4076 bzero((uchar*) anc_buff+key_block->last_length,
4077 keyinfo->block_length- key_block->last_length);
4078 key_file_length=info->state->key_file_length;
4079 if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
4080 DBUG_RETURN(1);
4081
4082 /* If we read the page from the key cache, we have to write it back to it */
4083 if (key_file_length == info->state->key_file_length)
4084 {
4085 if (_mi_write_keypage(info, keyinfo, filepos, DFLT_INIT_HITS, anc_buff))
4086 DBUG_RETURN(1);
4087 }
4088 else if (mysql_file_pwrite(info->s->kfile, (uchar*) anc_buff,
4089 (uint) keyinfo->block_length, filepos,
4090 param->myf_rw))
4091 DBUG_RETURN(1);
4092 DBUG_DUMP("buff",(uchar*) anc_buff,mi_getint(anc_buff));
4093
4094 /* Write separator-key to block in next level */
4095 if (sort_insert_key(sort_param,key_block+1,(uchar*) key_block->lastkey,
4096 filepos))
4097 DBUG_RETURN(1);
4098
4099 /* clear old block and write new key in it */
4100 key_block->inited=0;
4101 DBUG_RETURN(sort_insert_key(sort_param, key_block,key,prev_block));
4102 } /* sort_insert_key */
4103
4104
4105 /* Delete record when we found a duplicated key */
4106
4107 static int sort_delete_record(MI_SORT_PARAM *sort_param)
4108 {
4109 uint i;
4110 int old_file,error;
4111 uchar *key;
4112 MI_SORT_INFO *sort_info=sort_param->sort_info;
4113 HA_CHECK *param=sort_info->param;
4114 MI_INFO *info=sort_info->info;
4115 DBUG_ENTER("sort_delete_record");
4116
4117 if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
4118 {
4119 mi_check_print_error(param,
4120 "Quick-recover aborted; Run recovery without switch -q or with switch -qq");
4121 DBUG_RETURN(1);
4122 }
4123 if (info->s->options & HA_OPTION_COMPRESS_RECORD)
4124 {
4125 mi_check_print_error(param,
4126 "Recover aborted; Can't run standard recovery on compressed tables with errors in data-file. Use switch 'myisamchk --safe-recover' to fix it\n",stderr);;
4127 DBUG_RETURN(1);
4128 }
4129
4130 old_file=info->dfile;
4131 info->dfile=info->rec_cache.file;
4132 if (sort_info->current_key)
4133 {
4134 key=info->lastkey+info->s->base.max_key_length;
4135 if ((error=(*info->s->read_rnd)(info,sort_param->record,info->lastpos,0)) &&
4136 error != HA_ERR_RECORD_DELETED)
4137 {
4138 mi_check_print_error(param,"Can't read record to be removed");
4139 info->dfile=old_file;
4140 DBUG_RETURN(1);
4141 }
4142
4143 for (i=0 ; i < sort_info->current_key ; i++)
4144 {
4145 uint key_length=_mi_make_key(info,i,key,sort_param->record,info->lastpos);
4146 if (_mi_ck_delete(info,i,key,key_length))
4147 {
4148 mi_check_print_error(param,"Can't delete key %d from record to be removed",i+1);
4149 info->dfile=old_file;
4150 DBUG_RETURN(1);
4151 }
4152 }
4153 if (sort_param->calc_checksum)
4154 param->glob_crc-=(*info->s->calc_checksum)(info, sort_param->record);
4155 }
4156 error=flush_io_cache(&info->rec_cache) || (*info->s->delete_record)(info);
4157 info->dfile=old_file; /* restore actual value */
4158 info->state->records--;
4159 DBUG_RETURN(error);
4160 } /* sort_delete_record */
4161
4162 /* Fix all pending blocks and flush everything to disk */
4163
4164 int flush_pending_blocks(MI_SORT_PARAM *sort_param)
4165 {
4166 uint nod_flag,length;
4167 my_off_t filepos,key_file_length;
4168 SORT_KEY_BLOCKS *key_block;
4169 MI_SORT_INFO *sort_info= sort_param->sort_info;
4170 myf myf_rw=sort_info->param->myf_rw;
4171 MI_INFO *info=sort_info->info;
4172 MI_KEYDEF *keyinfo=sort_param->keyinfo;
4173 DBUG_ENTER("flush_pending_blocks");
4174
4175 filepos= HA_OFFSET_ERROR; /* if empty file */
4176 nod_flag=0;
4177 for (key_block=sort_info->key_block ; key_block->inited ; key_block++)
4178 {
4179 key_block->inited=0;
4180 length=mi_getint(key_block->buff);
4181 if (nod_flag)
4182 _mi_kpointer(info,(uchar*) key_block->end_pos,filepos);
4183 key_file_length=info->state->key_file_length;
4184 bzero((uchar*) key_block->buff+length, keyinfo->block_length-length);
4185 if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
4186 DBUG_RETURN(1);
4187
4188 /* If we read the page from the key cache, we have to write it back */
4189 if (key_file_length == info->state->key_file_length)
4190 {
4191 if (_mi_write_keypage(info, keyinfo, filepos,
4192 DFLT_INIT_HITS, (uchar*) key_block->buff))
4193 DBUG_RETURN(1);
4194 }
4195 else if (mysql_file_pwrite(info->s->kfile, (uchar*) key_block->buff,
4196 (uint) keyinfo->block_length, filepos, myf_rw))
4197 DBUG_RETURN(1);
4198 DBUG_DUMP("buff",(uchar*) key_block->buff,length);
4199 nod_flag=1;
4200 }
4201 info->s->state.key_root[sort_param->key]=filepos; /* Last is root for tree */
4202 DBUG_RETURN(0);
4203 } /* flush_pending_blocks */
4204
4205 /* alloc space and pointers for key_blocks */
4206
4207 static SORT_KEY_BLOCKS *alloc_key_blocks(HA_CHECK *param, uint blocks,
4208 uint buffer_length)
4209 {
4210 reg1 uint i;
4211 SORT_KEY_BLOCKS *block;
4212 DBUG_ENTER("alloc_key_blocks");
4213
4214 if (!(block=(SORT_KEY_BLOCKS*) my_malloc((sizeof(SORT_KEY_BLOCKS)+
4215 buffer_length+IO_SIZE)*blocks,
4216 MYF(0))))
4217 {
4218 mi_check_print_error(param,"Not enough memory for sort-key-blocks");
4219 return(0);
4220 }
4221 for (i=0 ; i < blocks ; i++)
4222 {
4223 block[i].inited=0;
4224 block[i].buff=(uchar*) (block+blocks)+(buffer_length+IO_SIZE)*i;
4225 }
4226 DBUG_RETURN(block);
4227 } /* alloc_key_blocks */
4228
4229
4230 /* Check if file is almost full */
4231
4232 int test_if_almost_full(MI_INFO *info)
4233 {
4234 if (info->s->options & HA_OPTION_COMPRESS_RECORD)
4235 return 0;
4236 return mysql_file_seek(info->s->kfile, 0L, MY_SEEK_END,
4237 MYF(MY_THREADSAFE)) / 10 * 9 >
4238 (my_off_t) info->s->base.max_key_file_length ||
4239 mysql_file_seek(info->dfile, 0L, MY_SEEK_END,
4240 MYF(0)) / 10 * 9 >
4241 (my_off_t) info->s->base.max_data_file_length;
4242 }
4243
4244 /* Recreate table with bigger more alloced record-data */
4245
4246 int recreate_table(HA_CHECK *param, MI_INFO **org_info, char *filename)
4247 {
4248 int error;
4249 MI_INFO info;
4250 MYISAM_SHARE share;
4251 MI_KEYDEF *keyinfo,*key,*key_end;
4252 HA_KEYSEG *keysegs,*keyseg;
4253 MI_COLUMNDEF *recdef,*rec,*end;
4254 MI_UNIQUEDEF *uniquedef,*u_ptr,*u_end;
4255 MI_STATUS_INFO status_info;
4256 uint unpack,key_parts;
4257 ha_rows max_records;
4258 ulonglong file_length,tmp_length;
4259 MI_CREATE_INFO create_info;
4260 DBUG_ENTER("recreate_table");
4261
4262 error=1; /* Default error */
4263 info= **org_info;
4264 status_info= (*org_info)->state[0];
4265 info.state= &status_info;
4266 share= *(*org_info)->s;
4267 unpack= (share.options & HA_OPTION_COMPRESS_RECORD) &&
4268 (param->testflag & T_UNPACK);
4269 if (!(keyinfo=(MI_KEYDEF*) my_alloca(sizeof(MI_KEYDEF)*share.base.keys)))
4270 DBUG_RETURN(0);
4271 memcpy((uchar*) keyinfo,(uchar*) share.keyinfo,
4272 (size_t) (sizeof(MI_KEYDEF)*share.base.keys));
4273
4274 key_parts= share.base.all_key_parts;
4275 if (!(keysegs=(HA_KEYSEG*) my_alloca(sizeof(HA_KEYSEG)*
4276 (key_parts+share.base.keys))))
4277 {
4278 my_afree((uchar*) keyinfo);
4279 DBUG_RETURN(1);
4280 }
4281 if (!(recdef=(MI_COLUMNDEF*)
4282 my_alloca(sizeof(MI_COLUMNDEF)*(share.base.fields+1))))
4283 {
4284 my_afree((uchar*) keyinfo);
4285 my_afree((uchar*) keysegs);
4286 DBUG_RETURN(1);
4287 }
4288 if (!(uniquedef=(MI_UNIQUEDEF*)
4289 my_alloca(sizeof(MI_UNIQUEDEF)*(share.state.header.uniques+1))))
4290 {
4291 my_afree((uchar*) recdef);
4292 my_afree((uchar*) keyinfo);
4293 my_afree((uchar*) keysegs);
4294 DBUG_RETURN(1);
4295 }
4296
4297 /* Copy the column definitions */
4298 memcpy((uchar*) recdef,(uchar*) share.rec,
4299 (size_t) (sizeof(MI_COLUMNDEF)*(share.base.fields+1)));
4300 for (rec=recdef,end=recdef+share.base.fields; rec != end ; rec++)
4301 {
4302 if (unpack && !(share.options & HA_OPTION_PACK_RECORD) &&
4303 rec->type != FIELD_BLOB &&
4304 rec->type != FIELD_VARCHAR &&
4305 rec->type != FIELD_CHECK)
4306 rec->type=(int) FIELD_NORMAL;
4307 }
4308
4309 /* Change the new key to point at the saved key segments */
4310 memcpy((uchar*) keysegs,(uchar*) share.keyparts,
4311 (size_t) (sizeof(HA_KEYSEG)*(key_parts+share.base.keys+
4312 share.state.header.uniques)));
4313 keyseg=keysegs;
4314 for (key=keyinfo,key_end=keyinfo+share.base.keys; key != key_end ; key++)
4315 {
4316 key->seg=keyseg;
4317 for (; keyseg->type ; keyseg++)
4318 {
4319 if (param->language)
4320 keyseg->language=param->language; /* change language */
4321 }
4322 keyseg++; /* Skip end pointer */
4323 }
4324
4325 /* Copy the unique definitions and change them to point at the new key
4326 segments*/
4327 memcpy((uchar*) uniquedef,(uchar*) share.uniqueinfo,
4328 (size_t) (sizeof(MI_UNIQUEDEF)*(share.state.header.uniques)));
4329 for (u_ptr=uniquedef,u_end=uniquedef+share.state.header.uniques;
4330 u_ptr != u_end ; u_ptr++)
4331 {
4332 u_ptr->seg=keyseg;
4333 keyseg+=u_ptr->keysegs+1;
4334 }
4335 unpack= (share.options & HA_OPTION_COMPRESS_RECORD) &&
4336 (param->testflag & T_UNPACK);
4337 share.options&= ~HA_OPTION_TEMP_COMPRESS_RECORD;
4338
4339 file_length=(ulonglong) mysql_file_seek(info.dfile, 0L, MY_SEEK_END, MYF(0));
4340 tmp_length= file_length+file_length/10;
4341 set_if_bigger(file_length,param->max_data_file_length);
4342 set_if_bigger(file_length,tmp_length);
4343 set_if_bigger(file_length,(ulonglong) share.base.max_data_file_length);
4344
4345 if (share.options & HA_OPTION_COMPRESS_RECORD)
4346 share.base.records= max_records= info.state->records;
4347 else if (!(share.options & HA_OPTION_PACK_RECORD))
4348 max_records= (ha_rows) (file_length / share.base.pack_reclength);
4349 else
4350 max_records= 0;
4351
4352 (void) mi_close(*org_info);
4353 bzero((char*) &create_info,sizeof(create_info));
4354 create_info.max_rows= max_records;
4355 create_info.reloc_rows=share.base.reloc;
4356 create_info.old_options=(share.options |
4357 (unpack ? HA_OPTION_TEMP_COMPRESS_RECORD : 0));
4358
4359 create_info.data_file_length=file_length;
4360 create_info.auto_increment=share.state.auto_increment;
4361 create_info.language = (param->language ? param->language :
4362 share.state.header.language);
4363 create_info.key_file_length= status_info.key_file_length;
4364 /*
4365 Allow for creating an auto_increment key. This has an effect only if
4366 an auto_increment key exists in the original table.
4367 */
4368 create_info.with_auto_increment= TRUE;
4369 /* We don't have to handle symlinks here because we are using
4370 HA_DONT_TOUCH_DATA */
4371 if (mi_create(filename,
4372 share.base.keys - share.state.header.uniques,
4373 keyinfo, share.base.fields, recdef,
4374 share.state.header.uniques, uniquedef,
4375 &create_info,
4376 HA_DONT_TOUCH_DATA))
4377 {
4378 mi_check_print_error(param,"Got error %d when trying to recreate indexfile",my_errno);
4379 goto end;
4380 }
4381 *org_info=mi_open(filename,O_RDWR,
4382 (param->testflag & T_WAIT_FOREVER) ? HA_OPEN_WAIT_IF_LOCKED :
4383 (param->testflag & T_DESCRIPT) ? HA_OPEN_IGNORE_IF_LOCKED :
4384 HA_OPEN_ABORT_IF_LOCKED);
4385 if (!*org_info)
4386 {
4387 mi_check_print_error(param,"Got error %d when trying to open re-created indexfile",
4388 my_errno);
4389 goto end;
4390 }
4391 /* We are modifing */
4392 (*org_info)->s->options&= ~HA_OPTION_READ_ONLY_DATA;
4393 (void) _mi_readinfo(*org_info,F_WRLCK,0);
4394 (*org_info)->state->records=info.state->records;
4395 if (share.state.create_time)
4396 (*org_info)->s->state.create_time=share.state.create_time;
4397 (*org_info)->s->state.unique=(*org_info)->this_unique=
4398 share.state.unique;
4399 (*org_info)->state->checksum=info.state->checksum;
4400 (*org_info)->state->del=info.state->del;
4401 (*org_info)->s->state.dellink=share.state.dellink;
4402 (*org_info)->state->empty=info.state->empty;
4403 (*org_info)->state->data_file_length=info.state->data_file_length;
4404 if (update_state_info(param,*org_info,UPDATE_TIME | UPDATE_STAT |
4405 UPDATE_OPEN_COUNT))
4406 goto end;
4407 error=0;
4408 end:
4409 my_afree((uchar*) uniquedef);
4410 my_afree((uchar*) keyinfo);
4411 my_afree((uchar*) recdef);
4412 my_afree((uchar*) keysegs);
4413 DBUG_RETURN(error);
4414 }
4415
4416
4417 /* write suffix to data file if neaded */
4418
4419 int write_data_suffix(MI_SORT_INFO *sort_info, my_bool fix_datafile)
4420 {
4421 MI_INFO *info=sort_info->info;
4422
4423 if (info->s->options & HA_OPTION_COMPRESS_RECORD && fix_datafile)
4424 {
4425 uchar buff[MEMMAP_EXTRA_MARGIN];
4426 bzero(buff,sizeof(buff));
4427 if (my_b_write(&info->rec_cache,buff,sizeof(buff)))
4428 {
4429 mi_check_print_error(sort_info->param,
4430 "%d when writing to datafile",my_errno);
4431 return 1;
4432 }
4433 sort_info->param->read_cache.end_of_file+=sizeof(buff);
4434 }
4435 return 0;
4436 }
4437
4438 /* Update state and myisamchk_time of indexfile */
4439
4440 int update_state_info(HA_CHECK *param, MI_INFO *info,uint update)
4441 {
4442 MYISAM_SHARE *share=info->s;
4443
4444 if (update & UPDATE_OPEN_COUNT)
4445 {
4446 share->state.open_count=0;
4447 share->global_changed=0;
4448 }
4449 if (update & UPDATE_STAT)
4450 {
4451 uint i, key_parts= mi_uint2korr(share->state.header.key_parts);
4452 share->state.rec_per_key_rows=info->state->records;
4453 share->state.changed&= ~STATE_NOT_ANALYZED;
4454 if (info->state->records)
4455 {
4456 for (i=0; i<key_parts; i++)
4457 {
4458 if (!(share->state.rec_per_key_part[i]=param->rec_per_key_part[i]))
4459 share->state.changed|= STATE_NOT_ANALYZED;
4460 }
4461 }
4462 }
4463 if (update & (UPDATE_STAT | UPDATE_SORT | UPDATE_TIME | UPDATE_AUTO_INC))
4464 {
4465 if (update & UPDATE_TIME)
4466 {
4467 share->state.check_time= time((time_t*) 0);
4468 if (!share->state.create_time)
4469 share->state.create_time=share->state.check_time;
4470 }
4471 /*
4472 When tables are locked we haven't synched the share state and the
4473 real state for a while so we better do it here before synching
4474 the share state to disk. Only when table is write locked is it
4475 necessary to perform this synch.
4476 */
4477 if (info->lock_type == F_WRLCK)
4478 share->state.state= *info->state;
4479 if (mi_state_info_write(share->kfile,&share->state,1+2))
4480 goto err;
4481 share->changed=0;
4482 }
4483 { /* Force update of status */
4484 int error;
4485 uint r_locks=share->r_locks,w_locks=share->w_locks;
4486 share->r_locks= share->w_locks= share->tot_locks= 0;
4487
4488 DBUG_EXECUTE_IF("simulate_incorrect_share_wlock_value",
4489 DEBUG_SYNC_C("after_share_wlock_set_to_0"););
4490
4491 error=_mi_writeinfo(info,WRITEINFO_NO_UNLOCK);
4492 share->r_locks=r_locks;
4493 share->w_locks=w_locks;
4494 share->tot_locks=r_locks+w_locks;
4495 if (!error)
4496 return 0;
4497 }
4498 err:
4499 mi_check_print_error(param,"%d when updating keyfile",my_errno);
4500 return 1;
4501 }
4502
4503 /*
4504 Update auto increment value for a table
4505 When setting the 'repair_only' flag we only want to change the
4506 old auto_increment value if its wrong (smaller than some given key).
4507 The reason is that we shouldn't change the auto_increment value
4508 for a table without good reason when only doing a repair; If the
4509 user have inserted and deleted rows, the auto_increment value
4510 may be bigger than the biggest current row and this is ok.
4511
4512 If repair_only is not set, we will update the flag to the value in
4513 param->auto_increment is bigger than the biggest key.
4514 */
4515
4516 void update_auto_increment_key(HA_CHECK *param, MI_INFO *info,
4517 my_bool repair_only)
4518 {
4519 uchar *record= 0;
4520 DBUG_ENTER("update_auto_increment_key");
4521
4522 if (!info->s->base.auto_key ||
4523 ! mi_is_key_active(info->s->state.key_map, info->s->base.auto_key - 1))
4524 {
4525 if (!(param->testflag & T_VERY_SILENT))
4526 mi_check_print_info(param,
4527 "Table: %s doesn't have an auto increment key\n",
4528 param->isam_file_name);
4529 DBUG_VOID_RETURN;
4530 }
4531 if (!(param->testflag & T_SILENT) &&
4532 !(param->testflag & T_REP_ANY))
4533 printf("Updating MyISAM file: %s\n", param->isam_file_name);
4534 /*
4535 We have to use an allocated buffer instead of info->rec_buff as
4536 _mi_put_key_in_record() may use info->rec_buff
4537 */
4538 if (!mi_alloc_rec_buff(info, -1, &record))
4539 {
4540 mi_check_print_error(param,"Not enough memory for extra record");
4541 DBUG_VOID_RETURN;
4542 }
4543
4544 mi_extra(info,HA_EXTRA_KEYREAD,0);
4545 if (mi_rlast(info, record, info->s->base.auto_key-1))
4546 {
4547 if (my_errno != HA_ERR_END_OF_FILE)
4548 {
4549 mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
4550 my_free(mi_get_rec_buff_ptr(info, record));
4551 mi_check_print_error(param,"%d when reading last record",my_errno);
4552 DBUG_VOID_RETURN;
4553 }
4554 if (!repair_only)
4555 info->s->state.auto_increment=param->auto_increment_value;
4556 }
4557 else
4558 {
4559 ulonglong auto_increment= retrieve_auto_increment(info, record);
4560 set_if_bigger(info->s->state.auto_increment,auto_increment);
4561 if (!repair_only)
4562 set_if_bigger(info->s->state.auto_increment, param->auto_increment_value);
4563 }
4564 mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
4565 my_free(mi_get_rec_buff_ptr(info, record));
4566 update_state_info(param, info, UPDATE_AUTO_INC);
4567 DBUG_VOID_RETURN;
4568 }
4569
4570
4571 /*
4572 Update statistics for each part of an index
4573
4574 SYNOPSIS
4575 update_key_parts()
4576 keyinfo IN Index information (only key->keysegs used)
4577 rec_per_key_part OUT Store statistics here
4578 unique IN Array of (#distinct tuples)
4579 notnull_tuples IN Array of (#tuples), or NULL
4580 records Number of records in the table
4581
4582 DESCRIPTION
4583 This function is called produce index statistics values from unique and
4584 notnull_tuples arrays after these arrays were produced with sequential
4585 index scan (the scan is done in two places: chk_index() and
4586 sort_key_write()).
4587
4588 This function handles all 3 index statistics collection methods.
4589
4590 Unique is an array:
4591 unique[0]= (#different values of {keypart1}) - 1
4592 unique[1]= (#different values of {keypart1,keypart2} tuple)-unique[0]-1
4593 ...
4594
4595 For MI_STATS_METHOD_IGNORE_NULLS method, notnull_tuples is an array too:
4596 notnull_tuples[0]= (#of {keypart1} tuples such that keypart1 is not NULL)
4597 notnull_tuples[1]= (#of {keypart1,keypart2} tuples such that all
4598 keypart{i} are not NULL)
4599 ...
4600 For all other statistics collection methods notnull_tuples==NULL.
4601
4602 Output is an array:
4603 rec_per_key_part[k] =
4604 = E(#records in the table such that keypart_1=c_1 AND ... AND
4605 keypart_k=c_k for arbitrary constants c_1 ... c_k)
4606
4607 = {assuming that values have uniform distribution and index contains all
4608 tuples from the domain (or that {c_1, ..., c_k} tuple is chosen from
4609 index tuples}
4610
4611 = #tuples-in-the-index / #distinct-tuples-in-the-index.
4612
4613 The #tuples-in-the-index and #distinct-tuples-in-the-index have different
4614 meaning depending on which statistics collection method is used:
4615
4616 MI_STATS_METHOD_* how are nulls compared? which tuples are counted?
4617 NULLS_EQUAL NULL == NULL all tuples in table
4618 NULLS_NOT_EQUAL NULL != NULL all tuples in table
4619 IGNORE_NULLS n/a tuples that don't have NULLs
4620 */
4621
4622 void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
4623 ulonglong *unique, ulonglong *notnull,
4624 ulonglong records)
4625 {
4626 ulonglong count=0,tmp, unique_tuples;
4627 ulonglong tuples= records;
4628 uint parts;
4629 for (parts=0 ; parts < keyinfo->keysegs ; parts++)
4630 {
4631 count+=unique[parts];
4632 unique_tuples= count + 1;
4633 if (notnull)
4634 {
4635 tuples= notnull[parts];
4636 /*
4637 #(unique_tuples not counting tuples with NULLs) =
4638 #(unique_tuples counting tuples with NULLs as different) -
4639 #(tuples with NULLs)
4640 */
4641 unique_tuples -= (records - notnull[parts]);
4642 }
4643
4644 if (unique_tuples == 0)
4645 tmp= 1;
4646 else if (count == 0)
4647 tmp= tuples; /* 1 unique tuple */
4648 else
4649 tmp= (tuples + unique_tuples/2) / unique_tuples;
4650
4651 /*
4652 for some weird keys (e.g. FULLTEXT) tmp can be <1 here.
4653 let's ensure it is not
4654 */
4655 set_if_bigger(tmp,1);
4656 /* Keys are stored as 32 byte int's; Ensure we don't get an overflow */
4657 if (tmp >= (ulonglong) ~(uint32) 0)
4658 tmp=(ulonglong) ~(uint32) 0;
4659
4660 *rec_per_key_part=(ulong) tmp;
4661 rec_per_key_part++;
4662 }
4663 }
4664
4665
4666 static ha_checksum mi_byte_checksum(const uchar *buf, uint length)
4667 {
4668 ha_checksum crc;
4669 const uchar *end=buf+length;
4670 for (crc=0; buf != end; buf++)
4671 crc=((crc << 1) + *((uchar*) buf)) +
4672 MY_TEST(crc & (((ha_checksum) 1) << (8 * sizeof(ha_checksum) - 1)));
4673 return crc;
4674 }
4675
4676 static my_bool mi_too_big_key_for_sort(MI_KEYDEF *key, ha_rows rows)
4677 {
4678 uint key_maxlength=key->maxlength;
4679 if (key->flag & HA_FULLTEXT)
4680 {
4681 uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT*
4682 key->seg->charset->mbmaxlen;
4683 key_maxlength+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
4684 }
4685 return (key->flag & HA_SPATIAL) ||
4686 (key->flag & (HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY | HA_FULLTEXT) &&
4687 ((ulonglong) rows * key_maxlength > myisam_max_temp_length));
4688 }
4689
4690 /*
4691 Deactivate all indexes that can be recreated fast.
4692 These include packed keys on which sorting will use more temporary
4693 space than the max allowed file length or for which the unpacked keys
4694 will take much more space than packed keys.
4695 Note that 'rows' may be zero for the case when we don't know how many
4696 rows we will put into the file.
4697 */
4698
4699 void mi_disable_indexes_for_rebuild(MI_INFO *info, ha_rows rows,
4700 my_bool all_keys)
4701 {
4702 MYISAM_SHARE *share=info->s;
4703 MI_KEYDEF *key=share->keyinfo;
4704 uint i;
4705
4706 DBUG_ASSERT(info->state->records == 0 &&
4707 (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES));
4708 for (i=0 ; i < share->base.keys ; i++,key++)
4709 {
4710 if (!(key->flag & (HA_SPATIAL | HA_AUTO_KEY)) &&
4711 ! mi_too_big_key_for_sort(key,rows) && info->s->base.auto_key != i+1 &&
4712 (all_keys || !(key->flag & HA_NOSAME)))
4713 {
4714 mi_clear_key_active(share->state.key_map, i);
4715 info->update|= HA_STATE_CHANGED;
4716 info->create_unique_index_by_sort= all_keys;
4717 }
4718 }
4719 }
4720
4721
4722 /*
4723 Return TRUE if we can use repair by sorting
4724 One can set the force argument to force to use sorting
4725 even if the temporary file would be quite big!
4726 */
4727
4728 my_bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows,
4729 ulonglong key_map, my_bool force)
4730 {
4731 MYISAM_SHARE *share=info->s;
4732 MI_KEYDEF *key=share->keyinfo;
4733 uint i;
4734
4735 /*
4736 mi_repair_by_sort only works if we have at least one key. If we don't
4737 have any keys, we should use the normal repair.
4738 */
4739 if (! mi_is_any_key_active(key_map))
4740 return FALSE; /* Can't use sort */
4741 for (i=0 ; i < share->base.keys ; i++,key++)
4742 {
4743 if (!force && mi_too_big_key_for_sort(key,rows))
4744 return FALSE;
4745 }
4746 return TRUE;
4747 }
4748
4749
4750 static void
4751 set_data_file_type(MI_SORT_INFO *sort_info, MYISAM_SHARE *share)
4752 {
4753 if ((sort_info->new_data_file_type=share->data_file_type) ==
4754 COMPRESSED_RECORD && sort_info->param->testflag & T_UNPACK)
4755 {
4756 MYISAM_SHARE tmp;
4757
4758 if (share->options & HA_OPTION_PACK_RECORD)
4759 sort_info->new_data_file_type = DYNAMIC_RECORD;
4760 else
4761 sort_info->new_data_file_type = STATIC_RECORD;
4762
4763 /* Set delete_function for sort_delete_record() */
4764 memcpy((char*) &tmp, share, sizeof(*share));
4765 tmp.options= ~HA_OPTION_COMPRESS_RECORD;
4766 mi_setup_functions(&tmp);
4767 share->delete_record=tmp.delete_record;
4768 }
4769 }
4770
4771
4772 int mi_make_backup_of_index(MI_INFO *info, time_t backup_time, myf flags)
4773 {
4774 char backup_name[FN_REFLEN + MY_BACKUP_NAME_EXTRA_LENGTH];
4775 my_create_backup_name(backup_name, info->s->index_file_name, backup_time);
4776 return my_copy(info->s->index_file_name, backup_name, flags);
4777 }
4778
4779
4780 static int replace_data_file(HA_CHECK *param, MI_INFO *info, File new_file)
4781 {
4782 MYISAM_SHARE *share=info->s;
4783
4784 mysql_file_close(new_file,MYF(0));
4785 info->dfile= -1;
4786 if (param->testflag & T_BACKUP_DATA)
4787 {
4788 char buff[MY_BACKUP_NAME_EXTRA_LENGTH+1];
4789 my_create_backup_name(buff, "", param->backup_time);
4790 my_printf_error(ER_GET_ERRMSG,
4791 "Making backup of data file %s with extension '%s'",
4792 MYF(ME_JUST_INFO | ME_NOREFRESH), share->data_file_name,
4793 buff);
4794 }
4795
4796 /*
4797 On Windows, the old data file cannot be deleted if it is either
4798 open, or memory mapped. Closing the file won't remove the memory
4799 map implicilty on Windows. We closed the data file, but we keep
4800 the MyISAM table open. A memory map will be closed on the final
4801 mi_close() only. So we need to unmap explicitly here. After
4802 renaming the new file under the hook, we couldn't use the map of
4803 the old file any more anyway.
4804 */
4805 if (info->s->file_map)
4806 {
4807 (void) my_munmap((char*) info->s->file_map, info->s->mmaped_length);
4808 info->s->file_map= NULL;
4809 }
4810
4811 if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
4812 DATA_TMP_EXT, param->backup_time,
4813 (param->testflag & T_BACKUP_DATA ?
4814 MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
4815 mi_open_datafile(info, share))
4816 return 1;
4817 return 0;
4818 }
4819