00001 /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 /* Describe, check and repair of MyISAM tables */ 00018 00019 #include "ftdefs.h" 00020 #include <m_ctype.h> 00021 #include <stdarg.h> 00022 #include <my_getopt.h> 00023 #ifdef HAVE_SYS_VADVISE_H 00024 #include <sys/vadvise.h> 00025 #endif 00026 #ifdef HAVE_SYS_MMAN_H 00027 #include <sys/mman.h> 00028 #endif 00029 #include "rt_index.h" 00030 00031 #ifndef USE_RAID 00032 #define my_raid_create(A,B,C,D,E,F,G) my_create(A,B,C,G) 00033 #define my_raid_delete(A,B,C) my_delete(A,B) 00034 #endif 00035 00036 /* Functions defined in this file */ 00037 00038 static int check_k_link(MI_CHECK *param, MI_INFO *info,uint nr); 00039 static int chk_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo, 00040 my_off_t page, uchar *buff, ha_rows *keys, 00041 ha_checksum *key_checksum, uint level); 00042 static uint isam_key_length(MI_INFO *info,MI_KEYDEF *keyinfo); 00043 static ha_checksum calc_checksum(ha_rows count); 00044 static int writekeys(MI_CHECK *param, MI_INFO *info,byte *buff, 00045 my_off_t filepos); 00046 static int sort_one_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo, 00047 my_off_t pagepos, File new_file); 00048 static int sort_key_read(MI_SORT_PARAM *sort_param,void *key); 00049 static int sort_ft_key_read(MI_SORT_PARAM *sort_param,void *key); 00050 static int sort_get_next_record(MI_SORT_PARAM *sort_param); 00051 static int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,const void *b); 00052 static int sort_ft_key_write(MI_SORT_PARAM *sort_param, const void *a); 00053 static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a); 00054 static my_off_t get_record_for_key(MI_INFO *info,MI_KEYDEF *keyinfo, 00055 uchar *key); 00056 static int sort_insert_key(MI_SORT_PARAM *sort_param, 00057 reg1 SORT_KEY_BLOCKS *key_block, 00058 uchar *key, my_off_t prev_block); 00059 static int sort_delete_record(MI_SORT_PARAM *sort_param); 00060 /*static int flush_pending_blocks(MI_CHECK *param);*/ 00061 static SORT_KEY_BLOCKS *alloc_key_blocks(MI_CHECK *param, uint blocks, 00062 uint buffer_length); 00063 static ha_checksum mi_byte_checksum(const byte *buf, uint length); 00064 static void set_data_file_type(SORT_INFO *sort_info, MYISAM_SHARE *share); 00065 00066 void myisamchk_init(MI_CHECK *param) 00067 { 00068 bzero((gptr) param,sizeof(*param)); 00069 param->opt_follow_links=1; 00070 param->keys_in_use= ~(ulonglong) 0; 00071 param->search_after_block=HA_OFFSET_ERROR; 00072 param->auto_increment_value= 0; 00073 param->use_buffers=USE_BUFFER_INIT; 00074 param->read_buffer_length=READ_BUFFER_INIT; 00075 param->write_buffer_length=READ_BUFFER_INIT; 00076 param->sort_buffer_length=SORT_BUFFER_INIT; 00077 param->sort_key_blocks=BUFFERS_WHEN_SORTING; 00078 param->tmpfile_createflag=O_RDWR | O_TRUNC | O_EXCL; 00079 param->myf_rw=MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL); 00080 param->start_check_pos=0; 00081 param->max_record_length= LONGLONG_MAX; 00082 param->key_cache_block_size= KEY_CACHE_BLOCK_SIZE; 00083 param->stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL; 00084 } 00085 00086 /* Check the status flags for the table */ 00087 00088 int chk_status(MI_CHECK *param, register MI_INFO *info) 00089 { 00090 MYISAM_SHARE *share=info->s; 00091 00092 if (mi_is_crashed_on_repair(info)) 00093 mi_check_print_warning(param, 00094 "Table is marked as crashed and last repair failed"); 00095 else if (mi_is_crashed(info)) 00096 mi_check_print_warning(param, 00097 "Table is marked as crashed"); 00098 if (share->state.open_count != (uint) (info->s->global_changed ? 1 : 0)) 00099 { 00100 /* Don't count this as a real warning, as check can correct this ! */ 00101 uint save=param->warning_printed; 00102 mi_check_print_warning(param, 00103 share->state.open_count==1 ? 00104 "%d client is using or hasn't closed the table properly" : 00105 "%d clients are using or haven't closed the table properly", 00106 share->state.open_count); 00107 /* If this will be fixed by the check, forget the warning */ 00108 if (param->testflag & T_UPDATE_STATE) 00109 param->warning_printed=save; 00110 } 00111 return 0; 00112 } 00113 00114 /* Check delete links */ 00115 00116 int chk_del(MI_CHECK *param, register MI_INFO *info, uint test_flag) 00117 { 00118 reg2 ha_rows i; 00119 uint delete_link_length; 00120 my_off_t empty,next_link,old_link; 00121 char buff[22],buff2[22]; 00122 DBUG_ENTER("chk_del"); 00123 00124 LINT_INIT(old_link); 00125 param->record_checksum=0; 00126 delete_link_length=((info->s->options & HA_OPTION_PACK_RECORD) ? 20 : 00127 info->s->rec_reflength+1); 00128 00129 if (!(test_flag & T_SILENT)) 00130 puts("- check record delete-chain"); 00131 00132 next_link=info->s->state.dellink; 00133 if (info->state->del == 0) 00134 { 00135 if (test_flag & T_VERBOSE) 00136 { 00137 puts("No recordlinks"); 00138 } 00139 } 00140 else 00141 { 00142 if (test_flag & T_VERBOSE) 00143 printf("Recordlinks: "); 00144 empty=0; 00145 for (i= info->state->del ; i > 0L && next_link != HA_OFFSET_ERROR ; i--) 00146 { 00147 if (*killed_ptr(param)) 00148 DBUG_RETURN(1); 00149 if (test_flag & T_VERBOSE) 00150 printf(" %9s",llstr(next_link,buff)); 00151 if (next_link >= info->state->data_file_length) 00152 goto wrong; 00153 if (my_pread(info->dfile,(char*) buff,delete_link_length, 00154 next_link,MYF(MY_NABP))) 00155 { 00156 if (test_flag & T_VERBOSE) puts(""); 00157 mi_check_print_error(param,"Can't read delete-link at filepos: %s", 00158 llstr(next_link,buff)); 00159 DBUG_RETURN(1); 00160 } 00161 if (*buff != '\0') 00162 { 00163 if (test_flag & T_VERBOSE) puts(""); 00164 mi_check_print_error(param,"Record at pos: %s is not remove-marked", 00165 llstr(next_link,buff)); 00166 goto wrong; 00167 } 00168 if (info->s->options & HA_OPTION_PACK_RECORD) 00169 { 00170 my_off_t prev_link=mi_sizekorr(buff+12); 00171 if (empty && prev_link != old_link) 00172 { 00173 if (test_flag & T_VERBOSE) puts(""); 00174 mi_check_print_error(param,"Deleted block at %s doesn't point back at previous delete link",llstr(next_link,buff2)); 00175 goto wrong; 00176 } 00177 old_link=next_link; 00178 next_link=mi_sizekorr(buff+4); 00179 empty+=mi_uint3korr(buff+1); 00180 } 00181 else 00182 { 00183 param->record_checksum+=(ha_checksum) next_link; 00184 next_link=_mi_rec_pos(info->s,(uchar*) buff+1); 00185 empty+=info->s->base.pack_reclength; 00186 } 00187 } 00188 if (test_flag & T_VERBOSE) 00189 puts("\n"); 00190 if (empty != info->state->empty) 00191 { 00192 mi_check_print_warning(param, 00193 "Found %s deleted space in delete link chain. Should be %s", 00194 llstr(empty,buff2), 00195 llstr(info->state->empty,buff)); 00196 } 00197 if (next_link != HA_OFFSET_ERROR) 00198 { 00199 mi_check_print_error(param, 00200 "Found more than the expected %s deleted rows in delete link chain", 00201 llstr(info->state->del, buff)); 00202 goto wrong; 00203 } 00204 if (i != 0) 00205 { 00206 mi_check_print_error(param, 00207 "Found %s deleted rows in delete link chain. Should be %s", 00208 llstr(info->state->del - i, buff2), 00209 llstr(info->state->del, buff)); 00210 goto wrong; 00211 } 00212 } 00213 DBUG_RETURN(0); 00214 00215 wrong: 00216 param->testflag|=T_RETRY_WITHOUT_QUICK; 00217 if (test_flag & T_VERBOSE) puts(""); 00218 mi_check_print_error(param,"record delete-link-chain corrupted"); 00219 DBUG_RETURN(1); 00220 } /* chk_del */ 00221 00222 00223 /* Check delete links in index file */ 00224 00225 static int check_k_link(MI_CHECK *param, register MI_INFO *info, uint nr) 00226 { 00227 my_off_t next_link; 00228 uint block_size=(nr+1)*MI_MIN_KEY_BLOCK_LENGTH; 00229 ha_rows records; 00230 char llbuff[21],*buff; 00231 DBUG_ENTER("check_k_link"); 00232 00233 if (param->testflag & T_VERBOSE) 00234 printf("block_size %4d:",block_size); 00235 00236 next_link=info->s->state.key_del[nr]; 00237 records= (ha_rows) (info->state->key_file_length / block_size); 00238 while (next_link != HA_OFFSET_ERROR && records > 0) 00239 { 00240 if (*killed_ptr(param)) 00241 DBUG_RETURN(1); 00242 if (param->testflag & T_VERBOSE) 00243 printf("%16s",llstr(next_link,llbuff)); 00244 if (next_link > info->state->key_file_length || 00245 next_link & (info->s->blocksize-1)) 00246 DBUG_RETURN(1); 00247 if (!(buff=key_cache_read(info->s->key_cache, 00248 info->s->kfile, next_link, DFLT_INIT_HITS, 00249 (byte*) info->buff, 00250 myisam_block_size, block_size, 1))) 00251 DBUG_RETURN(1); 00252 next_link=mi_sizekorr(buff); 00253 records--; 00254 param->key_file_blocks+=block_size; 00255 } 00256 if (param->testflag & T_VERBOSE) 00257 { 00258 if (next_link != HA_OFFSET_ERROR) 00259 printf("%16s\n",llstr(next_link,llbuff)); 00260 else 00261 puts(""); 00262 } 00263 DBUG_RETURN (next_link != HA_OFFSET_ERROR); 00264 } /* check_k_link */ 00265 00266 00267 /* Check sizes of files */ 00268 00269 int chk_size(MI_CHECK *param, register MI_INFO *info) 00270 { 00271 int error=0; 00272 register my_off_t skr,size; 00273 char buff[22],buff2[22]; 00274 DBUG_ENTER("chk_size"); 00275 00276 if (!(param->testflag & T_SILENT)) puts("- check file-size"); 00277 00278 /* The following is needed if called externally (not from myisamchk) */ 00279 flush_key_blocks(info->s->key_cache, 00280 info->s->kfile, FLUSH_FORCE_WRITE); 00281 00282 size=my_seek(info->s->kfile,0L,MY_SEEK_END,MYF(0)); 00283 if ((skr=(my_off_t) info->state->key_file_length) != size) 00284 { 00285 /* Don't give error if file generated by myisampack */ 00286 if (skr > size && mi_is_any_key_active(info->s->state.key_map)) 00287 { 00288 error=1; 00289 mi_check_print_error(param, 00290 "Size of indexfile is: %-8s Should be: %s", 00291 llstr(size,buff), llstr(skr,buff2)); 00292 } 00293 else 00294 mi_check_print_warning(param, 00295 "Size of indexfile is: %-8s Should be: %s", 00296 llstr(size,buff), llstr(skr,buff2)); 00297 } 00298 if (!(param->testflag & T_VERY_SILENT) && 00299 ! (info->s->options & HA_OPTION_COMPRESS_RECORD) && 00300 ulonglong2double(info->state->key_file_length) > 00301 ulonglong2double(info->s->base.margin_key_file_length)*0.9) 00302 mi_check_print_warning(param,"Keyfile is almost full, %10s of %10s used", 00303 llstr(info->state->key_file_length,buff), 00304 llstr(info->s->base.max_key_file_length-1,buff)); 00305 00306 size=my_seek(info->dfile,0L,MY_SEEK_END,MYF(0)); 00307 skr=(my_off_t) info->state->data_file_length; 00308 if (info->s->options & HA_OPTION_COMPRESS_RECORD) 00309 skr+= MEMMAP_EXTRA_MARGIN; 00310 #ifdef USE_RELOC 00311 if (info->data_file_type == STATIC_RECORD && 00312 skr < (my_off_t) info->s->base.reloc*info->s->base.min_pack_length) 00313 skr=(my_off_t) info->s->base.reloc*info->s->base.min_pack_length; 00314 #endif 00315 if (skr != size) 00316 { 00317 info->state->data_file_length=size; /* Skip other errors */ 00318 if (skr > size && skr != size + MEMMAP_EXTRA_MARGIN) 00319 { 00320 error=1; 00321 mi_check_print_error(param,"Size of datafile is: %-9s Should be: %s", 00322 llstr(size,buff), llstr(skr,buff2)); 00323 param->testflag|=T_RETRY_WITHOUT_QUICK; 00324 } 00325 else 00326 { 00327 mi_check_print_warning(param, 00328 "Size of datafile is: %-9s Should be: %s", 00329 llstr(size,buff), llstr(skr,buff2)); 00330 } 00331 } 00332 if (!(param->testflag & T_VERY_SILENT) && 00333 !(info->s->options & HA_OPTION_COMPRESS_RECORD) && 00334 ulonglong2double(info->state->data_file_length) > 00335 (ulonglong2double(info->s->base.max_data_file_length)*0.9)) 00336 mi_check_print_warning(param, "Datafile is almost full, %10s of %10s used", 00337 llstr(info->state->data_file_length,buff), 00338 llstr(info->s->base.max_data_file_length-1,buff2)); 00339 DBUG_RETURN(error); 00340 } /* chk_size */ 00341 00342 00343 /* Check keys */ 00344 00345 int chk_key(MI_CHECK *param, register MI_INFO *info) 00346 { 00347 uint key,found_keys=0,full_text_keys=0,result=0; 00348 ha_rows keys; 00349 ha_checksum old_record_checksum,init_checksum; 00350 my_off_t all_keydata,all_totaldata,key_totlength,length; 00351 ulong *rec_per_key_part; 00352 MYISAM_SHARE *share=info->s; 00353 MI_KEYDEF *keyinfo; 00354 char buff[22],buff2[22]; 00355 DBUG_ENTER("chk_key"); 00356 00357 if (!(param->testflag & T_SILENT)) 00358 puts("- check key delete-chain"); 00359 00360 param->key_file_blocks=info->s->base.keystart; 00361 for (key=0 ; key < info->s->state.header.max_block_size_index ; key++) 00362 if (check_k_link(param,info,key)) 00363 { 00364 if (param->testflag & T_VERBOSE) puts(""); 00365 mi_check_print_error(param,"key delete-link-chain corrupted"); 00366 DBUG_RETURN(-1); 00367 } 00368 00369 if (!(param->testflag & T_SILENT)) puts("- check index reference"); 00370 00371 all_keydata=all_totaldata=key_totlength=0; 00372 old_record_checksum=0; 00373 init_checksum=param->record_checksum; 00374 if (!(share->options & 00375 (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD))) 00376 old_record_checksum=calc_checksum(info->state->records+info->state->del-1)* 00377 share->base.pack_reclength; 00378 rec_per_key_part= param->rec_per_key_part; 00379 for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ; 00380 rec_per_key_part+=keyinfo->keysegs, key++, keyinfo++) 00381 { 00382 param->key_crc[key]=0; 00383 if (! mi_is_key_active(share->state.key_map, key)) 00384 { 00385 /* Remember old statistics for key */ 00386 memcpy((char*) rec_per_key_part, 00387 (char*) (share->state.rec_per_key_part + 00388 (uint) (rec_per_key_part - param->rec_per_key_part)), 00389 keyinfo->keysegs*sizeof(*rec_per_key_part)); 00390 continue; 00391 } 00392 found_keys++; 00393 00394 param->record_checksum=init_checksum; 00395 00396 bzero((char*) ¶m->unique_count,sizeof(param->unique_count)); 00397 bzero((char*) ¶m->notnull_count,sizeof(param->notnull_count)); 00398 00399 if ((!(param->testflag & T_SILENT))) 00400 printf ("- check data record references index: %d\n",key+1); 00401 if (keyinfo->flag & HA_FULLTEXT) 00402 full_text_keys++; 00403 if (share->state.key_root[key] == HA_OFFSET_ERROR && 00404 (info->state->records == 0 || keyinfo->flag & HA_FULLTEXT)) 00405 goto do_stat; 00406 if (!_mi_fetch_keypage(info,keyinfo,share->state.key_root[key], 00407 DFLT_INIT_HITS,info->buff,0)) 00408 { 00409 mi_check_print_error(param,"Can't read indexpage from filepos: %s", 00410 llstr(share->state.key_root[key],buff)); 00411 if (!(param->testflag & T_INFO)) 00412 DBUG_RETURN(-1); 00413 result= -1; 00414 continue; 00415 } 00416 param->key_file_blocks+=keyinfo->block_length; 00417 keys=0; 00418 param->keydata=param->totaldata=0; 00419 param->key_blocks=0; 00420 param->max_level=0; 00421 if (chk_index(param,info,keyinfo,share->state.key_root[key],info->buff, 00422 &keys, param->key_crc+key,1)) 00423 DBUG_RETURN(-1); 00424 if(!(keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL))) 00425 { 00426 if (keys != info->state->records) 00427 { 00428 mi_check_print_error(param,"Found %s keys of %s",llstr(keys,buff), 00429 llstr(info->state->records,buff2)); 00430 if (!(param->testflag & T_INFO)) 00431 DBUG_RETURN(-1); 00432 result= -1; 00433 continue; 00434 } 00435 if (found_keys - full_text_keys == 1 && 00436 ((share->options & 00437 (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) || 00438 (param->testflag & T_DONT_CHECK_CHECKSUM))) 00439 old_record_checksum=param->record_checksum; 00440 else if (old_record_checksum != param->record_checksum) 00441 { 00442 if (key) 00443 mi_check_print_error(param,"Key %u doesn't point at same records that key 1", 00444 key+1); 00445 else 00446 mi_check_print_error(param,"Key 1 doesn't point at all records"); 00447 if (!(param->testflag & T_INFO)) 00448 DBUG_RETURN(-1); 00449 result= -1; 00450 continue; 00451 } 00452 } 00453 if ((uint) share->base.auto_key -1 == key) 00454 { 00455 /* Check that auto_increment key is bigger than max key value */ 00456 ulonglong auto_increment; 00457 info->lastinx=key; 00458 _mi_read_key_record(info, 0L, info->rec_buff); 00459 auto_increment= retrieve_auto_increment(info, info->rec_buff); 00460 if (auto_increment > info->s->state.auto_increment) 00461 { 00462 mi_check_print_warning(param, "Auto-increment value: %s is smaller " 00463 "than max used value: %s", 00464 llstr(info->s->state.auto_increment,buff2), 00465 llstr(auto_increment, buff)); 00466 } 00467 if (param->testflag & T_AUTO_INC) 00468 { 00469 set_if_bigger(info->s->state.auto_increment, 00470 auto_increment); 00471 set_if_bigger(info->s->state.auto_increment, 00472 param->auto_increment_value); 00473 } 00474 00475 /* Check that there isn't a row with auto_increment = 0 in the table */ 00476 mi_extra(info,HA_EXTRA_KEYREAD,0); 00477 bzero(info->lastkey,keyinfo->seg->length); 00478 if (!mi_rkey(info, info->rec_buff, key, (const byte*) info->lastkey, 00479 keyinfo->seg->length, HA_READ_KEY_EXACT)) 00480 { 00481 /* Don't count this as a real warning, as myisamchk can't correct it */ 00482 uint save=param->warning_printed; 00483 mi_check_print_warning(param, "Found row where the auto_increment " 00484 "column has the value 0"); 00485 param->warning_printed=save; 00486 } 00487 mi_extra(info,HA_EXTRA_NO_KEYREAD,0); 00488 } 00489 00490 length=(my_off_t) isam_key_length(info,keyinfo)*keys + param->key_blocks*2; 00491 if (param->testflag & T_INFO && param->totaldata != 0L && keys != 0L) 00492 printf("Key: %2d: Keyblocks used: %3d%% Packed: %4d%% Max levels: %2d\n", 00493 key+1, 00494 (int) (my_off_t2double(param->keydata)*100.0/my_off_t2double(param->totaldata)), 00495 (int) ((my_off_t2double(length) - my_off_t2double(param->keydata))*100.0/ 00496 my_off_t2double(length)), 00497 param->max_level); 00498 all_keydata+=param->keydata; all_totaldata+=param->totaldata; key_totlength+=length; 00499 00500 do_stat: 00501 if (param->testflag & T_STATISTICS) 00502 update_key_parts(keyinfo, rec_per_key_part, param->unique_count, 00503 param->stats_method == MI_STATS_METHOD_IGNORE_NULLS? 00504 param->notnull_count: NULL, 00505 (ulonglong)info->state->records); 00506 } 00507 if (param->testflag & T_INFO) 00508 { 00509 if (all_totaldata != 0L && found_keys > 0) 00510 printf("Total: Keyblocks used: %3d%% Packed: %4d%%\n\n", 00511 (int) (my_off_t2double(all_keydata)*100.0/ 00512 my_off_t2double(all_totaldata)), 00513 (int) ((my_off_t2double(key_totlength) - 00514 my_off_t2double(all_keydata))*100.0/ 00515 my_off_t2double(key_totlength))); 00516 else if (all_totaldata != 0L && mi_is_any_key_active(share->state.key_map)) 00517 puts(""); 00518 } 00519 if (param->key_file_blocks != info->state->key_file_length && 00520 param->keys_in_use != ~(ulonglong) 0) 00521 mi_check_print_warning(param, "Some data are unreferenced in keyfile"); 00522 if (found_keys != full_text_keys) 00523 param->record_checksum=old_record_checksum-init_checksum; /* Remove delete links */ 00524 else 00525 param->record_checksum=0; 00526 DBUG_RETURN(result); 00527 } /* chk_key */ 00528 00529 00530 static int chk_index_down(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo, 00531 my_off_t page, uchar *buff, ha_rows *keys, 00532 ha_checksum *key_checksum, uint level) 00533 { 00534 char llbuff[22],llbuff2[22]; 00535 if (page > info->state->key_file_length || (page & (info->s->blocksize -1))) 00536 { 00537 my_off_t max_length=my_seek(info->s->kfile,0L,MY_SEEK_END,MYF(0)); 00538 mi_check_print_error(param,"Wrong pagepointer: %s at page: %s", 00539 llstr(page,llbuff),llstr(page,llbuff2)); 00540 00541 if (page+info->s->blocksize > max_length) 00542 goto err; 00543 info->state->key_file_length=(max_length & 00544 ~ (my_off_t) (info->s->blocksize-1)); 00545 } 00546 if (!_mi_fetch_keypage(info,keyinfo,page, DFLT_INIT_HITS,buff,0)) 00547 { 00548 mi_check_print_error(param,"Can't read key from filepos: %s", 00549 llstr(page,llbuff)); 00550 goto err; 00551 } 00552 param->key_file_blocks+=keyinfo->block_length; 00553 if (chk_index(param,info,keyinfo,page,buff,keys,key_checksum,level)) 00554 goto err; 00555 00556 return 0; 00557 err: 00558 return 1; 00559 } 00560 00561 00562 /* 00563 "Ignore NULLs" statistics collection method: process first index tuple. 00564 00565 SYNOPSIS 00566 mi_collect_stats_nonulls_first() 00567 keyseg IN Array of key part descriptions 00568 notnull INOUT Array, notnull[i] = (number of {keypart1...keypart_i} 00569 tuples that don't contain NULLs) 00570 key IN Key values tuple 00571 00572 DESCRIPTION 00573 Process the first index tuple - find out which prefix tuples don't 00574 contain NULLs, and update the array of notnull counters accordingly. 00575 */ 00576 00577 static 00578 void mi_collect_stats_nonulls_first(HA_KEYSEG *keyseg, ulonglong *notnull, 00579 uchar *key) 00580 { 00581 uint first_null, kp; 00582 first_null= ha_find_null(keyseg, key) - keyseg; 00583 /* 00584 All prefix tuples that don't include keypart_{first_null} are not-null 00585 tuples (and all others aren't), increment counters for them. 00586 */ 00587 for (kp= 0; kp < first_null; kp++) 00588 notnull[kp]++; 00589 } 00590 00591 00592 /* 00593 "Ignore NULLs" statistics collection method: process next index tuple. 00594 00595 SYNOPSIS 00596 mi_collect_stats_nonulls_next() 00597 keyseg IN Array of key part descriptions 00598 notnull INOUT Array, notnull[i] = (number of {keypart1...keypart_i} 00599 tuples that don't contain NULLs) 00600 prev_key IN Previous key values tuple 00601 last_key IN Next key values tuple 00602 00603 DESCRIPTION 00604 Process the next index tuple: 00605 1. Find out which prefix tuples of last_key don't contain NULLs, and 00606 update the array of notnull counters accordingly. 00607 2. Find the first keypart number where the prev_key and last_key tuples 00608 are different(A), or last_key has NULL value(B), and return it, so the 00609 caller can count number of unique tuples for each key prefix. We don't 00610 need (B) to be counted, and that is compensated back in 00611 update_key_parts(). 00612 00613 RETURN 00614 1 + number of first keypart where values differ or last_key tuple has NULL 00615 */ 00616 00617 static 00618 int mi_collect_stats_nonulls_next(HA_KEYSEG *keyseg, ulonglong *notnull, 00619 uchar *prev_key, uchar *last_key) 00620 { 00621 uint diffs[2]; 00622 uint first_null_seg, kp; 00623 HA_KEYSEG *seg; 00624 00625 /* 00626 Find the first keypart where values are different or either of them is 00627 NULL. We get results in diffs array: 00628 diffs[0]= 1 + number of first different keypart 00629 diffs[1]=offset: (last_key + diffs[1]) points to first value in 00630 last_key that is NULL or different from corresponding 00631 value in prev_key. 00632 */ 00633 ha_key_cmp(keyseg, prev_key, last_key, USE_WHOLE_KEY, 00634 SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diffs); 00635 seg= keyseg + diffs[0] - 1; 00636 00637 /* Find first NULL in last_key */ 00638 first_null_seg= ha_find_null(seg, last_key + diffs[1]) - keyseg; 00639 for (kp= 0; kp < first_null_seg; kp++) 00640 notnull[kp]++; 00641 00642 /* 00643 Return 1+ number of first key part where values differ. Don't care if 00644 these were NULLs and not .... We compensate for that in 00645 update_key_parts. 00646 */ 00647 return diffs[0]; 00648 } 00649 00650 00651 /* Check if index is ok */ 00652 00653 static int chk_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo, 00654 my_off_t page, uchar *buff, ha_rows *keys, 00655 ha_checksum *key_checksum, uint level) 00656 { 00657 int flag; 00658 uint used_length,comp_flag,nod_flag,key_length=0; 00659 uchar key[MI_MAX_POSSIBLE_KEY_BUFF],*temp_buff,*keypos,*old_keypos,*endpos; 00660 my_off_t next_page,record; 00661 char llbuff[22]; 00662 uint diff_pos[2]; 00663 DBUG_ENTER("chk_index"); 00664 DBUG_DUMP("buff",(byte*) buff,mi_getint(buff)); 00665 00666 /* TODO: implement appropriate check for RTree keys */ 00667 if (keyinfo->flag & HA_SPATIAL) 00668 DBUG_RETURN(0); 00669 00670 if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length))) 00671 { 00672 mi_check_print_error(param,"Not enough memory for keyblock"); 00673 DBUG_RETURN(-1); 00674 } 00675 00676 if (keyinfo->flag & HA_NOSAME) 00677 comp_flag=SEARCH_FIND | SEARCH_UPDATE; /* Not real duplicates */ 00678 else 00679 comp_flag=SEARCH_SAME; /* Keys in positionorder */ 00680 nod_flag=mi_test_if_nod(buff); 00681 used_length=mi_getint(buff); 00682 keypos=buff+2+nod_flag; 00683 endpos=buff+used_length; 00684 00685 param->keydata+=used_length; param->totaldata+=keyinfo->block_length; /* INFO */ 00686 param->key_blocks++; 00687 if (level > param->max_level) 00688 param->max_level=level; 00689 00690 if (used_length > keyinfo->block_length) 00691 { 00692 mi_check_print_error(param,"Wrong pageinfo at page: %s", 00693 llstr(page,llbuff)); 00694 goto err; 00695 } 00696 for ( ;; ) 00697 { 00698 if (*killed_ptr(param)) 00699 goto err; 00700 memcpy((char*) info->lastkey,(char*) key,key_length); 00701 info->lastkey_length=key_length; 00702 if (nod_flag) 00703 { 00704 next_page=_mi_kpos(nod_flag,keypos); 00705 if (chk_index_down(param,info,keyinfo,next_page, 00706 temp_buff,keys,key_checksum,level+1)) 00707 goto err; 00708 } 00709 old_keypos=keypos; 00710 if (keypos >= endpos || 00711 (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0) 00712 break; 00713 if (keypos > endpos) 00714 { 00715 mi_check_print_error(param,"Wrong key block length at page: %s",llstr(page,llbuff)); 00716 goto err; 00717 } 00718 if ((*keys)++ && 00719 (flag=ha_key_cmp(keyinfo->seg,info->lastkey,key,key_length, 00720 comp_flag, diff_pos)) >=0) 00721 { 00722 DBUG_DUMP("old",(byte*) info->lastkey, info->lastkey_length); 00723 DBUG_DUMP("new",(byte*) key, key_length); 00724 DBUG_DUMP("new_in_page",(char*) old_keypos,(uint) (keypos-old_keypos)); 00725 00726 if (comp_flag & SEARCH_FIND && flag == 0) 00727 mi_check_print_error(param,"Found duplicated key at page %s",llstr(page,llbuff)); 00728 else 00729 mi_check_print_error(param,"Key in wrong position at page %s",llstr(page,llbuff)); 00730 goto err; 00731 } 00732 if (param->testflag & T_STATISTICS) 00733 { 00734 if (*keys != 1L) /* not first_key */ 00735 { 00736 if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL) 00737 ha_key_cmp(keyinfo->seg,info->lastkey,key,USE_WHOLE_KEY, 00738 SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, 00739 diff_pos); 00740 else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS) 00741 { 00742 diff_pos[0]= mi_collect_stats_nonulls_next(keyinfo->seg, 00743 param->notnull_count, 00744 info->lastkey, key); 00745 } 00746 param->unique_count[diff_pos[0]-1]++; 00747 } 00748 else 00749 { 00750 if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS) 00751 mi_collect_stats_nonulls_first(keyinfo->seg, param->notnull_count, 00752 key); 00753 } 00754 } 00755 (*key_checksum)+= mi_byte_checksum((byte*) key, 00756 key_length- info->s->rec_reflength); 00757 record= _mi_dpos(info,0,key+key_length); 00758 if (keyinfo->flag & HA_FULLTEXT) /* special handling for ft2 */ 00759 { 00760 uint off; 00761 int subkeys; 00762 get_key_full_length_rdonly(off, key); 00763 subkeys=ft_sintXkorr(key+off); 00764 if (subkeys < 0) 00765 { 00766 ha_rows tmp_keys=0; 00767 if (chk_index_down(param,info,&info->s->ft2_keyinfo,record, 00768 temp_buff,&tmp_keys,key_checksum,1)) 00769 goto err; 00770 if (tmp_keys + subkeys) 00771 { 00772 mi_check_print_error(param, 00773 "Number of words in the 2nd level tree " 00774 "does not match the number in the header. " 00775 "Parent word in on the page %s, offset %u", 00776 llstr(page,llbuff), (uint) (old_keypos-buff)); 00777 goto err; 00778 } 00779 (*keys)+=tmp_keys-1; 00780 continue; 00781 } 00782 /* fall through */ 00783 } 00784 if (record >= info->state->data_file_length) 00785 { 00786 #ifndef DBUG_OFF 00787 char llbuff2[22], llbuff3[22]; 00788 #endif 00789 mi_check_print_error(param,"Found key at page %s that points to record outside datafile",llstr(page,llbuff)); 00790 DBUG_PRINT("test",("page: %s record: %s filelength: %s", 00791 llstr(page,llbuff),llstr(record,llbuff2), 00792 llstr(info->state->data_file_length,llbuff3))); 00793 DBUG_DUMP("key",(byte*) key,key_length); 00794 DBUG_DUMP("new_in_page",(char*) old_keypos,(uint) (keypos-old_keypos)); 00795 goto err; 00796 } 00797 param->record_checksum+=(ha_checksum) record; 00798 } 00799 if (keypos != endpos) 00800 { 00801 mi_check_print_error(param,"Keyblock size at page %s is not correct. Block length: %d key length: %d", 00802 llstr(page,llbuff), used_length, (keypos - buff)); 00803 goto err; 00804 } 00805 my_afree((byte*) temp_buff); 00806 DBUG_RETURN(0); 00807 err: 00808 my_afree((byte*) temp_buff); 00809 DBUG_RETURN(1); 00810 } /* chk_index */ 00811 00812 00813 /* Calculate a checksum of 1+2+3+4...N = N*(N+1)/2 without overflow */ 00814 00815 static ha_checksum calc_checksum(ha_rows count) 00816 { 00817 ulonglong sum,a,b; 00818 DBUG_ENTER("calc_checksum"); 00819 00820 sum=0; 00821 a=count; b=count+1; 00822 if (a & 1) 00823 b>>=1; 00824 else 00825 a>>=1; 00826 while (b) 00827 { 00828 if (b & 1) 00829 sum+=a; 00830 a<<=1; b>>=1; 00831 } 00832 DBUG_PRINT("exit",("sum: %lx",(ulong) sum)); 00833 DBUG_RETURN((ha_checksum) sum); 00834 } /* calc_checksum */ 00835 00836 00837 /* Calc length of key in normal isam */ 00838 00839 static uint isam_key_length(MI_INFO *info, register MI_KEYDEF *keyinfo) 00840 { 00841 uint length; 00842 HA_KEYSEG *keyseg; 00843 DBUG_ENTER("isam_key_length"); 00844 00845 length= info->s->rec_reflength; 00846 for (keyseg=keyinfo->seg ; keyseg->type ; keyseg++) 00847 length+= keyseg->length; 00848 00849 DBUG_PRINT("exit",("length: %d",length)); 00850 DBUG_RETURN(length); 00851 } /* key_length */ 00852 00853 00854 /* Check that record-link is ok */ 00855 00856 int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend) 00857 { 00858 int error,got_error,flag; 00859 uint key,left_length,b_type,field; 00860 ha_rows records,del_blocks; 00861 my_off_t used,empty,pos,splits,start_recpos, 00862 del_length,link_used,start_block; 00863 byte *record,*to; 00864 char llbuff[22],llbuff2[22],llbuff3[22]; 00865 ha_checksum intern_record_checksum; 00866 ha_checksum key_checksum[MI_MAX_POSSIBLE_KEY]; 00867 my_bool static_row_size; 00868 MI_KEYDEF *keyinfo; 00869 MI_BLOCK_INFO block_info; 00870 DBUG_ENTER("chk_data_link"); 00871 00872 if (!(param->testflag & T_SILENT)) 00873 { 00874 if (extend) 00875 puts("- check records and index references"); 00876 else 00877 puts("- check record links"); 00878 } 00879 00880 if (!(record= (byte*) my_malloc(info->s->base.pack_reclength,MYF(0)))) 00881 { 00882 mi_check_print_error(param,"Not enough memory for record"); 00883 DBUG_RETURN(-1); 00884 } 00885 records=del_blocks=0; 00886 used=link_used=splits=del_length=0; 00887 intern_record_checksum=param->glob_crc=0; 00888 LINT_INIT(left_length); LINT_INIT(start_recpos); LINT_INIT(to); 00889 got_error=error=0; 00890 empty=info->s->pack.header_length; 00891 00892 /* Check how to calculate checksum of rows */ 00893 static_row_size=1; 00894 if (info->s->data_file_type == COMPRESSED_RECORD) 00895 { 00896 for (field=0 ; field < info->s->base.fields ; field++) 00897 { 00898 if (info->s->rec[field].base_type == FIELD_BLOB || 00899 info->s->rec[field].base_type == FIELD_VARCHAR) 00900 { 00901 static_row_size=0; 00902 break; 00903 } 00904 } 00905 } 00906 00907 pos=my_b_tell(¶m->read_cache); 00908 bzero((char*) key_checksum, info->s->base.keys * sizeof(key_checksum[0])); 00909 while (pos < info->state->data_file_length) 00910 { 00911 if (*killed_ptr(param)) 00912 goto err2; 00913 switch (info->s->data_file_type) { 00914 case STATIC_RECORD: 00915 if (my_b_read(¶m->read_cache,(byte*) record, 00916 info->s->base.pack_reclength)) 00917 goto err; 00918 start_recpos=pos; 00919 pos+=info->s->base.pack_reclength; 00920 splits++; 00921 if (*record == '\0') 00922 { 00923 del_blocks++; 00924 del_length+=info->s->base.pack_reclength; 00925 continue; /* Record removed */ 00926 } 00927 param->glob_crc+= mi_static_checksum(info,record); 00928 used+=info->s->base.pack_reclength; 00929 break; 00930 case DYNAMIC_RECORD: 00931 flag=block_info.second_read=0; 00932 block_info.next_filepos=pos; 00933 do 00934 { 00935 if (_mi_read_cache(¶m->read_cache,(byte*) block_info.header, 00936 (start_block=block_info.next_filepos), 00937 sizeof(block_info.header), 00938 (flag ? 0 : READING_NEXT) | READING_HEADER)) 00939 goto err; 00940 if (start_block & (MI_DYN_ALIGN_SIZE-1)) 00941 { 00942 mi_check_print_error(param,"Wrong aligned block at %s", 00943 llstr(start_block,llbuff)); 00944 goto err2; 00945 } 00946 b_type=_mi_get_block_info(&block_info,-1,start_block); 00947 if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR | 00948 BLOCK_FATAL_ERROR)) 00949 { 00950 if (b_type & BLOCK_SYNC_ERROR) 00951 { 00952 if (flag) 00953 { 00954 mi_check_print_error(param,"Unexpected byte: %d at link: %s", 00955 (int) block_info.header[0], 00956 llstr(start_block,llbuff)); 00957 goto err2; 00958 } 00959 pos=block_info.filepos+block_info.block_len; 00960 goto next; 00961 } 00962 if (b_type & BLOCK_DELETED) 00963 { 00964 if (block_info.block_len < info->s->base.min_block_length) 00965 { 00966 mi_check_print_error(param, 00967 "Deleted block with impossible length %lu at %s", 00968 block_info.block_len,llstr(pos,llbuff)); 00969 goto err2; 00970 } 00971 if ((block_info.next_filepos != HA_OFFSET_ERROR && 00972 block_info.next_filepos >= info->state->data_file_length) || 00973 (block_info.prev_filepos != HA_OFFSET_ERROR && 00974 block_info.prev_filepos >= info->state->data_file_length)) 00975 { 00976 mi_check_print_error(param,"Delete link points outside datafile at %s", 00977 llstr(pos,llbuff)); 00978 goto err2; 00979 } 00980 del_blocks++; 00981 del_length+=block_info.block_len; 00982 pos=block_info.filepos+block_info.block_len; 00983 splits++; 00984 goto next; 00985 } 00986 mi_check_print_error(param,"Wrong bytesec: %d-%d-%d at linkstart: %s", 00987 block_info.header[0],block_info.header[1], 00988 block_info.header[2], 00989 llstr(start_block,llbuff)); 00990 goto err2; 00991 } 00992 if (info->state->data_file_length < block_info.filepos+ 00993 block_info.block_len) 00994 { 00995 mi_check_print_error(param, 00996 "Recordlink that points outside datafile at %s", 00997 llstr(pos,llbuff)); 00998 got_error=1; 00999 break; 01000 } 01001 splits++; 01002 if (!flag++) /* First block */ 01003 { 01004 start_recpos=pos; 01005 pos=block_info.filepos+block_info.block_len; 01006 if (block_info.rec_len > (uint) info->s->base.max_pack_length) 01007 { 01008 mi_check_print_error(param,"Found too long record (%lu) at %s", 01009 (ulong) block_info.rec_len, 01010 llstr(start_recpos,llbuff)); 01011 got_error=1; 01012 break; 01013 } 01014 if (info->s->base.blobs) 01015 { 01016 if (!(to= mi_alloc_rec_buff(info, block_info.rec_len, 01017 &info->rec_buff))) 01018 { 01019 mi_check_print_error(param, 01020 "Not enough memory (%lu) for blob at %s", 01021 (ulong) block_info.rec_len, 01022 llstr(start_recpos,llbuff)); 01023 got_error=1; 01024 break; 01025 } 01026 } 01027 else 01028 to= info->rec_buff; 01029 left_length=block_info.rec_len; 01030 } 01031 if (left_length < block_info.data_len) 01032 { 01033 mi_check_print_error(param,"Found too long record (%lu) at %s", 01034 (ulong) block_info.data_len, 01035 llstr(start_recpos,llbuff)); 01036 got_error=1; 01037 break; 01038 } 01039 if (_mi_read_cache(¶m->read_cache,(byte*) to,block_info.filepos, 01040 (uint) block_info.data_len, 01041 flag == 1 ? READING_NEXT : 0)) 01042 goto err; 01043 to+=block_info.data_len; 01044 link_used+= block_info.filepos-start_block; 01045 used+= block_info.filepos - start_block + block_info.data_len; 01046 empty+=block_info.block_len-block_info.data_len; 01047 left_length-=block_info.data_len; 01048 if (left_length) 01049 { 01050 if (b_type & BLOCK_LAST) 01051 { 01052 mi_check_print_error(param, 01053 "Wrong record length %s of %s at %s", 01054 llstr(block_info.rec_len-left_length,llbuff), 01055 llstr(block_info.rec_len, llbuff2), 01056 llstr(start_recpos,llbuff3)); 01057 got_error=1; 01058 break; 01059 } 01060 if (info->state->data_file_length < block_info.next_filepos) 01061 { 01062 mi_check_print_error(param, 01063 "Found next-recordlink that points outside datafile at %s", 01064 llstr(block_info.filepos,llbuff)); 01065 got_error=1; 01066 break; 01067 } 01068 } 01069 } while (left_length); 01070 if (! got_error) 01071 { 01072 if (_mi_rec_unpack(info,record,info->rec_buff,block_info.rec_len) == 01073 MY_FILE_ERROR) 01074 { 01075 mi_check_print_error(param,"Found wrong record at %s", 01076 llstr(start_recpos,llbuff)); 01077 got_error=1; 01078 } 01079 else 01080 { 01081 info->checksum=mi_checksum(info,record); 01082 if (param->testflag & (T_EXTEND | T_MEDIUM | T_VERBOSE)) 01083 { 01084 if (_mi_rec_check(info,record, info->rec_buff,block_info.rec_len, 01085 test(info->s->calc_checksum))) 01086 { 01087 mi_check_print_error(param,"Found wrong packed record at %s", 01088 llstr(start_recpos,llbuff)); 01089 got_error=1; 01090 } 01091 } 01092 if (!got_error) 01093 param->glob_crc+= info->checksum; 01094 } 01095 } 01096 else if (!flag) 01097 pos=block_info.filepos+block_info.block_len; 01098 break; 01099 case COMPRESSED_RECORD: 01100 if (_mi_read_cache(¶m->read_cache,(byte*) block_info.header, pos, 01101 info->s->pack.ref_length, READING_NEXT)) 01102 goto err; 01103 start_recpos=pos; 01104 splits++; 01105 VOID(_mi_pack_get_block_info(info,&block_info, -1, start_recpos)); 01106 pos=block_info.filepos+block_info.rec_len; 01107 if (block_info.rec_len < (uint) info->s->min_pack_length || 01108 block_info.rec_len > (uint) info->s->max_pack_length) 01109 { 01110 mi_check_print_error(param, 01111 "Found block with wrong recordlength: %d at %s", 01112 block_info.rec_len, llstr(start_recpos,llbuff)); 01113 got_error=1; 01114 break; 01115 } 01116 if (_mi_read_cache(¶m->read_cache,(byte*) info->rec_buff, 01117 block_info.filepos, block_info.rec_len, READING_NEXT)) 01118 goto err; 01119 if (_mi_pack_rec_unpack(info,record,info->rec_buff,block_info.rec_len)) 01120 { 01121 mi_check_print_error(param,"Found wrong record at %s", 01122 llstr(start_recpos,llbuff)); 01123 got_error=1; 01124 } 01125 if (static_row_size) 01126 param->glob_crc+= mi_static_checksum(info,record); 01127 else 01128 param->glob_crc+= mi_checksum(info,record); 01129 link_used+= (block_info.filepos - start_recpos); 01130 used+= (pos-start_recpos); 01131 } /* switch */ 01132 if (! got_error) 01133 { 01134 intern_record_checksum+=(ha_checksum) start_recpos; 01135 records++; 01136 if (param->testflag & T_WRITE_LOOP && records % WRITE_COUNT == 0) 01137 { 01138 printf("%s\r", llstr(records,llbuff)); VOID(fflush(stdout)); 01139 } 01140 01141 /* Check if keys match the record */ 01142 01143 for (key=0,keyinfo= info->s->keyinfo; key < info->s->base.keys; 01144 key++,keyinfo++) 01145 { 01146 if (mi_is_key_active(info->s->state.key_map, key)) 01147 { 01148 if(!(keyinfo->flag & HA_FULLTEXT)) 01149 { 01150 uint key_length=_mi_make_key(info,key,info->lastkey,record, 01151 start_recpos); 01152 if (extend) 01153 { 01154 /* We don't need to lock the key tree here as we don't allow 01155 concurrent threads when running myisamchk 01156 */ 01157 int search_result= 01158 #ifdef HAVE_RTREE_KEYS 01159 (keyinfo->flag & HA_SPATIAL) ? 01160 rtree_find_first(info, key, info->lastkey, key_length, 01161 MBR_EQUAL | MBR_DATA) : 01162 #endif 01163 _mi_search(info,keyinfo,info->lastkey,key_length, 01164 SEARCH_SAME, info->s->state.key_root[key]); 01165 if (search_result) 01166 { 01167 mi_check_print_error(param,"Record at: %10s " 01168 "Can't find key for index: %2d", 01169 llstr(start_recpos,llbuff),key+1); 01170 if (error++ > MAXERR || !(param->testflag & T_VERBOSE)) 01171 goto err2; 01172 } 01173 } 01174 else 01175 key_checksum[key]+=mi_byte_checksum((byte*) info->lastkey, 01176 key_length); 01177 } 01178 } 01179 } 01180 } 01181 else 01182 { 01183 got_error=0; 01184 if (error++ > MAXERR || !(param->testflag & T_VERBOSE)) 01185 goto err2; 01186 } 01187 next:; /* Next record */ 01188 } 01189 if (param->testflag & T_WRITE_LOOP) 01190 { 01191 VOID(fputs(" \r",stdout)); VOID(fflush(stdout)); 01192 } 01193 if (records != info->state->records) 01194 { 01195 mi_check_print_error(param,"Record-count is not ok; is %-10s Should be: %s", 01196 llstr(records,llbuff), llstr(info->state->records,llbuff2)); 01197 error=1; 01198 } 01199 else if (param->record_checksum && 01200 param->record_checksum != intern_record_checksum) 01201 { 01202 mi_check_print_error(param, 01203 "Keypointers and record positions doesn't match"); 01204 error=1; 01205 } 01206 else if (param->glob_crc != info->state->checksum && 01207 (info->s->options & 01208 (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))) 01209 { 01210 mi_check_print_warning(param, 01211 "Record checksum is not the same as checksum stored in the index file\n"); 01212 error=1; 01213 } 01214 else if (!extend) 01215 { 01216 for (key=0 ; key < info->s->base.keys; key++) 01217 { 01218 if (key_checksum[key] != param->key_crc[key] && 01219 !(info->s->keyinfo[key].flag & (HA_FULLTEXT | HA_SPATIAL))) 01220 { 01221 mi_check_print_error(param,"Checksum for key: %2d doesn't match checksum for records", 01222 key+1); 01223 error=1; 01224 } 01225 } 01226 } 01227 01228 if (del_length != info->state->empty) 01229 { 01230 mi_check_print_warning(param, 01231 "Found %s deleted space. Should be %s", 01232 llstr(del_length,llbuff2), 01233 llstr(info->state->empty,llbuff)); 01234 } 01235 if (used+empty+del_length != info->state->data_file_length) 01236 { 01237 mi_check_print_warning(param, 01238 "Found %s record-data and %s unused data and %s deleted-data", 01239 llstr(used,llbuff),llstr(empty,llbuff2), 01240 llstr(del_length,llbuff3)); 01241 mi_check_print_warning(param, 01242 "Total %s, Should be: %s", 01243 llstr((used+empty+del_length),llbuff), 01244 llstr(info->state->data_file_length,llbuff2)); 01245 } 01246 if (del_blocks != info->state->del) 01247 { 01248 mi_check_print_warning(param, 01249 "Found %10s deleted blocks Should be: %s", 01250 llstr(del_blocks,llbuff), 01251 llstr(info->state->del,llbuff2)); 01252 } 01253 if (splits != info->s->state.split) 01254 { 01255 mi_check_print_warning(param, 01256 "Found %10s parts Should be: %s parts", 01257 llstr(splits,llbuff), 01258 llstr(info->s->state.split,llbuff2)); 01259 } 01260 if (param->testflag & T_INFO) 01261 { 01262 if (param->warning_printed || param->error_printed) 01263 puts(""); 01264 if (used != 0 && ! param->error_printed) 01265 { 01266 printf("Records:%18s M.recordlength:%9lu Packed:%14.0f%%\n", 01267 llstr(records,llbuff), (long)((used-link_used)/records), 01268 (info->s->base.blobs ? 0.0 : 01269 (ulonglong2double((ulonglong) info->s->base.reclength*records)- 01270 my_off_t2double(used))/ 01271 ulonglong2double((ulonglong) info->s->base.reclength*records)*100.0)); 01272 printf("Recordspace used:%9.0f%% Empty space:%12d%% Blocks/Record: %6.2f\n", 01273 (ulonglong2double(used-link_used)/ulonglong2double(used-link_used+empty)*100.0), 01274 (!records ? 100 : (int) (ulonglong2double(del_length+empty)/ 01275 my_off_t2double(used)*100.0)), 01276 ulonglong2double(splits - del_blocks) / records); 01277 } 01278 printf("Record blocks:%12s Delete blocks:%10s\n", 01279 llstr(splits-del_blocks,llbuff),llstr(del_blocks,llbuff2)); 01280 printf("Record data: %12s Deleted data: %10s\n", 01281 llstr(used-link_used,llbuff),llstr(del_length,llbuff2)); 01282 printf("Lost space: %12s Linkdata: %10s\n", 01283 llstr(empty,llbuff),llstr(link_used,llbuff2)); 01284 } 01285 my_free((gptr) record,MYF(0)); 01286 DBUG_RETURN (error); 01287 err: 01288 mi_check_print_error(param,"got error: %d when reading datafile at record: %s",my_errno, llstr(records,llbuff)); 01289 err2: 01290 my_free((gptr) record,MYF(0)); 01291 param->testflag|=T_RETRY_WITHOUT_QUICK; 01292 DBUG_RETURN(1); 01293 } /* chk_data_link */ 01294 01295 01296 /* Recover old table by reading each record and writing all keys */ 01297 /* Save new datafile-name in temp_filename */ 01298 01299 int mi_repair(MI_CHECK *param, register MI_INFO *info, 01300 my_string name, int rep_quick) 01301 { 01302 int error,got_error; 01303 uint i; 01304 ha_rows start_records,new_header_length; 01305 my_off_t del; 01306 File new_file; 01307 MYISAM_SHARE *share=info->s; 01308 char llbuff[22],llbuff2[22]; 01309 SORT_INFO sort_info; 01310 MI_SORT_PARAM sort_param; 01311 DBUG_ENTER("mi_repair"); 01312 01313 bzero((char *)&sort_info, sizeof(sort_info)); 01314 bzero((char *)&sort_param, sizeof(sort_param)); 01315 start_records=info->state->records; 01316 new_header_length=(param->testflag & T_UNPACK) ? 0L : 01317 share->pack.header_length; 01318 got_error=1; 01319 new_file= -1; 01320 sort_param.sort_info=&sort_info; 01321 01322 if (!(param->testflag & T_SILENT)) 01323 { 01324 printf("- recovering (with keycache) MyISAM-table '%s'\n",name); 01325 printf("Data records: %s\n", llstr(info->state->records,llbuff)); 01326 } 01327 param->testflag|=T_REP; /* for easy checking */ 01328 01329 if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)) 01330 param->testflag|=T_CALC_CHECKSUM; 01331 01332 if (!param->using_global_keycache) 01333 VOID(init_key_cache(dflt_key_cache, param->key_cache_block_size, 01334 param->use_buffers, 0, 0)); 01335 01336 if (init_io_cache(¶m->read_cache,info->dfile, 01337 (uint) param->read_buffer_length, 01338 READ_CACHE,share->pack.header_length,1,MYF(MY_WME))) 01339 { 01340 bzero(&info->rec_cache,sizeof(info->rec_cache)); 01341 goto err; 01342 } 01343 if (!rep_quick) 01344 if (init_io_cache(&info->rec_cache,-1,(uint) param->write_buffer_length, 01345 WRITE_CACHE, new_header_length, 1, 01346 MYF(MY_WME | MY_WAIT_IF_FULL))) 01347 goto err; 01348 info->opt_flag|=WRITE_CACHE_USED; 01349 if (!(sort_param.record=(byte*) my_malloc((uint) share->base.pack_reclength, 01350 MYF(0))) || 01351 !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff)) 01352 { 01353 mi_check_print_error(param, "Not enough memory for extra record"); 01354 goto err; 01355 } 01356 01357 if (!rep_quick) 01358 { 01359 /* Get real path for data file */ 01360 if ((new_file=my_raid_create(fn_format(param->temp_filename, 01361 share->data_file_name, "", 01362 DATA_TMP_EXT, 2+4), 01363 0,param->tmpfile_createflag, 01364 share->base.raid_type, 01365 share->base.raid_chunks, 01366 share->base.raid_chunksize, 01367 MYF(0))) < 0) 01368 { 01369 mi_check_print_error(param,"Can't create new tempfile: '%s'", 01370 param->temp_filename); 01371 goto err; 01372 } 01373 if (filecopy(param,new_file,info->dfile,0L,new_header_length, 01374 "datafile-header")) 01375 goto err; 01376 info->s->state.dellink= HA_OFFSET_ERROR; 01377 info->rec_cache.file=new_file; 01378 if (param->testflag & T_UNPACK) 01379 { 01380 share->options&= ~HA_OPTION_COMPRESS_RECORD; 01381 mi_int2store(share->state.header.options,share->options); 01382 } 01383 } 01384 sort_info.info=info; 01385 sort_info.param = param; 01386 sort_param.read_cache=param->read_cache; 01387 sort_param.pos=sort_param.max_pos=share->pack.header_length; 01388 sort_param.filepos=new_header_length; 01389 param->read_cache.end_of_file=sort_info.filelength= 01390 my_seek(info->dfile,0L,MY_SEEK_END,MYF(0)); 01391 sort_info.dupp=0; 01392 sort_param.fix_datafile= (my_bool) (! rep_quick); 01393 sort_param.master=1; 01394 sort_info.max_records= ~(ha_rows) 0; 01395 01396 set_data_file_type(&sort_info, share); 01397 del=info->state->del; 01398 info->state->records=info->state->del=share->state.split=0; 01399 info->state->empty=0; 01400 param->glob_crc=0; 01401 if (param->testflag & T_CALC_CHECKSUM) 01402 param->calc_checksum=1; 01403 01404 info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED); 01405 01406 /* 01407 Clear all keys. Note that all key blocks allocated until now remain 01408 "dead" parts of the key file. (Bug #4692) 01409 */ 01410 for (i=0 ; i < info->s->base.keys ; i++) 01411 share->state.key_root[i]= HA_OFFSET_ERROR; 01412 01413 /* Drop the delete chain. */ 01414 for (i=0 ; i < share->state.header.max_block_size_index ; i++) 01415 share->state.key_del[i]= HA_OFFSET_ERROR; 01416 01417 /* 01418 If requested, activate (enable) all keys in key_map. In this case, 01419 all indexes will be (re-)built. 01420 */ 01421 if (param->testflag & T_CREATE_MISSING_KEYS) 01422 mi_set_all_keys_active(share->state.key_map, share->base.keys); 01423 01424 info->state->key_file_length=share->base.keystart; 01425 01426 lock_memory(param); /* Everything is alloced */ 01427 01428 /* Re-create all keys, which are set in key_map. */ 01429 while (!(error=sort_get_next_record(&sort_param))) 01430 { 01431 if (writekeys(param,info,(byte*)sort_param.record,sort_param.filepos)) 01432 { 01433 if (my_errno != HA_ERR_FOUND_DUPP_KEY) 01434 goto err; 01435 DBUG_DUMP("record",(byte*) sort_param.record,share->base.pack_reclength); 01436 mi_check_print_info(param,"Duplicate key %2d for record at %10s against new record at %10s", 01437 info->errkey+1, 01438 llstr(sort_param.start_recpos,llbuff), 01439 llstr(info->dupp_key_pos,llbuff2)); 01440 if (param->testflag & T_VERBOSE) 01441 { 01442 VOID(_mi_make_key(info,(uint) info->errkey,info->lastkey, 01443 sort_param.record,0L)); 01444 _mi_print_key(stdout,share->keyinfo[info->errkey].seg,info->lastkey, 01445 USE_WHOLE_KEY); 01446 } 01447 sort_info.dupp++; 01448 if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK) 01449 { 01450 param->testflag|=T_RETRY_WITHOUT_QUICK; 01451 param->error_printed=1; 01452 goto err; 01453 } 01454 continue; 01455 } 01456 if (sort_write_record(&sort_param)) 01457 goto err; 01458 } 01459 if (error > 0 || write_data_suffix(&sort_info, (my_bool)!rep_quick) || 01460 flush_io_cache(&info->rec_cache) || param->read_cache.error < 0) 01461 goto err; 01462 01463 if (param->testflag & T_WRITE_LOOP) 01464 { 01465 VOID(fputs(" \r",stdout)); VOID(fflush(stdout)); 01466 } 01467 if (my_chsize(share->kfile,info->state->key_file_length,0,MYF(0))) 01468 { 01469 mi_check_print_warning(param, 01470 "Can't change size of indexfile, error: %d", 01471 my_errno); 01472 goto err; 01473 } 01474 01475 if (rep_quick && del+sort_info.dupp != info->state->del) 01476 { 01477 mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records"); 01478 mi_check_print_error(param,"Run recovery again without -q"); 01479 got_error=1; 01480 param->retry_repair=1; 01481 param->testflag|=T_RETRY_WITHOUT_QUICK; 01482 goto err; 01483 } 01484 if (param->testflag & T_SAFE_REPAIR) 01485 { 01486 /* Don't repair if we loosed more than one row */ 01487 if (info->state->records+1 < start_records) 01488 { 01489 info->state->records=start_records; 01490 got_error=1; 01491 goto err; 01492 } 01493 } 01494 01495 if (!rep_quick) 01496 { 01497 my_close(info->dfile,MYF(0)); 01498 info->dfile=new_file; 01499 info->state->data_file_length=sort_param.filepos; 01500 share->state.version=(ulong) time((time_t*) 0); /* Force reopen */ 01501 } 01502 else 01503 { 01504 info->state->data_file_length=sort_param.max_pos; 01505 } 01506 if (param->testflag & T_CALC_CHECKSUM) 01507 info->state->checksum=param->glob_crc; 01508 01509 if (!(param->testflag & T_SILENT)) 01510 { 01511 if (start_records != info->state->records) 01512 printf("Data records: %s\n", llstr(info->state->records,llbuff)); 01513 if (sort_info.dupp) 01514 mi_check_print_warning(param, 01515 "%s records have been removed", 01516 llstr(sort_info.dupp,llbuff)); 01517 } 01518 01519 got_error=0; 01520 /* If invoked by external program that uses thr_lock */ 01521 if (&share->state.state != info->state) 01522 memcpy( &share->state.state, info->state, sizeof(*info->state)); 01523 01524 err: 01525 if (!got_error) 01526 { 01527 /* Replace the actual file with the temporary file */ 01528 if (new_file >= 0) 01529 { 01530 my_close(new_file,MYF(0)); 01531 info->dfile=new_file= -1; 01532 if (change_to_newfile(share->data_file_name,MI_NAME_DEXT, 01533 DATA_TMP_EXT, share->base.raid_chunks, 01534 (param->testflag & T_BACKUP_DATA ? 01535 MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) || 01536 mi_open_datafile(info,share,-1)) 01537 got_error=1; 01538 } 01539 } 01540 if (got_error) 01541 { 01542 if (! param->error_printed) 01543 mi_check_print_error(param,"%d for record at pos %s",my_errno, 01544 llstr(sort_param.start_recpos,llbuff)); 01545 if (new_file >= 0) 01546 { 01547 VOID(my_close(new_file,MYF(0))); 01548 VOID(my_raid_delete(param->temp_filename,info->s->base.raid_chunks, 01549 MYF(MY_WME))); 01550 info->rec_cache.file=-1; /* don't flush data to new_file, it's closed */ 01551 } 01552 mi_mark_crashed_on_repair(info); 01553 } 01554 my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff), 01555 MYF(MY_ALLOW_ZERO_PTR)); 01556 my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR)); 01557 my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR)); 01558 VOID(end_io_cache(¶m->read_cache)); 01559 info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); 01560 VOID(end_io_cache(&info->rec_cache)); 01561 got_error|=flush_blocks(param, share->key_cache, share->kfile); 01562 if (!got_error && param->testflag & T_UNPACK) 01563 { 01564 share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD; 01565 share->pack.header_length=0; 01566 share->data_file_type=sort_info.new_data_file_type; 01567 } 01568 share->state.changed|= (STATE_NOT_OPTIMIZED_KEYS | STATE_NOT_SORTED_PAGES | 01569 STATE_NOT_ANALYZED); 01570 DBUG_RETURN(got_error); 01571 } 01572 01573 01574 /* Uppate keyfile when doing repair */ 01575 01576 static int writekeys(MI_CHECK *param, register MI_INFO *info, byte *buff, 01577 my_off_t filepos) 01578 { 01579 register uint i; 01580 uchar *key; 01581 DBUG_ENTER("writekeys"); 01582 01583 key=info->lastkey+info->s->base.max_key_length; 01584 for (i=0 ; i < info->s->base.keys ; i++) 01585 { 01586 if (mi_is_key_active(info->s->state.key_map, i)) 01587 { 01588 if (info->s->keyinfo[i].flag & HA_FULLTEXT ) 01589 { 01590 if (_mi_ft_add(info,i,(char*) key,buff,filepos)) 01591 goto err; 01592 } 01593 #ifdef HAVE_SPATIAL 01594 else if (info->s->keyinfo[i].flag & HA_SPATIAL) 01595 { 01596 uint key_length=_mi_make_key(info,i,key,buff,filepos); 01597 if (rtree_insert(info, i, key, key_length)) 01598 goto err; 01599 } 01600 #endif /*HAVE_SPATIAL*/ 01601 else 01602 { 01603 uint key_length=_mi_make_key(info,i,key,buff,filepos); 01604 if (_mi_ck_write(info,i,key,key_length)) 01605 goto err; 01606 } 01607 } 01608 } 01609 DBUG_RETURN(0); 01610 01611 err: 01612 if (my_errno == HA_ERR_FOUND_DUPP_KEY) 01613 { 01614 info->errkey=(int) i; /* This key was found */ 01615 while ( i-- > 0 ) 01616 { 01617 if (mi_is_key_active(info->s->state.key_map, i)) 01618 { 01619 if (info->s->keyinfo[i].flag & HA_FULLTEXT) 01620 { 01621 if (_mi_ft_del(info,i,(char*) key,buff,filepos)) 01622 break; 01623 } 01624 else 01625 { 01626 uint key_length=_mi_make_key(info,i,key,buff,filepos); 01627 if (_mi_ck_delete(info,i,key,key_length)) 01628 break; 01629 } 01630 } 01631 } 01632 } 01633 /* Remove checksum that was added to glob_crc in sort_get_next_record */ 01634 if (param->calc_checksum) 01635 param->glob_crc-= info->checksum; 01636 DBUG_PRINT("error",("errno: %d",my_errno)); 01637 DBUG_RETURN(-1); 01638 } /* writekeys */ 01639 01640 01641 /* Change all key-pointers that points to a records */ 01642 01643 int movepoint(register MI_INFO *info, byte *record, my_off_t oldpos, 01644 my_off_t newpos, uint prot_key) 01645 { 01646 register uint i; 01647 uchar *key; 01648 uint key_length; 01649 DBUG_ENTER("movepoint"); 01650 01651 key=info->lastkey+info->s->base.max_key_length; 01652 for (i=0 ; i < info->s->base.keys; i++) 01653 { 01654 if (i != prot_key && mi_is_key_active(info->s->state.key_map, i)) 01655 { 01656 key_length=_mi_make_key(info,i,key,record,oldpos); 01657 if (info->s->keyinfo[i].flag & HA_NOSAME) 01658 { /* Change pointer direct */ 01659 uint nod_flag; 01660 MI_KEYDEF *keyinfo; 01661 keyinfo=info->s->keyinfo+i; 01662 if (_mi_search(info,keyinfo,key,USE_WHOLE_KEY, 01663 (uint) (SEARCH_SAME | SEARCH_SAVE_BUFF), 01664 info->s->state.key_root[i])) 01665 DBUG_RETURN(-1); 01666 nod_flag=mi_test_if_nod(info->buff); 01667 _mi_dpointer(info,info->int_keypos-nod_flag- 01668 info->s->rec_reflength,newpos); 01669 if (_mi_write_keypage(info,keyinfo,info->last_keypage, 01670 DFLT_INIT_HITS,info->buff)) 01671 DBUG_RETURN(-1); 01672 } 01673 else 01674 { /* Change old key to new */ 01675 if (_mi_ck_delete(info,i,key,key_length)) 01676 DBUG_RETURN(-1); 01677 key_length=_mi_make_key(info,i,key,record,newpos); 01678 if (_mi_ck_write(info,i,key,key_length)) 01679 DBUG_RETURN(-1); 01680 } 01681 } 01682 } 01683 DBUG_RETURN(0); 01684 } /* movepoint */ 01685 01686 01687 /* Tell system that we want all memory for our cache */ 01688 01689 void lock_memory(MI_CHECK *param __attribute__((unused))) 01690 { 01691 #ifdef SUN_OS /* Key-cacheing thrases on sun 4.1 */ 01692 if (param->opt_lock_memory) 01693 { 01694 int success = mlockall(MCL_CURRENT); /* or plock(DATLOCK); */ 01695 if (geteuid() == 0 && success != 0) 01696 mi_check_print_warning(param, 01697 "Failed to lock memory. errno %d",my_errno); 01698 } 01699 #endif 01700 } /* lock_memory */ 01701 01702 01703 /* Flush all changed blocks to disk */ 01704 01705 int flush_blocks(MI_CHECK *param, KEY_CACHE *key_cache, File file) 01706 { 01707 if (flush_key_blocks(key_cache, file, FLUSH_RELEASE)) 01708 { 01709 mi_check_print_error(param,"%d when trying to write bufferts",my_errno); 01710 return(1); 01711 } 01712 if (!param->using_global_keycache) 01713 end_key_cache(key_cache,1); 01714 return 0; 01715 } /* flush_blocks */ 01716 01717 01718 /* Sort index for more efficent reads */ 01719 01720 int mi_sort_index(MI_CHECK *param, register MI_INFO *info, my_string name) 01721 { 01722 reg2 uint key; 01723 reg1 MI_KEYDEF *keyinfo; 01724 File new_file; 01725 my_off_t index_pos[MI_MAX_POSSIBLE_KEY]; 01726 uint r_locks,w_locks; 01727 int old_lock; 01728 MYISAM_SHARE *share=info->s; 01729 MI_STATE_INFO old_state; 01730 DBUG_ENTER("mi_sort_index"); 01731 01732 if (!(param->testflag & T_SILENT)) 01733 printf("- Sorting index for MyISAM-table '%s'\n",name); 01734 01735 /* Get real path for index file */ 01736 fn_format(param->temp_filename,name,"", MI_NAME_IEXT,2+4+32); 01737 if ((new_file=my_create(fn_format(param->temp_filename,param->temp_filename, 01738 "", INDEX_TMP_EXT,2+4), 01739 0,param->tmpfile_createflag,MYF(0))) <= 0) 01740 { 01741 mi_check_print_error(param,"Can't create new tempfile: '%s'", 01742 param->temp_filename); 01743 DBUG_RETURN(-1); 01744 } 01745 if (filecopy(param, new_file,share->kfile,0L, 01746 (ulong) share->base.keystart, "headerblock")) 01747 goto err; 01748 01749 param->new_file_pos=share->base.keystart; 01750 for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ; 01751 key++,keyinfo++) 01752 { 01753 if (! mi_is_key_active(info->s->state.key_map, key)) 01754 continue; 01755 01756 if (share->state.key_root[key] != HA_OFFSET_ERROR) 01757 { 01758 index_pos[key]=param->new_file_pos; /* Write first block here */ 01759 if (sort_one_index(param,info,keyinfo,share->state.key_root[key], 01760 new_file)) 01761 goto err; 01762 } 01763 else 01764 index_pos[key]= HA_OFFSET_ERROR; /* No blocks */ 01765 } 01766 01767 /* Flush key cache for this file if we are calling this outside myisamchk */ 01768 flush_key_blocks(share->key_cache,share->kfile, FLUSH_IGNORE_CHANGED); 01769 01770 share->state.version=(ulong) time((time_t*) 0); 01771 old_state= share->state; /* save state if not stored */ 01772 r_locks= share->r_locks; 01773 w_locks= share->w_locks; 01774 old_lock= info->lock_type; 01775 01776 /* Put same locks as old file */ 01777 share->r_locks= share->w_locks= share->tot_locks= 0; 01778 (void) _mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE); 01779 VOID(my_close(share->kfile,MYF(MY_WME))); 01780 share->kfile = -1; 01781 VOID(my_close(new_file,MYF(MY_WME))); 01782 if (change_to_newfile(share->index_file_name,MI_NAME_IEXT,INDEX_TMP_EXT,0, 01783 MYF(0)) || 01784 mi_open_keyfile(share)) 01785 goto err2; 01786 info->lock_type= F_UNLCK; /* Force mi_readinfo to lock */ 01787 _mi_readinfo(info,F_WRLCK,0); /* Will lock the table */ 01788 info->lock_type= old_lock; 01789 share->r_locks= r_locks; 01790 share->w_locks= w_locks; 01791 share->tot_locks= r_locks+w_locks; 01792 share->state= old_state; /* Restore old state */ 01793 01794 info->state->key_file_length=param->new_file_pos; 01795 info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED); 01796 for (key=0 ; key < info->s->base.keys ; key++) 01797 info->s->state.key_root[key]=index_pos[key]; 01798 for (key=0 ; key < info->s->state.header.max_block_size_index ; key++) 01799 info->s->state.key_del[key]= HA_OFFSET_ERROR; 01800 01801 info->s->state.changed&= ~STATE_NOT_SORTED_PAGES; 01802 DBUG_RETURN(0); 01803 01804 err: 01805 VOID(my_close(new_file,MYF(MY_WME))); 01806 err2: 01807 VOID(my_delete(param->temp_filename,MYF(MY_WME))); 01808 DBUG_RETURN(-1); 01809 } /* mi_sort_index */ 01810 01811 01812 /* Sort records recursive using one index */ 01813 01814 static int sort_one_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo, 01815 my_off_t pagepos, File new_file) 01816 { 01817 uint length,nod_flag,used_length, key_length; 01818 uchar *buff,*keypos,*endpos; 01819 uchar key[MI_MAX_POSSIBLE_KEY_BUFF]; 01820 my_off_t new_page_pos,next_page; 01821 char llbuff[22]; 01822 DBUG_ENTER("sort_one_index"); 01823 01824 new_page_pos=param->new_file_pos; 01825 param->new_file_pos+=keyinfo->block_length; 01826 01827 if (!(buff=(uchar*) my_alloca((uint) keyinfo->block_length))) 01828 { 01829 mi_check_print_error(param,"Not enough memory for key block"); 01830 DBUG_RETURN(-1); 01831 } 01832 if (!_mi_fetch_keypage(info,keyinfo,pagepos,DFLT_INIT_HITS,buff,0)) 01833 { 01834 mi_check_print_error(param,"Can't read key block from filepos: %s", 01835 llstr(pagepos,llbuff)); 01836 goto err; 01837 } 01838 if ((nod_flag=mi_test_if_nod(buff)) || keyinfo->flag & HA_FULLTEXT) 01839 { 01840 used_length=mi_getint(buff); 01841 keypos=buff+2+nod_flag; 01842 endpos=buff+used_length; 01843 for ( ;; ) 01844 { 01845 if (nod_flag) 01846 { 01847 next_page=_mi_kpos(nod_flag,keypos); 01848 _mi_kpointer(info,keypos-nod_flag,param->new_file_pos); /* Save new pos */ 01849 if (sort_one_index(param,info,keyinfo,next_page,new_file)) 01850 { 01851 DBUG_PRINT("error", 01852 ("From page: %ld, keyoffset: %lu used_length: %d", 01853 (ulong) pagepos, (ulong) (keypos - buff), 01854 (int) used_length)); 01855 DBUG_DUMP("buff",(byte*) buff,used_length); 01856 goto err; 01857 } 01858 } 01859 if (keypos >= endpos || 01860 (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0) 01861 break; 01862 DBUG_ASSERT(keypos <= endpos); 01863 if (keyinfo->flag & HA_FULLTEXT) 01864 { 01865 uint off; 01866 int subkeys; 01867 get_key_full_length_rdonly(off, key); 01868 subkeys=ft_sintXkorr(key+off); 01869 if (subkeys < 0) 01870 { 01871 next_page= _mi_dpos(info,0,key+key_length); 01872 _mi_dpointer(info,keypos-nod_flag-info->s->rec_reflength, 01873 param->new_file_pos); /* Save new pos */ 01874 if (sort_one_index(param,info,&info->s->ft2_keyinfo, 01875 next_page,new_file)) 01876 goto err; 01877 } 01878 } 01879 } 01880 } 01881 01882 /* Fill block with zero and write it to the new index file */ 01883 length=mi_getint(buff); 01884 bzero((byte*) buff+length,keyinfo->block_length-length); 01885 if (my_pwrite(new_file,(byte*) buff,(uint) keyinfo->block_length, 01886 new_page_pos,MYF(MY_NABP | MY_WAIT_IF_FULL))) 01887 { 01888 mi_check_print_error(param,"Can't write indexblock, error: %d",my_errno); 01889 goto err; 01890 } 01891 my_afree((gptr) buff); 01892 DBUG_RETURN(0); 01893 err: 01894 my_afree((gptr) buff); 01895 DBUG_RETURN(1); 01896 } /* sort_one_index */ 01897 01898 01899 /* 01900 Let temporary file replace old file. 01901 This assumes that the new file was created in the same 01902 directory as given by realpath(filename). 01903 This will ensure that any symlinks that are used will still work. 01904 Copy stats from old file to new file, deletes orignal and 01905 changes new file name to old file name 01906 */ 01907 01908 int change_to_newfile(const char * filename, const char * old_ext, 01909 const char * new_ext, 01910 uint raid_chunks __attribute__((unused)), 01911 myf MyFlags) 01912 { 01913 char old_filename[FN_REFLEN],new_filename[FN_REFLEN]; 01914 #ifdef USE_RAID 01915 if (raid_chunks) 01916 return my_raid_redel(fn_format(old_filename,filename,"",old_ext,2+4), 01917 fn_format(new_filename,filename,"",new_ext,2+4), 01918 raid_chunks, 01919 MYF(MY_WME | MY_LINK_WARNING | MyFlags)); 01920 #endif 01921 /* Get real path to filename */ 01922 (void) fn_format(old_filename,filename,"",old_ext,2+4+32); 01923 return my_redel(old_filename, 01924 fn_format(new_filename,old_filename,"",new_ext,2+4), 01925 MYF(MY_WME | MY_LINK_WARNING | MyFlags)); 01926 } /* change_to_newfile */ 01927 01928 01929 /* Locks a whole file */ 01930 /* Gives an error-message if file can't be locked */ 01931 01932 int lock_file(MI_CHECK *param, File file, my_off_t start, int lock_type, 01933 const char *filetype, const char *filename) 01934 { 01935 if (my_lock(file,lock_type,start,F_TO_EOF, 01936 param->testflag & T_WAIT_FOREVER ? MYF(MY_SEEK_NOT_DONE) : 01937 MYF(MY_SEEK_NOT_DONE | MY_DONT_WAIT))) 01938 { 01939 mi_check_print_error(param," %d when locking %s '%s'",my_errno,filetype,filename); 01940 param->error_printed=2; /* Don't give that data is crashed */ 01941 return 1; 01942 } 01943 return 0; 01944 } /* lock_file */ 01945 01946 01947 /* Copy a block between two files */ 01948 01949 int filecopy(MI_CHECK *param, File to,File from,my_off_t start, 01950 my_off_t length, const char *type) 01951 { 01952 char tmp_buff[IO_SIZE],*buff; 01953 ulong buff_length; 01954 DBUG_ENTER("filecopy"); 01955 01956 buff_length=(ulong) min(param->write_buffer_length,length); 01957 if (!(buff=my_malloc(buff_length,MYF(0)))) 01958 { 01959 buff=tmp_buff; buff_length=IO_SIZE; 01960 } 01961 01962 VOID(my_seek(from,start,MY_SEEK_SET,MYF(0))); 01963 while (length > buff_length) 01964 { 01965 if (my_read(from,(byte*) buff,buff_length,MYF(MY_NABP)) || 01966 my_write(to,(byte*) buff,buff_length,param->myf_rw)) 01967 goto err; 01968 length-= buff_length; 01969 } 01970 if (my_read(from,(byte*) buff,(uint) length,MYF(MY_NABP)) || 01971 my_write(to,(byte*) buff,(uint) length,param->myf_rw)) 01972 goto err; 01973 if (buff != tmp_buff) 01974 my_free(buff,MYF(0)); 01975 DBUG_RETURN(0); 01976 err: 01977 if (buff != tmp_buff) 01978 my_free(buff,MYF(0)); 01979 mi_check_print_error(param,"Can't copy %s to tempfile, error %d", 01980 type,my_errno); 01981 DBUG_RETURN(1); 01982 } 01983 01984 01985 /* 01986 Repair table or given index using sorting 01987 01988 SYNOPSIS 01989 mi_repair_by_sort() 01990 param Repair parameters 01991 info MyISAM handler to repair 01992 name Name of table (for warnings) 01993 rep_quick set to <> 0 if we should not change data file 01994 01995 RESULT 01996 0 ok 01997 <>0 Error 01998 */ 01999 02000 int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, 02001 const char * name, int rep_quick) 02002 { 02003 int got_error; 02004 uint i; 02005 ulong length; 02006 ha_rows start_records; 02007 my_off_t new_header_length,del; 02008 File new_file; 02009 MI_SORT_PARAM sort_param; 02010 MYISAM_SHARE *share=info->s; 02011 HA_KEYSEG *keyseg; 02012 ulong *rec_per_key_part; 02013 char llbuff[22]; 02014 SORT_INFO sort_info; 02015 ulonglong key_map=share->state.key_map; 02016 DBUG_ENTER("mi_repair_by_sort"); 02017 02018 start_records=info->state->records; 02019 got_error=1; 02020 new_file= -1; 02021 new_header_length=(param->testflag & T_UNPACK) ? 0 : 02022 share->pack.header_length; 02023 if (!(param->testflag & T_SILENT)) 02024 { 02025 printf("- recovering (with sort) MyISAM-table '%s'\n",name); 02026 printf("Data records: %s\n", llstr(start_records,llbuff)); 02027 } 02028 param->testflag|=T_REP; /* for easy checking */ 02029 02030 if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)) 02031 param->testflag|=T_CALC_CHECKSUM; 02032 02033 bzero((char*)&sort_info,sizeof(sort_info)); 02034 bzero((char *)&sort_param, sizeof(sort_param)); 02035 if (!(sort_info.key_block= 02036 alloc_key_blocks(param, 02037 (uint) param->sort_key_blocks, 02038 share->base.max_key_block_length)) 02039 || init_io_cache(¶m->read_cache,info->dfile, 02040 (uint) param->read_buffer_length, 02041 READ_CACHE,share->pack.header_length,1,MYF(MY_WME)) || 02042 (! rep_quick && 02043 init_io_cache(&info->rec_cache,info->dfile, 02044 (uint) param->write_buffer_length, 02045 WRITE_CACHE,new_header_length,1, 02046 MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw))) 02047 goto err; 02048 sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks; 02049 info->opt_flag|=WRITE_CACHE_USED; 02050 info->rec_cache.file=info->dfile; /* for sort_delete_record */ 02051 02052 if (!(sort_param.record=(byte*) my_malloc((uint) share->base.pack_reclength, 02053 MYF(0))) || 02054 !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff)) 02055 { 02056 mi_check_print_error(param, "Not enough memory for extra record"); 02057 goto err; 02058 } 02059 if (!rep_quick) 02060 { 02061 /* Get real path for data file */ 02062 if ((new_file=my_raid_create(fn_format(param->temp_filename, 02063 share->data_file_name, "", 02064 DATA_TMP_EXT, 2+4), 02065 0,param->tmpfile_createflag, 02066 share->base.raid_type, 02067 share->base.raid_chunks, 02068 share->base.raid_chunksize, 02069 MYF(0))) < 0) 02070 { 02071 mi_check_print_error(param,"Can't create new tempfile: '%s'", 02072 param->temp_filename); 02073 goto err; 02074 } 02075 if (filecopy(param, new_file,info->dfile,0L,new_header_length, 02076 "datafile-header")) 02077 goto err; 02078 if (param->testflag & T_UNPACK) 02079 { 02080 share->options&= ~HA_OPTION_COMPRESS_RECORD; 02081 mi_int2store(share->state.header.options,share->options); 02082 } 02083 share->state.dellink= HA_OFFSET_ERROR; 02084 info->rec_cache.file=new_file; 02085 } 02086 02087 info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED); 02088 if (!(param->testflag & T_CREATE_MISSING_KEYS)) 02089 { 02090 /* 02091 Flush key cache for this file if we are calling this outside 02092 myisamchk 02093 */ 02094 flush_key_blocks(share->key_cache,share->kfile, FLUSH_IGNORE_CHANGED); 02095 /* Clear the pointers to the given rows */ 02096 for (i=0 ; i < share->base.keys ; i++) 02097 share->state.key_root[i]= HA_OFFSET_ERROR; 02098 for (i=0 ; i < share->state.header.max_block_size_index ; i++) 02099 share->state.key_del[i]= HA_OFFSET_ERROR; 02100 info->state->key_file_length=share->base.keystart; 02101 } 02102 else 02103 { 02104 if (flush_key_blocks(share->key_cache,share->kfile, FLUSH_FORCE_WRITE)) 02105 goto err; 02106 key_map= ~key_map; /* Create the missing keys */ 02107 } 02108 02109 sort_info.info=info; 02110 sort_info.param = param; 02111 02112 set_data_file_type(&sort_info, share); 02113 sort_param.filepos=new_header_length; 02114 sort_info.dupp=0; 02115 sort_info.buff=0; 02116 param->read_cache.end_of_file=sort_info.filelength= 02117 my_seek(param->read_cache.file,0L,MY_SEEK_END,MYF(0)); 02118 02119 sort_param.wordlist=NULL; 02120 init_alloc_root(&sort_param.wordroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0); 02121 02122 if (share->data_file_type == DYNAMIC_RECORD) 02123 length=max(share->base.min_pack_length+1,share->base.min_block_length); 02124 else if (share->data_file_type == COMPRESSED_RECORD) 02125 length=share->base.min_block_length; 02126 else 02127 length=share->base.pack_reclength; 02128 sort_info.max_records= 02129 ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records : 02130 (ha_rows) (sort_info.filelength/length+1)); 02131 sort_param.key_cmp=sort_key_cmp; 02132 sort_param.lock_in_memory=lock_memory; 02133 sort_param.tmpdir=param->tmpdir; 02134 sort_param.sort_info=&sort_info; 02135 sort_param.fix_datafile= (my_bool) (! rep_quick); 02136 sort_param.master =1; 02137 02138 del=info->state->del; 02139 param->glob_crc=0; 02140 if (param->testflag & T_CALC_CHECKSUM) 02141 param->calc_checksum=1; 02142 02143 rec_per_key_part= param->rec_per_key_part; 02144 for (sort_param.key=0 ; sort_param.key < share->base.keys ; 02145 rec_per_key_part+=sort_param.keyinfo->keysegs, sort_param.key++) 02146 { 02147 sort_param.read_cache=param->read_cache; 02148 sort_param.keyinfo=share->keyinfo+sort_param.key; 02149 sort_param.seg=sort_param.keyinfo->seg; 02150 if (! mi_is_key_active(key_map, sort_param.key)) 02151 { 02152 /* Remember old statistics for key */ 02153 memcpy((char*) rec_per_key_part, 02154 (char*) (share->state.rec_per_key_part + 02155 (uint) (rec_per_key_part - param->rec_per_key_part)), 02156 sort_param.keyinfo->keysegs*sizeof(*rec_per_key_part)); 02157 continue; 02158 } 02159 02160 if ((!(param->testflag & T_SILENT))) 02161 printf ("- Fixing index %d\n",sort_param.key+1); 02162 sort_param.max_pos=sort_param.pos=share->pack.header_length; 02163 keyseg=sort_param.seg; 02164 bzero((char*) sort_param.unique,sizeof(sort_param.unique)); 02165 sort_param.key_length=share->rec_reflength; 02166 for (i=0 ; keyseg[i].type != HA_KEYTYPE_END; i++) 02167 { 02168 sort_param.key_length+=keyseg[i].length; 02169 if (keyseg[i].flag & HA_SPACE_PACK) 02170 sort_param.key_length+=get_pack_length(keyseg[i].length); 02171 if (keyseg[i].flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART)) 02172 sort_param.key_length+=2 + test(keyseg[i].length >= 127); 02173 if (keyseg[i].flag & HA_NULL_PART) 02174 sort_param.key_length++; 02175 } 02176 info->state->records=info->state->del=share->state.split=0; 02177 info->state->empty=0; 02178 02179 if (sort_param.keyinfo->flag & HA_FULLTEXT) 02180 { 02181 uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT* 02182 sort_param.keyinfo->seg->charset->mbmaxlen; 02183 sort_param.key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN; 02184 /* 02185 fulltext indexes may have much more entries than the 02186 number of rows in the table. We estimate the number here. 02187 02188 Note, built-in parser is always nr. 0 - see ftparser_call_initializer() 02189 */ 02190 if (sort_param.keyinfo->ftparser_nr == 0) 02191 { 02192 /* 02193 for built-in parser the number of generated index entries 02194 cannot be larger than the size of the data file divided 02195 by the minimal word's length 02196 */ 02197 sort_info.max_records= 02198 (ha_rows) (sort_info.filelength/ft_min_word_len+1); 02199 } 02200 else 02201 { 02202 /* 02203 for external plugin parser we cannot tell anything at all :( 02204 so, we'll use all the sort memory and start from ~10 buffpeks. 02205 (see _create_index_by_sort) 02206 */ 02207 sort_info.max_records= 02208 10*param->sort_buffer_length/sort_param.key_length; 02209 } 02210 02211 sort_param.key_read=sort_ft_key_read; 02212 sort_param.key_write=sort_ft_key_write; 02213 } 02214 else 02215 { 02216 sort_param.key_read=sort_key_read; 02217 sort_param.key_write=sort_key_write; 02218 } 02219 02220 if (_create_index_by_sort(&sort_param, 02221 (my_bool) (!(param->testflag & T_VERBOSE)), 02222 (uint) param->sort_buffer_length)) 02223 { 02224 param->retry_repair=1; 02225 goto err; 02226 } 02227 param->calc_checksum=0; /* No need to calc glob_crc */ 02228 free_root(&sort_param.wordroot, MYF(0)); 02229 02230 /* Set for next loop */ 02231 sort_info.max_records= (ha_rows) info->state->records; 02232 02233 if (param->testflag & T_STATISTICS) 02234 update_key_parts(sort_param.keyinfo, rec_per_key_part, sort_param.unique, 02235 param->stats_method == MI_STATS_METHOD_IGNORE_NULLS? 02236 sort_param.notnull: NULL,(ulonglong) info->state->records); 02237 mi_set_key_active(share->state.key_map, sort_param.key); 02238 02239 if (sort_param.fix_datafile) 02240 { 02241 param->read_cache.end_of_file=sort_param.filepos; 02242 if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache)) 02243 goto err; 02244 if (param->testflag & T_SAFE_REPAIR) 02245 { 02246 /* Don't repair if we loosed more than one row */ 02247 if (info->state->records+1 < start_records) 02248 { 02249 info->state->records=start_records; 02250 goto err; 02251 } 02252 } 02253 share->state.state.data_file_length = info->state->data_file_length= 02254 sort_param.filepos; 02255 /* Only whole records */ 02256 share->state.version=(ulong) time((time_t*) 0); 02257 my_close(info->dfile,MYF(0)); 02258 info->dfile=new_file; 02259 share->data_file_type=sort_info.new_data_file_type; 02260 share->pack.header_length=(ulong) new_header_length; 02261 sort_param.fix_datafile=0; 02262 } 02263 else 02264 info->state->data_file_length=sort_param.max_pos; 02265 02266 param->read_cache.file=info->dfile; /* re-init read cache */ 02267 reinit_io_cache(¶m->read_cache,READ_CACHE,share->pack.header_length, 02268 1,1); 02269 } 02270 02271 if (param->testflag & T_WRITE_LOOP) 02272 { 02273 VOID(fputs(" \r",stdout)); VOID(fflush(stdout)); 02274 } 02275 02276 if (rep_quick && del+sort_info.dupp != info->state->del) 02277 { 02278 mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records"); 02279 mi_check_print_error(param,"Run recovery again without -q"); 02280 got_error=1; 02281 param->retry_repair=1; 02282 param->testflag|=T_RETRY_WITHOUT_QUICK; 02283 goto err; 02284 } 02285 02286 if (rep_quick & T_FORCE_UNIQUENESS) 02287 { 02288 my_off_t skr=info->state->data_file_length+ 02289 (share->options & HA_OPTION_COMPRESS_RECORD ? 02290 MEMMAP_EXTRA_MARGIN : 0); 02291 #ifdef USE_RELOC 02292 if (share->data_file_type == STATIC_RECORD && 02293 skr < share->base.reloc*share->base.min_pack_length) 02294 skr=share->base.reloc*share->base.min_pack_length; 02295 #endif 02296 if (skr != sort_info.filelength && !info->s->base.raid_type) 02297 if (my_chsize(info->dfile,skr,0,MYF(0))) 02298 mi_check_print_warning(param, 02299 "Can't change size of datafile, error: %d", 02300 my_errno); 02301 } 02302 if (param->testflag & T_CALC_CHECKSUM) 02303 info->state->checksum=param->glob_crc; 02304 02305 if (my_chsize(share->kfile,info->state->key_file_length,0,MYF(0))) 02306 mi_check_print_warning(param, 02307 "Can't change size of indexfile, error: %d", 02308 my_errno); 02309 02310 if (!(param->testflag & T_SILENT)) 02311 { 02312 if (start_records != info->state->records) 02313 printf("Data records: %s\n", llstr(info->state->records,llbuff)); 02314 if (sort_info.dupp) 02315 mi_check_print_warning(param, 02316 "%s records have been removed", 02317 llstr(sort_info.dupp,llbuff)); 02318 } 02319 got_error=0; 02320 02321 if (&share->state.state != info->state) 02322 memcpy( &share->state.state, info->state, sizeof(*info->state)); 02323 02324 err: 02325 got_error|= flush_blocks(param, share->key_cache, share->kfile); 02326 VOID(end_io_cache(&info->rec_cache)); 02327 if (!got_error) 02328 { 02329 /* Replace the actual file with the temporary file */ 02330 if (new_file >= 0) 02331 { 02332 my_close(new_file,MYF(0)); 02333 info->dfile=new_file= -1; 02334 if (change_to_newfile(share->data_file_name,MI_NAME_DEXT, 02335 DATA_TMP_EXT, share->base.raid_chunks, 02336 (param->testflag & T_BACKUP_DATA ? 02337 MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) || 02338 mi_open_datafile(info,share,-1)) 02339 got_error=1; 02340 } 02341 } 02342 if (got_error) 02343 { 02344 if (! param->error_printed) 02345 mi_check_print_error(param,"%d when fixing table",my_errno); 02346 if (new_file >= 0) 02347 { 02348 VOID(my_close(new_file,MYF(0))); 02349 VOID(my_raid_delete(param->temp_filename,share->base.raid_chunks, 02350 MYF(MY_WME))); 02351 if (info->dfile == new_file) 02352 info->dfile= -1; 02353 } 02354 mi_mark_crashed_on_repair(info); 02355 } 02356 else if (key_map == share->state.key_map) 02357 share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS; 02358 share->state.changed|=STATE_NOT_SORTED_PAGES; 02359 02360 my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff), 02361 MYF(MY_ALLOW_ZERO_PTR)); 02362 my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR)); 02363 my_free((gptr) sort_info.key_block,MYF(MY_ALLOW_ZERO_PTR)); 02364 my_free((gptr) sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR)); 02365 my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR)); 02366 VOID(end_io_cache(¶m->read_cache)); 02367 info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); 02368 if (!got_error && (param->testflag & T_UNPACK)) 02369 { 02370 share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD; 02371 share->pack.header_length=0; 02372 } 02373 DBUG_RETURN(got_error); 02374 } 02375 02376 /* 02377 Threaded repair of table using sorting 02378 02379 SYNOPSIS 02380 mi_repair_parallel() 02381 param Repair parameters 02382 info MyISAM handler to repair 02383 name Name of table (for warnings) 02384 rep_quick set to <> 0 if we should not change data file 02385 02386 DESCRIPTION 02387 Same as mi_repair_by_sort but do it multithreaded 02388 Each key is handled by a separate thread. 02389 TODO: make a number of threads a parameter 02390 02391 RESULT 02392 0 ok 02393 <>0 Error 02394 */ 02395 02396 int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, 02397 const char * name, int rep_quick) 02398 { 02399 #ifndef THREAD 02400 return mi_repair_by_sort(param, info, name, rep_quick); 02401 #else 02402 int got_error; 02403 uint i,key, total_key_length, istep; 02404 ulong rec_length; 02405 ha_rows start_records; 02406 my_off_t new_header_length,del; 02407 File new_file; 02408 MI_SORT_PARAM *sort_param=0; 02409 MYISAM_SHARE *share=info->s; 02410 ulong *rec_per_key_part; 02411 HA_KEYSEG *keyseg; 02412 char llbuff[22]; 02413 IO_CACHE_SHARE io_share; 02414 SORT_INFO sort_info; 02415 ulonglong key_map=share->state.key_map; 02416 pthread_attr_t thr_attr; 02417 DBUG_ENTER("mi_repair_parallel"); 02418 02419 start_records=info->state->records; 02420 got_error=1; 02421 new_file= -1; 02422 new_header_length=(param->testflag & T_UNPACK) ? 0 : 02423 share->pack.header_length; 02424 if (!(param->testflag & T_SILENT)) 02425 { 02426 printf("- parallel recovering (with sort) MyISAM-table '%s'\n",name); 02427 printf("Data records: %s\n", llstr(start_records,llbuff)); 02428 } 02429 param->testflag|=T_REP; /* for easy checking */ 02430 02431 if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)) 02432 param->testflag|=T_CALC_CHECKSUM; 02433 02434 bzero((char*)&sort_info,sizeof(sort_info)); 02435 if (!(sort_info.key_block= 02436 alloc_key_blocks(param, 02437 (uint) param->sort_key_blocks, 02438 share->base.max_key_block_length)) 02439 || init_io_cache(¶m->read_cache,info->dfile, 02440 (uint) param->read_buffer_length, 02441 READ_CACHE,share->pack.header_length,1,MYF(MY_WME)) || 02442 (! rep_quick && 02443 init_io_cache(&info->rec_cache,info->dfile, 02444 (uint) param->write_buffer_length, 02445 WRITE_CACHE,new_header_length,1, 02446 MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw))) 02447 goto err; 02448 sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks; 02449 info->opt_flag|=WRITE_CACHE_USED; 02450 info->rec_cache.file=info->dfile; /* for sort_delete_record */ 02451 02452 if (!rep_quick) 02453 { 02454 /* Get real path for data file */ 02455 if ((new_file=my_raid_create(fn_format(param->temp_filename, 02456 share->data_file_name, "", 02457 DATA_TMP_EXT, 02458 2+4), 02459 0,param->tmpfile_createflag, 02460 share->base.raid_type, 02461 share->base.raid_chunks, 02462 share->base.raid_chunksize, 02463 MYF(0))) < 0) 02464 { 02465 mi_check_print_error(param,"Can't create new tempfile: '%s'", 02466 param->temp_filename); 02467 goto err; 02468 } 02469 if (filecopy(param, new_file,info->dfile,0L,new_header_length, 02470 "datafile-header")) 02471 goto err; 02472 if (param->testflag & T_UNPACK) 02473 { 02474 share->options&= ~HA_OPTION_COMPRESS_RECORD; 02475 mi_int2store(share->state.header.options,share->options); 02476 } 02477 share->state.dellink= HA_OFFSET_ERROR; 02478 info->rec_cache.file=new_file; 02479 } 02480 02481 info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED); 02482 if (!(param->testflag & T_CREATE_MISSING_KEYS)) 02483 { 02484 /* 02485 Flush key cache for this file if we are calling this outside 02486 myisamchk 02487 */ 02488 flush_key_blocks(share->key_cache,share->kfile, FLUSH_IGNORE_CHANGED); 02489 /* Clear the pointers to the given rows */ 02490 for (i=0 ; i < share->base.keys ; i++) 02491 share->state.key_root[i]= HA_OFFSET_ERROR; 02492 for (i=0 ; i < share->state.header.max_block_size_index ; i++) 02493 share->state.key_del[i]= HA_OFFSET_ERROR; 02494 info->state->key_file_length=share->base.keystart; 02495 } 02496 else 02497 { 02498 if (flush_key_blocks(share->key_cache,share->kfile, FLUSH_FORCE_WRITE)) 02499 goto err; 02500 key_map= ~key_map; /* Create the missing keys */ 02501 } 02502 02503 sort_info.info=info; 02504 sort_info.param = param; 02505 02506 set_data_file_type(&sort_info, share); 02507 sort_info.dupp=0; 02508 sort_info.buff=0; 02509 param->read_cache.end_of_file=sort_info.filelength= 02510 my_seek(param->read_cache.file,0L,MY_SEEK_END,MYF(0)); 02511 02512 if (share->data_file_type == DYNAMIC_RECORD) 02513 rec_length=max(share->base.min_pack_length+1,share->base.min_block_length); 02514 else if (share->data_file_type == COMPRESSED_RECORD) 02515 rec_length=share->base.min_block_length; 02516 else 02517 rec_length=share->base.pack_reclength; 02518 /* 02519 +1 below is required hack for parallel repair mode. 02520 The info->state->records value, that is compared later 02521 to sort_info.max_records and cannot exceed it, is 02522 increased in sort_key_write. In mi_repair_by_sort, sort_key_write 02523 is called after sort_key_read, where the comparison is performed, 02524 but in parallel mode master thread can call sort_key_write 02525 before some other repair thread calls sort_key_read. 02526 Furthermore I'm not even sure +1 would be enough. 02527 May be sort_info.max_records shold be always set to max value in 02528 parallel mode. 02529 */ 02530 sort_info.max_records= 02531 ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records + 1: 02532 (ha_rows) (sort_info.filelength/rec_length+1)); 02533 02534 del=info->state->del; 02535 param->glob_crc=0; 02536 if (param->testflag & T_CALC_CHECKSUM) 02537 param->calc_checksum=1; 02538 02539 if (!(sort_param=(MI_SORT_PARAM *) 02540 my_malloc((uint) share->base.keys * 02541 (sizeof(MI_SORT_PARAM) + share->base.pack_reclength), 02542 MYF(MY_ZEROFILL)))) 02543 { 02544 mi_check_print_error(param,"Not enough memory for key!"); 02545 goto err; 02546 } 02547 total_key_length=0; 02548 rec_per_key_part= param->rec_per_key_part; 02549 info->state->records=info->state->del=share->state.split=0; 02550 info->state->empty=0; 02551 02552 for (i=key=0, istep=1 ; key < share->base.keys ; 02553 rec_per_key_part+=sort_param[i].keyinfo->keysegs, i+=istep, key++) 02554 { 02555 sort_param[i].key=key; 02556 sort_param[i].keyinfo=share->keyinfo+key; 02557 sort_param[i].seg=sort_param[i].keyinfo->seg; 02558 if (! mi_is_key_active(key_map, key)) 02559 { 02560 /* Remember old statistics for key */ 02561 memcpy((char*) rec_per_key_part, 02562 (char*) (share->state.rec_per_key_part+ 02563 (uint) (rec_per_key_part - param->rec_per_key_part)), 02564 sort_param[i].keyinfo->keysegs*sizeof(*rec_per_key_part)); 02565 istep=0; 02566 continue; 02567 } 02568 istep=1; 02569 if ((!(param->testflag & T_SILENT))) 02570 printf ("- Fixing index %d\n",key+1); 02571 if (sort_param[i].keyinfo->flag & HA_FULLTEXT) 02572 { 02573 sort_param[i].key_read=sort_ft_key_read; 02574 sort_param[i].key_write=sort_ft_key_write; 02575 } 02576 else 02577 { 02578 sort_param[i].key_read=sort_key_read; 02579 sort_param[i].key_write=sort_key_write; 02580 } 02581 sort_param[i].key_cmp=sort_key_cmp; 02582 sort_param[i].lock_in_memory=lock_memory; 02583 sort_param[i].tmpdir=param->tmpdir; 02584 sort_param[i].sort_info=&sort_info; 02585 sort_param[i].master=0; 02586 sort_param[i].fix_datafile=0; 02587 02588 sort_param[i].filepos=new_header_length; 02589 sort_param[i].max_pos=sort_param[i].pos=share->pack.header_length; 02590 02591 sort_param[i].record= (((char *)(sort_param+share->base.keys))+ 02592 (share->base.pack_reclength * i)); 02593 if (!mi_alloc_rec_buff(info, -1, &sort_param[i].rec_buff)) 02594 { 02595 mi_check_print_error(param,"Not enough memory!"); 02596 goto err; 02597 } 02598 02599 sort_param[i].key_length=share->rec_reflength; 02600 for (keyseg=sort_param[i].seg; keyseg->type != HA_KEYTYPE_END; 02601 keyseg++) 02602 { 02603 sort_param[i].key_length+=keyseg->length; 02604 if (keyseg->flag & HA_SPACE_PACK) 02605 sort_param[i].key_length+=get_pack_length(keyseg->length); 02606 if (keyseg->flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART)) 02607 sort_param[i].key_length+=2 + test(keyseg->length >= 127); 02608 if (keyseg->flag & HA_NULL_PART) 02609 sort_param[i].key_length++; 02610 } 02611 total_key_length+=sort_param[i].key_length; 02612 02613 if (sort_param[i].keyinfo->flag & HA_FULLTEXT) 02614 { 02615 uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT* 02616 sort_param[i].keyinfo->seg->charset->mbmaxlen; 02617 sort_param[i].key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN; 02618 init_alloc_root(&sort_param[i].wordroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0); 02619 } 02620 } 02621 sort_info.total_keys=i; 02622 sort_param[0].master= 1; 02623 sort_param[0].fix_datafile= (my_bool)(! rep_quick); 02624 02625 sort_info.got_error=0; 02626 pthread_mutex_init(&sort_info.mutex, MY_MUTEX_INIT_FAST); 02627 pthread_cond_init(&sort_info.cond, 0); 02628 pthread_mutex_lock(&sort_info.mutex); 02629 02630 init_io_cache_share(¶m->read_cache, &io_share, i); 02631 (void) pthread_attr_init(&thr_attr); 02632 (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); 02633 02634 for (i=0 ; i < sort_info.total_keys ; i++) 02635 { 02636 sort_param[i].read_cache=param->read_cache; 02637 /* 02638 two approaches: the same amount of memory for each thread 02639 or the memory for the same number of keys for each thread... 02640 In the second one all the threads will fill their sort_buffers 02641 (and call write_keys) at the same time, putting more stress on i/o. 02642 */ 02643 sort_param[i].sortbuff_size= 02644 #ifndef USING_SECOND_APPROACH 02645 param->sort_buffer_length/sort_info.total_keys; 02646 #else 02647 param->sort_buffer_length*sort_param[i].key_length/total_key_length; 02648 #endif 02649 if (pthread_create(&sort_param[i].thr, &thr_attr, 02650 thr_find_all_keys, 02651 (void *) (sort_param+i))) 02652 { 02653 mi_check_print_error(param,"Cannot start a repair thread"); 02654 remove_io_thread(¶m->read_cache); 02655 sort_info.got_error=1; 02656 } 02657 else 02658 sort_info.threads_running++; 02659 } 02660 (void) pthread_attr_destroy(&thr_attr); 02661 02662 /* waiting for all threads to finish */ 02663 while (sort_info.threads_running) 02664 pthread_cond_wait(&sort_info.cond, &sort_info.mutex); 02665 pthread_mutex_unlock(&sort_info.mutex); 02666 02667 if ((got_error= thr_write_keys(sort_param))) 02668 { 02669 param->retry_repair=1; 02670 goto err; 02671 } 02672 got_error=1; /* Assume the following may go wrong */ 02673 02674 if (sort_param[0].fix_datafile) 02675 { 02676 if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache)) 02677 goto err; 02678 if (param->testflag & T_SAFE_REPAIR) 02679 { 02680 /* Don't repair if we loosed more than one row */ 02681 if (info->state->records+1 < start_records) 02682 { 02683 info->state->records=start_records; 02684 goto err; 02685 } 02686 } 02687 share->state.state.data_file_length= info->state->data_file_length= 02688 sort_param->filepos; 02689 /* Only whole records */ 02690 share->state.version=(ulong) time((time_t*) 0); 02691 my_close(info->dfile,MYF(0)); 02692 info->dfile=new_file; 02693 share->data_file_type=sort_info.new_data_file_type; 02694 share->pack.header_length=(ulong) new_header_length; 02695 } 02696 else 02697 info->state->data_file_length=sort_param->max_pos; 02698 02699 if (rep_quick && del+sort_info.dupp != info->state->del) 02700 { 02701 mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records"); 02702 mi_check_print_error(param,"Run recovery again without -q"); 02703 param->retry_repair=1; 02704 param->testflag|=T_RETRY_WITHOUT_QUICK; 02705 goto err; 02706 } 02707 02708 if (rep_quick & T_FORCE_UNIQUENESS) 02709 { 02710 my_off_t skr=info->state->data_file_length+ 02711 (share->options & HA_OPTION_COMPRESS_RECORD ? 02712 MEMMAP_EXTRA_MARGIN : 0); 02713 #ifdef USE_RELOC 02714 if (share->data_file_type == STATIC_RECORD && 02715 skr < share->base.reloc*share->base.min_pack_length) 02716 skr=share->base.reloc*share->base.min_pack_length; 02717 #endif 02718 if (skr != sort_info.filelength && !info->s->base.raid_type) 02719 if (my_chsize(info->dfile,skr,0,MYF(0))) 02720 mi_check_print_warning(param, 02721 "Can't change size of datafile, error: %d", 02722 my_errno); 02723 } 02724 if (param->testflag & T_CALC_CHECKSUM) 02725 info->state->checksum=param->glob_crc; 02726 02727 if (my_chsize(share->kfile,info->state->key_file_length,0,MYF(0))) 02728 mi_check_print_warning(param, 02729 "Can't change size of indexfile, error: %d", my_errno); 02730 02731 if (!(param->testflag & T_SILENT)) 02732 { 02733 if (start_records != info->state->records) 02734 printf("Data records: %s\n", llstr(info->state->records,llbuff)); 02735 if (sort_info.dupp) 02736 mi_check_print_warning(param, 02737 "%s records have been removed", 02738 llstr(sort_info.dupp,llbuff)); 02739 } 02740 got_error=0; 02741 02742 if (&share->state.state != info->state) 02743 memcpy(&share->state.state, info->state, sizeof(*info->state)); 02744 02745 err: 02746 got_error|= flush_blocks(param, share->key_cache, share->kfile); 02747 VOID(end_io_cache(&info->rec_cache)); 02748 if (!got_error) 02749 { 02750 /* Replace the actual file with the temporary file */ 02751 if (new_file >= 0) 02752 { 02753 my_close(new_file,MYF(0)); 02754 info->dfile=new_file= -1; 02755 if (change_to_newfile(share->data_file_name,MI_NAME_DEXT, 02756 DATA_TMP_EXT, share->base.raid_chunks, 02757 (param->testflag & T_BACKUP_DATA ? 02758 MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) || 02759 mi_open_datafile(info,share,-1)) 02760 got_error=1; 02761 } 02762 } 02763 if (got_error) 02764 { 02765 if (! param->error_printed) 02766 mi_check_print_error(param,"%d when fixing table",my_errno); 02767 if (new_file >= 0) 02768 { 02769 VOID(my_close(new_file,MYF(0))); 02770 VOID(my_raid_delete(param->temp_filename,share->base.raid_chunks, 02771 MYF(MY_WME))); 02772 if (info->dfile == new_file) 02773 info->dfile= -1; 02774 } 02775 mi_mark_crashed_on_repair(info); 02776 } 02777 else if (key_map == share->state.key_map) 02778 share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS; 02779 share->state.changed|=STATE_NOT_SORTED_PAGES; 02780 02781 pthread_cond_destroy (&sort_info.cond); 02782 pthread_mutex_destroy(&sort_info.mutex); 02783 02784 my_free((gptr) sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR)); 02785 my_free((gptr) sort_info.key_block,MYF(MY_ALLOW_ZERO_PTR)); 02786 my_free((gptr) sort_param,MYF(MY_ALLOW_ZERO_PTR)); 02787 my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR)); 02788 VOID(end_io_cache(¶m->read_cache)); 02789 info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); 02790 if (!got_error && (param->testflag & T_UNPACK)) 02791 { 02792 share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD; 02793 share->pack.header_length=0; 02794 } 02795 DBUG_RETURN(got_error); 02796 #endif /* THREAD */ 02797 } 02798 02799 /* Read next record and return next key */ 02800 02801 static int sort_key_read(MI_SORT_PARAM *sort_param, void *key) 02802 { 02803 int error; 02804 SORT_INFO *sort_info=sort_param->sort_info; 02805 MI_INFO *info=sort_info->info; 02806 DBUG_ENTER("sort_key_read"); 02807 02808 if ((error=sort_get_next_record(sort_param))) 02809 DBUG_RETURN(error); 02810 if (info->state->records == sort_info->max_records) 02811 { 02812 mi_check_print_error(sort_info->param, 02813 "Key %d - Found too many records; Can't continue", 02814 sort_param->key+1); 02815 DBUG_RETURN(1); 02816 } 02817 sort_param->real_key_length= 02818 (info->s->rec_reflength+ 02819 _mi_make_key(info, sort_param->key, (uchar*) key, 02820 sort_param->record, sort_param->filepos)); 02821 #ifdef HAVE_purify 02822 bzero(key+sort_param->real_key_length, 02823 (sort_param->key_length-sort_param->real_key_length)); 02824 #endif 02825 DBUG_RETURN(sort_write_record(sort_param)); 02826 } /* sort_key_read */ 02827 02828 static int sort_ft_key_read(MI_SORT_PARAM *sort_param, void *key) 02829 { 02830 int error; 02831 SORT_INFO *sort_info=sort_param->sort_info; 02832 MI_INFO *info=sort_info->info; 02833 FT_WORD *wptr=0; 02834 DBUG_ENTER("sort_ft_key_read"); 02835 02836 if (!sort_param->wordlist) 02837 { 02838 for (;;) 02839 { 02840 free_root(&sort_param->wordroot, MYF(MY_MARK_BLOCKS_FREE)); 02841 if ((error=sort_get_next_record(sort_param))) 02842 DBUG_RETURN(error); 02843 if (!(wptr=_mi_ft_parserecord(info,sort_param->key,sort_param->record, 02844 &sort_param->wordroot))) 02845 DBUG_RETURN(1); 02846 if (wptr->pos) 02847 break; 02848 error=sort_write_record(sort_param); 02849 } 02850 sort_param->wordptr=sort_param->wordlist=wptr; 02851 } 02852 else 02853 { 02854 error=0; 02855 wptr=(FT_WORD*)(sort_param->wordptr); 02856 } 02857 02858 sort_param->real_key_length=(info->s->rec_reflength+ 02859 _ft_make_key(info, sort_param->key, 02860 key, wptr++, sort_param->filepos)); 02861 #ifdef HAVE_purify 02862 if (sort_param->key_length > sort_param->real_key_length) 02863 bzero(key+sort_param->real_key_length, 02864 (sort_param->key_length-sort_param->real_key_length)); 02865 #endif 02866 if (!wptr->pos) 02867 { 02868 free_root(&sort_param->wordroot, MYF(MY_MARK_BLOCKS_FREE)); 02869 sort_param->wordlist=0; 02870 error=sort_write_record(sort_param); 02871 } 02872 else 02873 sort_param->wordptr=(void*)wptr; 02874 02875 DBUG_RETURN(error); 02876 } /* sort_ft_key_read */ 02877 02878 02879 /* Read next record from file using parameters in sort_info */ 02880 /* Return -1 if end of file, 0 if ok and > 0 if error */ 02881 02882 static int sort_get_next_record(MI_SORT_PARAM *sort_param) 02883 { 02884 int searching; 02885 uint found_record,b_type,left_length; 02886 my_off_t pos; 02887 byte *to; 02888 MI_BLOCK_INFO block_info; 02889 SORT_INFO *sort_info=sort_param->sort_info; 02890 MI_CHECK *param=sort_info->param; 02891 MI_INFO *info=sort_info->info; 02892 MYISAM_SHARE *share=info->s; 02893 char llbuff[22],llbuff2[22]; 02894 DBUG_ENTER("sort_get_next_record"); 02895 02896 if (*killed_ptr(param)) 02897 DBUG_RETURN(1); 02898 02899 switch (share->data_file_type) { 02900 case STATIC_RECORD: 02901 for (;;) 02902 { 02903 if (my_b_read(&sort_param->read_cache,sort_param->record, 02904 share->base.pack_reclength)) 02905 { 02906 if (sort_param->read_cache.error) 02907 param->out_flag |= O_DATA_LOST; 02908 param->retry_repair=1; 02909 param->testflag|=T_RETRY_WITHOUT_QUICK; 02910 DBUG_RETURN(-1); 02911 } 02912 sort_param->start_recpos=sort_param->pos; 02913 if (!sort_param->fix_datafile) 02914 { 02915 sort_param->filepos=sort_param->pos; 02916 if (sort_param->master) 02917 share->state.split++; 02918 } 02919 sort_param->max_pos=(sort_param->pos+=share->base.pack_reclength); 02920 if (*sort_param->record) 02921 { 02922 if (param->calc_checksum) 02923 param->glob_crc+= (info->checksum= 02924 mi_static_checksum(info,sort_param->record)); 02925 DBUG_RETURN(0); 02926 } 02927 if (!sort_param->fix_datafile && sort_param->master) 02928 { 02929 info->state->del++; 02930 info->state->empty+=share->base.pack_reclength; 02931 } 02932 } 02933 case DYNAMIC_RECORD: 02934 LINT_INIT(to); 02935 pos=sort_param->pos; 02936 searching=(sort_param->fix_datafile && (param->testflag & T_EXTEND)); 02937 for (;;) 02938 { 02939 found_record=block_info.second_read= 0; 02940 left_length=1; 02941 if (searching) 02942 { 02943 pos=MY_ALIGN(pos,MI_DYN_ALIGN_SIZE); 02944 param->testflag|=T_RETRY_WITHOUT_QUICK; 02945 sort_param->start_recpos=pos; 02946 } 02947 do 02948 { 02949 if (pos > sort_param->max_pos) 02950 sort_param->max_pos=pos; 02951 if (pos & (MI_DYN_ALIGN_SIZE-1)) 02952 { 02953 if ((param->testflag & T_VERBOSE) || searching == 0) 02954 mi_check_print_info(param,"Wrong aligned block at %s", 02955 llstr(pos,llbuff)); 02956 if (searching) 02957 goto try_next; 02958 } 02959 if (found_record && pos == param->search_after_block) 02960 mi_check_print_info(param,"Block: %s used by record at %s", 02961 llstr(param->search_after_block,llbuff), 02962 llstr(sort_param->start_recpos,llbuff2)); 02963 if (_mi_read_cache(&sort_param->read_cache, 02964 (byte*) block_info.header,pos, 02965 MI_BLOCK_INFO_HEADER_LENGTH, 02966 (! found_record ? READING_NEXT : 0) | 02967 READING_HEADER)) 02968 { 02969 if (found_record) 02970 { 02971 mi_check_print_info(param, 02972 "Can't read whole record at %s (errno: %d)", 02973 llstr(sort_param->start_recpos,llbuff),errno); 02974 goto try_next; 02975 } 02976 DBUG_RETURN(-1); 02977 } 02978 if (searching && ! sort_param->fix_datafile) 02979 { 02980 param->error_printed=1; 02981 param->retry_repair=1; 02982 param->testflag|=T_RETRY_WITHOUT_QUICK; 02983 DBUG_RETURN(1); /* Something wrong with data */ 02984 } 02985 b_type=_mi_get_block_info(&block_info,-1,pos); 02986 if ((b_type & (BLOCK_ERROR | BLOCK_FATAL_ERROR)) || 02987 ((b_type & BLOCK_FIRST) && 02988 (block_info.rec_len < (uint) share->base.min_pack_length || 02989 block_info.rec_len > (uint) share->base.max_pack_length))) 02990 { 02991 uint i; 02992 if (param->testflag & T_VERBOSE || searching == 0) 02993 mi_check_print_info(param, 02994 "Wrong bytesec: %3d-%3d-%3d at %10s; Skipped", 02995 block_info.header[0],block_info.header[1], 02996 block_info.header[2],llstr(pos,llbuff)); 02997 if (found_record) 02998 goto try_next; 02999 block_info.second_read=0; 03000 searching=1; 03001 /* Search after block in read header string */ 03002 for (i=MI_DYN_ALIGN_SIZE ; 03003 i < MI_BLOCK_INFO_HEADER_LENGTH ; 03004 i+= MI_DYN_ALIGN_SIZE) 03005 if (block_info.header[i] >= 1 && 03006 block_info.header[i] <= MI_MAX_DYN_HEADER_BYTE) 03007 break; 03008 pos+=(ulong) i; 03009 sort_param->start_recpos=pos; 03010 continue; 03011 } 03012 if (b_type & BLOCK_DELETED) 03013 { 03014 bool error=0; 03015 if (block_info.block_len+ (uint) (block_info.filepos-pos) < 03016 share->base.min_block_length) 03017 { 03018 if (!searching) 03019 mi_check_print_info(param, 03020 "Deleted block with impossible length %u at %s", 03021 block_info.block_len,llstr(pos,llbuff)); 03022 error=1; 03023 } 03024 else 03025 { 03026 if ((block_info.next_filepos != HA_OFFSET_ERROR && 03027 block_info.next_filepos >= 03028 info->state->data_file_length) || 03029 (block_info.prev_filepos != HA_OFFSET_ERROR && 03030 block_info.prev_filepos >= info->state->data_file_length)) 03031 { 03032 if (!searching) 03033 mi_check_print_info(param, 03034 "Delete link points outside datafile at %s", 03035 llstr(pos,llbuff)); 03036 error=1; 03037 } 03038 } 03039 if (error) 03040 { 03041 if (found_record) 03042 goto try_next; 03043 searching=1; 03044 pos+= MI_DYN_ALIGN_SIZE; 03045 sort_param->start_recpos=pos; 03046 block_info.second_read=0; 03047 continue; 03048 } 03049 } 03050 else 03051 { 03052 if (block_info.block_len+ (uint) (block_info.filepos-pos) < 03053 share->base.min_block_length || 03054 block_info.block_len > (uint) share->base.max_pack_length+ 03055 MI_SPLIT_LENGTH) 03056 { 03057 if (!searching) 03058 mi_check_print_info(param, 03059 "Found block with impossible length %u at %s; Skipped", 03060 block_info.block_len+ (uint) (block_info.filepos-pos), 03061 llstr(pos,llbuff)); 03062 if (found_record) 03063 goto try_next; 03064 searching=1; 03065 pos+= MI_DYN_ALIGN_SIZE; 03066 sort_param->start_recpos=pos; 03067 block_info.second_read=0; 03068 continue; 03069 } 03070 } 03071 if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR)) 03072 { 03073 if (!sort_param->fix_datafile && sort_param->master && 03074 (b_type & BLOCK_DELETED)) 03075 { 03076 info->state->empty+=block_info.block_len; 03077 info->state->del++; 03078 share->state.split++; 03079 } 03080 if (found_record) 03081 goto try_next; 03082 if (searching) 03083 { 03084 pos+=MI_DYN_ALIGN_SIZE; 03085 sort_param->start_recpos=pos; 03086 } 03087 else 03088 pos=block_info.filepos+block_info.block_len; 03089 block_info.second_read=0; 03090 continue; 03091 } 03092 03093 if (!sort_param->fix_datafile && sort_param->master) 03094 share->state.split++; 03095 if (! found_record++) 03096 { 03097 sort_param->find_length=left_length=block_info.rec_len; 03098 sort_param->start_recpos=pos; 03099 if (!sort_param->fix_datafile) 03100 sort_param->filepos=sort_param->start_recpos; 03101 if (sort_param->fix_datafile && (param->testflag & T_EXTEND)) 03102 sort_param->pos=block_info.filepos+1; 03103 else 03104 sort_param->pos=block_info.filepos+block_info.block_len; 03105 if (share->base.blobs) 03106 { 03107 if (!(to=mi_alloc_rec_buff(info,block_info.rec_len, 03108 &(sort_param->rec_buff)))) 03109 { 03110 if (param->max_record_length >= block_info.rec_len) 03111 { 03112 mi_check_print_error(param,"Not enough memory for blob at %s (need %lu)", 03113 llstr(sort_param->start_recpos,llbuff), 03114 (ulong) block_info.rec_len); 03115 DBUG_RETURN(1); 03116 } 03117 else 03118 { 03119 mi_check_print_info(param,"Not enough memory for blob at %s (need %lu); Row skipped", 03120 llstr(sort_param->start_recpos,llbuff), 03121 (ulong) block_info.rec_len); 03122 goto try_next; 03123 } 03124 } 03125 } 03126 else 03127 to= sort_param->rec_buff; 03128 } 03129 if (left_length < block_info.data_len || ! block_info.data_len) 03130 { 03131 mi_check_print_info(param, 03132 "Found block with too small length at %s; Skipped", 03133 llstr(sort_param->start_recpos,llbuff)); 03134 goto try_next; 03135 } 03136 if (block_info.filepos + block_info.data_len > 03137 sort_param->read_cache.end_of_file) 03138 { 03139 mi_check_print_info(param, 03140 "Found block that points outside data file at %s", 03141 llstr(sort_param->start_recpos,llbuff)); 03142 goto try_next; 03143 } 03144 if (_mi_read_cache(&sort_param->read_cache,to,block_info.filepos, 03145 block_info.data_len, 03146 (found_record == 1 ? READING_NEXT : 0))) 03147 { 03148 mi_check_print_info(param, 03149 "Read error for block at: %s (error: %d); Skipped", 03150 llstr(block_info.filepos,llbuff),my_errno); 03151 goto try_next; 03152 } 03153 left_length-=block_info.data_len; 03154 to+=block_info.data_len; 03155 pos=block_info.next_filepos; 03156 if (pos == HA_OFFSET_ERROR && left_length) 03157 { 03158 mi_check_print_info(param,"Wrong block with wrong total length starting at %s", 03159 llstr(sort_param->start_recpos,llbuff)); 03160 goto try_next; 03161 } 03162 if (pos + MI_BLOCK_INFO_HEADER_LENGTH > sort_param->read_cache.end_of_file) 03163 { 03164 mi_check_print_info(param,"Found link that points at %s (outside data file) at %s", 03165 llstr(pos,llbuff2), 03166 llstr(sort_param->start_recpos,llbuff)); 03167 goto try_next; 03168 } 03169 } while (left_length); 03170 03171 if (_mi_rec_unpack(info,sort_param->record,sort_param->rec_buff, 03172 sort_param->find_length) != MY_FILE_ERROR) 03173 { 03174 if (sort_param->read_cache.error < 0) 03175 DBUG_RETURN(1); 03176 if (info->s->calc_checksum) 03177 info->checksum=mi_checksum(info,sort_param->record); 03178 if ((param->testflag & (T_EXTEND | T_REP)) || searching) 03179 { 03180 if (_mi_rec_check(info, sort_param->record, sort_param->rec_buff, 03181 sort_param->find_length, 03182 (param->testflag & T_QUICK) && 03183 test(info->s->calc_checksum))) 03184 { 03185 mi_check_print_info(param,"Found wrong packed record at %s", 03186 llstr(sort_param->start_recpos,llbuff)); 03187 goto try_next; 03188 } 03189 } 03190 if (param->calc_checksum) 03191 param->glob_crc+= info->checksum; 03192 DBUG_RETURN(0); 03193 } 03194 if (!searching) 03195 mi_check_print_info(param,"Key %d - Found wrong stored record at %s", 03196 sort_param->key+1, 03197 llstr(sort_param->start_recpos,llbuff)); 03198 try_next: 03199 pos=(sort_param->start_recpos+=MI_DYN_ALIGN_SIZE); 03200 searching=1; 03201 } 03202 case COMPRESSED_RECORD: 03203 for (searching=0 ;; searching=1, sort_param->pos++) 03204 { 03205 if (_mi_read_cache(&sort_param->read_cache,(byte*) block_info.header, 03206 sort_param->pos, 03207 share->pack.ref_length,READING_NEXT)) 03208 DBUG_RETURN(-1); 03209 if (searching && ! sort_param->fix_datafile) 03210 { 03211 param->error_printed=1; 03212 param->retry_repair=1; 03213 param->testflag|=T_RETRY_WITHOUT_QUICK; 03214 DBUG_RETURN(1); /* Something wrong with data */ 03215 } 03216 sort_param->start_recpos=sort_param->pos; 03217 if (_mi_pack_get_block_info(info,&block_info,-1,sort_param->pos)) 03218 DBUG_RETURN(-1); 03219 if (!block_info.rec_len && 03220 sort_param->pos + MEMMAP_EXTRA_MARGIN == 03221 sort_param->read_cache.end_of_file) 03222 DBUG_RETURN(-1); 03223 if (block_info.rec_len < (uint) share->min_pack_length || 03224 block_info.rec_len > (uint) share->max_pack_length) 03225 { 03226 if (! searching) 03227 mi_check_print_info(param,"Found block with wrong recordlength: %d at %s\n", 03228 block_info.rec_len, 03229 llstr(sort_param->pos,llbuff)); 03230 continue; 03231 } 03232 if (_mi_read_cache(&sort_param->read_cache,(byte*) sort_param->rec_buff, 03233 block_info.filepos, block_info.rec_len, 03234 READING_NEXT)) 03235 { 03236 if (! searching) 03237 mi_check_print_info(param,"Couldn't read whole record from %s", 03238 llstr(sort_param->pos,llbuff)); 03239 continue; 03240 } 03241 if (_mi_pack_rec_unpack(info,sort_param->record,sort_param->rec_buff, 03242 block_info.rec_len)) 03243 { 03244 if (! searching) 03245 mi_check_print_info(param,"Found wrong record at %s", 03246 llstr(sort_param->pos,llbuff)); 03247 continue; 03248 } 03249 info->checksum=mi_checksum(info,sort_param->record); 03250 if (!sort_param->fix_datafile) 03251 { 03252 sort_param->filepos=sort_param->pos; 03253 if (sort_param->master) 03254 share->state.split++; 03255 } 03256 sort_param->max_pos=(sort_param->pos=block_info.filepos+ 03257 block_info.rec_len); 03258 info->packed_length=block_info.rec_len; 03259 if (param->calc_checksum) 03260 param->glob_crc+= info->checksum; 03261 DBUG_RETURN(0); 03262 } 03263 } 03264 DBUG_RETURN(1); /* Impossible */ 03265 } 03266 03267 03268 /* Write record to new file */ 03269 03270 int sort_write_record(MI_SORT_PARAM *sort_param) 03271 { 03272 int flag; 03273 uint length; 03274 ulong block_length,reclength; 03275 byte *from; 03276 byte block_buff[8]; 03277 SORT_INFO *sort_info=sort_param->sort_info; 03278 MI_CHECK *param=sort_info->param; 03279 MI_INFO *info=sort_info->info; 03280 MYISAM_SHARE *share=info->s; 03281 DBUG_ENTER("sort_write_record"); 03282 03283 if (sort_param->fix_datafile) 03284 { 03285 switch (sort_info->new_data_file_type) { 03286 case STATIC_RECORD: 03287 if (my_b_write(&info->rec_cache,sort_param->record, 03288 share->base.pack_reclength)) 03289 { 03290 mi_check_print_error(param,"%d when writing to datafile",my_errno); 03291 DBUG_RETURN(1); 03292 } 03293 sort_param->filepos+=share->base.pack_reclength; 03294 info->s->state.split++; 03295 /* sort_info->param->glob_crc+=mi_static_checksum(info, sort_param->record); */ 03296 break; 03297 case DYNAMIC_RECORD: 03298 if (! info->blobs) 03299 from=sort_param->rec_buff; 03300 else 03301 { 03302 /* must be sure that local buffer is big enough */ 03303 reclength=info->s->base.pack_reclength+ 03304 _my_calc_total_blob_length(info,sort_param->record)+ 03305 ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+ 03306 MI_DYN_DELETE_BLOCK_HEADER; 03307 if (sort_info->buff_length < reclength) 03308 { 03309 if (!(sort_info->buff=my_realloc(sort_info->buff, (uint) reclength, 03310 MYF(MY_FREE_ON_ERROR | 03311 MY_ALLOW_ZERO_PTR)))) 03312 DBUG_RETURN(1); 03313 sort_info->buff_length=reclength; 03314 } 03315 from=sort_info->buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER); 03316 } 03317 info->checksum=mi_checksum(info,sort_param->record); 03318 reclength=_mi_rec_pack(info,from,sort_param->record); 03319 flag=0; 03320 /* sort_info->param->glob_crc+=info->checksum; */ 03321 03322 do 03323 { 03324 block_length=reclength+ 3 + test(reclength >= (65520-3)); 03325 if (block_length < share->base.min_block_length) 03326 block_length=share->base.min_block_length; 03327 info->update|=HA_STATE_WRITE_AT_END; 03328 block_length=MY_ALIGN(block_length,MI_DYN_ALIGN_SIZE); 03329 if (block_length > MI_MAX_BLOCK_LENGTH) 03330 block_length=MI_MAX_BLOCK_LENGTH; 03331 if (_mi_write_part_record(info,0L,block_length, 03332 sort_param->filepos+block_length, 03333 &from,&reclength,&flag)) 03334 { 03335 mi_check_print_error(param,"%d when writing to datafile",my_errno); 03336 DBUG_RETURN(1); 03337 } 03338 sort_param->filepos+=block_length; 03339 info->s->state.split++; 03340 } while (reclength); 03341 /* sort_info->param->glob_crc+=info->checksum; */ 03342 break; 03343 case COMPRESSED_RECORD: 03344 reclength=info->packed_length; 03345 length= save_pack_length((uint) share->pack.version, block_buff, 03346 reclength); 03347 if (info->s->base.blobs) 03348 length+= save_pack_length((uint) share->pack.version, 03349 block_buff + length, info->blob_length); 03350 if (my_b_write(&info->rec_cache,block_buff,length) || 03351 my_b_write(&info->rec_cache,(byte*) sort_param->rec_buff,reclength)) 03352 { 03353 mi_check_print_error(param,"%d when writing to datafile",my_errno); 03354 DBUG_RETURN(1); 03355 } 03356 /* sort_info->param->glob_crc+=info->checksum; */ 03357 sort_param->filepos+=reclength+length; 03358 info->s->state.split++; 03359 break; 03360 } 03361 } 03362 if (sort_param->master) 03363 { 03364 info->state->records++; 03365 if ((param->testflag & T_WRITE_LOOP) && 03366 (info->state->records % WRITE_COUNT) == 0) 03367 { 03368 char llbuff[22]; 03369 printf("%s\r", llstr(info->state->records,llbuff)); 03370 VOID(fflush(stdout)); 03371 } 03372 } 03373 DBUG_RETURN(0); 03374 } /* sort_write_record */ 03375 03376 03377 /* Compare two keys from _create_index_by_sort */ 03378 03379 static int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a, 03380 const void *b) 03381 { 03382 uint not_used[2]; 03383 return (ha_key_cmp(sort_param->seg, *((uchar**) a), *((uchar**) b), 03384 USE_WHOLE_KEY, SEARCH_SAME, not_used)); 03385 } /* sort_key_cmp */ 03386 03387 03388 static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a) 03389 { 03390 uint diff_pos[2]; 03391 char llbuff[22],llbuff2[22]; 03392 SORT_INFO *sort_info=sort_param->sort_info; 03393 MI_CHECK *param= sort_info->param; 03394 int cmp; 03395 03396 if (sort_info->key_block->inited) 03397 { 03398 cmp=ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey, 03399 (uchar*) a, USE_WHOLE_KEY,SEARCH_FIND | SEARCH_UPDATE, 03400 diff_pos); 03401 if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL) 03402 ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey, 03403 (uchar*) a, USE_WHOLE_KEY, 03404 SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diff_pos); 03405 else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS) 03406 { 03407 diff_pos[0]= mi_collect_stats_nonulls_next(sort_param->seg, 03408 sort_param->notnull, 03409 sort_info->key_block->lastkey, 03410 (uchar*)a); 03411 } 03412 sort_param->unique[diff_pos[0]-1]++; 03413 } 03414 else 03415 { 03416 cmp= -1; 03417 if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS) 03418 mi_collect_stats_nonulls_first(sort_param->seg, sort_param->notnull, 03419 (uchar*)a); 03420 } 03421 if ((sort_param->keyinfo->flag & HA_NOSAME) && cmp == 0) 03422 { 03423 sort_info->dupp++; 03424 sort_info->info->lastpos=get_record_for_key(sort_info->info, 03425 sort_param->keyinfo, 03426 (uchar*) a); 03427 mi_check_print_warning(param, 03428 "Duplicate key for record at %10s against record at %10s", 03429 llstr(sort_info->info->lastpos,llbuff), 03430 llstr(get_record_for_key(sort_info->info, 03431 sort_param->keyinfo, 03432 sort_info->key_block-> 03433 lastkey), 03434 llbuff2)); 03435 param->testflag|=T_RETRY_WITHOUT_QUICK; 03436 if (sort_info->param->testflag & T_VERBOSE) 03437 _mi_print_key(stdout,sort_param->seg,(uchar*) a, USE_WHOLE_KEY); 03438 return (sort_delete_record(sort_param)); 03439 } 03440 #ifndef DBUG_OFF 03441 if (cmp > 0) 03442 { 03443 mi_check_print_error(param, 03444 "Internal error: Keys are not in order from sort"); 03445 return(1); 03446 } 03447 #endif 03448 return (sort_insert_key(sort_param,sort_info->key_block, 03449 (uchar*) a, HA_OFFSET_ERROR)); 03450 } /* sort_key_write */ 03451 03452 int sort_ft_buf_flush(MI_SORT_PARAM *sort_param) 03453 { 03454 SORT_INFO *sort_info=sort_param->sort_info; 03455 SORT_KEY_BLOCKS *key_block=sort_info->key_block; 03456 MYISAM_SHARE *share=sort_info->info->s; 03457 uint val_off, val_len; 03458 int error; 03459 SORT_FT_BUF *ft_buf=sort_info->ft_buf; 03460 uchar *from, *to; 03461 03462 val_len=share->ft2_keyinfo.keylength; 03463 get_key_full_length_rdonly(val_off, ft_buf->lastkey); 03464 to=ft_buf->lastkey+val_off; 03465 03466 if (ft_buf->buf) 03467 { 03468 /* flushing first-level tree */ 03469 error=sort_insert_key(sort_param,key_block,ft_buf->lastkey, 03470 HA_OFFSET_ERROR); 03471 for (from=to+val_len; 03472 !error && from < ft_buf->buf; 03473 from+= val_len) 03474 { 03475 memcpy(to, from, val_len); 03476 error=sort_insert_key(sort_param,key_block,ft_buf->lastkey, 03477 HA_OFFSET_ERROR); 03478 } 03479 return error; 03480 } 03481 /* flushing second-level tree keyblocks */ 03482 error=flush_pending_blocks(sort_param); 03483 /* updating lastkey with second-level tree info */ 03484 ft_intXstore(ft_buf->lastkey+val_off, -ft_buf->count); 03485 _mi_dpointer(sort_info->info, ft_buf->lastkey+val_off+HA_FT_WLEN, 03486 share->state.key_root[sort_param->key]); 03487 /* restoring first level tree data in sort_info/sort_param */ 03488 sort_info->key_block=sort_info->key_block_end- sort_info->param->sort_key_blocks; 03489 sort_param->keyinfo=share->keyinfo+sort_param->key; 03490 share->state.key_root[sort_param->key]=HA_OFFSET_ERROR; 03491 /* writing lastkey in first-level tree */ 03492 return error ? error : 03493 sort_insert_key(sort_param,sort_info->key_block, 03494 ft_buf->lastkey,HA_OFFSET_ERROR); 03495 } 03496 03497 static int sort_ft_key_write(MI_SORT_PARAM *sort_param, const void *a) 03498 { 03499 uint a_len, val_off, val_len, error; 03500 uchar *p; 03501 SORT_INFO *sort_info=sort_param->sort_info; 03502 SORT_FT_BUF *ft_buf=sort_info->ft_buf; 03503 SORT_KEY_BLOCKS *key_block=sort_info->key_block; 03504 03505 val_len=HA_FT_WLEN+sort_info->info->s->base.rec_reflength; 03506 get_key_full_length_rdonly(a_len, (uchar *)a); 03507 03508 if (!ft_buf) 03509 { 03510 /* 03511 use two-level tree only if key_reflength fits in rec_reflength place 03512 and row format is NOT static - for _mi_dpointer not to garble offsets 03513 */ 03514 if ((sort_info->info->s->base.key_reflength <= 03515 sort_info->info->s->base.rec_reflength) && 03516 (sort_info->info->s->options & 03517 (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD))) 03518 ft_buf=(SORT_FT_BUF *)my_malloc(sort_param->keyinfo->block_length + 03519 sizeof(SORT_FT_BUF), MYF(MY_WME)); 03520 03521 if (!ft_buf) 03522 { 03523 sort_param->key_write=sort_key_write; 03524 return sort_key_write(sort_param, a); 03525 } 03526 sort_info->ft_buf=ft_buf; 03527 goto word_init_ft_buf; /* no need to duplicate the code */ 03528 } 03529 get_key_full_length_rdonly(val_off, ft_buf->lastkey); 03530 03531 if (mi_compare_text(sort_param->seg->charset, 03532 ((uchar *)a)+1,a_len-1, 03533 ft_buf->lastkey+1,val_off-1, 0, 0)==0) 03534 { 03535 if (!ft_buf->buf) /* store in second-level tree */ 03536 { 03537 ft_buf->count++; 03538 return sort_insert_key(sort_param,key_block, 03539 ((uchar *)a)+a_len, HA_OFFSET_ERROR); 03540 } 03541 03542 /* storing the key in the buffer. */ 03543 memcpy (ft_buf->buf, (char *)a+a_len, val_len); 03544 ft_buf->buf+=val_len; 03545 if (ft_buf->buf < ft_buf->end) 03546 return 0; 03547 03548 /* converting to two-level tree */ 03549 p=ft_buf->lastkey+val_off; 03550 03551 while (key_block->inited) 03552 key_block++; 03553 sort_info->key_block=key_block; 03554 sort_param->keyinfo=& sort_info->info->s->ft2_keyinfo; 03555 ft_buf->count=(ft_buf->buf - p)/val_len; 03556 03557 /* flushing buffer to second-level tree */ 03558 for (error=0; !error && p < ft_buf->buf; p+= val_len) 03559 error=sort_insert_key(sort_param,key_block,p,HA_OFFSET_ERROR); 03560 ft_buf->buf=0; 03561 return error; 03562 } 03563 03564 /* flushing buffer */ 03565 if ((error=sort_ft_buf_flush(sort_param))) 03566 return error; 03567 03568 word_init_ft_buf: 03569 a_len+=val_len; 03570 memcpy(ft_buf->lastkey, a, a_len); 03571 ft_buf->buf=ft_buf->lastkey+a_len; 03572 /* 03573 32 is just a safety margin here 03574 (at least max(val_len, sizeof(nod_flag)) should be there). 03575 May be better performance could be achieved if we'd put 03576 (sort_info->keyinfo->block_length-32)/XXX 03577 instead. 03578 TODO: benchmark the best value for XXX. 03579 */ 03580 ft_buf->end=ft_buf->lastkey+ (sort_param->keyinfo->block_length-32); 03581 return 0; 03582 } /* sort_ft_key_write */ 03583 03584 03585 /* get pointer to record from a key */ 03586 03587 static my_off_t get_record_for_key(MI_INFO *info, MI_KEYDEF *keyinfo, 03588 uchar *key) 03589 { 03590 return _mi_dpos(info,0,key+_mi_keylength(keyinfo,key)); 03591 } /* get_record_for_key */ 03592 03593 03594 /* Insert a key in sort-key-blocks */ 03595 03596 static int sort_insert_key(MI_SORT_PARAM *sort_param, 03597 register SORT_KEY_BLOCKS *key_block, uchar *key, 03598 my_off_t prev_block) 03599 { 03600 uint a_length,t_length,nod_flag; 03601 my_off_t filepos,key_file_length; 03602 uchar *anc_buff,*lastkey; 03603 MI_KEY_PARAM s_temp; 03604 MI_INFO *info; 03605 MI_KEYDEF *keyinfo=sort_param->keyinfo; 03606 SORT_INFO *sort_info= sort_param->sort_info; 03607 MI_CHECK *param=sort_info->param; 03608 DBUG_ENTER("sort_insert_key"); 03609 03610 anc_buff=key_block->buff; 03611 info=sort_info->info; 03612 lastkey=key_block->lastkey; 03613 nod_flag= (key_block == sort_info->key_block ? 0 : 03614 info->s->base.key_reflength); 03615 03616 if (!key_block->inited) 03617 { 03618 key_block->inited=1; 03619 if (key_block == sort_info->key_block_end) 03620 { 03621 mi_check_print_error(param,"To many key-block-levels; Try increasing sort_key_blocks"); 03622 DBUG_RETURN(1); 03623 } 03624 a_length=2+nod_flag; 03625 key_block->end_pos=anc_buff+2; 03626 lastkey=0; /* No previous key in block */ 03627 } 03628 else 03629 a_length=mi_getint(anc_buff); 03630 03631 /* Save pointer to previous block */ 03632 if (nod_flag) 03633 _mi_kpointer(info,key_block->end_pos,prev_block); 03634 03635 t_length=(*keyinfo->pack_key)(keyinfo,nod_flag, 03636 (uchar*) 0,lastkey,lastkey,key, 03637 &s_temp); 03638 (*keyinfo->store_key)(keyinfo, key_block->end_pos+nod_flag,&s_temp); 03639 a_length+=t_length; 03640 mi_putint(anc_buff,a_length,nod_flag); 03641 key_block->end_pos+=t_length; 03642 if (a_length <= keyinfo->block_length) 03643 { 03644 VOID(_mi_move_key(keyinfo,key_block->lastkey,key)); 03645 key_block->last_length=a_length-t_length; 03646 DBUG_RETURN(0); 03647 } 03648 03649 /* Fill block with end-zero and write filled block */ 03650 mi_putint(anc_buff,key_block->last_length,nod_flag); 03651 bzero((byte*) anc_buff+key_block->last_length, 03652 keyinfo->block_length- key_block->last_length); 03653 key_file_length=info->state->key_file_length; 03654 if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR) 03655 DBUG_RETURN(1); 03656 03657 /* If we read the page from the key cache, we have to write it back to it */ 03658 if (key_file_length == info->state->key_file_length) 03659 { 03660 if (_mi_write_keypage(info, keyinfo, filepos, DFLT_INIT_HITS, anc_buff)) 03661 DBUG_RETURN(1); 03662 } 03663 else if (my_pwrite(info->s->kfile,(byte*) anc_buff, 03664 (uint) keyinfo->block_length,filepos, param->myf_rw)) 03665 DBUG_RETURN(1); 03666 DBUG_DUMP("buff",(byte*) anc_buff,mi_getint(anc_buff)); 03667 03668 /* Write separator-key to block in next level */ 03669 if (sort_insert_key(sort_param,key_block+1,key_block->lastkey,filepos)) 03670 DBUG_RETURN(1); 03671 03672 /* clear old block and write new key in it */ 03673 key_block->inited=0; 03674 DBUG_RETURN(sort_insert_key(sort_param, key_block,key,prev_block)); 03675 } /* sort_insert_key */ 03676 03677 03678 /* Delete record when we found a duplicated key */ 03679 03680 static int sort_delete_record(MI_SORT_PARAM *sort_param) 03681 { 03682 uint i; 03683 int old_file,error; 03684 uchar *key; 03685 SORT_INFO *sort_info=sort_param->sort_info; 03686 MI_CHECK *param=sort_info->param; 03687 MI_INFO *info=sort_info->info; 03688 DBUG_ENTER("sort_delete_record"); 03689 03690 if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK) 03691 { 03692 mi_check_print_error(param, 03693 "Quick-recover aborted; Run recovery without switch -q or with switch -qq"); 03694 DBUG_RETURN(1); 03695 } 03696 if (info->s->options & HA_OPTION_COMPRESS_RECORD) 03697 { 03698 mi_check_print_error(param, 03699 "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);; 03700 DBUG_RETURN(1); 03701 } 03702 03703 old_file=info->dfile; 03704 info->dfile=info->rec_cache.file; 03705 if (sort_info->current_key) 03706 { 03707 key=info->lastkey+info->s->base.max_key_length; 03708 if ((error=(*info->s->read_rnd)(info,sort_param->record,info->lastpos,0)) && 03709 error != HA_ERR_RECORD_DELETED) 03710 { 03711 mi_check_print_error(param,"Can't read record to be removed"); 03712 info->dfile=old_file; 03713 DBUG_RETURN(1); 03714 } 03715 03716 for (i=0 ; i < sort_info->current_key ; i++) 03717 { 03718 uint key_length=_mi_make_key(info,i,key,sort_param->record,info->lastpos); 03719 if (_mi_ck_delete(info,i,key,key_length)) 03720 { 03721 mi_check_print_error(param,"Can't delete key %d from record to be removed",i+1); 03722 info->dfile=old_file; 03723 DBUG_RETURN(1); 03724 } 03725 } 03726 if (param->calc_checksum) 03727 param->glob_crc-=(*info->s->calc_checksum)(info, sort_param->record); 03728 } 03729 error=flush_io_cache(&info->rec_cache) || (*info->s->delete_record)(info); 03730 info->dfile=old_file; /* restore actual value */ 03731 info->state->records--; 03732 DBUG_RETURN(error); 03733 } /* sort_delete_record */ 03734 03735 /* Fix all pending blocks and flush everything to disk */ 03736 03737 int flush_pending_blocks(MI_SORT_PARAM *sort_param) 03738 { 03739 uint nod_flag,length; 03740 my_off_t filepos,key_file_length; 03741 SORT_KEY_BLOCKS *key_block; 03742 SORT_INFO *sort_info= sort_param->sort_info; 03743 myf myf_rw=sort_info->param->myf_rw; 03744 MI_INFO *info=sort_info->info; 03745 MI_KEYDEF *keyinfo=sort_param->keyinfo; 03746 DBUG_ENTER("flush_pending_blocks"); 03747 03748 filepos= HA_OFFSET_ERROR; /* if empty file */ 03749 nod_flag=0; 03750 for (key_block=sort_info->key_block ; key_block->inited ; key_block++) 03751 { 03752 key_block->inited=0; 03753 length=mi_getint(key_block->buff); 03754 if (nod_flag) 03755 _mi_kpointer(info,key_block->end_pos,filepos); 03756 key_file_length=info->state->key_file_length; 03757 bzero((byte*) key_block->buff+length, keyinfo->block_length-length); 03758 if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR) 03759 DBUG_RETURN(1); 03760 03761 /* If we read the page from the key cache, we have to write it back */ 03762 if (key_file_length == info->state->key_file_length) 03763 { 03764 if (_mi_write_keypage(info, keyinfo, filepos, 03765 DFLT_INIT_HITS, key_block->buff)) 03766 DBUG_RETURN(1); 03767 } 03768 else if (my_pwrite(info->s->kfile,(byte*) key_block->buff, 03769 (uint) keyinfo->block_length,filepos, myf_rw)) 03770 DBUG_RETURN(1); 03771 DBUG_DUMP("buff",(byte*) key_block->buff,length); 03772 nod_flag=1; 03773 } 03774 info->s->state.key_root[sort_param->key]=filepos; /* Last is root for tree */ 03775 DBUG_RETURN(0); 03776 } /* flush_pending_blocks */ 03777 03778 /* alloc space and pointers for key_blocks */ 03779 03780 static SORT_KEY_BLOCKS *alloc_key_blocks(MI_CHECK *param, uint blocks, 03781 uint buffer_length) 03782 { 03783 reg1 uint i; 03784 SORT_KEY_BLOCKS *block; 03785 DBUG_ENTER("alloc_key_blocks"); 03786 03787 if (!(block=(SORT_KEY_BLOCKS*) my_malloc((sizeof(SORT_KEY_BLOCKS)+ 03788 buffer_length+IO_SIZE)*blocks, 03789 MYF(0)))) 03790 { 03791 mi_check_print_error(param,"Not enough memory for sort-key-blocks"); 03792 return(0); 03793 } 03794 for (i=0 ; i < blocks ; i++) 03795 { 03796 block[i].inited=0; 03797 block[i].buff=(uchar*) (block+blocks)+(buffer_length+IO_SIZE)*i; 03798 } 03799 DBUG_RETURN(block); 03800 } /* alloc_key_blocks */ 03801 03802 03803 /* Check if file is almost full */ 03804 03805 int test_if_almost_full(MI_INFO *info) 03806 { 03807 if (info->s->options & HA_OPTION_COMPRESS_RECORD) 03808 return 0; 03809 return (my_seek(info->s->kfile,0L,MY_SEEK_END,MYF(0))/10*9 > 03810 (my_off_t) (info->s->base.max_key_file_length) || 03811 my_seek(info->dfile,0L,MY_SEEK_END,MYF(0))/10*9 > 03812 (my_off_t) info->s->base.max_data_file_length); 03813 } 03814 03815 /* Recreate table with bigger more alloced record-data */ 03816 03817 int recreate_table(MI_CHECK *param, MI_INFO **org_info, char *filename) 03818 { 03819 int error; 03820 MI_INFO info; 03821 MYISAM_SHARE share; 03822 MI_KEYDEF *keyinfo,*key,*key_end; 03823 HA_KEYSEG *keysegs,*keyseg; 03824 MI_COLUMNDEF *recdef,*rec,*end; 03825 MI_UNIQUEDEF *uniquedef,*u_ptr,*u_end; 03826 MI_STATUS_INFO status_info; 03827 uint unpack,key_parts; 03828 ha_rows max_records; 03829 ulonglong file_length,tmp_length; 03830 MI_CREATE_INFO create_info; 03831 DBUG_ENTER("recreate_table"); 03832 03833 error=1; /* Default error */ 03834 info= **org_info; 03835 status_info= (*org_info)->state[0]; 03836 info.state= &status_info; 03837 share= *(*org_info)->s; 03838 unpack= (share.options & HA_OPTION_COMPRESS_RECORD) && 03839 (param->testflag & T_UNPACK); 03840 if (!(keyinfo=(MI_KEYDEF*) my_alloca(sizeof(MI_KEYDEF)*share.base.keys))) 03841 DBUG_RETURN(0); 03842 memcpy((byte*) keyinfo,(byte*) share.keyinfo, 03843 (size_t) (sizeof(MI_KEYDEF)*share.base.keys)); 03844 03845 key_parts= share.base.all_key_parts; 03846 if (!(keysegs=(HA_KEYSEG*) my_alloca(sizeof(HA_KEYSEG)* 03847 (key_parts+share.base.keys)))) 03848 { 03849 my_afree((gptr) keyinfo); 03850 DBUG_RETURN(1); 03851 } 03852 if (!(recdef=(MI_COLUMNDEF*) 03853 my_alloca(sizeof(MI_COLUMNDEF)*(share.base.fields+1)))) 03854 { 03855 my_afree((gptr) keyinfo); 03856 my_afree((gptr) keysegs); 03857 DBUG_RETURN(1); 03858 } 03859 if (!(uniquedef=(MI_UNIQUEDEF*) 03860 my_alloca(sizeof(MI_UNIQUEDEF)*(share.state.header.uniques+1)))) 03861 { 03862 my_afree((gptr) recdef); 03863 my_afree((gptr) keyinfo); 03864 my_afree((gptr) keysegs); 03865 DBUG_RETURN(1); 03866 } 03867 03868 /* Copy the column definitions */ 03869 memcpy((byte*) recdef,(byte*) share.rec, 03870 (size_t) (sizeof(MI_COLUMNDEF)*(share.base.fields+1))); 03871 for (rec=recdef,end=recdef+share.base.fields; rec != end ; rec++) 03872 { 03873 if (unpack && !(share.options & HA_OPTION_PACK_RECORD) && 03874 rec->type != FIELD_BLOB && 03875 rec->type != FIELD_VARCHAR && 03876 rec->type != FIELD_CHECK) 03877 rec->type=(int) FIELD_NORMAL; 03878 } 03879 03880 /* Change the new key to point at the saved key segments */ 03881 memcpy((byte*) keysegs,(byte*) share.keyparts, 03882 (size_t) (sizeof(HA_KEYSEG)*(key_parts+share.base.keys+ 03883 share.state.header.uniques))); 03884 keyseg=keysegs; 03885 for (key=keyinfo,key_end=keyinfo+share.base.keys; key != key_end ; key++) 03886 { 03887 key->seg=keyseg; 03888 for (; keyseg->type ; keyseg++) 03889 { 03890 if (param->language) 03891 keyseg->language=param->language; /* change language */ 03892 } 03893 keyseg++; /* Skip end pointer */ 03894 } 03895 03896 /* Copy the unique definitions and change them to point at the new key 03897 segments*/ 03898 memcpy((byte*) uniquedef,(byte*) share.uniqueinfo, 03899 (size_t) (sizeof(MI_UNIQUEDEF)*(share.state.header.uniques))); 03900 for (u_ptr=uniquedef,u_end=uniquedef+share.state.header.uniques; 03901 u_ptr != u_end ; u_ptr++) 03902 { 03903 u_ptr->seg=keyseg; 03904 keyseg+=u_ptr->keysegs+1; 03905 } 03906 if (share.options & HA_OPTION_COMPRESS_RECORD) 03907 share.base.records=max_records=info.state->records; 03908 else if (share.base.min_pack_length) 03909 max_records=(ha_rows) (my_seek(info.dfile,0L,MY_SEEK_END,MYF(0)) / 03910 (ulong) share.base.min_pack_length); 03911 else 03912 max_records=0; 03913 unpack= (share.options & HA_OPTION_COMPRESS_RECORD) && 03914 (param->testflag & T_UNPACK); 03915 share.options&= ~HA_OPTION_TEMP_COMPRESS_RECORD; 03916 03917 file_length=(ulonglong) my_seek(info.dfile,0L,MY_SEEK_END,MYF(0)); 03918 tmp_length= file_length+file_length/10; 03919 set_if_bigger(file_length,param->max_data_file_length); 03920 set_if_bigger(file_length,tmp_length); 03921 set_if_bigger(file_length,(ulonglong) share.base.max_data_file_length); 03922 03923 VOID(mi_close(*org_info)); 03924 bzero((char*) &create_info,sizeof(create_info)); 03925 create_info.max_rows=max(max_records,share.base.records); 03926 create_info.reloc_rows=share.base.reloc; 03927 create_info.old_options=(share.options | 03928 (unpack ? HA_OPTION_TEMP_COMPRESS_RECORD : 0)); 03929 03930 create_info.data_file_length=file_length; 03931 create_info.auto_increment=share.state.auto_increment; 03932 create_info.language = (param->language ? param->language : 03933 share.state.header.language); 03934 create_info.key_file_length= status_info.key_file_length; 03935 /* 03936 Allow for creating an auto_increment key. This has an effect only if 03937 an auto_increment key exists in the original table. 03938 */ 03939 create_info.with_auto_increment= TRUE; 03940 /* We don't have to handle symlinks here because we are using 03941 HA_DONT_TOUCH_DATA */ 03942 if (mi_create(filename, 03943 share.base.keys - share.state.header.uniques, 03944 keyinfo, share.base.fields, recdef, 03945 share.state.header.uniques, uniquedef, 03946 &create_info, 03947 HA_DONT_TOUCH_DATA)) 03948 { 03949 mi_check_print_error(param,"Got error %d when trying to recreate indexfile",my_errno); 03950 goto end; 03951 } 03952 *org_info=mi_open(filename,O_RDWR, 03953 (param->testflag & T_WAIT_FOREVER) ? HA_OPEN_WAIT_IF_LOCKED : 03954 (param->testflag & T_DESCRIPT) ? HA_OPEN_IGNORE_IF_LOCKED : 03955 HA_OPEN_ABORT_IF_LOCKED); 03956 if (!*org_info) 03957 { 03958 mi_check_print_error(param,"Got error %d when trying to open re-created indexfile", 03959 my_errno); 03960 goto end; 03961 } 03962 /* We are modifing */ 03963 (*org_info)->s->options&= ~HA_OPTION_READ_ONLY_DATA; 03964 VOID(_mi_readinfo(*org_info,F_WRLCK,0)); 03965 (*org_info)->state->records=info.state->records; 03966 if (share.state.create_time) 03967 (*org_info)->s->state.create_time=share.state.create_time; 03968 (*org_info)->s->state.unique=(*org_info)->this_unique= 03969 share.state.unique; 03970 (*org_info)->state->checksum=info.state->checksum; 03971 (*org_info)->state->del=info.state->del; 03972 (*org_info)->s->state.dellink=share.state.dellink; 03973 (*org_info)->state->empty=info.state->empty; 03974 (*org_info)->state->data_file_length=info.state->data_file_length; 03975 if (update_state_info(param,*org_info,UPDATE_TIME | UPDATE_STAT | 03976 UPDATE_OPEN_COUNT)) 03977 goto end; 03978 error=0; 03979 end: 03980 my_afree((gptr) uniquedef); 03981 my_afree((gptr) keyinfo); 03982 my_afree((gptr) recdef); 03983 my_afree((gptr) keysegs); 03984 DBUG_RETURN(error); 03985 } 03986 03987 03988 /* write suffix to data file if neaded */ 03989 03990 int write_data_suffix(SORT_INFO *sort_info, my_bool fix_datafile) 03991 { 03992 MI_INFO *info=sort_info->info; 03993 03994 if (info->s->options & HA_OPTION_COMPRESS_RECORD && fix_datafile) 03995 { 03996 char buff[MEMMAP_EXTRA_MARGIN]; 03997 bzero(buff,sizeof(buff)); 03998 if (my_b_write(&info->rec_cache,buff,sizeof(buff))) 03999 { 04000 mi_check_print_error(sort_info->param, 04001 "%d when writing to datafile",my_errno); 04002 return 1; 04003 } 04004 sort_info->param->read_cache.end_of_file+=sizeof(buff); 04005 } 04006 return 0; 04007 } 04008 04009 /* Update state and myisamchk_time of indexfile */ 04010 04011 int update_state_info(MI_CHECK *param, MI_INFO *info,uint update) 04012 { 04013 MYISAM_SHARE *share=info->s; 04014 04015 if (update & UPDATE_OPEN_COUNT) 04016 { 04017 share->state.open_count=0; 04018 share->global_changed=0; 04019 } 04020 if (update & UPDATE_STAT) 04021 { 04022 uint i, key_parts= mi_uint2korr(share->state.header.key_parts); 04023 share->state.rec_per_key_rows=info->state->records; 04024 share->state.changed&= ~STATE_NOT_ANALYZED; 04025 if (info->state->records) 04026 { 04027 for (i=0; i<key_parts; i++) 04028 { 04029 if (!(share->state.rec_per_key_part[i]=param->rec_per_key_part[i])) 04030 share->state.changed|= STATE_NOT_ANALYZED; 04031 } 04032 } 04033 } 04034 if (update & (UPDATE_STAT | UPDATE_SORT | UPDATE_TIME | UPDATE_AUTO_INC)) 04035 { 04036 if (update & UPDATE_TIME) 04037 { 04038 share->state.check_time= (long) time((time_t*) 0); 04039 if (!share->state.create_time) 04040 share->state.create_time=share->state.check_time; 04041 } 04042 /* 04043 When tables are locked we haven't synched the share state and the 04044 real state for a while so we better do it here before synching 04045 the share state to disk. Only when table is write locked is it 04046 necessary to perform this synch. 04047 */ 04048 if (info->lock_type == F_WRLCK) 04049 share->state.state= *info->state; 04050 if (mi_state_info_write(share->kfile,&share->state,1+2)) 04051 goto err; 04052 share->changed=0; 04053 } 04054 { /* Force update of status */ 04055 int error; 04056 uint r_locks=share->r_locks,w_locks=share->w_locks; 04057 share->r_locks= share->w_locks= share->tot_locks= 0; 04058 error=_mi_writeinfo(info,WRITEINFO_NO_UNLOCK); 04059 share->r_locks=r_locks; 04060 share->w_locks=w_locks; 04061 share->tot_locks=r_locks+w_locks; 04062 if (!error) 04063 return 0; 04064 } 04065 err: 04066 mi_check_print_error(param,"%d when updating keyfile",my_errno); 04067 return 1; 04068 } 04069 04070 /* 04071 Update auto increment value for a table 04072 When setting the 'repair_only' flag we only want to change the 04073 old auto_increment value if its wrong (smaller than some given key). 04074 The reason is that we shouldn't change the auto_increment value 04075 for a table without good reason when only doing a repair; If the 04076 user have inserted and deleted rows, the auto_increment value 04077 may be bigger than the biggest current row and this is ok. 04078 04079 If repair_only is not set, we will update the flag to the value in 04080 param->auto_increment is bigger than the biggest key. 04081 */ 04082 04083 void update_auto_increment_key(MI_CHECK *param, MI_INFO *info, 04084 my_bool repair_only) 04085 { 04086 byte *record; 04087 DBUG_ENTER("update_auto_increment_key"); 04088 04089 if (!info->s->base.auto_key || 04090 ! mi_is_key_active(info->s->state.key_map, info->s->base.auto_key - 1)) 04091 { 04092 if (!(param->testflag & T_VERY_SILENT)) 04093 mi_check_print_info(param, 04094 "Table: %s doesn't have an auto increment key\n", 04095 param->isam_file_name); 04096 DBUG_VOID_RETURN; 04097 } 04098 if (!(param->testflag & T_SILENT) && 04099 !(param->testflag & T_REP)) 04100 printf("Updating MyISAM file: %s\n", param->isam_file_name); 04101 /* 04102 We have to use an allocated buffer instead of info->rec_buff as 04103 _mi_put_key_in_record() may use info->rec_buff 04104 */ 04105 if (!(record= (byte*) my_malloc((uint) info->s->base.pack_reclength, 04106 MYF(0)))) 04107 { 04108 mi_check_print_error(param,"Not enough memory for extra record"); 04109 DBUG_VOID_RETURN; 04110 } 04111 04112 mi_extra(info,HA_EXTRA_KEYREAD,0); 04113 if (mi_rlast(info, record, info->s->base.auto_key-1)) 04114 { 04115 if (my_errno != HA_ERR_END_OF_FILE) 04116 { 04117 mi_extra(info,HA_EXTRA_NO_KEYREAD,0); 04118 my_free((char*) record, MYF(0)); 04119 mi_check_print_error(param,"%d when reading last record",my_errno); 04120 DBUG_VOID_RETURN; 04121 } 04122 if (!repair_only) 04123 info->s->state.auto_increment=param->auto_increment_value; 04124 } 04125 else 04126 { 04127 ulonglong auto_increment= retrieve_auto_increment(info, record); 04128 set_if_bigger(info->s->state.auto_increment,auto_increment); 04129 if (!repair_only) 04130 set_if_bigger(info->s->state.auto_increment, param->auto_increment_value); 04131 } 04132 mi_extra(info,HA_EXTRA_NO_KEYREAD,0); 04133 my_free((char*) record, MYF(0)); 04134 update_state_info(param, info, UPDATE_AUTO_INC); 04135 DBUG_VOID_RETURN; 04136 } 04137 04138 04139 /* 04140 Update statistics for each part of an index 04141 04142 SYNOPSIS 04143 update_key_parts() 04144 keyinfo IN Index information (only key->keysegs used) 04145 rec_per_key_part OUT Store statistics here 04146 unique IN Array of (#distinct tuples) 04147 notnull_tuples IN Array of (#tuples), or NULL 04148 records Number of records in the table 04149 04150 DESCRIPTION 04151 This function is called produce index statistics values from unique and 04152 notnull_tuples arrays after these arrays were produced with sequential 04153 index scan (the scan is done in two places: chk_index() and 04154 sort_key_write()). 04155 04156 This function handles all 3 index statistics collection methods. 04157 04158 Unique is an array: 04159 unique[0]= (#different values of {keypart1}) - 1 04160 unique[1]= (#different values of {keypart1,keypart2} tuple)-unique[0]-1 04161 ... 04162 04163 For MI_STATS_METHOD_IGNORE_NULLS method, notnull_tuples is an array too: 04164 notnull_tuples[0]= (#of {keypart1} tuples such that keypart1 is not NULL) 04165 notnull_tuples[1]= (#of {keypart1,keypart2} tuples such that all 04166 keypart{i} are not NULL) 04167 ... 04168 For all other statistics collection methods notnull_tuples==NULL. 04169 04170 Output is an array: 04171 rec_per_key_part[k] = 04172 = E(#records in the table such that keypart_1=c_1 AND ... AND 04173 keypart_k=c_k for arbitrary constants c_1 ... c_k) 04174 04175 = {assuming that values have uniform distribution and index contains all 04176 tuples from the domain (or that {c_1, ..., c_k} tuple is choosen from 04177 index tuples} 04178 04179 = #tuples-in-the-index / #distinct-tuples-in-the-index. 04180 04181 The #tuples-in-the-index and #distinct-tuples-in-the-index have different 04182 meaning depending on which statistics collection method is used: 04183 04184 MI_STATS_METHOD_* how are nulls compared? which tuples are counted? 04185 NULLS_EQUAL NULL == NULL all tuples in table 04186 NULLS_NOT_EQUAL NULL != NULL all tuples in table 04187 IGNORE_NULLS n/a tuples that don't have NULLs 04188 */ 04189 04190 void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part, 04191 ulonglong *unique, ulonglong *notnull, 04192 ulonglong records) 04193 { 04194 ulonglong count=0,tmp, unique_tuples; 04195 ulonglong tuples= records; 04196 uint parts; 04197 for (parts=0 ; parts < keyinfo->keysegs ; parts++) 04198 { 04199 count+=unique[parts]; 04200 unique_tuples= count + 1; 04201 if (notnull) 04202 { 04203 tuples= notnull[parts]; 04204 /* 04205 #(unique_tuples not counting tuples with NULLs) = 04206 #(unique_tuples counting tuples with NULLs as different) - 04207 #(tuples with NULLs) 04208 */ 04209 unique_tuples -= (records - notnull[parts]); 04210 } 04211 04212 if (unique_tuples == 0) 04213 tmp= 1; 04214 else if (count == 0) 04215 tmp= tuples; /* 1 unique tuple */ 04216 else 04217 tmp= (tuples + unique_tuples/2) / unique_tuples; 04218 04219 /* 04220 for some weird keys (e.g. FULLTEXT) tmp can be <1 here. 04221 let's ensure it is not 04222 */ 04223 set_if_bigger(tmp,1); 04224 if (tmp >= (ulonglong) ~(ulong) 0) 04225 tmp=(ulonglong) ~(ulong) 0; 04226 04227 *rec_per_key_part=(ulong) tmp; 04228 rec_per_key_part++; 04229 } 04230 } 04231 04232 04233 static ha_checksum mi_byte_checksum(const byte *buf, uint length) 04234 { 04235 ha_checksum crc; 04236 const byte *end=buf+length; 04237 for (crc=0; buf != end; buf++) 04238 crc=((crc << 1) + *((uchar*) buf)) + 04239 test(crc & (((ha_checksum) 1) << (8*sizeof(ha_checksum)-1))); 04240 return crc; 04241 } 04242 04243 static my_bool mi_too_big_key_for_sort(MI_KEYDEF *key, ha_rows rows) 04244 { 04245 uint key_maxlength=key->maxlength; 04246 if (key->flag & HA_FULLTEXT) 04247 { 04248 uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT* 04249 key->seg->charset->mbmaxlen; 04250 key_maxlength+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN; 04251 } 04252 return (key->flag & HA_SPATIAL) || 04253 (key->flag & (HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY | HA_FULLTEXT) && 04254 ((ulonglong) rows * key_maxlength > 04255 (ulonglong) myisam_max_temp_length)); 04256 } 04257 04258 /* 04259 Deactivate all not unique index that can be recreated fast 04260 These include packed keys on which sorting will use more temporary 04261 space than the max allowed file length or for which the unpacked keys 04262 will take much more space than packed keys. 04263 Note that 'rows' may be zero for the case when we don't know how many 04264 rows we will put into the file. 04265 */ 04266 04267 void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows) 04268 { 04269 MYISAM_SHARE *share=info->s; 04270 MI_KEYDEF *key=share->keyinfo; 04271 uint i; 04272 04273 DBUG_ASSERT(info->state->records == 0 && 04274 (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES)); 04275 for (i=0 ; i < share->base.keys ; i++,key++) 04276 { 04277 if (!(key->flag & (HA_NOSAME | HA_SPATIAL | HA_AUTO_KEY)) && 04278 ! mi_too_big_key_for_sort(key,rows) && info->s->base.auto_key != i+1) 04279 { 04280 mi_clear_key_active(share->state.key_map, i); 04281 info->update|= HA_STATE_CHANGED; 04282 } 04283 } 04284 } 04285 04286 04287 /* 04288 Return TRUE if we can use repair by sorting 04289 One can set the force argument to force to use sorting 04290 even if the temporary file would be quite big! 04291 */ 04292 04293 my_bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows, 04294 ulonglong key_map, my_bool force) 04295 { 04296 MYISAM_SHARE *share=info->s; 04297 MI_KEYDEF *key=share->keyinfo; 04298 uint i; 04299 04300 /* 04301 mi_repair_by_sort only works if we have at least one key. If we don't 04302 have any keys, we should use the normal repair. 04303 */ 04304 if (! mi_is_any_key_active(key_map)) 04305 return FALSE; /* Can't use sort */ 04306 for (i=0 ; i < share->base.keys ; i++,key++) 04307 { 04308 if (!force && mi_too_big_key_for_sort(key,rows)) 04309 return FALSE; 04310 } 04311 return TRUE; 04312 } 04313 04314 04315 static void 04316 set_data_file_type(SORT_INFO *sort_info, MYISAM_SHARE *share) 04317 { 04318 if ((sort_info->new_data_file_type=share->data_file_type) == 04319 COMPRESSED_RECORD && sort_info->param->testflag & T_UNPACK) 04320 { 04321 MYISAM_SHARE tmp; 04322 04323 if (share->options & HA_OPTION_PACK_RECORD) 04324 sort_info->new_data_file_type = DYNAMIC_RECORD; 04325 else 04326 sort_info->new_data_file_type = STATIC_RECORD; 04327 04328 /* Set delete_function for sort_delete_record() */ 04329 memcpy((char*) &tmp, share, sizeof(*share)); 04330 tmp.options= ~HA_OPTION_COMPRESS_RECORD; 04331 mi_setup_functions(&tmp); 04332 share->delete_record=tmp.delete_record; 04333 } 04334 }
1.4.7

