YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense alongwiththisprogram;ifnot,writetotheFreeSoftware
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
void myisamchk_init(HA_CHECK *param)
{
bzero((uchar*) param,sizeof(*param)); /* Set all params that are not 0 */
param->opt_follow_links=1;
param->keys_in_use= ~(ulonglong) 0;
param->search_after_block=HA_OFFSET_ERROR;
param->use_buffers= KEY_BUFFER_INIT;
param->read_buffer_length=READ_BUFFER_INIT;
param->write_buffer_length=READ_BUFFER_INIT;
param->sort_buffer_length=SORT_BUFFER_INIT;
param->sort_key_blocks=BUFFERS_WHEN_SORTING;
param->tmpfile_createflag=O_RDWR | O_TRUNC | O_EXCL;
param->myf_rw=MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL);
param->max_record_length= LONGLONG_MAX;
param->key_cache_block_size= KEY_CACHE_BLOCK_SIZE;
param->stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
param->need_print_msg_lock= 0;
}
/* Check the status flags for the table */
int chk_status(HA_CHECK *param, register MI_INFO *info)
{
MYISAM_SHARE *share=info->s;
/* Protection for HA_EXTRA_FLUSH */
mysql_mutex_lock(&share->intern_lock);
if (mi_is_crashed_on_repair(info))
mi_check_print_warning(param, "Table is marked as crashed and last repair failed"); elseif (mi_is_crashed(info))
mi_check_print_warning(param, "Table is marked as crashed"); if (share->state.open_count != (uint) (info->s->global_changed ? 1 : 0))
{ /* Don't count this as a real warning, as check can correct this ! */
my_bool save=param->warning_printed;
mi_check_print_warning(param,
share->state.open_count==1 ? "%d client is using or hasn't closed the table properly" : "%d clients are using or haven't closed the table properly",
share->state.open_count); /* If this will be fixed by the check, forget the warning */ if (param->testflag & T_UPDATE_STATE)
param->warning_printed=save;
}
mysql_mutex_unlock(&share->intern_lock); return0;
}
int chk_size(HA_CHECK *param, register MI_INFO *info)
{ int error=0; register my_off_t skr,size; char buff[22],buff2[22];
DBUG_ENTER("chk_size");
if (!(param->testflag & T_SILENT)) puts("- check file-size");
/* The following is needed if called externally (not from myisamchk) */
flush_key_blocks(info->s->key_cache,
info->s->kfile, &info->s->dirty_part_map,
FLUSH_FORCE_WRITE);
size= mysql_file_seek(info->s->kfile, 0L, MY_SEEK_END, MYF(MY_THREADSAFE)); if ((skr=(my_off_t) info->state->key_file_length) != size)
{ /* Don't give error if file generated by myisampack */ if (skr > size && mi_is_any_key_active(info->s->state.key_map))
{
error=1;
mi_check_print_error(param, "Size of indexfile is: %-8s Should be: %s",
llstr(size,buff), llstr(skr,buff2));
} else
mi_check_print_warning(param, "Size of indexfile is: %-8s Should be: %s",
llstr(size,buff), llstr(skr,buff2));
} if (!(param->testflag & T_VERY_SILENT) &&
! (info->s->options & HA_OPTION_COMPRESS_RECORD) &&
ulonglong2double(info->state->key_file_length) >
ulonglong2double(info->s->base.margin_key_file_length)*0.9)
mi_check_print_warning(param,"Keyfile is almost full, %10s of %10s used",
llstr(info->state->key_file_length,buff),
llstr(info->s->base.max_key_file_length-1,buff));
size= mysql_file_seek(info->dfile, 0L, MY_SEEK_END, MYF(0));
skr=(my_off_t) info->state->data_file_length; if (info->s->options & HA_OPTION_COMPRESS_RECORD)
skr+= MEMMAP_EXTRA_MARGIN; #ifdef USE_RELOC if (info->data_file_type == STATIC_RECORD &&
skr < (my_off_t) info->s->base.reloc*info->s->base.min_pack_length)
skr=(my_off_t) info->s->base.reloc*info->s->base.min_pack_length; #endif if (skr != size)
{
info->state->data_file_length=size; /* Skip other errors */ if (skr > size && skr != size + MEMMAP_EXTRA_MARGIN)
{
error=1;
mi_check_print_error(param,"Size of datafile is: %-9s Should be: %s",
llstr(size,buff), llstr(skr,buff2));
param->testflag|=T_RETRY_WITHOUT_QUICK;
} else
{
mi_check_print_warning(param, "Size of datafile is: %-9s Should be: %s",
llstr(size,buff), llstr(skr,buff2));
}
} if (!(param->testflag & T_VERY_SILENT) &&
!(info->s->options & HA_OPTION_COMPRESS_RECORD) &&
ulonglong2double(info->state->data_file_length) >
(ulonglong2double(info->s->base.max_data_file_length)*0.9))
mi_check_print_warning(param, "Datafile is almost full, %10s of %10s used",
llstr(info->state->data_file_length,buff),
llstr(info->s->base.max_data_file_length-1,buff2));
DBUG_RETURN(error);
} /* chk_size */
if ((!(param->testflag & T_SILENT)))
printf ("- check data record references index: %d\n",key+1); if (keyinfo->key_alg > HA_KEY_ALG_BTREE)
full_text_keys++; if (share->state.key_root[key] == HA_OFFSET_ERROR &&
(info->state->records == 0 || keyinfo->key_alg == HA_KEY_ALG_FULLTEXT)) goto do_stat; if (!_mi_fetch_keypage(info,keyinfo,share->state.key_root[key],
DFLT_INIT_HITS,info->buff,0))
{
mi_check_print_error(param,"Can't read indexpage from filepos: %s",
llstr(share->state.key_root[key],buff)); if (!(param->testflag & T_INFO))
DBUG_RETURN(-1);
result= -1; continue;
}
param->key_file_blocks+=keyinfo->block_length;
keys=0;
param->keydata=param->totaldata=0;
param->key_blocks=0;
param->max_level=0; if (chk_index(param,info,keyinfo,share->state.key_root[key],info->buff,
&keys, param->key_crc+key,1))
DBUG_RETURN(-1); if ((param->testflag & T_WRITE_LOOP) && param->verbose)
{
puts(" \r");
fflush(stdout);
} if (keyinfo->key_alg <= HA_KEY_ALG_BTREE)
{ if (keys != info->state->records)
{
mi_check_print_error(param,"Found %s keys of %s",llstr(keys,buff),
llstr(info->state->records,buff2)); if (!(param->testflag & T_INFO))
DBUG_RETURN(-1);
result= -1; continue;
} if (found_keys - full_text_keys == 1 &&
((share->options &
(HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
(param->testflag & T_DONT_CHECK_CHECKSUM)))
old_record_checksum=param->record_checksum; elseif (old_record_checksum != param->record_checksum)
{ if (key)
mi_check_print_error(param,"Key %u doesn't point at same records that key 1",
key+1); else
mi_check_print_error(param,"Key 1 doesn't point at all records"); if (!(param->testflag & T_INFO))
DBUG_RETURN(-1);
result= -1; continue;
}
} if ((uint) share->base.auto_key -1 == key)
{ /* Check that auto_increment key is bigger than max key value */
ulonglong auto_increment;
info->lastinx=key;
_mi_read_key_record(info, 0L, info->rec_buff);
auto_increment= retrieve_auto_increment(info, info->rec_buff); if (auto_increment > info->s->state.auto_increment)
{
mi_check_print_warning(param, "Auto-increment value: %s is smaller " "than max used value: %s",
llstr(info->s->state.auto_increment,buff2),
llstr(auto_increment, buff));
} if (param->testflag & T_AUTO_INC)
{
set_if_bigger(info->s->state.auto_increment,
auto_increment);
set_if_bigger(info->s->state.auto_increment,
param->auto_increment_value);
}
/* Check that there isn't a row with auto_increment = 0 in the table */
mi_extra(info,HA_EXTRA_KEYREAD,0);
bzero(info->lastkey,keyinfo->seg->length); if (!mi_rkey(info, info->rec_buff, key, (const uchar*) info->lastkey,
(key_part_map)1, HA_READ_KEY_EXACT))
{ /* Don't count this as a real warning, as myisamchk can't correct it */
my_bool save=param->warning_printed;
mi_check_print_warning(param, "Found row where the auto_increment " "column has the value 0");
param->warning_printed=save;
}
mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
}
Ifwewanttorecreatedisabledindexesonly_and_allofthese indexesareempty,wedon'tneedtorecreatetheexistingindexes.
*/ if (!force && (param->testflag & T_CREATE_MISSING_KEYS))
{
DBUG_PRINT("repair", ("creating missing indexes")); for (i= 0; i < share->base.keys; i++)
{
DBUG_PRINT("repair", ("index #: %u key_root: 0x%lx active: %d",
i, (long) state->key_root[i],
mi_is_key_active(state->key_map, i))); if ((state->key_root[i] != HA_OFFSET_ERROR) &&
!mi_is_key_active(state->key_map, i))
{ /* Thisindexhasatleastonekeyblockanditisdisabled. Wewouldloseitsblock(s)ifwouldjustrecreateit. Soweneedtodropandrecreateallindexes.
*/
DBUG_PRINT("repair", ("nonempty and disabled: recreate all")); break;
}
} if (i >= share->base.keys)
{ /* Allofthedisabledindexesareempty.Wecanjustrecreatethem. Flushdirtyblocksofthisindexfilefromkeycacheandremove allblocksofthisindexfilefromkeycache.
*/
DBUG_PRINT("repair", ("all disabled are empty: create missing"));
error= flush_key_blocks(share->key_cache, share->kfile,
&share->dirty_part_map,
FLUSH_FORCE_WRITE); goto end;
} /* Wedonowdropallindexesanddeclarethemdisabled.Withthe T_CREATE_MISSING_KEYSflag,mi_repair*()willrecreateall disabledindexesandenablethem.
*/
mi_clear_all_keys_active(state->key_map);
DBUG_PRINT("repair", ("declared all indexes disabled"));
}
/* Remove all key blocks of this index file from key cache. */ if ((error= flush_key_blocks(share->key_cache, share->kfile,
&share->dirty_part_map,
FLUSH_IGNORE_CHANGED))) goto end; /* purecov: inspected */
/* Clear index root block pointers. */ for (i= 0; i < share->base.keys; i++)
state->key_root[i]= HA_OFFSET_ERROR;
/* Clear the delete chains. */ for (i= 0; i < state->header.max_block_size_index; i++)
state->key_del[i]= HA_OFFSET_ERROR;
/* Reset index file length to end of index file header. */
info->state->key_file_length= share->base.keystart;
DBUG_PRINT("repair", ("dropped all indexes")); /* error= 0; set by last (error= flush_key_bocks()). */
end:
DBUG_RETURN(error);
}
/* Recover old table by reading each record and writing all keys */ /* Save new datafile-name in temp_filename */
int mi_repair(HA_CHECK *param, register MI_INFO *info, char * name, int rep_quick)
{ int error,got_error;
ha_rows start_records,new_header_length;
my_off_t del;
File new_file;
MYISAM_SHARE *share=info->s; char llbuff[22],llbuff2[22];
MI_SORT_INFO sort_info;
MI_SORT_PARAM sort_param;
DBUG_ENTER("mi_repair");
/* This function always recreates all enabled indexes. */ if (param->testflag & T_CREATE_MISSING_KEYS)
mi_set_all_keys_active(share->state.key_map, share->base.keys);
mi_drop_all_indexes(param, info, TRUE);
lock_memory(param); /* Everything is alloced */
/* Re-create all keys, which are set in key_map. */ while (!(error=sort_get_next_record(&sort_param)))
{ if (writekeys(&sort_param))
{ if (my_errno != HA_ERR_FOUND_DUPP_KEY) goto err;
DBUG_DUMP("record",(uchar*) sort_param.record,share->base.pack_reclength);
mi_check_print_info(param,"Duplicate key %2d for record at %10s against new record at %10s",
info->errkey+1,
llstr(sort_param.start_recpos,llbuff),
llstr(info->dupp_key_pos,llbuff2)); if (param->testflag & T_VERBOSE)
{
(void) _mi_make_key(info,(uint) info->errkey,info->lastkey,
sort_param.record,0L);
_mi_print_key(stdout,share->keyinfo[info->errkey].seg,info->lastkey,
USE_WHOLE_KEY);
}
sort_info.dupp++; if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
{
param->testflag|=T_RETRY_WITHOUT_QUICK;
param->error_printed=1; goto err;
} continue;
} if (sort_write_record(&sort_param)) goto err;
} if (error > 0 || write_data_suffix(&sort_info, (my_bool)!rep_quick) ||
flush_io_cache(&info->rec_cache) || param->read_cache.error < 0) goto err;
if (rep_quick && del+sort_info.dupp != info->state->del)
{
mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
mi_check_print_error(param,"Run recovery again without --quick");
got_error=1;
param->retry_repair=1;
param->testflag|=T_RETRY_WITHOUT_QUICK; goto err;
} if (param->testflag & T_SAFE_REPAIR)
{ /* Don't repair if we loosed more than one row */ if (info->state->records+1 < start_records)
{
info->state->records=start_records;
got_error=1; goto err;
}
}
if (!rep_quick)
{
mysql_file_close(info->dfile, MYF(0));
info->dfile=new_file;
info->state->data_file_length=sort_param.filepos;
share->state.version=(ulong) time((time_t*) 0); /* Force reopen */
} else
{
info->state->data_file_length=sort_param.max_pos;
} if (param->testflag & T_CALC_CHECKSUM)
info->state->checksum=param->glob_crc;
if (!(param->testflag & T_SILENT))
{ if (start_records != info->state->records)
printf("Data records: %s\n", llstr(info->state->records,llbuff)); if (sort_info.dupp)
mi_check_print_warning(param, "%s records have been removed",
llstr(sort_info.dupp,llbuff));
}
got_error=0; /* If invoked by external program that uses thr_lock */ if (&share->state.state != info->state)
memcpy( &share->state.state, info->state, sizeof(*info->state));
err: if (!got_error)
{ /* Replace the actual file with the temporary file */ if (new_file >= 0)
{
got_error= replace_data_file(param, info, new_file);
new_file= -1;
param->retry_repair= 0;
}
} if (got_error)
{ if (! param->error_printed)
mi_check_print_error(param,"%d for record at pos %s",my_errno,
llstr(sort_param.start_recpos,llbuff)); if (new_file >= 0)
{
(void) mysql_file_close(new_file, MYF(0));
(void) mysql_file_delete(mi_key_file_datatmp,
param->temp_filename, MYF(MY_WME));
info->rec_cache.file=-1; /* don't flush data to new_file, it's closed */
}
mi_mark_crashed_on_repair(info);
}
my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff));
my_free(mi_get_rec_buff_ptr(info, sort_param.record));
my_free(sort_info.buff);
(void) end_io_cache(¶m->read_cache);
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
(void) end_io_cache(&info->rec_cache);
got_error|=flush_blocks(param, share->key_cache, share->kfile,
&share->dirty_part_map); if (!got_error && param->testflag & T_UNPACK)
{
share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
share->pack.header_length=0;
share->data_file_type=sort_info.new_data_file_type;
}
share->state.changed|= (STATE_NOT_OPTIMIZED_KEYS | STATE_NOT_SORTED_PAGES |
STATE_NOT_ANALYZED);
DBUG_RETURN(got_error);
}
key=info->lastkey+info->s->base.max_key_length; for (i=0 ; i < info->s->base.keys ; i++)
{ if (mi_is_key_active(info->s->state.key_map, i))
{ if (info->s->keyinfo[i].key_alg == HA_KEY_ALG_FULLTEXT)
{ if (_mi_ft_add(info, i, key, buff, filepos)) goto err;
} elseif (info->s->keyinfo[i].key_alg == HA_KEY_ALG_RTREE)
{
uint key_length=_mi_make_key(info,i,key,buff,filepos); if (rtree_insert(info, i, key, key_length)) goto err;
} else
{
uint key_length=_mi_make_key(info,i,key,buff,filepos); if (_mi_ck_write(info,i,key,key_length)) goto err;
}
}
}
DBUG_RETURN(0);
err: if (my_errno == HA_ERR_FOUND_DUPP_KEY)
{
info->errkey=(int) i; /* This key was found */ while ( i-- > 0 )
{ if (mi_is_key_active(info->s->state.key_map, i))
{ if (info->s->keyinfo[i].key_alg == HA_KEY_ALG_FULLTEXT)
{ if (_mi_ft_del(info,i, key,buff,filepos)) break;
} else
{
uint key_length=_mi_make_key(info,i,key,buff,filepos); if (_mi_ck_delete(info,i,key,key_length)) break;
}
}
}
} /* Remove checksum that was added to glob_crc in sort_get_next_record */ if (sort_param->calc_checksum)
sort_param->sort_info->param->glob_crc-= info->checksum;
DBUG_PRINT("error",("errno: %d",my_errno));
DBUG_RETURN(-1);
} /* writekeys */
/* Change all key-pointers that points to a records */
/* cannot sort index files with R-tree indexes */ for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
key++,keyinfo++) if (keyinfo->key_alg == HA_KEY_ALG_RTREE)
DBUG_RETURN(0);
if (!(param->testflag & T_SILENT))
printf("- Sorting index for MyISAM-table '%s'\n",name);
/* Get real path for index file */
fn_format(param->temp_filename,name,"", MI_NAME_IEXT,2+4+32); if ((new_file= mysql_file_create(mi_key_file_datatmp,
fn_format(param->temp_filename,
param->temp_filename, "", INDEX_TMP_EXT, 2+4), 0, param->tmpfile_createflag, MYF(0))) < 0)
{
mi_check_print_error(param,"Can't create new tempfile: '%s'",
param->temp_filename);
DBUG_RETURN(-1);
} if (filecopy(param, new_file,share->kfile,0L,
(ulong) share->base.keystart, "headerblock")) goto err;
param->new_file_pos=share->base.keystart; for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
key++,keyinfo++)
{ if (mi_is_key_active(info->s->state.key_map, key) &&
share->state.key_root[key] != HA_OFFSET_ERROR)
{
index_pos[key]=param->new_file_pos; /* Write first block here */ if (sort_one_index(param,info,keyinfo,share->state.key_root[key],
new_file)) goto err;
} else
index_pos[key]= HA_OFFSET_ERROR; /* No blocks */
}
/* Flush key cache for this file if we are calling this outside myisamchk */
flush_key_blocks(share->key_cache, share->kfile, &share->dirty_part_map,
FLUSH_IGNORE_CHANGED);
share->state.version=(ulong) time((time_t*) 0);
old_state= share->state; /* save state if not stored */
r_locks= share->r_locks;
w_locks= share->w_locks;
old_lock= info->lock_type;
/* Put same locks as old file */
share->r_locks= share->w_locks= share->tot_locks= 0;
(void) _mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE);
(void) mysql_file_close(share->kfile, MYF(MY_WME));
share->kfile = -1;
(void) mysql_file_close(new_file, MYF(MY_WME)); if (change_to_newfile(share->index_file_name,MI_NAME_IEXT,INDEX_TMP_EXT, 0, MYF(0)) ||
mi_open_keyfile(share)) goto err2;
info->lock_type= F_UNLCK; /* Force mi_readinfo to lock */
_mi_readinfo(info,F_WRLCK,0); /* Will lock the table */
info->lock_type= old_lock;
share->r_locks= r_locks;
share->w_locks= w_locks;
share->tot_locks= r_locks+w_locks;
share->state= old_state; /* Restore old state */
/* cannot walk over R-tree indices */
DBUG_ASSERT(keyinfo->key_alg != HA_KEY_ALG_RTREE);
new_page_pos=param->new_file_pos;
param->new_file_pos+=keyinfo->block_length;
if (!(buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
{
mi_check_print_error(param,"Not enough memory for key block");
DBUG_RETURN(-1);
} if (!_mi_fetch_keypage(info,keyinfo,pagepos,DFLT_INIT_HITS,buff,0))
{
mi_check_print_error(param,"Can't read key block from filepos: %s",
llstr(pagepos,llbuff)); goto err;
} if ((nod_flag=mi_test_if_nod(buff)) || keyinfo->key_alg == HA_KEY_ALG_FULLTEXT)
{
used_length=mi_getint(buff);
keypos=buff+2+nod_flag;
endpos=buff+used_length; for ( ;; )
{ if (nod_flag)
{
next_page=_mi_kpos(nod_flag,keypos);
_mi_kpointer(info,keypos-nod_flag,param->new_file_pos); /* Save new pos */ if (sort_one_index(param,info,keyinfo,next_page,new_file))
{
DBUG_PRINT("error",
("From page: %ld, keyoffset: %lu used_length: %d",
(ulong) pagepos, (ulong) (keypos - buff),
(int) used_length));
DBUG_DUMP("buff",(uchar*) buff,used_length); goto err;
}
} if (keypos >= endpos ||
(key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0) break;
DBUG_ASSERT(keypos <= endpos); if (keyinfo->key_alg == HA_KEY_ALG_FULLTEXT)
{
uint off; int subkeys;
get_key_full_length_rdonly(off, key);
subkeys=ft_sintXkorr(key+off); if (subkeys < 0)
{
next_page= _mi_dpos(info,0,key+key_length);
_mi_dpointer(info,keypos-nod_flag-info->s->rec_reflength,
param->new_file_pos); /* Save new pos */ if (sort_one_index(param,info,&info->s->ft2_keyinfo,
next_page,new_file)) goto err;
}
}
}
}
/* Fill block with zero and write it to the new index file */
length=mi_getint(buff);
bzero((uchar*) buff+length,keyinfo->block_length-length); if (mysql_file_pwrite(new_file, (uchar*) buff, (uint) keyinfo->block_length,
new_page_pos, MYF(MY_NABP | MY_WAIT_IF_FULL)))
{
mi_check_print_error(param,"Can't write indexblock, error: %d",my_errno); goto err;
}
my_afree((uchar*) buff);
DBUG_RETURN(0);
err:
my_afree((uchar*) buff);
DBUG_RETURN(1);
} /* sort_one_index */
/* Optionally drop indexes and optionally modify the key_map. */
mi_drop_all_indexes(param, info, FALSE);
key_map= share->state.key_map; if (param->testflag & T_CREATE_MISSING_KEYS)
{ /* Invert the copied key_map to recreate all disabled indexes. */
key_map= ~key_map;
}
if (_create_index_by_sort(&sort_param,
(my_bool) (!(param->testflag & T_VERBOSE)),
param->sort_buffer_length))
{ if ((param->testflag & T_CREATE_UNIQUE_BY_SORT) && sort_param.sort_info->dupp)
share->state.dupp_key= sort_param.key; else
param->retry_repair= 1; if (! param->error_printed)
mi_check_print_error(param, "Couldn't fix table with create_index_by_sort(). Error: %d",
my_errno); goto err;
} /* No need to calculate checksum again. */
sort_param.calc_checksum= 0;
free_root(&sort_param.wordroot, MYF(0));
/* Set for next loop */
sort_info.max_records= (ha_rows) info->state->records;
if (param->testflag & T_STATISTICS)
update_key_parts(sort_param.keyinfo, rec_per_key_part, sort_param.unique,
param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
sort_param.notnull: NULL,
(ulonglong) info->state->records); /* Enable this index in the permanent (not the copied) key_map. */
mi_set_key_active(share->state.key_map, sort_param.key);
DBUG_PRINT("repair", ("set enabled index #: %u", sort_param.key));
if (sort_param.fix_datafile)
{
param->read_cache.end_of_file=sort_param.filepos; if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache)) goto err; if (param->testflag & T_SAFE_REPAIR)
{ /* Don't repair if we loosed more than one row */ if (info->state->records+1 < start_records)
{
mi_check_print_error(param, "Couldn't fix table as SAFE_REPAIR was requested and we would loose too many rows. %s -> %s",
llstr(start_records, llbuff), llstr(info->state->records, llbuff2));
info->state->records= start_records; goto err;
}
}
share->state.state.data_file_length = info->state->data_file_length=
sort_param.filepos; /* Only whole records */
share->state.version=(ulong) time((time_t*) 0);
mysql_file_close(info->dfile, MYF(0));
info->dfile=new_file;
share->data_file_type=sort_info.new_data_file_type;
share->pack.header_length=(ulong) new_header_length;
sort_param.fix_datafile=0;
} else
info->state->data_file_length=sort_param.max_pos;
if (rep_quick && del+sort_info.dupp != info->state->del)
{
mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
mi_check_print_error(param,"Run recovery again without --quick");
got_error=1;
param->retry_repair=1;
param->testflag|=T_RETRY_WITHOUT_QUICK; goto err;
}
if (rep_quick && (param->testflag & T_FORCE_UNIQUENESS))
{
my_off_t skr=info->state->data_file_length+
(share->options & HA_OPTION_COMPRESS_RECORD ?
MEMMAP_EXTRA_MARGIN : 0); #ifdef USE_RELOC if (share->data_file_type == STATIC_RECORD &&
skr < share->base.reloc*share->base.min_pack_length)
skr=share->base.reloc*share->base.min_pack_length; #endif if (skr != sort_info.filelength) if (mysql_file_chsize(info->dfile, skr, 0, MYF(0)) > 0)
mi_check_print_warning(param, "Can't change size of datafile, error: %d",
my_errno);
} if (param->testflag & T_CALC_CHECKSUM)
info->state->checksum=param->glob_crc;
if (mysql_file_chsize(share->kfile, info->state->key_file_length, 0, MYF(0)) > 0)
mi_check_print_warning(param, "Can't change size of indexfile, error: %d",
my_errno);
if (!(param->testflag & T_SILENT))
{ if (start_records != info->state->records)
printf("Data records: %s\n", llstr(info->state->records,llbuff)); if (sort_info.dupp)
mi_check_print_warning(param, "%s records have been removed",
llstr(sort_info.dupp,llbuff));
}
got_error=0;
if (&share->state.state != info->state)
memcpy( &share->state.state, info->state, sizeof(*info->state));
err:
got_error|= flush_blocks(param, share->key_cache, share->kfile,
&share->dirty_part_map);
(void) end_io_cache(&info->rec_cache); if (!got_error)
{ /* Replace the actual file with the temporary file */ if (new_file >= 0)
{
got_error= replace_data_file(param, info, new_file);
new_file= -1;
}
} if (got_error)
{ if (! param->error_printed)
mi_check_print_error(param,"%d when fixing table",my_errno); if (new_file >= 0)
{
(void) mysql_file_close(new_file, MYF(0));
(void) mysql_file_delete(mi_key_file_datatmp,
param->temp_filename, MYF(MY_WME)); if (info->dfile == new_file) /* Retry with key cache */ if (unlikely(mi_open_datafile(info, share)))
param->retry_repair= 0; /* Safety */
}
mi_mark_crashed_on_repair(info); if (killed_ptr(param))
param->retry_repair= 0; /* No use to retry repair */
} elseif (key_map == share->state.key_map)
share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
share->state.changed|=STATE_NOT_SORTED_PAGES;
/* Optionally drop indexes and optionally modify the key_map. */
mi_drop_all_indexes(param, info, FALSE);
key_map= share->state.key_map; if (param->testflag & T_CREATE_MISSING_KEYS)
{ /* Invert the copied key_map to recreate all disabled indexes. */
key_map= ~key_map;
}
if ((error= mysql_thread_create(mi_key_thread_find_all_keys,
&sort_param[i].thr, &thr_attr,
thr_find_all_keys,
(void *) (sort_param+i))))
{
mi_check_print_error(param,"Cannot start a repair thread (errno= %d)",
error); /* Cleanup: Detach from the share. Avoid others to be blocked. */ if (io_share.total_threads)
remove_io_thread(&sort_param[i].read_cache);
DBUG_PRINT("error", ("Cannot start a repair thread"));
sort_info.got_error=1;
} else
sort_info.threads_running++;
}
(void) pthread_attr_destroy(&thr_attr);
/* waiting for all threads to finish */ while (sort_info.threads_running)
mysql_cond_wait(&sort_info.cond, &sort_info.mutex);
mysql_mutex_unlock(&sort_info.mutex);
if ((got_error= thr_write_keys(sort_param)))
{
param->retry_repair=1; goto err;
}
got_error=1; /* Assume the following may go wrong */
if (sort_param[0].fix_datafile)
{ /* Appendsomenullstotheendofamemorymappedfile.Destroythe writecache.Themasterthreaddidalreadydetachfromtheshare byremove_io_thread()insort.c:thr_find_all_keys().
*/ if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache)) goto err; if (param->testflag & T_SAFE_REPAIR)
{ /* Don't repair if we loosed more than one row */ if (info->state->records+1 < start_records)
{
info->state->records=start_records; goto err;
}
}
share->state.state.data_file_length= info->state->data_file_length=
sort_param->filepos; /* Only whole records */
share->state.version=(ulong) time((time_t*) 0);
if (rep_quick && del+sort_info.dupp != info->state->del)
{
mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
mi_check_print_error(param,"Run recovery again without -q");
param->retry_repair=1;
param->testflag|=T_RETRY_WITHOUT_QUICK; goto err;
}
if (rep_quick && (param->testflag & T_FORCE_UNIQUENESS))
{
my_off_t skr=info->state->data_file_length+
(share->options & HA_OPTION_COMPRESS_RECORD ?
MEMMAP_EXTRA_MARGIN : 0); #ifdef USE_RELOC if (share->data_file_type == STATIC_RECORD &&
skr < share->base.reloc*share->base.min_pack_length)
skr=share->base.reloc*share->base.min_pack_length; #endif if (skr != sort_info.filelength) if (mysql_file_chsize(info->dfile, skr, 0, MYF(0)) > 0)
mi_check_print_warning(param, "Can't change size of datafile, error: %d",
my_errno);
} if (param->testflag & T_CALC_CHECKSUM)
info->state->checksum=param->glob_crc;
if (mysql_file_chsize(share->kfile, info->state->key_file_length, 0, MYF(0)) > 0)
mi_check_print_warning(param, "Can't change size of indexfile, error: %d", my_errno);
if (!(param->testflag & T_SILENT))
{ if (start_records != info->state->records)
printf("Data records: %s\n", llstr(info->state->records,llbuff)); if (sort_info.dupp)
mi_check_print_warning(param, "%s records have been removed",
llstr(sort_info.dupp,llbuff));
}
got_error=0;
if (&share->state.state != info->state)
memcpy(&share->state.state, info->state, sizeof(*info->state));
err:
got_error|= flush_blocks(param, share->key_cache, share->kfile,
&share->dirty_part_map); /* Destroythewritecache.Themasterthreaddidalreadydetachfrom thesharebyremove_io_thread()oritwasnotyetstarted(ifthe errorhappenedbeforecreatingthethread).
*/
(void) end_io_cache(&info->rec_cache); /* Destroythenewdatacacheincaseofnon-quickrepair.Allslave threadsdideitherdetachfromthesharebyremove_io_thread() alreadyortheywerenotyetstarted(iftheerrorhappenedbefore creatingthethreads).
*/ if (!rep_quick && my_b_inited(&new_data_cache))
(void) end_io_cache(&new_data_cache); if (!got_error)
{ /* Replace the actual file with the temporary file */ if (new_file >= 0)
{
got_error= replace_data_file(param, info, new_file);
new_file= -1;
}
} if (got_error)
{ if (! param->error_printed)
mi_check_print_error(param,"%d when fixing table",my_errno); if (new_file >= 0)
{
(void) mysql_file_close(new_file, MYF(0));
(void) mysql_file_delete(mi_key_file_datatmp,
param->temp_filename, MYF(MY_WME)); if (info->dfile == new_file) /* Retry with key cache */ if (unlikely(mi_open_datafile(info, share)))
param->retry_repair= 0; /* Safety */
}
mi_mark_crashed_on_repair(info); if (killed_ptr(param))
param->retry_repair= 0;
} elseif (key_map == share->state.key_map)
share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
share->state.changed|=STATE_NOT_SORTED_PAGES;
if (killed_ptr(param))
{
mi_check_print_error(param, "Repair killed by user with cause: %d",
(int) killed_ptr(param));
param->retry_repair= 0;
DBUG_RETURN(1);
}
switch (share->data_file_type) { case STATIC_RECORD: for (;;)
{ if (my_b_read(&sort_param->read_cache,sort_param->record,
share->base.pack_reclength))
{ if (sort_param->read_cache.error)
param->out_flag |= O_DATA_LOST;
param->retry_repair=1;
param->testflag|=T_RETRY_WITHOUT_QUICK;
DBUG_RETURN(-1);
}
sort_param->start_recpos=sort_param->pos; if (!sort_param->fix_datafile)
{
sort_param->filepos=sort_param->pos; if (sort_param->master)
share->state.split++;
}
sort_param->max_pos=(sort_param->pos+=share->base.pack_reclength); if (*sort_param->record)
{ if (sort_param->calc_checksum)
info->checksum= (*info->s->calc_check_checksum)(info, sort_param->record); goto finish;
} if (!sort_param->fix_datafile && sort_param->master)
{
info->state->del++;
info->state->empty+=share->base.pack_reclength;
}
} case DYNAMIC_RECORD:
pos=sort_param->pos;
searching=(sort_param->fix_datafile && (param->testflag & T_EXTEND));
parallel_flag= (sort_param->read_cache.file < 0) ? READING_NEXT : 0; for (;;)
{
found_record=block_info.second_read= 0;
left_length=1; if (searching)
{
pos=MY_ALIGN(pos,MI_DYN_ALIGN_SIZE);
param->testflag|=T_RETRY_WITHOUT_QUICK;
sort_param->start_recpos=pos;
} do
{ if (pos > sort_param->max_pos)
sort_param->max_pos=pos; if (pos & (MI_DYN_ALIGN_SIZE-1))
{ if ((param->testflag & T_VERBOSE) || searching == 0)
mi_check_print_info(param,"Wrong aligned block at %s",
llstr(pos,llbuff)); if (searching) goto try_next;
} if (found_record && pos == param->search_after_block)
mi_check_print_info(param,"Block: %s used by record at %s",
llstr(param->search_after_block,llbuff),
llstr(sort_param->start_recpos,llbuff2)); if (_mi_read_cache(&sort_param->read_cache,
(uchar*) block_info.header,pos,
MI_BLOCK_INFO_HEADER_LENGTH,
(! found_record ? READING_NEXT : 0) |
parallel_flag | READING_HEADER))
{ if (found_record)
{
mi_check_print_info(param, "Can't read whole record at %s (errno: %d)",
llstr(sort_param->start_recpos,llbuff),errno); goto try_next;
}
DBUG_RETURN(-1);
} if (searching && ! sort_param->fix_datafile)
{
mi_check_print_info(param, "Datafile is corrupted; Restart repair with option to copy datafile");
param->error_printed=1;
param->retry_repair=1;
param->testflag|=T_RETRY_WITHOUT_QUICK;
my_errno= HA_ERR_WRONG_IN_RECORD;
DBUG_RETURN(1); /* Something wrong with data */
}
b_type=_mi_get_block_info(&block_info,-1,pos); if ((b_type & (BLOCK_ERROR | BLOCK_FATAL_ERROR)) ||
((b_type & BLOCK_FIRST) &&
(block_info.rec_len < (uint) share->base.min_pack_length ||
block_info.rec_len > (uint) share->base.max_pack_length)))
{
uint i; if (param->testflag & T_VERBOSE || searching == 0)
mi_check_print_info(param, "Wrong bytesec: %3d-%3d-%3d at %10s; Skipped",
block_info.header[0],block_info.header[1],
block_info.header[2],llstr(pos,llbuff)); if (found_record) goto try_next;
block_info.second_read=0;
searching=1; /* Search after block in read header string */ for (i=MI_DYN_ALIGN_SIZE ;
i < MI_BLOCK_INFO_HEADER_LENGTH ;
i+= MI_DYN_ALIGN_SIZE) if (block_info.header[i] >= 1 &&
block_info.header[i] <= MI_MAX_DYN_HEADER_BYTE) break;
pos+=(ulong) i;
sort_param->start_recpos=pos; continue;
} if (b_type & BLOCK_DELETED)
{
my_bool error=0; if (block_info.block_len+ (uint) (block_info.filepos-pos) <
share->base.min_block_length)
{ if (!searching)
mi_check_print_info(param, "Deleted block with impossible length %lu at %s",
block_info.block_len,llstr(pos,llbuff));
error=1;
} else
{ if ((block_info.next_filepos != HA_OFFSET_ERROR &&
block_info.next_filepos >=
info->state->data_file_length) ||
(block_info.prev_filepos != HA_OFFSET_ERROR &&
block_info.prev_filepos >= info->state->data_file_length))
{ if (!searching)
mi_check_print_info(param, "Delete link points outside datafile at %s",
llstr(pos,llbuff));
error=1;
}
} if (error)
{
DBUG_ASSERT(param->note_printed); if (found_record) goto try_next;
searching=1;
pos+= MI_DYN_ALIGN_SIZE;
sort_param->start_recpos=pos;
block_info.second_read=0; continue;
}
} else
{ if (block_info.block_len+ (uint) (block_info.filepos-pos) <
share->base.min_block_length ||
block_info.block_len > (uint) share->base.max_pack_length+
MI_SPLIT_LENGTH)
{ if (!searching)
mi_check_print_info(param, "Found block with impossible length %lu at %s; Skipped",
block_info.block_len + (unsignedlong) (block_info.filepos-pos),
llstr(pos, llbuff)); if (found_record) goto try_next;
searching=1;
pos+= MI_DYN_ALIGN_SIZE;
sort_param->start_recpos=pos;
block_info.second_read=0; continue;
}
} if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
{ if (!sort_param->fix_datafile && sort_param->master &&
(b_type & BLOCK_DELETED))
{
info->state->empty+=block_info.block_len;
info->state->del++;
share->state.split++;
} if (found_record)
{
mi_check_print_info(param, "Found row block followed by deleted block"); goto try_next;
} if (searching)
{
pos+=MI_DYN_ALIGN_SIZE;
sort_param->start_recpos=pos;
} else
pos=block_info.filepos+block_info.block_len;
block_info.second_read=0; continue;
}
if (!sort_param->fix_datafile && sort_param->master)
share->state.split++; if (! found_record++)
{
sort_param->find_length=left_length=block_info.rec_len;
sort_param->start_recpos=pos; if (!sort_param->fix_datafile)
sort_param->filepos=sort_param->start_recpos; if (sort_param->fix_datafile && (param->testflag & T_EXTEND))
sort_param->pos=block_info.filepos+1; else
sort_param->pos=block_info.filepos+block_info.block_len; if (share->base.blobs)
{ if (!(to=mi_alloc_rec_buff(info,block_info.rec_len,
&(sort_param->rec_buff))))
{ if (param->max_record_length >= block_info.rec_len)
{
mi_check_print_error(param,"Not enough memory for blob at %s (need %lu)",
llstr(sort_param->start_recpos,llbuff),
(ulong) block_info.rec_len);
DBUG_ASSERT(param->error_printed);
DBUG_RETURN(1);
} else
{
mi_check_print_info(param,"Not enough memory for blob at %s (need %lu); Row skipped",
llstr(sort_param->start_recpos,llbuff),
(ulong) block_info.rec_len); goto try_next;
}
}
} else
to= sort_param->rec_buff;
} if (left_length < block_info.data_len || ! block_info.data_len)
{
mi_check_print_info(param, "Found block with too small length at %s; Skipped",
llstr(sort_param->start_recpos,llbuff)); goto try_next;
} if (block_info.filepos + block_info.data_len >
sort_param->read_cache.end_of_file)
{
mi_check_print_info(param, "Found block that points outside data file at %s",
llstr(sort_param->start_recpos,llbuff)); goto try_next;
} /* Copyinformationthatisalreadyread.Avoidaccessingdata belowthecachestart.Thiscouldhappeniftheheader strechedovertheendofthepreviousbuffercontents.
*/
{
uint header_len= (uint) (block_info.filepos - pos);
uint prefetch_len= (MI_BLOCK_INFO_HEADER_LENGTH - header_len);
if (prefetch_len > block_info.data_len)
prefetch_len= block_info.data_len; if (prefetch_len)
{
memcpy(to, block_info.header + header_len, prefetch_len);
block_info.filepos+= prefetch_len;
block_info.data_len-= prefetch_len;
left_length-= prefetch_len;
to+= prefetch_len;
}
} if (block_info.data_len &&
_mi_read_cache(&sort_param->read_cache,to,block_info.filepos,
block_info.data_len,
(found_record == 1 ? READING_NEXT : 0) |
parallel_flag))
{
mi_check_print_info(param, "Read error for block at: %s (error: %d); Skipped",
llstr(block_info.filepos,llbuff),my_errno); goto try_next;
}
left_length-=block_info.data_len;
to+=block_info.data_len;
pos=block_info.next_filepos; if (pos == HA_OFFSET_ERROR && left_length)
{
mi_check_print_info(param,"Wrong block with wrong total length starting at %s",
llstr(sort_param->start_recpos,llbuff)); goto try_next;
} if (pos + MI_BLOCK_INFO_HEADER_LENGTH > sort_param->read_cache.end_of_file)
{
mi_check_print_info(param,"Found link that points at %s (outside data file) at %s",
llstr(pos,llbuff2),
llstr(sort_param->start_recpos,llbuff)); goto try_next;
}
} while (left_length);
if (_mi_rec_unpack(info,sort_param->record,sort_param->rec_buff,
sort_param->find_length) != MY_FILE_ERROR)
{ if (sort_param->calc_checksum)
info->checksum= (*info->s->calc_check_checksum)(info,
sort_param->record); if ((param->testflag & (T_EXTEND | T_REP_ANY)) || searching)
{ if (_mi_rec_check(info, sort_param->record, sort_param->rec_buff,
sort_param->find_length,
(param->testflag & T_QUICK) &&
sort_param->calc_checksum &&
MY_TEST(info->s->calc_checksum)))
{
mi_check_print_info(param,"Found wrong packed record at %s",
llstr(sort_param->start_recpos,llbuff)); goto try_next;
}
} goto finish;
} if (!searching)
mi_check_print_info(param,"Key %d - Found wrong stored record at %s",
sort_param->key+1,
llstr(sort_param->start_recpos,llbuff));
try_next:
DBUG_ASSERT(param->error_printed || param->note_printed);
pos=(sort_param->start_recpos+=MI_DYN_ALIGN_SIZE);
searching=1;
} case COMPRESSED_RECORD: for (searching=0 ;; searching=1, sort_param->pos++)
{ if (_mi_read_cache(&sort_param->read_cache,(uchar*) block_info.header,
sort_param->pos,
share->pack.ref_length,READING_NEXT))
DBUG_RETURN(-1); if (searching && ! sort_param->fix_datafile)
{
param->error_printed=1;
param->retry_repair=1;
param->testflag|=T_RETRY_WITHOUT_QUICK;
my_errno= HA_ERR_WRONG_IN_RECORD;
DBUG_RETURN(1); /* Something wrong with data */
}
sort_param->start_recpos=sort_param->pos; if (_mi_pack_get_block_info(info, &sort_param->bit_buff, &block_info,
&sort_param->rec_buff, -1, sort_param->pos))
DBUG_RETURN(-1); if (!block_info.rec_len &&
sort_param->pos + MEMMAP_EXTRA_MARGIN ==
sort_param->read_cache.end_of_file)
DBUG_RETURN(-1); if (block_info.rec_len < (uint) share->min_pack_length ||
block_info.rec_len > (uint) share->max_pack_length)
{ if (! searching)
mi_check_print_info(param,"Found block with wrong recordlength: %ld at %s",
block_info.rec_len,
llstr(sort_param->pos,llbuff)); continue;
} if (_mi_read_cache(&sort_param->read_cache,(uchar*) sort_param->rec_buff,
block_info.filepos, block_info.rec_len,
READING_NEXT))
{ if (! searching)
mi_check_print_info(param,"Couldn't read whole record from %s",
llstr(sort_param->pos,llbuff)); continue;
}
sort_param->rec_buff[block_info.rec_len]= 0; /* Keep valgrind happy */ if (_mi_pack_rec_unpack(info, &sort_param->bit_buff, sort_param->record,
sort_param->rec_buff, block_info.rec_len))
{ if (! searching)
mi_check_print_info(param,"Found wrong record at %s",
llstr(sort_param->pos,llbuff)); continue;
} if (!sort_param->fix_datafile)
{
sort_param->filepos=sort_param->pos; if (sort_param->master)
share->state.split++;
}
sort_param->max_pos=(sort_param->pos=block_info.filepos+
block_info.rec_len);
info->packed_length=block_info.rec_len; if (sort_param->calc_checksum)
info->checksum= (*info->s->calc_check_checksum)(info, sort_param->record); goto finish;
} default:
DBUG_ASSERT(0); /* Impossible */ break;
}
DBUG_ASSERT(0); /* Impossible */
DBUG_RETURN(1); /* Impossible */
finish: if (sort_param->calc_checksum)
param->glob_crc+= info->checksum; if (param->fix_record)
param->fix_record(info, sort_param->record,
param->testflag & T_REP_BY_SORT ? (int)sort_param->key
: -1);
DBUG_RETURN(0);
}
if (sort_param->fix_datafile)
{ switch (sort_info->new_data_file_type) { case STATIC_RECORD: if (my_b_write(&info->rec_cache,sort_param->record,
share->base.pack_reclength))
{
mi_check_print_error(param,"%d when writing to datafile",my_errno);
DBUG_RETURN(1);
}
sort_param->filepos+=share->base.pack_reclength;
info->s->state.split++; break; case DYNAMIC_RECORD: if (! info->blobs)
from=sort_param->rec_buff; else
{ /* must be sure that local buffer is big enough */
reclength=info->s->base.pack_reclength+
_mi_calc_total_blob_length(info,sort_param->record)+
ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
MI_DYN_DELETE_BLOCK_HEADER; if (sort_info->buff_length < reclength)
{ if (!(sort_info->buff=my_realloc(mi_key_memory_SORT_INFO_buffer,
sort_info->buff, (uint) reclength,
MYF(MY_FREE_ON_ERROR | MY_WME |
MY_ALLOW_ZERO_PTR))))
DBUG_RETURN(1);
sort_info->buff_length=reclength;
}
from= sort_info->buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER);
} /* We can use info->checksum here as only one thread calls this. */
info->checksum= (*info->s->calc_check_checksum)(info,sort_param->record);
reclength=_mi_rec_pack(info,from,sort_param->record);
flag=0;
do
{
block_length= reclength + 3 + MY_TEST(reclength >= (65520 - 3)); if (block_length < share->base.min_block_length)
block_length=share->base.min_block_length;
info->update|=HA_STATE_WRITE_AT_END;
block_length=MY_ALIGN(block_length,MI_DYN_ALIGN_SIZE); if (block_length > MI_MAX_BLOCK_LENGTH)
block_length=MI_MAX_BLOCK_LENGTH; if (_mi_write_part_record(info,0L,block_length,
sort_param->filepos+block_length,
&from,&reclength,&flag))
{
mi_check_print_error(param,"%d when writing to datafile",my_errno);
DBUG_RETURN(1);
}
sort_param->filepos+=block_length;
info->s->state.split++;
} while (reclength); /* sort_info->param->glob_crc+=info->checksum; */ break; case COMPRESSED_RECORD:
reclength=info->packed_length;
length= save_pack_length((uint) share->pack.version, block_buff,
reclength); if (info->s->base.blobs)
length+= save_pack_length((uint) share->pack.version,
block_buff + length, info->blob_length); if (my_b_write(&info->rec_cache,block_buff,length) ||
my_b_write(&info->rec_cache,(uchar*) sort_param->rec_buff,reclength))
{
mi_check_print_error(param,"%d when writing to datafile",my_errno);
DBUG_RETURN(1);
} /* sort_info->param->glob_crc+=info->checksum; */
sort_param->filepos+=reclength+length;
info->s->state.split++; break; default:
DBUG_ASSERT(0); /* Impossible */ break;
}
} if (sort_param->master)
{
info->state->records++; if ((param->testflag & T_WRITE_LOOP) &&
(info->state->records % WRITE_COUNT) == 0)
{ char llbuff[22];
printf("%s\r", llstr(info->state->records,llbuff));
(void) fflush(stdout);
}
}
DBUG_RETURN(0);
} /* sort_write_record */
if (!ft_buf)
{
sort_param->key_write=sort_key_write; return sort_key_write(sort_param, a);
}
sort_info->ft_buf=ft_buf; goto word_init_ft_buf; /* no need to duplicate the code */
}
get_key_full_length_rdonly(val_off, ft_buf->lastkey);
if (ha_compare_word(sort_param->seg->charset,
((uchar *)a) + 1, a_len - 1,
(uchar*) ft_buf->lastkey + 1, val_off - 1) == 0)
{ if (!ft_buf->buf) /* store in second-level tree */
{
ft_buf->count++; return sort_insert_key(sort_param,key_block,
((uchar *)a)+a_len, HA_OFFSET_ERROR);
}
/* storing the key in the buffer. */
memcpy (ft_buf->buf, (char *)a+a_len, val_len);
ft_buf->buf+=val_len; if (ft_buf->buf < ft_buf->end) return0;
/* converting to two-level tree */
p= (uchar*) ft_buf->lastkey+val_off;
while (key_block->inited)
key_block++;
sort_info->key_block=key_block;
sort_param->keyinfo=& sort_info->info->s->ft2_keyinfo;
ft_buf->count=(int)((uchar*) ft_buf->buf - p)/val_len;
/* flushing buffer to second-level tree */ for (error=0; !error && p < (uchar*) ft_buf->buf; p+= val_len)
error=sort_insert_key(sort_param,key_block,p,HA_OFFSET_ERROR);
ft_buf->buf=0; return error;
}
/* flushing buffer */ if ((error=sort_ft_buf_flush(sort_param))) return error;
/* Fill block with end-zero and write filled block */
mi_putint(anc_buff,key_block->last_length,nod_flag);
bzero((uchar*) anc_buff+key_block->last_length,
keyinfo->block_length- key_block->last_length);
key_file_length=info->state->key_file_length; if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
DBUG_RETURN(1);
/* If we read the page from the key cache, we have to write it back to it */ if (key_file_length == info->state->key_file_length)
{ if (_mi_write_keypage(info, keyinfo, filepos, DFLT_INIT_HITS, anc_buff))
DBUG_RETURN(1);
} elseif (mysql_file_pwrite(info->s->kfile, (uchar*) anc_buff,
(uint) keyinfo->block_length, filepos,
param->myf_rw))
DBUG_RETURN(1);
DBUG_DUMP("buff",(uchar*) anc_buff,mi_getint(anc_buff));
/* Write separator-key to block in next level */ if (sort_insert_key(sort_param,key_block+1,(uchar*) key_block->lastkey,
filepos))
DBUG_RETURN(1);
/* clear old block and write new key in it */
key_block->inited=0;
DBUG_RETURN(sort_insert_key(sort_param, key_block,key,prev_block));
} /* sort_insert_key */
/* Delete record when we found a duplicated key */
if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
{
mi_check_print_error(param, "Quick-recover aborted; Run recovery without switch -q or with switch -qq");
DBUG_RETURN(1);
} if (info->s->options & HA_OPTION_COMPRESS_RECORD)
{
mi_check_print_error(param, "Recover aborted; Can't run standard recovery on compressed tables with errors in data-file. Use switch 'myisamchk --safe-recover' to fix it");
DBUG_RETURN(1);
}
old_file=info->dfile;
info->dfile=info->rec_cache.file; if (sort_info->current_key)
{
key=info->lastkey+info->s->base.max_key_length; if ((error=(*info->s->read_rnd)(info,sort_param->record,info->lastpos,0)) &&
error != HA_ERR_RECORD_DELETED)
{
mi_check_print_error(param,"Can't read record to be removed");
info->dfile=old_file;
DBUG_RETURN(1);
}
for (i=0 ; i < sort_info->current_key ; i++)
{
uint key_length=_mi_make_key(info,i,key,sort_param->record,info->lastpos); if (_mi_ck_delete(info,i,key,key_length))
{
mi_check_print_error(param,"Can't delete key %d from record to be removed",i+1);
info->dfile=old_file;
DBUG_RETURN(1);
}
} if (sort_param->calc_checksum)
param->glob_crc-=(*info->s->calc_checksum)(info, sort_param->record);
}
error=flush_io_cache(&info->rec_cache) || (*info->s->delete_record)(info);
info->dfile=old_file; /* restore actual value */
info->state->records--;
DBUG_RETURN(error);
} /* sort_delete_record */
/* Fix all pending blocks and flush everything to disk */
filepos= HA_OFFSET_ERROR; /* if empty file */
nod_flag=0; for (key_block=sort_info->key_block ; key_block->inited ; key_block++)
{
key_block->inited=0;
length=mi_getint(key_block->buff); if (nod_flag)
_mi_kpointer(info,(uchar*) key_block->end_pos,filepos);
key_file_length=info->state->key_file_length;
bzero((uchar*) key_block->buff+length, keyinfo->block_length-length); if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
DBUG_RETURN(1);
/* If we read the page from the key cache, we have to write it back */ if (key_file_length == info->state->key_file_length)
{ if (_mi_write_keypage(info, keyinfo, filepos,
DFLT_INIT_HITS, (uchar*) key_block->buff))
DBUG_RETURN(1);
} elseif (mysql_file_pwrite(info->s->kfile, (uchar*) key_block->buff,
(uint) keyinfo->block_length, filepos, myf_rw))
DBUG_RETURN(1);
DBUG_DUMP("buff",(uchar*) key_block->buff,length);
nod_flag=1;
}
info->s->state.key_root[sort_param->key]=filepos; /* Last is root for tree */
DBUG_RETURN(0);
} /* flush_pending_blocks */
/* Copy the column definitions */
memcpy((uchar*) recdef,(uchar*) share.rec,
(size_t) (sizeof(MI_COLUMNDEF)*(share.base.fields+1))); for (rec=recdef,end=recdef+share.base.fields; rec != end ; rec++)
{ if (unpack && !(share.options & HA_OPTION_PACK_RECORD) &&
rec->type != FIELD_BLOB &&
rec->type != FIELD_VARCHAR &&
rec->type != FIELD_CHECK)
rec->type=(int) FIELD_NORMAL;
}
/* Change the new key to point at the saved key segments */
memcpy((uchar*) keysegs,(uchar*) share.keyparts,
(size_t) (sizeof(HA_KEYSEG)*(key_parts+share.base.keys+
share.state.header.uniques)));
keyseg=keysegs; for (key=keyinfo,key_end=keyinfo+share.base.keys; key != key_end ; key++)
{
key->seg=keyseg; for (; keyseg->type ; keyseg++)
{ if (param->language)
keyseg->language=param->language; /* change language */
}
keyseg++; /* Skip end pointer */
}
/* Copy the unique definitions and change them to point at the new key
segments*/
memcpy((uchar*) uniquedef,(uchar*) share.uniqueinfo,
(size_t) (sizeof(MI_UNIQUEDEF)*(share.state.header.uniques))); for (u_ptr=uniquedef,u_end=uniquedef+share.state.header.uniques;
u_ptr != u_end ; u_ptr++)
{
u_ptr->seg=keyseg;
keyseg+=u_ptr->keysegs+1;
}
unpack= (share.options & HA_OPTION_COMPRESS_RECORD) &&
(param->testflag & T_UNPACK);
share.options&= ~HA_OPTION_TEMP_COMPRESS_RECORD;
/* forsomeweirdkeys(e.g.FULLTEXT)tmpcanbe<1here. let'sensureitisnot
*/
set_if_bigger(tmp,1); /* Keys are stored as 32 byte int's; Ensure we don't get an overflow */ if (tmp >= (ulonglong) ~(uint32) 0)
tmp=(ulonglong) ~(uint32) 0;