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