Drizzled Public API Documentation

mi_check.cc
00001 /* Copyright (C) 2000-2006 MySQL 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; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 /* Describe, check and repair of MyISAM tables */
00017 
00018 /*
00019   About checksum calculation.
00020 
00021   There are two types of checksums. Table checksum and row checksum.
00022 
00023   Row checksum is an additional byte at the end of dynamic length
00024   records. It must be calculated if the table is configured for them.
00025   Otherwise they must not be used. The variable
00026   MYISAM_SHARE::calc_checksum determines if row checksums are used.
00027   MI_INFO::checksum is used as temporary storage during row handling.
00028   For parallel repair we must assure that only one thread can use this
00029   variable. There is no problem on the write side as this is done by one
00030   thread only. But when checking a record after read this could go
00031   wrong. But since all threads read through a common read buffer, it is
00032   sufficient if only one thread checks it.
00033 
00034   Table checksum is an eight byte value in the header of the index file.
00035   It can be calculated even if row checksums are not used. The variable
00036   MI_CHECK::glob_crc is calculated over all records.
00037   MI_SORT_PARAM::calc_checksum determines if this should be done. This
00038   variable is not part of MI_CHECK because it must be set per thread for
00039   parallel repair. The global glob_crc must be changed by one thread
00040   only. And it is sufficient to calculate the checksum once only.
00041 */
00042 
00043 #include "myisam_priv.h"
00044 #include <drizzled/internal/m_string.h>
00045 #include <stdarg.h>
00046 #ifdef HAVE_SYS_VADVISE_H
00047 #include <sys/vadvise.h>
00048 #endif
00049 #ifdef HAVE_SYS_TYPES
00050 #include <sys/types.h>
00051 #endif
00052 #ifdef HAVE_SYS_MMAN_H
00053 #include <sys/mman.h>
00054 #endif
00055 #include <drizzled/util/test.h>
00056 #include <drizzled/error.h>
00057 
00058 #include <algorithm>
00059 
00060 using namespace std;
00061 using namespace drizzled;
00062 using namespace drizzled::internal;
00063 
00064 
00065 #define my_off_t2double(A)  ((double) (my_off_t) (A))
00066 
00067 /* Functions defined in this file */
00068 
00069 static int check_k_link(MI_CHECK *param, MI_INFO *info,uint32_t nr);
00070 static int chk_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo,
00071          my_off_t page, unsigned char *buff, ha_rows *keys,
00072          ha_checksum *key_checksum, uint32_t level);
00073 static uint32_t isam_key_length(MI_INFO *info,MI_KEYDEF *keyinfo);
00074 static ha_checksum calc_checksum(ha_rows count);
00075 static int writekeys(MI_SORT_PARAM *sort_param);
00076 static int sort_one_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo,
00077         my_off_t pagepos, int new_file);
00078 int sort_key_read(MI_SORT_PARAM *sort_param,void *key);
00079 int sort_get_next_record(MI_SORT_PARAM *sort_param);
00080 int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,const void *b);
00081 int sort_key_write(MI_SORT_PARAM *sort_param, const void *a);
00082 my_off_t get_record_for_key(MI_INFO *info,MI_KEYDEF *keyinfo,
00083                             unsigned char *key);
00084 int sort_insert_key(MI_SORT_PARAM  *sort_param,
00085                     register SORT_KEY_BLOCKS *key_block,
00086                     unsigned char *key, my_off_t prev_block);
00087 int sort_delete_record(MI_SORT_PARAM *sort_param);
00088 
00089 /*static int flush_pending_blocks(MI_CHECK *param);*/
00090 static SORT_KEY_BLOCKS  *alloc_key_blocks(MI_CHECK *param, uint32_t blocks,
00091             uint32_t buffer_length);
00092 static ha_checksum mi_byte_checksum(const unsigned char *buf, uint32_t length);
00093 static void set_data_file_type(SORT_INFO *sort_info, MYISAM_SHARE *share);
00094 
00095 void myisamchk_init(MI_CHECK *param)
00096 {
00097   memset(param, 0, sizeof(*param));
00098   param->opt_follow_links=1;
00099   param->keys_in_use= ~(uint64_t) 0;
00100   param->search_after_block=HA_OFFSET_ERROR;
00101   param->auto_increment_value= 0;
00102   param->use_buffers=USE_BUFFER_INIT;
00103   param->read_buffer_length=READ_BUFFER_INIT;
00104   param->write_buffer_length=READ_BUFFER_INIT;
00105   param->sort_buffer_length=SORT_BUFFER_INIT;
00106   param->sort_key_blocks=BUFFERS_WHEN_SORTING;
00107   param->tmpfile_createflag=O_RDWR | O_TRUNC | O_EXCL;
00108   param->myf_rw=MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL);
00109   param->start_check_pos=0;
00110   param->max_record_length= INT64_MAX;
00111   param->key_cache_block_size= KEY_CACHE_BLOCK_SIZE;
00112   param->stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
00113 }
00114 
00115   /* Check the status flags for the table */
00116 
00117 int chk_status(MI_CHECK *param, register MI_INFO *info)
00118 {
00119   MYISAM_SHARE *share=info->s;
00120 
00121   if (mi_is_crashed_on_repair(info))
00122     mi_check_print_warning(param,
00123          "Table is marked as crashed and last repair failed");
00124   else if (mi_is_crashed(info))
00125     mi_check_print_warning(param,
00126          "Table is marked as crashed");
00127   if (share->state.open_count != (uint) (info->s->global_changed ? 1 : 0))
00128   {
00129     /* Don't count this as a real warning, as check can correct this ! */
00130     uint32_t save=param->warning_printed;
00131     mi_check_print_warning(param,
00132          share->state.open_count==1 ?
00133          "%d client is using or hasn't closed the table properly" :
00134          "%d clients are using or haven't closed the table properly",
00135          share->state.open_count);
00136     /* If this will be fixed by the check, forget the warning */
00137     if (param->testflag & T_UPDATE_STATE)
00138       param->warning_printed=save;
00139   }
00140   return 0;
00141 }
00142 
00143   /* Check delete links */
00144 
00145 int chk_del(MI_CHECK *param, register MI_INFO *info, uint32_t test_flag)
00146 {
00147   uint32_t delete_link_length;
00148   my_off_t empty, next_link, old_link= 0;
00149   char buff[22],buff2[22];
00150 
00151   param->record_checksum=0;
00152   delete_link_length=((info->s->options & HA_OPTION_PACK_RECORD) ? 20 : info->s->rec_reflength+1);
00153 
00154   if (!(test_flag & T_SILENT))
00155     puts("- check record delete-chain");
00156 
00157   next_link=info->s->state.dellink;
00158   if (info->state->del == 0)
00159   {
00160     if (test_flag & T_VERBOSE)
00161     {
00162       puts("No recordlinks");
00163     }
00164   }
00165   else
00166   {
00167     if (test_flag & T_VERBOSE)
00168       printf("Recordlinks:    ");
00169     empty=0;
00170     ha_rows i= info->state->del;
00171     for (; i > 0 && next_link != HA_OFFSET_ERROR; i--)
00172     {
00173       if (*killed_ptr(param))
00174         return(1);
00175       if (test_flag & T_VERBOSE)
00176         printf(" %9s",llstr(next_link,buff));
00177       if (next_link >= info->state->data_file_length)
00178         goto wrong;
00179       if (my_pread(info->dfile, (unsigned char*) buff,delete_link_length, next_link,MYF(MY_NABP)))
00180       {
00181         if (test_flag & T_VERBOSE) puts("");
00182         mi_check_print_error(param,"Can't read delete-link at filepos: %s",
00183           llstr(next_link,buff));
00184         return(1);
00185       }
00186       if (*buff != '\0')
00187       {
00188         if (test_flag & T_VERBOSE) puts("");
00189         mi_check_print_error(param,"Record at pos: %s is not remove-marked",
00190           llstr(next_link,buff));
00191         goto wrong;
00192       }
00193       if (info->s->options & HA_OPTION_PACK_RECORD)
00194       {
00195         my_off_t prev_link=mi_sizekorr(buff+12);
00196         if (empty && prev_link != old_link)
00197         {
00198           if (test_flag & T_VERBOSE) puts("");
00199           mi_check_print_error(param,"Deleted block at %s doesn't point back at previous delete link",llstr(next_link,buff2));
00200           goto wrong;
00201         }
00202         old_link=next_link;
00203         next_link=mi_sizekorr(buff+4);
00204         empty+=mi_uint3korr(buff+1);
00205       }
00206       else
00207       {
00208         param->record_checksum+=(ha_checksum) next_link;
00209         next_link=_mi_rec_pos(info->s,(unsigned char*) buff+1);
00210         empty+=info->s->base.pack_reclength;
00211       }
00212     }
00213     if (test_flag & T_VERBOSE)
00214       puts("\n");
00215     if (empty != info->state->empty)
00216     {
00217       mi_check_print_warning(param,
00218         "Found %s deleted space in delete link chain. Should be %s",
00219         llstr(empty,buff2),
00220         llstr(info->state->empty,buff));
00221     }
00222     if (next_link != HA_OFFSET_ERROR)
00223     {
00224       mi_check_print_error(param,
00225         "Found more than the expected %s deleted rows in delete link chain",
00226         llstr(info->state->del, buff));
00227       goto wrong;
00228     }
00229     if (i != 0)
00230     {
00231       mi_check_print_error(param, "Found %s deleted rows in delete link chain. Should be %s", llstr(info->state->del - i, buff2), llstr(info->state->del, buff));
00232       goto wrong;
00233     }
00234   }
00235   return 0;
00236 
00237 wrong:
00238   param->testflag|=T_RETRY_WITHOUT_QUICK;
00239   if (test_flag & T_VERBOSE) puts("");
00240   mi_check_print_error(param,"record delete-link-chain corrupted");
00241   return 1;
00242 } /* chk_del */
00243 
00244 
00245   /* Check delete links in index file */
00246 
00247 static int check_k_link(MI_CHECK *param, register MI_INFO *info, uint32_t nr)
00248 {
00249   my_off_t next_link;
00250   uint32_t block_size=(nr+1)*MI_MIN_KEY_BLOCK_LENGTH;
00251   ha_rows records;
00252   char llbuff[21], llbuff2[21];
00253   unsigned char *buff;
00254 
00255   if (param->testflag & T_VERBOSE)
00256     printf("block_size %4u:", block_size);
00257 
00258   next_link=info->s->state.key_del[nr];
00259   records= (ha_rows) (info->state->key_file_length / block_size);
00260   while (next_link != HA_OFFSET_ERROR && records > 0)
00261   {
00262     if (*killed_ptr(param))
00263       return(1);
00264     if (param->testflag & T_VERBOSE)
00265       printf("%16s",llstr(next_link,llbuff));
00266 
00267     /* Key blocks must lay within the key file length entirely. */
00268     if (next_link + block_size > info->state->key_file_length)
00269     {
00270       mi_check_print_error(param, "Invalid key block position: %s  "
00271                            "key block size: %u  file_length: %s",
00272                            llstr(next_link, llbuff), block_size,
00273                            llstr(info->state->key_file_length, llbuff2));
00274       return(1);
00275     }
00276 
00277     /* Key blocks must be aligned at MI_MIN_KEY_BLOCK_LENGTH. */
00278     if (next_link & (MI_MIN_KEY_BLOCK_LENGTH - 1))
00279     {
00280       mi_check_print_error(param, "Mis-aligned key block: %s  "
00281                            "minimum key block length: %u",
00282                            llstr(next_link, llbuff), MI_MIN_KEY_BLOCK_LENGTH);
00283       return(1);
00284     }
00285 
00286     /*
00287       Read the key block with MI_MIN_KEY_BLOCK_LENGTH to find next link.
00288       If the key cache block size is smaller than block_size, we can so
00289       avoid unecessary eviction of cache block.
00290     */
00291 
00292     if (not pread(info->s->kfile, info->buff, MI_MIN_KEY_BLOCK_LENGTH, next_link))
00293     {
00294       mi_check_print_error(param, "key cache read error for block: %s", llstr(next_link,llbuff));
00295       return(1);
00296     }
00297     buff= info->buff;
00298     next_link=mi_sizekorr(buff);
00299     records--;
00300     param->key_file_blocks+=block_size;
00301   }
00302   if (param->testflag & T_VERBOSE)
00303   {
00304     if (next_link != HA_OFFSET_ERROR)
00305       printf("%16s\n",llstr(next_link,llbuff));
00306     else
00307       puts("");
00308   }
00309   return (next_link != HA_OFFSET_ERROR);
00310 } /* check_k_link */
00311 
00312 
00313   /* Check sizes of files */
00314 
00315 int chk_size(MI_CHECK *param, register MI_INFO *info)
00316 {
00317   int error=0;
00318   register my_off_t skr,size;
00319   char buff[22],buff2[22];
00320 
00321   if (!(param->testflag & T_SILENT)) puts("- check file-size");
00322 
00323   size= lseek(info->s->kfile, 0, SEEK_END);
00324   if ((skr=(my_off_t) info->state->key_file_length) != size)
00325   {
00326     /* Don't give error if file generated by myisampack */
00327     if (skr > size && mi_is_any_key_active(info->s->state.key_map))
00328     {
00329       error=1;
00330       mi_check_print_error(param,
00331          "Size of indexfile is: %-8s        Should be: %s",
00332          llstr(size,buff), llstr(skr,buff2));
00333     }
00334     else
00335       mi_check_print_warning(param,
00336            "Size of indexfile is: %-8s      Should be: %s",
00337            llstr(size,buff), llstr(skr,buff2));
00338   }
00339   if (!(param->testflag & T_VERY_SILENT) &&
00340       ! (info->s->options & HA_OPTION_COMPRESS_RECORD) &&
00341       uint64_t2double(info->state->key_file_length) >
00342       uint64_t2double(info->s->base.margin_key_file_length)*0.9)
00343     mi_check_print_warning(param,"Keyfile is almost full, %10s of %10s used",
00344          llstr(info->state->key_file_length,buff),
00345          llstr(info->s->base.max_key_file_length-1,buff));
00346 
00347   size=lseek(info->dfile,0L,SEEK_END);
00348   skr=(my_off_t) info->state->data_file_length;
00349   if (info->s->options & HA_OPTION_COMPRESS_RECORD)
00350     skr+= MEMMAP_EXTRA_MARGIN;
00351 #ifdef USE_RELOC
00352   if (info->data_file_type == STATIC_RECORD &&
00353       skr < (my_off_t) info->s->base.reloc*info->s->base.min_pack_length)
00354     skr=(my_off_t) info->s->base.reloc*info->s->base.min_pack_length;
00355 #endif
00356   if (skr != size)
00357   {
00358     info->state->data_file_length=size; /* Skip other errors */
00359     if (skr > size && skr != size + MEMMAP_EXTRA_MARGIN)
00360     {
00361       error=1;
00362       mi_check_print_error(param,"Size of datafile is: %-9s         Should be: %s",
00363         llstr(size,buff), llstr(skr,buff2));
00364       param->testflag|=T_RETRY_WITHOUT_QUICK;
00365     }
00366     else
00367     {
00368       mi_check_print_warning(param,
00369            "Size of datafile is: %-9s       Should be: %s",
00370            llstr(size,buff), llstr(skr,buff2));
00371     }
00372   }
00373   if (!(param->testflag & T_VERY_SILENT) &&
00374       !(info->s->options & HA_OPTION_COMPRESS_RECORD) &&
00375       uint64_t2double(info->state->data_file_length) >
00376       (uint64_t2double(info->s->base.max_data_file_length)*0.9))
00377     mi_check_print_warning(param, "Datafile is almost full, %10s of %10s used",
00378          llstr(info->state->data_file_length,buff),
00379          llstr(info->s->base.max_data_file_length-1,buff2));
00380   return(error);
00381 } /* chk_size */
00382 
00383 
00384   /* Check keys */
00385 
00386 int chk_key(MI_CHECK *param, register MI_INFO *info)
00387 {
00388   uint32_t key,found_keys=0,full_text_keys=0,result=0;
00389   ha_rows keys;
00390   ha_checksum old_record_checksum,init_checksum;
00391   my_off_t all_keydata,all_totaldata,key_totlength,length;
00392   ulong   *rec_per_key_part;
00393   MYISAM_SHARE *share=info->s;
00394   MI_KEYDEF *keyinfo;
00395   char buff[22],buff2[22];
00396 
00397   if (!(param->testflag & T_SILENT))
00398     puts("- check key delete-chain");
00399 
00400   param->key_file_blocks=info->s->base.keystart;
00401   for (key=0 ; key < info->s->state.header.max_block_size_index ; key++)
00402     if (check_k_link(param,info,key))
00403     {
00404       if (param->testflag & T_VERBOSE) puts("");
00405       mi_check_print_error(param,"key delete-link-chain corrupted");
00406       return(-1);
00407     }
00408 
00409   if (!(param->testflag & T_SILENT)) puts("- check index reference");
00410 
00411   all_keydata=all_totaldata=key_totlength=0;
00412   old_record_checksum=0;
00413   init_checksum=param->record_checksum;
00414   if (!(share->options &
00415   (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
00416     old_record_checksum=calc_checksum(info->state->records+info->state->del-1)*
00417       share->base.pack_reclength;
00418   rec_per_key_part= param->rec_per_key_part;
00419   for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
00420        rec_per_key_part+=keyinfo->keysegs, key++, keyinfo++)
00421   {
00422     param->key_crc[key]=0;
00423     if (! mi_is_key_active(share->state.key_map, key))
00424     {
00425       /* Remember old statistics for key */
00426       assert(rec_per_key_part >= param->rec_per_key_part);
00427       memcpy(rec_per_key_part,
00428        (share->state.rec_per_key_part +
00429               (rec_per_key_part - param->rec_per_key_part)),
00430        keyinfo->keysegs*sizeof(*rec_per_key_part));
00431       continue;
00432     }
00433     found_keys++;
00434 
00435     param->record_checksum=init_checksum;
00436 
00437     memset(&param->unique_count, 0, sizeof(param->unique_count));
00438     memset(&param->notnull_count, 0, sizeof(param->notnull_count));
00439 
00440     if ((!(param->testflag & T_SILENT)))
00441       printf ("- check data record references index: %d\n",key+1);
00442     if (share->state.key_root[key] == HA_OFFSET_ERROR && (info->state->records == 0))
00443       goto do_stat;
00444     if (!_mi_fetch_keypage(info,keyinfo,share->state.key_root[key],
00445                            DFLT_INIT_HITS,info->buff,0))
00446     {
00447       mi_check_print_error(param,"Can't read indexpage from filepos: %s",
00448       llstr(share->state.key_root[key],buff));
00449       if (!(param->testflag & T_INFO))
00450   return(-1);
00451       result= UINT32_MAX;
00452       continue;
00453     }
00454     param->key_file_blocks+=keyinfo->block_length;
00455     keys=0;
00456     param->keydata=param->totaldata=0;
00457     param->key_blocks=0;
00458     param->max_level=0;
00459     if (chk_index(param,info,keyinfo,share->state.key_root[key],info->buff,
00460       &keys, param->key_crc+key,1))
00461       return(-1);
00462     if(!(0))
00463     {
00464       if (keys != info->state->records)
00465       {
00466   mi_check_print_error(param,"Found %s keys of %s",llstr(keys,buff),
00467         llstr(info->state->records,buff2));
00468   if (!(param->testflag & T_INFO))
00469   return(-1);
00470   result= UINT32_MAX;
00471   continue;
00472       }
00473       if (found_keys - full_text_keys == 1 &&
00474     ((share->options &
00475       (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
00476      (param->testflag & T_DONT_CHECK_CHECKSUM)))
00477   old_record_checksum=param->record_checksum;
00478       else if (old_record_checksum != param->record_checksum)
00479       {
00480   if (key)
00481     mi_check_print_error(param,"Key %u doesn't point at same records that key 1",
00482           key+1);
00483   else
00484     mi_check_print_error(param,"Key 1 doesn't point at all records");
00485   if (!(param->testflag & T_INFO))
00486     return(-1);
00487   result= UINT32_MAX;
00488   continue;
00489       }
00490     }
00491     if ((uint) share->base.auto_key -1 == key)
00492     {
00493       /* Check that auto_increment key is bigger than max key value */
00494       uint64_t auto_increment;
00495       info->lastinx=key;
00496       _mi_read_key_record(info, 0L, info->rec_buff);
00497       auto_increment= retrieve_auto_increment(info, info->rec_buff);
00498       if (auto_increment > info->s->state.auto_increment)
00499       {
00500         mi_check_print_warning(param, "Auto-increment value: %s is smaller "
00501                                "than max used value: %s",
00502                                llstr(info->s->state.auto_increment,buff2),
00503                                llstr(auto_increment, buff));
00504       }
00505       if (param->testflag & T_AUTO_INC)
00506       {
00507         set_if_bigger(info->s->state.auto_increment,
00508                       auto_increment);
00509         set_if_bigger(info->s->state.auto_increment,
00510                       param->auto_increment_value);
00511       }
00512 
00513       /* Check that there isn't a row with auto_increment = 0 in the table */
00514       mi_extra(info,HA_EXTRA_KEYREAD,0);
00515       memset(info->lastkey, 0, keyinfo->seg->length);
00516       if (!mi_rkey(info, info->rec_buff, key, (const unsigned char*) info->lastkey,
00517        (key_part_map)1, HA_READ_KEY_EXACT))
00518       {
00519   /* Don't count this as a real warning, as myisamchk can't correct it */
00520   uint32_t save=param->warning_printed;
00521         mi_check_print_warning(param, "Found row where the auto_increment "
00522                                "column has the value 0");
00523   param->warning_printed=save;
00524       }
00525       mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
00526     }
00527 
00528     length=(my_off_t) isam_key_length(info,keyinfo)*keys + param->key_blocks*2;
00529     if (param->testflag & T_INFO && param->totaldata != 0L && keys != 0L)
00530       printf("Key: %2d:  Keyblocks used: %3d%%  Packed: %4d%%  Max levels: %2d\n",
00531        key+1,
00532        (int) (my_off_t2double(param->keydata)*100.0/my_off_t2double(param->totaldata)),
00533        (int) ((my_off_t2double(length) - my_off_t2double(param->keydata))*100.0/
00534         my_off_t2double(length)),
00535        param->max_level);
00536     all_keydata+=param->keydata; all_totaldata+=param->totaldata; key_totlength+=length;
00537 
00538 do_stat:
00539     if (param->testflag & T_STATISTICS)
00540       update_key_parts(keyinfo, rec_per_key_part, param->unique_count,
00541                        param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
00542                        param->notnull_count: NULL,
00543                        (uint64_t)info->state->records);
00544   }
00545   if (param->testflag & T_INFO)
00546   {
00547     if (all_totaldata != 0L && found_keys > 0)
00548       printf("Total:    Keyblocks used: %3d%%  Packed: %4d%%\n\n",
00549        (int) (my_off_t2double(all_keydata)*100.0/
00550         my_off_t2double(all_totaldata)),
00551        (int) ((my_off_t2double(key_totlength) -
00552          my_off_t2double(all_keydata))*100.0/
00553          my_off_t2double(key_totlength)));
00554     else if (all_totaldata != 0L && mi_is_any_key_active(share->state.key_map))
00555       puts("");
00556   }
00557   if (param->key_file_blocks != info->state->key_file_length &&
00558       param->keys_in_use != ~(uint64_t) 0)
00559     mi_check_print_warning(param, "Some data are unreferenced in keyfile");
00560   if (found_keys != full_text_keys)
00561     param->record_checksum=old_record_checksum-init_checksum; /* Remove delete links */
00562   else
00563     param->record_checksum=0;
00564   return(result);
00565 } /* chk_key */
00566 
00567 
00568 static int chk_index_down(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
00569                      my_off_t page, unsigned char *buff, ha_rows *keys,
00570                      ha_checksum *key_checksum, uint32_t level)
00571 {
00572   char llbuff[22],llbuff2[22];
00573 
00574   /* Key blocks must lay within the key file length entirely. */
00575   if (page + keyinfo->block_length > info->state->key_file_length)
00576   {
00577     /* Give it a chance to fit in the real file size. */
00578     my_off_t max_length= lseek(info->s->kfile, 0, SEEK_END);
00579     mi_check_print_error(param, "Invalid key block position: %s  "
00580                          "key block size: %u  file_length: %s",
00581                          llstr(page, llbuff), keyinfo->block_length,
00582                          llstr(info->state->key_file_length, llbuff2));
00583     if (page + keyinfo->block_length > max_length)
00584       goto err;
00585     /* Fix the remebered key file length. */
00586     info->state->key_file_length= (max_length &
00587                                    ~ (my_off_t) (keyinfo->block_length - 1));
00588   }
00589 
00590   /* Key blocks must be aligned at MI_MIN_KEY_BLOCK_LENGTH. */
00591   if (page & (MI_MIN_KEY_BLOCK_LENGTH - 1))
00592   {
00593     mi_check_print_error(param, "Mis-aligned key block: %s  "
00594                          "minimum key block length: %u",
00595                          llstr(page, llbuff), MI_MIN_KEY_BLOCK_LENGTH);
00596     goto err;
00597   }
00598 
00599   if (!_mi_fetch_keypage(info,keyinfo,page, DFLT_INIT_HITS,buff,0))
00600   {
00601     mi_check_print_error(param,"Can't read key from filepos: %s",
00602         llstr(page,llbuff));
00603     goto err;
00604   }
00605   param->key_file_blocks+=keyinfo->block_length;
00606   if (chk_index(param,info,keyinfo,page,buff,keys,key_checksum,level))
00607     goto err;
00608 
00609   return(0);
00610 
00611 err:
00612   return(1);
00613 }
00614 
00615 
00616 /*
00617   "Ignore NULLs" statistics collection method: process first index tuple.
00618 
00619   SYNOPSIS
00620     mi_collect_stats_nonulls_first()
00621       keyseg   IN     Array of key part descriptions
00622       notnull  INOUT  Array, notnull[i] = (number of {keypart1...keypart_i}
00623                                            tuples that don't contain NULLs)
00624       key      IN     Key values tuple
00625 
00626   DESCRIPTION
00627     Process the first index tuple - find out which prefix tuples don't
00628     contain NULLs, and update the array of notnull counters accordingly.
00629 */
00630 
00631 static
00632 void mi_collect_stats_nonulls_first(HA_KEYSEG *keyseg, uint64_t *notnull,
00633                                     unsigned char *key)
00634 {
00635   uint32_t first_null, kp;
00636   first_null= ha_find_null(keyseg, key) - keyseg;
00637   /*
00638     All prefix tuples that don't include keypart_{first_null} are not-null
00639     tuples (and all others aren't), increment counters for them.
00640   */
00641   for (kp= 0; kp < first_null; kp++)
00642     notnull[kp]++;
00643 }
00644 
00645 
00646 /*
00647   "Ignore NULLs" statistics collection method: process next index tuple.
00648 
00649   SYNOPSIS
00650     mi_collect_stats_nonulls_next()
00651       keyseg   IN     Array of key part descriptions
00652       notnull  INOUT  Array, notnull[i] = (number of {keypart1...keypart_i}
00653                                            tuples that don't contain NULLs)
00654       prev_key IN     Previous key values tuple
00655       last_key IN     Next key values tuple
00656 
00657   DESCRIPTION
00658     Process the next index tuple:
00659     1. Find out which prefix tuples of last_key don't contain NULLs, and
00660        update the array of notnull counters accordingly.
00661     2. Find the first keypart number where the prev_key and last_key tuples
00662        are different(A), or last_key has NULL value(B), and return it, so the
00663        caller can count number of unique tuples for each key prefix. We don't
00664        need (B) to be counted, and that is compensated back in
00665        update_key_parts().
00666 
00667   RETURN
00668     1 + number of first keypart where values differ or last_key tuple has NULL
00669 */
00670 
00671 static
00672 int mi_collect_stats_nonulls_next(HA_KEYSEG *keyseg, uint64_t *notnull,
00673                                   unsigned char *prev_key, unsigned char *last_key)
00674 {
00675   uint32_t diffs[2];
00676   uint32_t first_null_seg, kp;
00677   HA_KEYSEG *seg;
00678 
00679   /*
00680      Find the first keypart where values are different or either of them is
00681      NULL. We get results in diffs array:
00682      diffs[0]= 1 + number of first different keypart
00683      diffs[1]=offset: (last_key + diffs[1]) points to first value in
00684                       last_key that is NULL or different from corresponding
00685                       value in prev_key.
00686   */
00687   ha_key_cmp(keyseg, prev_key, last_key, USE_WHOLE_KEY,
00688              SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diffs);
00689   seg= keyseg + diffs[0] - 1;
00690 
00691   /* Find first NULL in last_key */
00692   first_null_seg= ha_find_null(seg, last_key + diffs[1]) - keyseg;
00693   for (kp= 0; kp < first_null_seg; kp++)
00694     notnull[kp]++;
00695 
00696   /*
00697     Return 1+ number of first key part where values differ. Don't care if
00698     these were NULLs and not .... We compensate for that in
00699     update_key_parts.
00700   */
00701   return diffs[0];
00702 }
00703 
00704 
00705   /* Check if index is ok */
00706 
00707 static int chk_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
00708          my_off_t page, unsigned char *buff, ha_rows *keys,
00709          ha_checksum *key_checksum, uint32_t level)
00710 {
00711   int flag;
00712   uint32_t used_length,comp_flag,nod_flag,key_length=0;
00713   unsigned char key[HA_MAX_POSSIBLE_KEY_BUFF],*temp_buff,*keypos,*endpos;
00714   my_off_t next_page,record;
00715   char llbuff[22];
00716   uint32_t diff_pos[2];
00717 
00718   if (!(temp_buff=(unsigned char*) malloc(keyinfo->block_length)))
00719   {
00720     mi_check_print_error(param,"Not enough memory for keyblock");
00721     return(-1);
00722   }
00723 
00724   if (keyinfo->flag & HA_NOSAME)
00725     comp_flag=SEARCH_FIND | SEARCH_UPDATE;  /* Not real duplicates */
00726   else
00727     comp_flag=SEARCH_SAME;      /* Keys in positionorder */
00728   nod_flag=mi_test_if_nod(buff);
00729   used_length=mi_getint(buff);
00730   keypos=buff+2+nod_flag;
00731   endpos=buff+used_length;
00732 
00733   param->keydata+=used_length; param->totaldata+=keyinfo->block_length; /* INFO */
00734   param->key_blocks++;
00735   if (level > param->max_level)
00736     param->max_level=level;
00737 
00738   if (used_length > keyinfo->block_length)
00739   {
00740     mi_check_print_error(param,"Wrong pageinfo at page: %s",
00741        llstr(page,llbuff));
00742     goto err;
00743   }
00744   for ( ;; )
00745   {
00746     if (*killed_ptr(param))
00747       goto err;
00748     memcpy(info->lastkey,key,key_length);
00749     info->lastkey_length=key_length;
00750     if (nod_flag)
00751     {
00752       next_page=_mi_kpos(nod_flag,keypos);
00753       if (chk_index_down(param,info,keyinfo,next_page,
00754                          temp_buff,keys,key_checksum,level+1))
00755   goto err;
00756     }
00757     if (keypos >= endpos ||
00758   (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0)
00759       break;
00760     assert(key_length <= sizeof(key));
00761     if (keypos > endpos)
00762     {
00763       mi_check_print_error(param,"Wrong key block length at page: %s",llstr(page,llbuff));
00764       goto err;
00765     }
00766     if ((*keys)++ &&
00767   (flag=ha_key_cmp(keyinfo->seg,info->lastkey,key,key_length,
00768        comp_flag, diff_pos)) >=0)
00769     {
00770       if (comp_flag & SEARCH_FIND && flag == 0)
00771   mi_check_print_error(param,"Found duplicated key at page %s",llstr(page,llbuff));
00772       else
00773   mi_check_print_error(param,"Key in wrong position at page %s",llstr(page,llbuff));
00774       goto err;
00775     }
00776     if (param->testflag & T_STATISTICS)
00777     {
00778       if (*keys != 1L)        /* not first_key */
00779       {
00780         if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL)
00781           ha_key_cmp(keyinfo->seg,info->lastkey,key,USE_WHOLE_KEY,
00782                      SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL,
00783                      diff_pos);
00784         else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
00785         {
00786           diff_pos[0]= mi_collect_stats_nonulls_next(keyinfo->seg,
00787                                                   param->notnull_count,
00788                                                   info->lastkey, key);
00789         }
00790   param->unique_count[diff_pos[0]-1]++;
00791       }
00792       else
00793       {
00794         if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
00795           mi_collect_stats_nonulls_first(keyinfo->seg, param->notnull_count,
00796                                          key);
00797       }
00798     }
00799     (*key_checksum)+= mi_byte_checksum((unsigned char*) key,
00800                key_length- info->s->rec_reflength);
00801     record= _mi_dpos(info,0,key+key_length);
00802     if (record >= info->state->data_file_length)
00803     {
00804       mi_check_print_error(param,"Found key at page %s that points to record outside datafile",llstr(page,llbuff));
00805       goto err;
00806     }
00807     param->record_checksum+=(ha_checksum) record;
00808   }
00809   if (keypos != endpos)
00810   {
00811     mi_check_print_error(param,"Keyblock size at page %s is not correct.  Block length: %d  key length: %d",
00812                 llstr(page,llbuff), used_length, (keypos - buff));
00813     goto err;
00814   }
00815   free(temp_buff);
00816   return(0);
00817  err:
00818   free(temp_buff);
00819   return(1);
00820 } /* chk_index */
00821 
00822 
00823   /* Calculate a checksum of 1+2+3+4...N = N*(N+1)/2 without overflow */
00824 
00825 static ha_checksum calc_checksum(ha_rows count)
00826 {
00827   uint64_t sum,a,b;
00828 
00829   sum=0;
00830   a=count; b=count+1;
00831   if (a & 1)
00832     b>>=1;
00833   else
00834     a>>=1;
00835   while (b)
00836   {
00837     if (b & 1)
00838       sum+=a;
00839     a<<=1; b>>=1;
00840   }
00841   return((ha_checksum) sum);
00842 } /* calc_checksum */
00843 
00844 
00845   /* Calc length of key in normal isam */
00846 
00847 static uint32_t isam_key_length(MI_INFO *info, register MI_KEYDEF *keyinfo)
00848 {
00849   uint32_t length;
00850   HA_KEYSEG *keyseg;
00851 
00852   length= info->s->rec_reflength;
00853   for (keyseg=keyinfo->seg ; keyseg->type ; keyseg++)
00854     length+= keyseg->length;
00855 
00856   return(length);
00857 } /* key_length */
00858 
00859 
00860   /* Check that record-link is ok */
00861 
00862 int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend)
00863 {
00864   int error,got_error,flag;
00865   uint  key, left_length= 0, b_type;
00866   ha_rows records, del_blocks;
00867   my_off_t used, empty, pos, splits, start_recpos= 0,
00868            del_length, link_used, start_block;
00869   unsigned char *record= NULL, *to= NULL;
00870   char llbuff[22],llbuff2[22],llbuff3[22];
00871   ha_checksum intern_record_checksum;
00872   ha_checksum key_checksum[HA_MAX_POSSIBLE_KEY];
00873   MI_KEYDEF *keyinfo;
00874   MI_BLOCK_INFO block_info;
00875 
00876   if (!(param->testflag & T_SILENT))
00877   {
00878     if (extend)
00879       puts("- check records and index references");
00880     else
00881       puts("- check record links");
00882   }
00883 
00884   if (!mi_alloc_rec_buff(info, SIZE_MAX, &record))
00885   {
00886     mi_check_print_error(param,"Not enough memory for record");
00887     return(-1);
00888   }
00889   records=del_blocks=0;
00890   used=link_used=splits=del_length=0;
00891   intern_record_checksum=param->glob_crc=0;
00892   got_error=error=0;
00893   empty=info->s->pack.header_length;
00894 
00895   pos= param->read_cache.tell();
00896   memset(key_checksum, 0, info->s->base.keys * sizeof(key_checksum[0]));
00897   while (pos < info->state->data_file_length)
00898   {
00899     if (*killed_ptr(param))
00900       goto err2;
00901     switch (info->s->data_file_type) {
00902     case STATIC_RECORD:
00903       if (param->read_cache.read(record, info->s->base.pack_reclength))
00904         goto err;
00905       start_recpos=pos;
00906       pos+=info->s->base.pack_reclength;
00907       splits++;
00908       if (*record == '\0')
00909       {
00910   del_blocks++;
00911   del_length+=info->s->base.pack_reclength;
00912   continue;         /* Record removed */
00913       }
00914       param->glob_crc+= mi_static_checksum(info,record);
00915       used+=info->s->base.pack_reclength;
00916       break;
00917     case DYNAMIC_RECORD:
00918       flag=block_info.second_read=0;
00919       block_info.next_filepos=pos;
00920       do
00921       {
00922   if (_mi_read_cache(&param->read_cache,(unsigned char*) block_info.header,
00923          (start_block=block_info.next_filepos),
00924          sizeof(block_info.header),
00925          (flag ? 0 : READING_NEXT) | READING_HEADER))
00926     goto err;
00927   if (start_block & (MI_DYN_ALIGN_SIZE-1))
00928   {
00929     mi_check_print_error(param,"Wrong aligned block at %s",
00930              llstr(start_block,llbuff));
00931     goto err2;
00932   }
00933   b_type=_mi_get_block_info(&block_info,-1,start_block);
00934   if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
00935           BLOCK_FATAL_ERROR))
00936   {
00937     if (b_type & BLOCK_SYNC_ERROR)
00938     {
00939       if (flag)
00940       {
00941         mi_check_print_error(param,"Unexpected byte: %d at link: %s",
00942         (int) block_info.header[0],
00943         llstr(start_block,llbuff));
00944         goto err2;
00945       }
00946       pos=block_info.filepos+block_info.block_len;
00947       goto next;
00948     }
00949     if (b_type & BLOCK_DELETED)
00950     {
00951       if (block_info.block_len < info->s->base.min_block_length)
00952       {
00953         mi_check_print_error(param,
00954            "Deleted block with impossible length %lu at %s",
00955            block_info.block_len,llstr(pos,llbuff));
00956         goto err2;
00957       }
00958       if ((block_info.next_filepos != HA_OFFSET_ERROR &&
00959      block_info.next_filepos >= info->state->data_file_length) ||
00960     (block_info.prev_filepos != HA_OFFSET_ERROR &&
00961      block_info.prev_filepos >= info->state->data_file_length))
00962       {
00963         mi_check_print_error(param,"Delete link points outside datafile at %s",
00964         llstr(pos,llbuff));
00965         goto err2;
00966       }
00967       del_blocks++;
00968       del_length+=block_info.block_len;
00969       pos=block_info.filepos+block_info.block_len;
00970       splits++;
00971       goto next;
00972     }
00973     mi_check_print_error(param,"Wrong bytesec: %d-%d-%d at linkstart: %s",
00974              block_info.header[0],block_info.header[1],
00975              block_info.header[2],
00976              llstr(start_block,llbuff));
00977     goto err2;
00978   }
00979   if (info->state->data_file_length < block_info.filepos+
00980       block_info.block_len)
00981   {
00982     mi_check_print_error(param,
00983              "Recordlink that points outside datafile at %s",
00984              llstr(pos,llbuff));
00985     got_error=1;
00986     break;
00987   }
00988   splits++;
00989   if (!flag++)        /* First block */
00990   {
00991     start_recpos=pos;
00992     pos=block_info.filepos+block_info.block_len;
00993     if (block_info.rec_len > (uint) info->s->base.max_pack_length)
00994     {
00995       mi_check_print_error(param,"Found too long record (%lu) at %s",
00996          (ulong) block_info.rec_len,
00997          llstr(start_recpos,llbuff));
00998       got_error=1;
00999       break;
01000     }
01001     if (info->s->base.blobs)
01002     {
01003       if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
01004           &info->rec_buff)))
01005       {
01006         mi_check_print_error(param,
01007            "Not enough memory (%lu) for blob at %s",
01008            (ulong) block_info.rec_len,
01009            llstr(start_recpos,llbuff));
01010         got_error=1;
01011         break;
01012       }
01013     }
01014     else
01015       to= info->rec_buff;
01016     left_length=block_info.rec_len;
01017   }
01018   if (left_length < block_info.data_len)
01019   {
01020     mi_check_print_error(param,"Found too long record (%lu) at %s",
01021              (ulong) block_info.data_len,
01022              llstr(start_recpos,llbuff));
01023     got_error=1;
01024     break;
01025   }
01026   if (_mi_read_cache(&param->read_cache,(unsigned char*) to,block_info.filepos,
01027          (uint) block_info.data_len,
01028          flag == 1 ? READING_NEXT : 0))
01029     goto err;
01030   to+=block_info.data_len;
01031   link_used+= block_info.filepos-start_block;
01032   used+= block_info.filepos - start_block + block_info.data_len;
01033   empty+=block_info.block_len-block_info.data_len;
01034   left_length-=block_info.data_len;
01035   if (left_length)
01036   {
01037     if (b_type & BLOCK_LAST)
01038     {
01039       mi_check_print_error(param,
01040          "Wrong record length %s of %s at %s",
01041          llstr(block_info.rec_len-left_length,llbuff),
01042          llstr(block_info.rec_len, llbuff2),
01043          llstr(start_recpos,llbuff3));
01044       got_error=1;
01045       break;
01046     }
01047     if (info->state->data_file_length < block_info.next_filepos)
01048     {
01049       mi_check_print_error(param,
01050          "Found next-recordlink that points outside datafile at %s",
01051          llstr(block_info.filepos,llbuff));
01052       got_error=1;
01053       break;
01054     }
01055   }
01056       } while (left_length);
01057       if (! got_error)
01058       {
01059   if (_mi_rec_unpack(info,record,info->rec_buff,block_info.rec_len) ==
01060       MY_FILE_ERROR)
01061   {
01062     mi_check_print_error(param,"Found wrong record at %s",
01063              llstr(start_recpos,llbuff));
01064     got_error=1;
01065   }
01066   else
01067   {
01068     info->checksum=mi_checksum(info,record);
01069     if (param->testflag & (T_EXTEND | T_MEDIUM | T_VERBOSE))
01070     {
01071       if (_mi_rec_check(info,record, info->rec_buff,block_info.rec_len,
01072                               test(info->s->calc_checksum)))
01073       {
01074         mi_check_print_error(param,"Found wrong packed record at %s",
01075         llstr(start_recpos,llbuff));
01076         got_error=1;
01077       }
01078     }
01079     if (!got_error)
01080       param->glob_crc+= info->checksum;
01081   }
01082       }
01083       else if (!flag)
01084   pos=block_info.filepos+block_info.block_len;
01085       break;
01086     case COMPRESSED_RECORD:
01087     case BLOCK_RECORD:
01088       assert(0);                                /* Impossible */
01089     } /* switch */
01090     if (! got_error)
01091     {
01092       intern_record_checksum+=(ha_checksum) start_recpos;
01093       records++;
01094       if (param->testflag & T_WRITE_LOOP && records % WRITE_COUNT == 0)
01095       {
01096   printf("%s\r", llstr(records,llbuff)); fflush(stdout);
01097       }
01098 
01099       /* Check if keys match the record */
01100 
01101       for (key=0,keyinfo= info->s->keyinfo; key < info->s->base.keys;
01102      key++,keyinfo++)
01103       {
01104         if (mi_is_key_active(info->s->state.key_map, key))
01105   {
01106     {
01107       uint32_t key_length=_mi_make_key(info,key,info->lastkey,record,
01108            start_recpos);
01109       if (extend)
01110       {
01111         /* We don't need to lock the key tree here as we don't allow
01112      concurrent threads when running myisamchk
01113         */
01114               int search_result=
01115                 _mi_search(info,keyinfo,info->lastkey,key_length,
01116                            SEARCH_SAME, info->s->state.key_root[key]);
01117               if (search_result)
01118               {
01119                 mi_check_print_error(param,"Record at: %10s  "
01120                                      "Can't find key for index: %2d",
01121                                      llstr(start_recpos,llbuff),key+1);
01122                 if (error++ > MAXERR || !(param->testflag & T_VERBOSE))
01123                   goto err2;
01124               }
01125       }
01126       else
01127         key_checksum[key]+=mi_byte_checksum((unsigned char*) info->lastkey,
01128               key_length);
01129     }
01130   }
01131       }
01132     }
01133     else
01134     {
01135       got_error=0;
01136       if (error++ > MAXERR || !(param->testflag & T_VERBOSE))
01137   goto err2;
01138     }
01139   next:;        /* Next record */
01140   }
01141   if (param->testflag & T_WRITE_LOOP)
01142   {
01143     fputs("          \r",stdout); fflush(stdout);
01144   }
01145   if (records != info->state->records)
01146   {
01147     mi_check_print_error(param,"Record-count is not ok; is %-10s   Should be: %s",
01148     llstr(records,llbuff), llstr(info->state->records,llbuff2));
01149     error=1;
01150   }
01151   else if (param->record_checksum &&
01152      param->record_checksum != intern_record_checksum)
01153   {
01154     mi_check_print_error(param,
01155        "Keypointers and record positions doesn't match");
01156     error=1;
01157   }
01158   else if (param->glob_crc != info->state->checksum &&
01159      (info->s->options &
01160       (HA_OPTION_COMPRESS_RECORD)))
01161   {
01162     mi_check_print_warning(param,
01163          "Record checksum is not the same as checksum stored in the index file\n");
01164     error=1;
01165   }
01166   else if (!extend)
01167   {
01168     for (key=0 ; key < info->s->base.keys;  key++)
01169     {
01170       if (key_checksum[key] != param->key_crc[key])
01171       {
01172   mi_check_print_error(param,"Checksum for key: %2d doesn't match checksum for records",
01173         key+1);
01174   error=1;
01175       }
01176     }
01177   }
01178 
01179   if (del_length != info->state->empty)
01180   {
01181     mi_check_print_warning(param,
01182          "Found %s deleted space.   Should be %s",
01183          llstr(del_length,llbuff2),
01184          llstr(info->state->empty,llbuff));
01185   }
01186   if (used+empty+del_length != info->state->data_file_length)
01187   {
01188     mi_check_print_warning(param,
01189          "Found %s record-data and %s unused data and %s deleted-data",
01190          llstr(used,llbuff),llstr(empty,llbuff2),
01191          llstr(del_length,llbuff3));
01192     mi_check_print_warning(param,
01193          "Total %s, Should be: %s",
01194          llstr((used+empty+del_length),llbuff),
01195          llstr(info->state->data_file_length,llbuff2));
01196   }
01197   if (del_blocks != info->state->del)
01198   {
01199     mi_check_print_warning(param,
01200          "Found %10s deleted blocks       Should be: %s",
01201          llstr(del_blocks,llbuff),
01202          llstr(info->state->del,llbuff2));
01203   }
01204   if (splits != info->s->state.split)
01205   {
01206     mi_check_print_warning(param,
01207          "Found %10s parts                Should be: %s parts",
01208          llstr(splits,llbuff),
01209          llstr(info->s->state.split,llbuff2));
01210   }
01211   if (param->testflag & T_INFO)
01212   {
01213     if (param->warning_printed || param->error_printed)
01214       puts("");
01215     if (used != 0 && ! param->error_printed)
01216     {
01217       printf("Records:%18s    M.recordlength:%9lu   Packed:%14.0f%%\n",
01218        llstr(records,llbuff), (long)((used-link_used)/records),
01219        (info->s->base.blobs ? 0.0 :
01220         (uint64_t2double((uint64_t) info->s->base.reclength*records)-
01221          my_off_t2double(used))/
01222         uint64_t2double((uint64_t) info->s->base.reclength*records)*100.0));
01223       printf("Recordspace used:%9.0f%%   Empty space:%12d%%  Blocks/Record: %6.2f\n",
01224        (uint64_t2double(used-link_used)/uint64_t2double(used-link_used+empty)*100.0),
01225        (!records ? 100 : (int) (uint64_t2double(del_length+empty)/
01226               my_off_t2double(used)*100.0)),
01227        uint64_t2double(splits - del_blocks) / records);
01228     }
01229     printf("Record blocks:%12s    Delete blocks:%10s\n",
01230      llstr(splits-del_blocks,llbuff),llstr(del_blocks,llbuff2));
01231     printf("Record data:  %12s    Deleted data: %10s\n",
01232      llstr(used-link_used,llbuff),llstr(del_length,llbuff2));
01233     printf("Lost space:   %12s    Linkdata:     %10s\n",
01234      llstr(empty,llbuff),llstr(link_used,llbuff2));
01235   }
01236   free(mi_get_rec_buff_ptr(info, record));
01237   return (error);
01238  err:
01239   mi_check_print_error(param,"got error: %d when reading datafile at record: %s",errno, llstr(records,llbuff));
01240  err2:
01241   free(mi_get_rec_buff_ptr(info, record));
01242   param->testflag|=T_RETRY_WITHOUT_QUICK;
01243   return(1);
01244 } /* chk_data_link */
01245 
01246 
01300 static void mi_drop_all_indexes(MI_CHECK *param, MI_INFO *info, bool force)
01301 {
01302   MYISAM_SHARE *share= info->s;
01303   MI_STATE_INFO *state= &share->state;
01304   uint32_t i;
01305 
01306   /*
01307     If any of the disabled indexes has a key block assigned, we must
01308     drop and recreate all indexes to avoid losing index blocks.
01309 
01310     If we want to recreate disabled indexes only _and_ all of these
01311     indexes are empty, we don't need to recreate the existing indexes.
01312   */
01313   if (!force && (param->testflag & T_CREATE_MISSING_KEYS))
01314   {
01315     for (i= 0; i < share->base.keys; i++)
01316     {
01317       if ((state->key_root[i] != HA_OFFSET_ERROR) &&
01318           !mi_is_key_active(state->key_map, i))
01319       {
01320         /*
01321           This index has at least one key block and it is disabled.
01322           We would lose its block(s) if would just recreate it.
01323           So we need to drop and recreate all indexes.
01324         */
01325         break;
01326       }
01327     }
01328     if (i >= share->base.keys)
01329     {
01330       /*
01331         All of the disabled indexes are empty. We can just recreate them.
01332         Flush dirty blocks of this index file from key cache and remove
01333         all blocks of this index file from key cache.
01334       */
01335       return;
01336     }
01337     /*
01338       We do now drop all indexes and declare them disabled. With the
01339       T_CREATE_MISSING_KEYS flag, mi_repair*() will recreate all
01340       disabled indexes and enable them.
01341     */
01342     mi_clear_all_keys_active(state->key_map);
01343   }
01344 
01345   /* Clear index root block pointers. */
01346   for (i= 0; i < share->base.keys; i++)
01347     state->key_root[i]= HA_OFFSET_ERROR;
01348 
01349   /* Clear the delete chains. */
01350   for (i= 0; i < state->header.max_block_size_index; i++)
01351     state->key_del[i]= HA_OFFSET_ERROR;
01352 
01353   /* Reset index file length to end of index file header. */
01354   info->state->key_file_length= share->base.keystart;
01355 }
01356 
01357 
01358   /* Recover old table by reading each record and writing all keys */
01359   /* Save new datafile-name in temp_filename */
01360 
01361 int mi_repair(MI_CHECK *param, register MI_INFO *info,
01362         char * name, int rep_quick)
01363 {
01364   int error,got_error;
01365   ha_rows start_records,new_header_length;
01366   my_off_t del;
01367   int new_file;
01368   MYISAM_SHARE *share=info->s;
01369   char llbuff[22],llbuff2[22];
01370   SORT_INFO sort_info;
01371   MI_SORT_PARAM sort_param;
01372 
01373   memset(&sort_info, 0, sizeof(sort_info));
01374   memset(&sort_param, 0, sizeof(sort_param));
01375   start_records=info->state->records;
01376   new_header_length=(param->testflag & T_UNPACK) ? 0L :
01377     share->pack.header_length;
01378   got_error=1;
01379   new_file= -1;
01380   sort_param.sort_info=&sort_info;
01381 
01382   if (!(param->testflag & T_SILENT))
01383   {
01384     printf("- recovering (with keycache) MyISAM-table '%s'\n",name);
01385     printf("Data records: %s\n", llstr(info->state->records,llbuff));
01386   }
01387   param->testflag|=T_REP; /* for easy checking */
01388 
01389   if (info->s->options & (HA_OPTION_COMPRESS_RECORD))
01390     param->testflag|=T_CALC_CHECKSUM;
01391 
01392   if (!param->using_global_keycache)
01393     assert(0);
01394 
01395   if (param->read_cache.init_io_cache(info->dfile, (uint) param->read_buffer_length, READ_CACHE,share->pack.header_length,1,MYF(MY_WME)))
01396   {
01397     memset(&info->rec_cache, 0, sizeof(info->rec_cache));
01398     goto err;
01399   }
01400   if (not rep_quick)
01401   {
01402     if (info->rec_cache.init_io_cache(-1, (uint) param->write_buffer_length, WRITE_CACHE, new_header_length, 1, MYF(MY_WME | MY_WAIT_IF_FULL)))
01403     {
01404       goto err;
01405     }
01406   }
01407   info->opt_flag|=WRITE_CACHE_USED;
01408   if (!mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.record) ||
01409       !mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.rec_buff))
01410   {
01411     mi_check_print_error(param, "Not enough memory for extra record");
01412     goto err;
01413   }
01414 
01415   if (!rep_quick)
01416   {
01417     /* Get real path for data file */
01418     if ((new_file=my_create(internal::fn_format(param->temp_filename,
01419                                       share->data_file_name, "",
01420                                       DATA_TMP_EXT, 2+4),
01421                             0,param->tmpfile_createflag,
01422                             MYF(0))) < 0)
01423     {
01424       mi_check_print_error(param,"Can't create new tempfile: '%s'",
01425                            param->temp_filename);
01426       goto err;
01427     }
01428     if (new_header_length &&
01429         filecopy(param,new_file,info->dfile,0L,new_header_length,
01430                  "datafile-header"))
01431       goto err;
01432     info->s->state.dellink= HA_OFFSET_ERROR;
01433     info->rec_cache.file=new_file;
01434     if (param->testflag & T_UNPACK)
01435     {
01436       share->options&= ~HA_OPTION_COMPRESS_RECORD;
01437       mi_int2store(share->state.header.options,share->options);
01438     }
01439   }
01440   sort_info.info=info;
01441   sort_info.param = param;
01442   sort_param.read_cache=param->read_cache;
01443   sort_param.pos=sort_param.max_pos=share->pack.header_length;
01444   sort_param.filepos=new_header_length;
01445   param->read_cache.end_of_file=sort_info.filelength=
01446     lseek(info->dfile,0L,SEEK_END);
01447   sort_info.dupp=0;
01448   sort_param.fix_datafile= (bool) (! rep_quick);
01449   sort_param.master=1;
01450   sort_info.max_records= ~(ha_rows) 0;
01451 
01452   set_data_file_type(&sort_info, share);
01453   del=info->state->del;
01454   info->state->records=info->state->del=share->state.split=0;
01455   info->state->empty=0;
01456   param->glob_crc=0;
01457   if (param->testflag & T_CALC_CHECKSUM)
01458     sort_param.calc_checksum= 1;
01459 
01460   info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
01461 
01462   /* This function always recreates all enabled indexes. */
01463   if (param->testflag & T_CREATE_MISSING_KEYS)
01464     mi_set_all_keys_active(share->state.key_map, share->base.keys);
01465   mi_drop_all_indexes(param, info, true);
01466 
01467   lock_memory(param);     /* Everything is alloced */
01468 
01469   /* Re-create all keys, which are set in key_map. */
01470   while (!(error=sort_get_next_record(&sort_param)))
01471   {
01472     if (writekeys(&sort_param))
01473     {
01474       if (errno != HA_ERR_FOUND_DUPP_KEY)
01475   goto err;
01476       mi_check_print_info(param,"Duplicate key %2d for record at %10s against new record at %10s",
01477         info->errkey+1,
01478         llstr(sort_param.start_recpos,llbuff),
01479         llstr(info->dupp_key_pos,llbuff2));
01480       if (param->testflag & T_VERBOSE)
01481       {
01482   _mi_make_key(info,(uint) info->errkey,info->lastkey,
01483                      sort_param.record,0L);
01484       }
01485       sort_info.dupp++;
01486       if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
01487       {
01488         param->testflag|=T_RETRY_WITHOUT_QUICK;
01489   param->error_printed=1;
01490   goto err;
01491       }
01492       continue;
01493     }
01494     if (sort_write_record(&sort_param))
01495       goto err;
01496   }
01497   if (error > 0 || write_data_suffix(&sort_info, (bool)!rep_quick) ||
01498       info->rec_cache.flush() || param->read_cache.error < 0)
01499     goto err;
01500 
01501   if (param->testflag & T_WRITE_LOOP)
01502   {
01503     fputs("          \r",stdout); fflush(stdout);
01504   }
01505   if (ftruncate(share->kfile, info->state->key_file_length))
01506   {
01507     mi_check_print_warning(param,
01508          "Can't change size of indexfile, error: %d",
01509          errno);
01510     goto err;
01511   }
01512 
01513   if (rep_quick && del+sort_info.dupp != info->state->del)
01514   {
01515     mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
01516     mi_check_print_error(param,"Run recovery again without -q");
01517     got_error=1;
01518     param->retry_repair=1;
01519     param->testflag|=T_RETRY_WITHOUT_QUICK;
01520     goto err;
01521   }
01522   if (param->testflag & T_SAFE_REPAIR)
01523   {
01524     /* Don't repair if we loosed more than one row */
01525     if (info->state->records+1 < start_records)
01526     {
01527       info->state->records=start_records;
01528       got_error=1;
01529       goto err;
01530     }
01531   }
01532 
01533   if (!rep_quick)
01534   {
01535     internal::my_close(info->dfile,MYF(0));
01536     info->dfile=new_file;
01537     info->state->data_file_length=sort_param.filepos;
01538     share->state.version=(ulong) time((time_t*) 0); /* Force reopen */
01539   }
01540   else
01541   {
01542     info->state->data_file_length=sort_param.max_pos;
01543   }
01544   if (param->testflag & T_CALC_CHECKSUM)
01545     info->state->checksum=param->glob_crc;
01546 
01547   if (!(param->testflag & T_SILENT))
01548   {
01549     if (start_records != info->state->records)
01550       printf("Data records: %s\n", llstr(info->state->records,llbuff));
01551     if (sort_info.dupp)
01552       mi_check_print_warning(param,
01553            "%s records have been removed",
01554            llstr(sort_info.dupp,llbuff));
01555   }
01556 
01557   got_error=0;
01558   /* If invoked by external program that uses thr_lock */
01559   if (&share->state.state != info->state)
01560     memcpy( &share->state.state, info->state, sizeof(*info->state));
01561 
01562 err:
01563   if (!got_error)
01564   {
01565     /* Replace the actual file with the temporary file */
01566     if (new_file >= 0)
01567     {
01568       internal::my_close(new_file,MYF(0));
01569       info->dfile=new_file= -1;
01570       if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
01571           DATA_TMP_EXT, share->base.raid_chunks,
01572           MYF(0)) ||
01573     mi_open_datafile(info,share,-1))
01574   got_error=1;
01575     }
01576   }
01577   if (got_error)
01578   {
01579     if (! param->error_printed)
01580       mi_check_print_error(param,"%d for record at pos %s",errno,
01581       llstr(sort_param.start_recpos,llbuff));
01582     if (new_file >= 0)
01583     {
01584       internal::my_close(new_file,MYF(0));
01585       my_delete(param->temp_filename, MYF(MY_WME));
01586       info->rec_cache.file=-1; /* don't flush data to new_file, it's closed */
01587     }
01588     mi_mark_crashed_on_repair(info);
01589   }
01590 
01591   void * rec_buff_ptr= NULL;
01592   rec_buff_ptr= mi_get_rec_buff_ptr(info, sort_param.rec_buff);
01593   free(rec_buff_ptr);
01594   rec_buff_ptr= mi_get_rec_buff_ptr(info, sort_param.record);
01595   free(rec_buff_ptr);
01596   rec_buff_ptr= NULL;
01597 
01598   free(sort_info.buff);
01599   param->read_cache.end_io_cache();
01600   info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
01601   info->rec_cache.end_io_cache();
01602   if (not got_error && param->testflag & T_UNPACK)
01603   {
01604     share->state.header.options[0]&= (unsigned char) ~HA_OPTION_COMPRESS_RECORD;
01605     share->pack.header_length=0;
01606     share->data_file_type=sort_info.new_data_file_type;
01607   }
01608   share->state.changed|= (STATE_NOT_OPTIMIZED_KEYS | STATE_NOT_SORTED_PAGES |
01609         STATE_NOT_ANALYZED);
01610   return(got_error);
01611 }
01612 
01613 
01614 /* Uppate keyfile when doing repair */
01615 
01616 static int writekeys(MI_SORT_PARAM *sort_param)
01617 {
01618   register uint32_t i;
01619   unsigned char    *key;
01620   MI_INFO  *info=   sort_param->sort_info->info;
01621   unsigned char    *buff=   sort_param->record;
01622   my_off_t filepos= sort_param->filepos;
01623 
01624   key=info->lastkey+info->s->base.max_key_length;
01625   for (i=0 ; i < info->s->base.keys ; i++)
01626   {
01627     if (mi_is_key_active(info->s->state.key_map, i))
01628     {
01629       {
01630   uint32_t key_length=_mi_make_key(info,i,key,buff,filepos);
01631   if (_mi_ck_write(info,i,key,key_length))
01632     goto err;
01633       }
01634     }
01635   }
01636   return(0);
01637 
01638  err:
01639   if (errno == HA_ERR_FOUND_DUPP_KEY)
01640   {
01641     info->errkey=(int) i;     /* This key was found */
01642     while ( i-- > 0 )
01643     {
01644       if (mi_is_key_active(info->s->state.key_map, i))
01645       {
01646   {
01647     uint32_t key_length=_mi_make_key(info,i,key,buff,filepos);
01648     if (_mi_ck_delete(info,i,key,key_length))
01649       break;
01650   }
01651       }
01652     }
01653   }
01654   /* Remove checksum that was added to glob_crc in sort_get_next_record */
01655   if (sort_param->calc_checksum)
01656     sort_param->sort_info->param->glob_crc-= info->checksum;
01657   return(-1);
01658 } /* writekeys */
01659 
01660 
01661   /* Change all key-pointers that points to a records */
01662 
01663 int movepoint(register MI_INFO *info, unsigned char *record, my_off_t oldpos,
01664         my_off_t newpos, uint32_t prot_key)
01665 {
01666   register uint32_t i;
01667   unsigned char *key;
01668   uint32_t key_length;
01669 
01670   key=info->lastkey+info->s->base.max_key_length;
01671   for (i=0 ; i < info->s->base.keys; i++)
01672   {
01673     if (i != prot_key && mi_is_key_active(info->s->state.key_map, i))
01674     {
01675       key_length=_mi_make_key(info,i,key,record,oldpos);
01676       if (info->s->keyinfo[i].flag & HA_NOSAME)
01677       {         /* Change pointer direct */
01678   uint32_t nod_flag;
01679   MI_KEYDEF *keyinfo;
01680   keyinfo=info->s->keyinfo+i;
01681   if (_mi_search(info,keyinfo,key,USE_WHOLE_KEY,
01682            (uint) (SEARCH_SAME | SEARCH_SAVE_BUFF),
01683            info->s->state.key_root[i]))
01684     return(-1);
01685   nod_flag=mi_test_if_nod(info->buff);
01686   _mi_dpointer(info,info->int_keypos-nod_flag-
01687          info->s->rec_reflength,newpos);
01688   if (_mi_write_keypage(info,keyinfo,info->last_keypage,
01689                               DFLT_INIT_HITS,info->buff))
01690     return(-1);
01691       }
01692       else
01693       {         /* Change old key to new */
01694   if (_mi_ck_delete(info,i,key,key_length))
01695     return(-1);
01696   key_length=_mi_make_key(info,i,key,record,newpos);
01697   if (_mi_ck_write(info,i,key,key_length))
01698     return(-1);
01699       }
01700     }
01701   }
01702   return(0);
01703 } /* movepoint */
01704 
01705 
01706   /* Tell system that we want all memory for our cache */
01707 
01708 void lock_memory(MI_CHECK *)
01709 {
01710 } /* lock_memory */
01711 
01712 
01713 /* Sort index for more efficent reads */
01714 
01715 int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name)
01716 {
01717   register uint32_t key;
01718   register MI_KEYDEF *keyinfo;
01719   int new_file;
01720   my_off_t index_pos[HA_MAX_POSSIBLE_KEY];
01721   uint32_t r_locks,w_locks;
01722   int old_lock;
01723   MYISAM_SHARE *share=info->s;
01724   MI_STATE_INFO old_state;
01725 
01726   /* cannot sort index files with R-tree indexes */
01727   for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
01728        key++,keyinfo++)
01729 
01730   if (!(param->testflag & T_SILENT))
01731     printf("- Sorting index for MyISAM-table '%s'\n",name);
01732 
01733   /* Get real path for index file */
01734   internal::fn_format(param->temp_filename,name,"", MI_NAME_IEXT,2+4+32);
01735   if ((new_file=my_create(internal::fn_format(param->temp_filename,param->temp_filename,
01736             "", INDEX_TMP_EXT,2+4),
01737         0,param->tmpfile_createflag,MYF(0))) <= 0)
01738   {
01739     mi_check_print_error(param,"Can't create new tempfile: '%s'",
01740        param->temp_filename);
01741     return(-1);
01742   }
01743   if (filecopy(param, new_file,share->kfile,0L,
01744          (ulong) share->base.keystart, "headerblock"))
01745     goto err;
01746 
01747   param->new_file_pos=share->base.keystart;
01748   for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
01749        key++,keyinfo++)
01750   {
01751     if (! mi_is_key_active(info->s->state.key_map, key))
01752       continue;
01753 
01754     if (share->state.key_root[key] != HA_OFFSET_ERROR)
01755     {
01756       index_pos[key]=param->new_file_pos; /* Write first block here */
01757       if (sort_one_index(param,info,keyinfo,share->state.key_root[key],
01758        new_file))
01759   goto err;
01760     }
01761     else
01762       index_pos[key]= HA_OFFSET_ERROR;    /* No blocks */
01763   }
01764 
01765   share->state.version=(ulong) time(NULL);
01766   old_state= share->state;      /* save state if not stored */
01767   r_locks=   share->r_locks;
01768   w_locks=   share->w_locks;
01769   old_lock=  info->lock_type;
01770 
01771   /* Put same locks as old file */
01772   share->r_locks= share->w_locks= share->tot_locks= 0;
01773   (void) _mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE);
01774   internal::my_close(share->kfile,MYF(MY_WME));
01775   share->kfile = -1;
01776   internal::my_close(new_file,MYF(MY_WME));
01777   if (change_to_newfile(share->index_file_name,MI_NAME_IEXT,INDEX_TMP_EXT,0,
01778       MYF(0)) ||
01779       mi_open_keyfile(share))
01780     goto err2;
01781   info->lock_type= F_UNLCK;     /* Force mi_readinfo to lock */
01782   _mi_readinfo(info,F_WRLCK,0);     /* Will lock the table */
01783   info->lock_type=  old_lock;
01784   share->r_locks=   r_locks;
01785   share->w_locks=   w_locks;
01786   share->tot_locks= r_locks+w_locks;
01787   share->state=     old_state;      /* Restore old state */
01788 
01789   info->state->key_file_length=param->new_file_pos;
01790   info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
01791   for (key=0 ; key < info->s->base.keys ; key++)
01792     info->s->state.key_root[key]=index_pos[key];
01793   for (key=0 ; key < info->s->state.header.max_block_size_index ; key++)
01794     info->s->state.key_del[key]=  HA_OFFSET_ERROR;
01795 
01796   info->s->state.changed&= ~STATE_NOT_SORTED_PAGES;
01797   return(0);
01798 
01799 err:
01800   internal::my_close(new_file,MYF(MY_WME));
01801 err2:
01802   my_delete(param->temp_filename,MYF(MY_WME));
01803   return(-1);
01804 } /* mi_sort_index */
01805 
01806 
01807    /* Sort records recursive using one index */
01808 
01809 static int sort_one_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
01810         my_off_t pagepos, int new_file)
01811 {
01812   uint32_t length,nod_flag,used_length, key_length;
01813   unsigned char *buff,*keypos,*endpos;
01814   unsigned char key[HA_MAX_POSSIBLE_KEY_BUFF];
01815   my_off_t new_page_pos,next_page;
01816   char llbuff[22];
01817 
01818   new_page_pos=param->new_file_pos;
01819   param->new_file_pos+=keyinfo->block_length;
01820 
01821   if (!(buff=(unsigned char*) malloc(keyinfo->block_length)))
01822   {
01823     mi_check_print_error(param,"Not enough memory for key block");
01824     return(-1);
01825   }
01826   if (!_mi_fetch_keypage(info,keyinfo,pagepos,DFLT_INIT_HITS,buff,0))
01827   {
01828     mi_check_print_error(param,"Can't read key block from filepos: %s",
01829     llstr(pagepos,llbuff));
01830     goto err;
01831   }
01832   if ((nod_flag=mi_test_if_nod(buff)))
01833   {
01834     used_length=mi_getint(buff);
01835     keypos=buff+2+nod_flag;
01836     endpos=buff+used_length;
01837     for ( ;; )
01838     {
01839       if (nod_flag)
01840       {
01841   next_page=_mi_kpos(nod_flag,keypos);
01842   _mi_kpointer(info,keypos-nod_flag,param->new_file_pos); /* Save new pos */
01843   if (sort_one_index(param,info,keyinfo,next_page,new_file))
01844   {
01845     goto err;
01846   }
01847       }
01848       if (keypos >= endpos ||
01849     (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0)
01850   break;
01851       assert(keypos <= endpos);
01852     }
01853   }
01854 
01855   /* Fill block with zero and write it to the new index file */
01856   length=mi_getint(buff);
01857   memset(buff+length, 0, keyinfo->block_length-length);
01858   if (my_pwrite(new_file,(unsigned char*) buff,(uint) keyinfo->block_length,
01859     new_page_pos,MYF(MY_NABP | MY_WAIT_IF_FULL)))
01860   {
01861     mi_check_print_error(param,"Can't write indexblock, error: %d",errno);
01862     goto err;
01863   }
01864   free(buff);
01865   return(0);
01866 err:
01867   free(buff);
01868   return(1);
01869 } /* sort_one_index */
01870 
01871 
01872   /*
01873     Let temporary file replace old file.
01874     This assumes that the new file was created in the same
01875     directory as given by realpath(filename).
01876     This will ensure that any symlinks that are used will still work.
01877     Copy stats from old file to new file, deletes orignal and
01878     changes new file name to old file name
01879   */
01880 
01881 int change_to_newfile(const char * filename, const char * old_ext,
01882           const char * new_ext,
01883           uint32_t raid_chunks,
01884           myf MyFlags)
01885 {
01886   (void)raid_chunks;
01887   char old_filename[FN_REFLEN],new_filename[FN_REFLEN];
01888   /* Get real path to filename */
01889   (void) internal::fn_format(old_filename,filename,"",old_ext,2+4+32);
01890   return my_redel(old_filename,
01891       internal::fn_format(new_filename,old_filename,"",new_ext,2+4),
01892       MYF(MY_WME | MY_LINK_WARNING | MyFlags));
01893 } /* change_to_newfile */
01894 
01895 
01896 
01897   /* Copy a block between two files */
01898 
01899 int filecopy(MI_CHECK *param, int to,int from,my_off_t start,
01900        my_off_t length, const char *type)
01901 {
01902   char tmp_buff[IO_SIZE],*buff;
01903   ulong buff_length;
01904 
01905   buff_length=(ulong) min(param->write_buffer_length, (size_t)length);
01906   if (!(buff=(char *)malloc(buff_length)))
01907   {
01908     buff=tmp_buff; buff_length=IO_SIZE;
01909   }
01910 
01911   lseek(from,start,SEEK_SET);
01912   while (length > buff_length)
01913   {
01914     if (my_read(from,(unsigned char*) buff,buff_length,MYF(MY_NABP)) ||
01915   my_write(to,(unsigned char*) buff,buff_length,param->myf_rw))
01916       goto err;
01917     length-= buff_length;
01918   }
01919   if (my_read(from,(unsigned char*) buff,(uint) length,MYF(MY_NABP)) ||
01920       my_write(to,(unsigned char*) buff,(uint) length,param->myf_rw))
01921     goto err;
01922   if (buff != tmp_buff)
01923     free(buff);
01924   return(0);
01925 err:
01926   if (buff != tmp_buff)
01927     free(buff);
01928   mi_check_print_error(param,"Can't copy %s to tempfile, error %d",
01929            type,errno);
01930   return(1);
01931 }
01932 
01933 
01934 /*
01935   Repair table or given index using sorting
01936 
01937   SYNOPSIS
01938     mi_repair_by_sort()
01939     param   Repair parameters
01940     info    MyISAM handler to repair
01941     name    Name of table (for warnings)
01942     rep_quick   set to <> 0 if we should not change data file
01943 
01944   RESULT
01945     0 ok
01946     <>0 Error
01947 */
01948 
01949 int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
01950           const char * name, int rep_quick)
01951 {
01952   int got_error;
01953   uint32_t i;
01954   ulong length;
01955   ha_rows start_records;
01956   my_off_t new_header_length,del;
01957   int new_file;
01958   MI_SORT_PARAM sort_param;
01959   MYISAM_SHARE *share=info->s;
01960   HA_KEYSEG *keyseg;
01961   ulong   *rec_per_key_part;
01962   char llbuff[22];
01963   SORT_INFO sort_info;
01964   uint64_t key_map= 0;
01965 
01966   start_records=info->state->records;
01967   got_error=1;
01968   new_file= -1;
01969   new_header_length=(param->testflag & T_UNPACK) ? 0 :
01970     share->pack.header_length;
01971   if (!(param->testflag & T_SILENT))
01972   {
01973     printf("- recovering (with sort) MyISAM-table '%s'\n",name);
01974     printf("Data records: %s\n", llstr(start_records,llbuff));
01975   }
01976   param->testflag|=T_REP; /* for easy checking */
01977 
01978   if (info->s->options & (HA_OPTION_COMPRESS_RECORD))
01979     param->testflag|=T_CALC_CHECKSUM;
01980 
01981   memset(&sort_info, 0, sizeof(sort_info));
01982   memset(&sort_param, 0, sizeof(sort_param));
01983   if (!(sort_info.key_block=
01984         alloc_key_blocks(param, (uint) param->sort_key_blocks, share->base.max_key_block_length))
01985       || param->read_cache.init_io_cache(info->dfile, (uint) param->read_buffer_length, READ_CACHE,share->pack.header_length,1,MYF(MY_WME))
01986       || (! rep_quick && info->rec_cache.init_io_cache(info->dfile, (uint) param->write_buffer_length, WRITE_CACHE,new_header_length,1, MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw)))
01987   {
01988     goto err;
01989   }
01990   sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
01991   info->opt_flag|=WRITE_CACHE_USED;
01992   info->rec_cache.file=info->dfile;   /* for sort_delete_record */
01993 
01994   if (!mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.record) ||
01995       !mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.rec_buff))
01996   {
01997     mi_check_print_error(param, "Not enough memory for extra record");
01998     goto err;
01999   }
02000   if (!rep_quick)
02001   {
02002     /* Get real path for data file */
02003     if ((new_file=my_create(internal::fn_format(param->temp_filename,
02004                                       share->data_file_name, "",
02005                                       DATA_TMP_EXT, 2+4),
02006                             0,param->tmpfile_createflag,
02007                             MYF(0))) < 0)
02008     {
02009       mi_check_print_error(param,"Can't create new tempfile: '%s'",
02010                            param->temp_filename);
02011       goto err;
02012     }
02013     if (new_header_length &&
02014         filecopy(param, new_file,info->dfile,0L,new_header_length,
02015      "datafile-header"))
02016       goto err;
02017     if (param->testflag & T_UNPACK)
02018     {
02019       share->options&= ~HA_OPTION_COMPRESS_RECORD;
02020       mi_int2store(share->state.header.options,share->options);
02021     }
02022     share->state.dellink= HA_OFFSET_ERROR;
02023     info->rec_cache.file=new_file;
02024   }
02025 
02026   info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
02027 
02028   /* Optionally drop indexes and optionally modify the key_map. */
02029   mi_drop_all_indexes(param, info, false);
02030   key_map= share->state.key_map;
02031   if (param->testflag & T_CREATE_MISSING_KEYS)
02032   {
02033     /* Invert the copied key_map to recreate all disabled indexes. */
02034     key_map= ~key_map;
02035   }
02036 
02037   sort_info.info=info;
02038   sort_info.param = param;
02039 
02040   set_data_file_type(&sort_info, share);
02041   sort_param.filepos=new_header_length;
02042   sort_info.dupp=0;
02043   sort_info.buff=0;
02044   param->read_cache.end_of_file=sort_info.filelength=
02045     lseek(param->read_cache.file,0L,SEEK_END);
02046 
02047   sort_param.wordlist=NULL;
02048 
02049   if (share->data_file_type == DYNAMIC_RECORD)
02050     length=max(share->base.min_pack_length+1,share->base.min_block_length);
02051   else if (share->data_file_type == COMPRESSED_RECORD)
02052     length=share->base.min_block_length;
02053   else
02054     length=share->base.pack_reclength;
02055   sort_info.max_records=
02056     ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records :
02057      (ha_rows) (sort_info.filelength/length+1));
02058   sort_param.key_cmp=sort_key_cmp;
02059   sort_param.lock_in_memory=lock_memory;
02060   sort_param.sort_info=&sort_info;
02061   sort_param.fix_datafile= (bool) (! rep_quick);
02062   sort_param.master =1;
02063 
02064   del=info->state->del;
02065   param->glob_crc=0;
02066   if (param->testflag & T_CALC_CHECKSUM)
02067     sort_param.calc_checksum= 1;
02068 
02069   rec_per_key_part= param->rec_per_key_part;
02070   for (sort_param.key=0 ; sort_param.key < share->base.keys ;
02071        rec_per_key_part+=sort_param.keyinfo->keysegs, sort_param.key++)
02072   {
02073     sort_param.read_cache=param->read_cache;
02074     sort_param.keyinfo=share->keyinfo+sort_param.key;
02075     sort_param.seg=sort_param.keyinfo->seg;
02076     /*
02077       Skip this index if it is marked disabled in the copied
02078       (and possibly inverted) key_map.
02079     */
02080     if (! mi_is_key_active(key_map, sort_param.key))
02081     {
02082       /* Remember old statistics for key */
02083       assert(rec_per_key_part >= param->rec_per_key_part);
02084       memcpy(rec_per_key_part,
02085        (share->state.rec_per_key_part +
02086               (rec_per_key_part - param->rec_per_key_part)),
02087        sort_param.keyinfo->keysegs*sizeof(*rec_per_key_part));
02088       continue;
02089     }
02090 
02091     if ((!(param->testflag & T_SILENT)))
02092       printf ("- Fixing index %d\n",sort_param.key+1);
02093     sort_param.max_pos=sort_param.pos=share->pack.header_length;
02094     keyseg=sort_param.seg;
02095     memset(sort_param.unique, 0, sizeof(sort_param.unique));
02096     sort_param.key_length=share->rec_reflength;
02097     for (i=0 ; keyseg[i].type != HA_KEYTYPE_END; i++)
02098     {
02099       sort_param.key_length+=keyseg[i].length;
02100       if (keyseg[i].flag & HA_SPACE_PACK)
02101   sort_param.key_length+=get_pack_length(keyseg[i].length);
02102       if (keyseg[i].flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
02103   sort_param.key_length+=2 + test(keyseg[i].length >= 127);
02104       if (keyseg[i].flag & HA_NULL_PART)
02105   sort_param.key_length++;
02106     }
02107     info->state->records=info->state->del=share->state.split=0;
02108     info->state->empty=0;
02109 
02110     {
02111       sort_param.key_read=sort_key_read;
02112       sort_param.key_write=sort_key_write;
02113     }
02114 
02115     if (_create_index_by_sort(&sort_param,
02116             (bool) (!(param->testflag & T_VERBOSE)),
02117             (uint) param->sort_buffer_length))
02118     {
02119       param->retry_repair=1;
02120       goto err;
02121     }
02122     /* No need to calculate checksum again. */
02123     sort_param.calc_checksum= 0;
02124     sort_param.wordroot.free_root(MYF(0));
02125 
02126     /* Set for next loop */
02127     sort_info.max_records= (ha_rows) info->state->records;
02128 
02129     if (param->testflag & T_STATISTICS)
02130       update_key_parts(sort_param.keyinfo, rec_per_key_part, sort_param.unique,
02131                        param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
02132                        sort_param.notnull: NULL,
02133                        (uint64_t) info->state->records);
02134     /* Enable this index in the permanent (not the copied) key_map. */
02135     mi_set_key_active(share->state.key_map, sort_param.key);
02136 
02137     if (sort_param.fix_datafile)
02138     {
02139       param->read_cache.end_of_file=sort_param.filepos;
02140       if (write_data_suffix(&sort_info, 1) || info->rec_cache.end_io_cache())
02141       {
02142   goto err;
02143       }
02144       if (param->testflag & T_SAFE_REPAIR)
02145       {
02146   /* Don't repair if we loosed more than one row */
02147   if (info->state->records+1 < start_records)
02148   {
02149     info->state->records=start_records;
02150     goto err;
02151   }
02152       }
02153       share->state.state.data_file_length = info->state->data_file_length=
02154   sort_param.filepos;
02155       /* Only whole records */
02156       share->state.version=(ulong) time((time_t*) 0);
02157       internal::my_close(info->dfile,MYF(0));
02158       info->dfile=new_file;
02159       share->data_file_type=sort_info.new_data_file_type;
02160       share->pack.header_length=(ulong) new_header_length;
02161       sort_param.fix_datafile=0;
02162     }
02163     else
02164       info->state->data_file_length=sort_param.max_pos;
02165 
02166     param->read_cache.file=info->dfile;   /* re-init read cache */
02167     param->read_cache.reinit_io_cache(READ_CACHE,share->pack.header_length, 1,1);
02168   }
02169 
02170   if (param->testflag & T_WRITE_LOOP)
02171   {
02172     fputs("          \r",stdout); fflush(stdout);
02173   }
02174 
02175   if (rep_quick && del+sort_info.dupp != info->state->del)
02176   {
02177     mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
02178     mi_check_print_error(param,"Run recovery again without -q");
02179     got_error=1;
02180     param->retry_repair=1;
02181     param->testflag|=T_RETRY_WITHOUT_QUICK;
02182     goto err;
02183   }
02184 
02185   if (rep_quick & T_FORCE_UNIQUENESS)
02186   {
02187     my_off_t skr=info->state->data_file_length+
02188       (share->options & HA_OPTION_COMPRESS_RECORD ?
02189        MEMMAP_EXTRA_MARGIN : 0);
02190 #ifdef USE_RELOC
02191     if (share->data_file_type == STATIC_RECORD &&
02192   skr < share->base.reloc*share->base.min_pack_length)
02193       skr=share->base.reloc*share->base.min_pack_length;
02194 #endif
02195     if (skr != sort_info.filelength && !info->s->base.raid_type)
02196       if (ftruncate(info->dfile, skr))
02197   mi_check_print_warning(param,
02198              "Can't change size of datafile,  error: %d",
02199              errno);
02200   }
02201   if (param->testflag & T_CALC_CHECKSUM)
02202     info->state->checksum=param->glob_crc;
02203 
02204   if (ftruncate(share->kfile, info->state->key_file_length))
02205     mi_check_print_warning(param,
02206          "Can't change size of indexfile, error: %d",
02207          errno);
02208 
02209   if (!(param->testflag & T_SILENT))
02210   {
02211     if (start_records != info->state->records)
02212       printf("Data records: %s\n", llstr(info->state->records,llbuff));
02213     if (sort_info.dupp)
02214       mi_check_print_warning(param,
02215            "%s records have been removed",
02216            llstr(sort_info.dupp,llbuff));
02217   }
02218   got_error=0;
02219 
02220   if (&share->state.state != info->state)
02221     memcpy( &share->state.state, info->state, sizeof(*info->state));
02222 
02223 err:
02224   info->rec_cache.end_io_cache();
02225   if (!got_error)
02226   {
02227     /* Replace the actual file with the temporary file */
02228     if (new_file >= 0)
02229     {
02230       internal::my_close(new_file,MYF(0));
02231       info->dfile=new_file= -1;
02232       if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
02233           DATA_TMP_EXT, share->base.raid_chunks,
02234           MYF(0)) ||
02235     mi_open_datafile(info,share,-1))
02236   got_error=1;
02237     }
02238   }
02239   if (got_error)
02240   {
02241     if (! param->error_printed)
02242       mi_check_print_error(param,"%d when fixing table",errno);
02243     if (new_file >= 0)
02244     {
02245       internal::my_close(new_file,MYF(0));
02246       my_delete(param->temp_filename, MYF(MY_WME));
02247       if (info->dfile == new_file)
02248         info->dfile= -1;
02249     }
02250     mi_mark_crashed_on_repair(info);
02251   }
02252   else if (key_map == share->state.key_map)
02253     share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
02254   share->state.changed|=STATE_NOT_SORTED_PAGES;
02255 
02256   void * rec_buff_ptr= NULL;
02257   rec_buff_ptr= mi_get_rec_buff_ptr(info, sort_param.rec_buff);
02258   free(rec_buff_ptr);
02259   rec_buff_ptr= mi_get_rec_buff_ptr(info, sort_param.record);
02260   free(rec_buff_ptr);
02261   rec_buff_ptr= NULL;
02262 
02263   free((unsigned char*) sort_info.key_block);
02264   free(sort_info.buff);
02265   param->read_cache.end_io_cache();
02266   info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
02267   if (!got_error && (param->testflag & T_UNPACK))
02268   {
02269     share->state.header.options[0]&= (unsigned char) ~HA_OPTION_COMPRESS_RECORD;
02270     share->pack.header_length=0;
02271   }
02272   return(got_error);
02273 }
02274 
02275   /* Read next record and return next key */
02276 
02277 int sort_key_read(MI_SORT_PARAM *sort_param, void *key)
02278 {
02279   int error;
02280   SORT_INFO *sort_info=sort_param->sort_info;
02281   MI_INFO *info=sort_info->info;
02282 
02283   if ((error=sort_get_next_record(sort_param)))
02284     return(error);
02285   if (info->state->records == sort_info->max_records)
02286   {
02287     mi_check_print_error(sort_info->param,
02288        "Key %d - Found too many records; Can't continue",
02289                          sort_param->key+1);
02290     return(1);
02291   }
02292   sort_param->real_key_length=
02293     (info->s->rec_reflength+
02294      _mi_make_key(info, sort_param->key, (unsigned char*) key,
02295       sort_param->record, sort_param->filepos));
02296 #ifdef HAVE_VALGRIND
02297   memset((unsigned char *)key+sort_param->real_key_length, 0,
02298          (sort_param->key_length-sort_param->real_key_length));
02299 #endif
02300   return(sort_write_record(sort_param));
02301 } /* sort_key_read */
02302 
02303 
02304 /*
02305   Read next record from file using parameters in sort_info.
02306 
02307   SYNOPSIS
02308     sort_get_next_record()
02309       sort_param                Information about and for the sort process
02310 
02311   NOTE
02312 
02313     Dynamic Records With Non-Quick Parallel Repair
02314 
02315       For non-quick parallel repair we use a synchronized read/write
02316       cache. This means that one thread is the master who fixes the data
02317       file by reading each record from the old data file and writing it
02318       to the new data file. By doing this the records in the new data
02319       file are written contiguously. Whenever the write buffer is full,
02320       it is copied to the read buffer. The slaves read from the read
02321       buffer, which is not associated with a file. Thus read_cache.file
02322       is -1. When using _mi_read_cache(), the slaves must always set
02323       flag to READING_NEXT so that the function never tries to read from
02324       file. This is safe because the records are contiguous. There is no
02325       need to read outside the cache. This condition is evaluated in the
02326       variable 'parallel_flag' for quick reference. read_cache.file must
02327       be >= 0 in every other case.
02328 
02329   RETURN
02330     -1          end of file
02331     0           ok
02332     > 0         error
02333 */
02334 
02335 int sort_get_next_record(MI_SORT_PARAM *sort_param)
02336 {
02337   int searching;
02338   int parallel_flag;
02339   uint32_t found_record,b_type,left_length;
02340   my_off_t pos;
02341   unsigned char *to= NULL;
02342   MI_BLOCK_INFO block_info;
02343   SORT_INFO *sort_info=sort_param->sort_info;
02344   MI_CHECK *param=sort_info->param;
02345   MI_INFO *info=sort_info->info;
02346   MYISAM_SHARE *share=info->s;
02347   char llbuff[22],llbuff2[22];
02348 
02349   if (*killed_ptr(param))
02350     return(1);
02351 
02352   switch (share->data_file_type) {
02353   case STATIC_RECORD:
02354     for (;;)
02355     {
02356       if (sort_param->read_cache.read(sort_param->record, share->base.pack_reclength))
02357       {
02358   if (sort_param->read_cache.error)
02359     param->out_flag |= O_DATA_LOST;
02360         param->retry_repair=1;
02361         param->testflag|=T_RETRY_WITHOUT_QUICK;
02362   return(-1);
02363       }
02364       sort_param->start_recpos=sort_param->pos;
02365       if (!sort_param->fix_datafile)
02366       {
02367   sort_param->filepos=sort_param->pos;
02368         if (sort_param->master)
02369     share->state.split++;
02370       }
02371       sort_param->max_pos=(sort_param->pos+=share->base.pack_reclength);
02372       if (*sort_param->record)
02373       {
02374   if (sort_param->calc_checksum)
02375     param->glob_crc+= (info->checksum=
02376            mi_static_checksum(info,sort_param->record));
02377   return(0);
02378       }
02379       if (!sort_param->fix_datafile && sort_param->master)
02380       {
02381   info->state->del++;
02382   info->state->empty+=share->base.pack_reclength;
02383       }
02384     }
02385   case DYNAMIC_RECORD:
02386     pos= sort_param->pos;
02387     searching= (sort_param->fix_datafile && (param->testflag & T_EXTEND));
02388     parallel_flag= (sort_param->read_cache.file < 0) ? READING_NEXT : 0;
02389     for (;;)
02390     {
02391       found_record=block_info.second_read= 0;
02392       left_length=1;
02393       if (searching)
02394       {
02395   pos=MY_ALIGN(pos,MI_DYN_ALIGN_SIZE);
02396         param->testflag|=T_RETRY_WITHOUT_QUICK;
02397   sort_param->start_recpos=pos;
02398       }
02399       do
02400       {
02401   if (pos > sort_param->max_pos)
02402     sort_param->max_pos=pos;
02403   if (pos & (MI_DYN_ALIGN_SIZE-1))
02404   {
02405     if ((param->testflag & T_VERBOSE) || searching == 0)
02406       mi_check_print_info(param,"Wrong aligned block at %s",
02407         llstr(pos,llbuff));
02408     if (searching)
02409       goto try_next;
02410   }
02411   if (found_record && pos == param->search_after_block)
02412     mi_check_print_info(param,"Block: %s used by record at %s",
02413          llstr(param->search_after_block,llbuff),
02414          llstr(sort_param->start_recpos,llbuff2));
02415   if (_mi_read_cache(&sort_param->read_cache,
02416                            (unsigned char*) block_info.header,pos,
02417          MI_BLOCK_INFO_HEADER_LENGTH,
02418          (! found_record ? READING_NEXT : 0) |
02419                            parallel_flag | READING_HEADER))
02420   {
02421     if (found_record)
02422     {
02423       mi_check_print_info(param,
02424         "Can't read whole record at %s (errno: %d)",
02425         llstr(sort_param->start_recpos,llbuff),errno);
02426       goto try_next;
02427     }
02428     return(-1);
02429   }
02430   if (searching && ! sort_param->fix_datafile)
02431   {
02432     param->error_printed=1;
02433           param->retry_repair=1;
02434           param->testflag|=T_RETRY_WITHOUT_QUICK;
02435     return(1);  /* Something wrong with data */
02436   }
02437   b_type=_mi_get_block_info(&block_info,-1,pos);
02438   if ((b_type & (BLOCK_ERROR | BLOCK_FATAL_ERROR)) ||
02439      ((b_type & BLOCK_FIRST) &&
02440        (block_info.rec_len < (uint) share->base.min_pack_length ||
02441         block_info.rec_len > (uint) share->base.max_pack_length)))
02442   {
02443     uint32_t i;
02444     if (param->testflag & T_VERBOSE || searching == 0)
02445       mi_check_print_info(param,
02446         "Wrong bytesec: %3d-%3d-%3d at %10s; Skipped",
02447            block_info.header[0],block_info.header[1],
02448            block_info.header[2],llstr(pos,llbuff));
02449     if (found_record)
02450       goto try_next;
02451     block_info.second_read=0;
02452     searching=1;
02453     /* Search after block in read header string */
02454     for (i=MI_DYN_ALIGN_SIZE ;
02455          i < MI_BLOCK_INFO_HEADER_LENGTH ;
02456          i+= MI_DYN_ALIGN_SIZE)
02457       if (block_info.header[i] >= 1 &&
02458     block_info.header[i] <= MI_MAX_DYN_HEADER_BYTE)
02459         break;
02460     pos+=(ulong) i;
02461     sort_param->start_recpos=pos;
02462     continue;
02463   }
02464   if (b_type & BLOCK_DELETED)
02465   {
02466     bool error=0;
02467     if (block_info.block_len+ (uint) (block_info.filepos-pos) <
02468         share->base.min_block_length)
02469     {
02470       if (!searching)
02471         mi_check_print_info(param,
02472           "Deleted block with impossible length %u at %s",
02473           block_info.block_len,llstr(pos,llbuff));
02474       error=1;
02475     }
02476     else
02477     {
02478       if ((block_info.next_filepos != HA_OFFSET_ERROR &&
02479      block_info.next_filepos >=
02480      info->state->data_file_length) ||
02481     (block_info.prev_filepos != HA_OFFSET_ERROR &&
02482      block_info.prev_filepos >= info->state->data_file_length))
02483       {
02484         if (!searching)
02485     mi_check_print_info(param,
02486             "Delete link points outside datafile at %s",
02487             llstr(pos,llbuff));
02488         error=1;
02489       }
02490     }
02491     if (error)
02492     {
02493       if (found_record)
02494         goto try_next;
02495       searching=1;
02496       pos+= MI_DYN_ALIGN_SIZE;
02497       sort_param->start_recpos=pos;
02498       block_info.second_read=0;
02499       continue;
02500     }
02501   }
02502   else
02503   {
02504     if (block_info.block_len+ (uint) (block_info.filepos-pos) <
02505         share->base.min_block_length ||
02506         block_info.block_len > (uint) share->base.max_pack_length+
02507         MI_SPLIT_LENGTH)
02508     {
02509       if (!searching)
02510         mi_check_print_info(param,
02511           "Found block with impossible length %u at %s; Skipped",
02512           block_info.block_len+ (uint) (block_info.filepos-pos),
02513           llstr(pos,llbuff));
02514       if (found_record)
02515         goto try_next;
02516       searching=1;
02517       pos+= MI_DYN_ALIGN_SIZE;
02518       sort_param->start_recpos=pos;
02519       block_info.second_read=0;
02520       continue;
02521     }
02522   }
02523   if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
02524   {
02525           if (!sort_param->fix_datafile && sort_param->master &&
02526               (b_type & BLOCK_DELETED))
02527     {
02528       info->state->empty+=block_info.block_len;
02529       info->state->del++;
02530       share->state.split++;
02531     }
02532     if (found_record)
02533       goto try_next;
02534     if (searching)
02535     {
02536       pos+=MI_DYN_ALIGN_SIZE;
02537       sort_param->start_recpos=pos;
02538     }
02539     else
02540       pos=block_info.filepos+block_info.block_len;
02541     block_info.second_read=0;
02542     continue;
02543   }
02544 
02545   if (!sort_param->fix_datafile && sort_param->master)
02546     share->state.split++;
02547   if (! found_record++)
02548   {
02549     sort_param->find_length=left_length=block_info.rec_len;
02550     sort_param->start_recpos=pos;
02551     if (!sort_param->fix_datafile)
02552       sort_param->filepos=sort_param->start_recpos;
02553     if (sort_param->fix_datafile && (param->testflag & T_EXTEND))
02554       sort_param->pos=block_info.filepos+1;
02555     else
02556       sort_param->pos=block_info.filepos+block_info.block_len;
02557     if (share->base.blobs)
02558     {
02559       if (!(to=mi_alloc_rec_buff(info,block_info.rec_len,
02560                &(sort_param->rec_buff))))
02561       {
02562         if (param->max_record_length >= block_info.rec_len)
02563         {
02564     mi_check_print_error(param,"Not enough memory for blob at %s (need %lu)",
02565              llstr(sort_param->start_recpos,llbuff),
02566              (ulong) block_info.rec_len);
02567     return(1);
02568         }
02569         else
02570         {
02571     mi_check_print_info(param,"Not enough memory for blob at %s (need %lu); Row skipped",
02572             llstr(sort_param->start_recpos,llbuff),
02573             (ulong) block_info.rec_len);
02574     goto try_next;
02575         }
02576       }
02577     }
02578     else
02579       to= sort_param->rec_buff;
02580   }
02581   if (left_length < block_info.data_len || ! block_info.data_len)
02582   {
02583     mi_check_print_info(param,
02584             "Found block with too small length at %s; Skipped",
02585             llstr(sort_param->start_recpos,llbuff));
02586     goto try_next;
02587   }
02588   if (block_info.filepos + block_info.data_len >
02589       sort_param->read_cache.end_of_file)
02590   {
02591     mi_check_print_info(param,
02592             "Found block that points outside data file at %s",
02593             llstr(sort_param->start_recpos,llbuff));
02594     goto try_next;
02595   }
02596         /*
02597           Copy information that is already read. Avoid accessing data
02598           below the cache start. This could happen if the header
02599           streched over the end of the previous buffer contents.
02600         */
02601         {
02602           uint32_t header_len= (uint) (block_info.filepos - pos);
02603           uint32_t prefetch_len= (MI_BLOCK_INFO_HEADER_LENGTH - header_len);
02604 
02605           if (prefetch_len > block_info.data_len)
02606             prefetch_len= block_info.data_len;
02607           if (prefetch_len)
02608           {
02609             memcpy(to, block_info.header + header_len, prefetch_len);
02610             block_info.filepos+= prefetch_len;
02611             block_info.data_len-= prefetch_len;
02612             left_length-= prefetch_len;
02613             to+= prefetch_len;
02614           }
02615         }
02616         if (block_info.data_len &&
02617             _mi_read_cache(&sort_param->read_cache,to,block_info.filepos,
02618                            block_info.data_len,
02619                            (found_record == 1 ? READING_NEXT : 0) |
02620                            parallel_flag))
02621   {
02622     mi_check_print_info(param,
02623             "Read error for block at: %s (error: %d); Skipped",
02624             llstr(block_info.filepos,llbuff),errno);
02625     goto try_next;
02626   }
02627   left_length-=block_info.data_len;
02628   to+=block_info.data_len;
02629   pos=block_info.next_filepos;
02630   if (pos == HA_OFFSET_ERROR && left_length)
02631   {
02632     mi_check_print_info(param,"Wrong block with wrong total length starting at %s",
02633             llstr(sort_param->start_recpos,llbuff));
02634     goto try_next;
02635   }
02636   if (pos + MI_BLOCK_INFO_HEADER_LENGTH > sort_param->read_cache.end_of_file)
02637   {
02638     mi_check_print_info(param,"Found link that points at %s (outside data file) at %s",
02639             llstr(pos,llbuff2),
02640             llstr(sort_param->start_recpos,llbuff));
02641     goto try_next;
02642   }
02643       } while (left_length);
02644 
02645       if (_mi_rec_unpack(info,sort_param->record,sort_param->rec_buff,
02646        sort_param->find_length) != MY_FILE_ERROR)
02647       {
02648   if (sort_param->read_cache.error < 0)
02649     return(1);
02650   if (sort_param->calc_checksum)
02651     info->checksum= mi_checksum(info, sort_param->record);
02652   if ((param->testflag & (T_EXTEND | T_REP)) || searching)
02653   {
02654     if (_mi_rec_check(info, sort_param->record, sort_param->rec_buff,
02655                             sort_param->find_length,
02656                             (param->testflag & T_QUICK) &&
02657                             sort_param->calc_checksum &&
02658                             test(info->s->calc_checksum)))
02659     {
02660       mi_check_print_info(param,"Found wrong packed record at %s",
02661         llstr(sort_param->start_recpos,llbuff));
02662       goto try_next;
02663     }
02664   }
02665   if (sort_param->calc_checksum)
02666     param->glob_crc+= info->checksum;
02667   return(0);
02668       }
02669       if (!searching)
02670         mi_check_print_info(param,"Key %d - Found wrong stored record at %s",
02671                             sort_param->key+1,
02672                             llstr(sort_param->start_recpos,llbuff));
02673     try_next:
02674       pos=(sort_param->start_recpos+=MI_DYN_ALIGN_SIZE);
02675       searching=1;
02676     }
02677   case COMPRESSED_RECORD:
02678   case BLOCK_RECORD:
02679     assert(0);                                  /* Impossible */
02680   }
02681   return(1);                               /* Impossible */
02682 }
02683 
02684 
02685 /*
02686   Write record to new file.
02687 
02688   SYNOPSIS
02689     sort_write_record()
02690       sort_param                Sort parameters.
02691 
02692   NOTE
02693     This is only called by a master thread if parallel repair is used.
02694 
02695   RETURN
02696     0           OK
02697     1           Error
02698 */
02699 
02700 int sort_write_record(MI_SORT_PARAM *sort_param)
02701 {
02702   int flag;
02703   ulong block_length,reclength;
02704   unsigned char *from;
02705   SORT_INFO *sort_info=sort_param->sort_info;
02706   MI_CHECK *param=sort_info->param;
02707   MI_INFO *info=sort_info->info;
02708   MYISAM_SHARE *share=info->s;
02709 
02710   if (sort_param->fix_datafile)
02711   {
02712     switch (sort_info->new_data_file_type) {
02713     case STATIC_RECORD:
02714       if (info->rec_cache.write(sort_param->record, share->base.pack_reclength))
02715       {
02716   mi_check_print_error(param,"%d when writing to datafile",errno);
02717   return(1);
02718       }
02719       sort_param->filepos+=share->base.pack_reclength;
02720       info->s->state.split++;
02721       /* sort_info->param->glob_crc+=mi_static_checksum(info, sort_param->record); */
02722       break;
02723     case DYNAMIC_RECORD:
02724       if (! info->blobs)
02725   from=sort_param->rec_buff;
02726       else
02727       {
02728   /* must be sure that local buffer is big enough */
02729   reclength=info->s->base.pack_reclength+
02730     _my_calc_total_blob_length(info,sort_param->record)+
02731     ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
02732     MI_DYN_DELETE_BLOCK_HEADER;
02733   if (sort_info->buff_length < reclength)
02734   {
02735           void *tmpptr= NULL;
02736     tmpptr= realloc(sort_info->buff, reclength);
02737           if(tmpptr)
02738           {
02739       sort_info->buff_length=reclength;
02740             sort_info->buff= (unsigned char *)tmpptr;
02741           }
02742           else
02743           {
02744             mi_check_print_error(param,"Could not realloc() sort_info->buff "
02745                                  " to %ul bytes", reclength);
02746             return(1);
02747           }
02748   }
02749   from= sort_info->buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER);
02750       }
02751       /* We can use info->checksum here as only one thread calls this. */
02752       info->checksum=mi_checksum(info,sort_param->record);
02753       reclength=_mi_rec_pack(info,from,sort_param->record);
02754       flag=0;
02755       /* sort_info->param->glob_crc+=info->checksum; */
02756 
02757       do
02758       {
02759   block_length=reclength+ 3 + test(reclength >= (65520-3));
02760   if (block_length < share->base.min_block_length)
02761     block_length=share->base.min_block_length;
02762   info->update|=HA_STATE_WRITE_AT_END;
02763   block_length=MY_ALIGN(block_length,MI_DYN_ALIGN_SIZE);
02764   if (block_length > MI_MAX_BLOCK_LENGTH)
02765     block_length=MI_MAX_BLOCK_LENGTH;
02766   if (_mi_write_part_record(info,0L,block_length,
02767           sort_param->filepos+block_length,
02768           &from,&reclength,&flag))
02769   {
02770     mi_check_print_error(param,"%d when writing to datafile",errno);
02771     return(1);
02772   }
02773   sort_param->filepos+=block_length;
02774   info->s->state.split++;
02775       } while (reclength);
02776       /* sort_info->param->glob_crc+=info->checksum; */
02777       break;
02778     case COMPRESSED_RECORD:
02779     case BLOCK_RECORD:
02780       assert(0);                                  /* Impossible */
02781     }
02782   }
02783   if (sort_param->master)
02784   {
02785     info->state->records++;
02786     if ((param->testflag & T_WRITE_LOOP) &&
02787         (info->state->records % WRITE_COUNT) == 0)
02788     {
02789       char llbuff[22];
02790       printf("%s\r", llstr(info->state->records,llbuff));
02791       fflush(stdout);
02792     }
02793   }
02794   return(0);
02795 } /* sort_write_record */
02796 
02797 
02798   /* Compare two keys from _create_index_by_sort */
02799 
02800 int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a, const void *b)
02801 {
02802   uint32_t not_used[2];
02803   return (ha_key_cmp(sort_param->seg, *((unsigned char* const *) a), *((unsigned char* const *) b),
02804          USE_WHOLE_KEY, SEARCH_SAME, not_used));
02805 } /* sort_key_cmp */
02806 
02807 
02808 int sort_key_write(MI_SORT_PARAM *sort_param, const void *a)
02809 {
02810   uint32_t diff_pos[2];
02811   char llbuff[22],llbuff2[22];
02812   SORT_INFO *sort_info=sort_param->sort_info;
02813   MI_CHECK *param= sort_info->param;
02814   int cmp;
02815 
02816   if (sort_info->key_block->inited)
02817   {
02818     cmp=ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey,
02819        (unsigned char*) a, USE_WHOLE_KEY,SEARCH_FIND | SEARCH_UPDATE,
02820        diff_pos);
02821     if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL)
02822       ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey,
02823                  (unsigned char*) a, USE_WHOLE_KEY,
02824                  SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diff_pos);
02825     else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
02826     {
02827       diff_pos[0]= mi_collect_stats_nonulls_next(sort_param->seg,
02828                                                  sort_param->notnull,
02829                                                  sort_info->key_block->lastkey,
02830                                                  (unsigned char*)a);
02831     }
02832     sort_param->unique[diff_pos[0]-1]++;
02833   }
02834   else
02835   {
02836     cmp= -1;
02837     if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
02838       mi_collect_stats_nonulls_first(sort_param->seg, sort_param->notnull,
02839                                      (unsigned char*)a);
02840   }
02841   if ((sort_param->keyinfo->flag & HA_NOSAME) && cmp == 0)
02842   {
02843     sort_info->dupp++;
02844     sort_info->info->lastpos=get_record_for_key(sort_info->info,
02845             sort_param->keyinfo,
02846             (unsigned char*) a);
02847     mi_check_print_warning(param,
02848          "Duplicate key for record at %10s against record at %10s",
02849          llstr(sort_info->info->lastpos,llbuff),
02850          llstr(get_record_for_key(sort_info->info,
02851                 sort_param->keyinfo,
02852                 sort_info->key_block->
02853                 lastkey),
02854          llbuff2));
02855     param->testflag|=T_RETRY_WITHOUT_QUICK;
02856     return (sort_delete_record(sort_param));
02857   }
02858   return (sort_insert_key(sort_param,sort_info->key_block,
02859         (unsigned char*) a, HA_OFFSET_ERROR));
02860 } /* sort_key_write */
02861 
02862 
02863   /* get pointer to record from a key */
02864 
02865 my_off_t get_record_for_key(MI_INFO *info, MI_KEYDEF *keyinfo,
02866                             unsigned char *key) {
02867   return _mi_dpos(info,0,key+_mi_keylength(keyinfo,key));
02868 } /* get_record_for_key */
02869 
02870 
02871   /* Insert a key in sort-key-blocks */
02872 
02873 int sort_insert_key(MI_SORT_PARAM *sort_param,
02874                     register SORT_KEY_BLOCKS *key_block, unsigned char *key,
02875                     my_off_t prev_block)
02876 {
02877   uint32_t a_length,t_length,nod_flag;
02878   my_off_t filepos,key_file_length;
02879   unsigned char *anc_buff,*lastkey;
02880   MI_KEY_PARAM s_temp;
02881   MI_INFO *info;
02882   MI_KEYDEF *keyinfo=sort_param->keyinfo;
02883   SORT_INFO *sort_info= sort_param->sort_info;
02884   MI_CHECK *param=sort_info->param;
02885 
02886   anc_buff=key_block->buff;
02887   info=sort_info->info;
02888   lastkey=key_block->lastkey;
02889   nod_flag= (key_block == sort_info->key_block ? 0 :
02890        info->s->base.key_reflength);
02891 
02892   if (!key_block->inited)
02893   {
02894     key_block->inited=1;
02895     if (key_block == sort_info->key_block_end)
02896     {
02897       mi_check_print_error(param,"To many key-block-levels; Try increasing sort_key_blocks");
02898       return(1);
02899     }
02900     a_length=2+nod_flag;
02901     key_block->end_pos=anc_buff+2;
02902     lastkey=0;          /* No previous key in block */
02903   }
02904   else
02905     a_length=mi_getint(anc_buff);
02906 
02907   /* Save pointer to previous block */
02908   if (nod_flag)
02909     _mi_kpointer(info,key_block->end_pos,prev_block);
02910 
02911   t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,
02912         (unsigned char*) 0,lastkey,lastkey,key,
02913          &s_temp);
02914   (*keyinfo->store_key)(keyinfo, key_block->end_pos+nod_flag,&s_temp);
02915   a_length+=t_length;
02916   mi_putint(anc_buff,a_length,nod_flag);
02917   key_block->end_pos+=t_length;
02918   if (a_length <= keyinfo->block_length)
02919   {
02920     _mi_move_key(keyinfo,key_block->lastkey,key);
02921     key_block->last_length=a_length-t_length;
02922     return(0);
02923   }
02924 
02925   /* Fill block with end-zero and write filled block */
02926   mi_putint(anc_buff,key_block->last_length,nod_flag);
02927   memset(anc_buff+key_block->last_length, 0,
02928          keyinfo->block_length - key_block->last_length);
02929   key_file_length=info->state->key_file_length;
02930   if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
02931     return(1);
02932 
02933   /* If we read the page from the key cache, we have to write it back to it */
02934   if (key_file_length == info->state->key_file_length)
02935   {
02936     if (_mi_write_keypage(info, keyinfo, filepos, DFLT_INIT_HITS, anc_buff))
02937       return(1);
02938   }
02939   else if (my_pwrite(info->s->kfile,(unsigned char*) anc_buff,
02940          (uint) keyinfo->block_length,filepos, param->myf_rw))
02941     return(1);
02942 
02943   /* Write separator-key to block in next level */
02944   if (sort_insert_key(sort_param,key_block+1,key_block->lastkey,filepos))
02945     return(1);
02946 
02947   /* clear old block and write new key in it */
02948   key_block->inited=0;
02949   return(sort_insert_key(sort_param, key_block,key,prev_block));
02950 } /* sort_insert_key */
02951 
02952 
02953   /* Delete record when we found a duplicated key */
02954 
02955 int sort_delete_record(MI_SORT_PARAM *sort_param)
02956 {
02957   uint32_t i;
02958   int old_file,error;
02959   unsigned char *key;
02960   SORT_INFO *sort_info=sort_param->sort_info;
02961   MI_CHECK *param=sort_info->param;
02962   MI_INFO *info=sort_info->info;
02963 
02964   if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
02965   {
02966     mi_check_print_error(param,
02967        "Quick-recover aborted; Run recovery without switch -q or with switch -qq");
02968     return(1);
02969   }
02970   if (info->s->options & HA_OPTION_COMPRESS_RECORD)
02971   {
02972     mi_check_print_error(param,
02973        "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);;
02974     return(1);
02975   }
02976 
02977   old_file=info->dfile;
02978   info->dfile=info->rec_cache.file;
02979   if (sort_info->current_key)
02980   {
02981     key=info->lastkey+info->s->base.max_key_length;
02982     if ((error=(*info->s->read_rnd)(info,sort_param->record,info->lastpos,0)) &&
02983   error != HA_ERR_RECORD_DELETED)
02984     {
02985       mi_check_print_error(param,"Can't read record to be removed");
02986       info->dfile=old_file;
02987       return(1);
02988     }
02989 
02990     for (i=0 ; i < sort_info->current_key ; i++)
02991     {
02992       uint32_t key_length=_mi_make_key(info,i,key,sort_param->record,info->lastpos);
02993       if (_mi_ck_delete(info,i,key,key_length))
02994       {
02995   mi_check_print_error(param,"Can't delete key %d from record to be removed",i+1);
02996   info->dfile=old_file;
02997   return(1);
02998       }
02999     }
03000     if (sort_param->calc_checksum)
03001       param->glob_crc-=(*info->s->calc_checksum)(info, sort_param->record);
03002   }
03003   error= info->rec_cache.flush() || (*info->s->delete_record)(info);
03004   info->dfile=old_file;       /* restore actual value */
03005   info->state->records--;
03006   return(error);
03007 } /* sort_delete_record */
03008 
03009   /* Fix all pending blocks and flush everything to disk */
03010 
03011 int flush_pending_blocks(MI_SORT_PARAM *sort_param)
03012 {
03013   uint32_t nod_flag,length;
03014   my_off_t filepos,key_file_length;
03015   SORT_KEY_BLOCKS *key_block;
03016   SORT_INFO *sort_info= sort_param->sort_info;
03017   myf myf_rw=sort_info->param->myf_rw;
03018   MI_INFO *info=sort_info->info;
03019   MI_KEYDEF *keyinfo=sort_param->keyinfo;
03020 
03021   filepos= HA_OFFSET_ERROR;     /* if empty file */
03022   nod_flag=0;
03023   for (key_block=sort_info->key_block ; key_block->inited ; key_block++)
03024   {
03025     key_block->inited=0;
03026     length=mi_getint(key_block->buff);
03027     if (nod_flag)
03028       _mi_kpointer(info,key_block->end_pos,filepos);
03029     key_file_length=info->state->key_file_length;
03030     memset(key_block->buff+length, 0, keyinfo->block_length-length);
03031     if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
03032       return(1);
03033 
03034     /* If we read the page from the key cache, we have to write it back */
03035     if (key_file_length == info->state->key_file_length)
03036     {
03037       if (_mi_write_keypage(info, keyinfo, filepos,
03038                             DFLT_INIT_HITS, key_block->buff))
03039   return(1);
03040     }
03041     else if (my_pwrite(info->s->kfile,(unsigned char*) key_block->buff,
03042            (uint) keyinfo->block_length,filepos, myf_rw))
03043       return(1);
03044     nod_flag=1;
03045   }
03046   info->s->state.key_root[sort_param->key]=filepos; /* Last is root for tree */
03047   return(0);
03048 } /* flush_pending_blocks */
03049 
03050   /* alloc space and pointers for key_blocks */
03051 
03052 static SORT_KEY_BLOCKS *alloc_key_blocks(MI_CHECK *param, uint32_t blocks,
03053                                          uint32_t buffer_length)
03054 {
03055   register uint32_t i;
03056   SORT_KEY_BLOCKS *block;
03057 
03058   if (!(block=(SORT_KEY_BLOCKS*) malloc((sizeof(SORT_KEY_BLOCKS)+
03059                                         buffer_length+IO_SIZE)*blocks)))
03060   {
03061     mi_check_print_error(param,"Not enough memory for sort-key-blocks");
03062     return(0);
03063   }
03064   for (i=0 ; i < blocks ; i++)
03065   {
03066     block[i].inited=0;
03067     block[i].buff=(unsigned char*) (block+blocks)+(buffer_length+IO_SIZE)*i;
03068   }
03069   return(block);
03070 } /* alloc_key_blocks */
03071 
03072 
03073   /* Check if file is almost full */
03074 
03075 int test_if_almost_full(MI_INFO *info)
03076 {
03077   if (info->s->options & HA_OPTION_COMPRESS_RECORD)
03078     return 0;
03079   return (my_off_t)(lseek(info->s->kfile, 0L, SEEK_END) / 10 * 9) >
03080          (my_off_t) info->s->base.max_key_file_length ||
03081          (my_off_t)(lseek(info->dfile, 0L, SEEK_END) / 10 * 9) >
03082          (my_off_t) info->s->base.max_data_file_length;
03083 }
03084 
03085 
03086   /* write suffix to data file if neaded */
03087 
03088 int write_data_suffix(SORT_INFO *sort_info, bool fix_datafile)
03089 {
03090   MI_INFO *info=sort_info->info;
03091 
03092   if (info->s->options & HA_OPTION_COMPRESS_RECORD && fix_datafile)
03093   {
03094     unsigned char buff[MEMMAP_EXTRA_MARGIN];
03095     memset(buff, 0, sizeof(buff));
03096     if (info->rec_cache.write(buff, sizeof(buff)))
03097     {
03098       mi_check_print_error(sort_info->param,
03099          "%d when writing to datafile",errno);
03100       return 1;
03101     }
03102     sort_info->param->read_cache.end_of_file+=sizeof(buff);
03103   }
03104   return 0;
03105 }
03106 
03107   /* Update state and myisamchk_time of indexfile */
03108 
03109 int update_state_info(MI_CHECK *param, MI_INFO *info,uint32_t update)
03110 {
03111   MYISAM_SHARE *share=info->s;
03112 
03113   if (update & UPDATE_OPEN_COUNT)
03114   {
03115     share->state.open_count=0;
03116     share->global_changed=0;
03117   }
03118   if (update & UPDATE_STAT)
03119   {
03120     uint32_t i, key_parts= mi_uint2korr(share->state.header.key_parts);
03121     share->state.rec_per_key_rows=info->state->records;
03122     share->state.changed&= ~STATE_NOT_ANALYZED;
03123     if (info->state->records)
03124     {
03125       for (i=0; i<key_parts; i++)
03126       {
03127         if (!(share->state.rec_per_key_part[i]=param->rec_per_key_part[i]))
03128           share->state.changed|= STATE_NOT_ANALYZED;
03129       }
03130     }
03131   }
03132   if (update & (UPDATE_STAT | UPDATE_SORT | UPDATE_TIME | UPDATE_AUTO_INC))
03133   {
03134     if (update & UPDATE_TIME)
03135     {
03136       share->state.check_time= (long) time((time_t*) 0);
03137       if (!share->state.create_time)
03138   share->state.create_time=share->state.check_time;
03139     }
03140     /*
03141       When tables are locked we haven't synched the share state and the
03142       real state for a while so we better do it here before synching
03143       the share state to disk. Only when table is write locked is it
03144       necessary to perform this synch.
03145     */
03146     if (info->lock_type == F_WRLCK)
03147       share->state.state= *info->state;
03148     if (mi_state_info_write(share->kfile,&share->state,1+2))
03149       goto err;
03150     share->changed=0;
03151   }
03152   {           /* Force update of status */
03153     int error;
03154     uint32_t r_locks=share->r_locks,w_locks=share->w_locks;
03155     share->r_locks= share->w_locks= share->tot_locks= 0;
03156     error=_mi_writeinfo(info,WRITEINFO_NO_UNLOCK);
03157     share->r_locks=r_locks;
03158     share->w_locks=w_locks;
03159     share->tot_locks=r_locks+w_locks;
03160     if (!error)
03161       return 0;
03162   }
03163 err:
03164   mi_check_print_error(param,"%d when updating keyfile",errno);
03165   return 1;
03166 }
03167 
03168   /*
03169     Update auto increment value for a table
03170     When setting the 'repair_only' flag we only want to change the
03171     old auto_increment value if its wrong (smaller than some given key).
03172     The reason is that we shouldn't change the auto_increment value
03173     for a table without good reason when only doing a repair; If the
03174     user have inserted and deleted rows, the auto_increment value
03175     may be bigger than the biggest current row and this is ok.
03176 
03177     If repair_only is not set, we will update the flag to the value in
03178     param->auto_increment is bigger than the biggest key.
03179   */
03180 
03181 void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
03182              bool repair_only)
03183 {
03184   unsigned char *record= 0;
03185 
03186   if (!info->s->base.auto_key ||
03187       ! mi_is_key_active(info->s->state.key_map, info->s->base.auto_key - 1))
03188   {
03189     if (!(param->testflag & T_VERY_SILENT))
03190       mi_check_print_info(param,
03191         "Table: %s doesn't have an auto increment key\n",
03192         param->isam_file_name);
03193     return;
03194   }
03195   if (!(param->testflag & T_SILENT) &&
03196       !(param->testflag & T_REP))
03197     printf("Updating MyISAM file: %s\n", param->isam_file_name);
03198   /*
03199     We have to use an allocated buffer instead of info->rec_buff as
03200     _mi_put_key_in_record() may use info->rec_buff
03201   */
03202   if (!mi_alloc_rec_buff(info, SIZE_MAX, &record))
03203   {
03204     mi_check_print_error(param,"Not enough memory for extra record");
03205     return;
03206   }
03207 
03208   mi_extra(info,HA_EXTRA_KEYREAD,0);
03209   if (mi_rlast(info, record, info->s->base.auto_key-1))
03210   {
03211     if (errno != HA_ERR_END_OF_FILE)
03212     {
03213       mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
03214       free(mi_get_rec_buff_ptr(info, record));
03215       mi_check_print_error(param,"%d when reading last record",errno);
03216       return;
03217     }
03218     if (!repair_only)
03219       info->s->state.auto_increment=param->auto_increment_value;
03220   }
03221   else
03222   {
03223     uint64_t auto_increment= retrieve_auto_increment(info, record);
03224     set_if_bigger(info->s->state.auto_increment,auto_increment);
03225     if (!repair_only)
03226       set_if_bigger(info->s->state.auto_increment, param->auto_increment_value);
03227   }
03228   mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
03229   free(mi_get_rec_buff_ptr(info, record));
03230   update_state_info(param, info, UPDATE_AUTO_INC);
03231   return;
03232 }
03233 
03234 
03235 /*
03236   Update statistics for each part of an index
03237 
03238   SYNOPSIS
03239     update_key_parts()
03240       keyinfo           IN  Index information (only key->keysegs used)
03241       rec_per_key_part  OUT Store statistics here
03242       unique            IN  Array of (#distinct tuples)
03243       notnull_tuples    IN  Array of (#tuples), or NULL
03244       records               Number of records in the table
03245 
03246   DESCRIPTION
03247     This function is called produce index statistics values from unique and
03248     notnull_tuples arrays after these arrays were produced with sequential
03249     index scan (the scan is done in two places: chk_index() and
03250     sort_key_write()).
03251 
03252     This function handles all 3 index statistics collection methods.
03253 
03254     Unique is an array:
03255       unique[0]= (#different values of {keypart1}) - 1
03256       unique[1]= (#different values of {keypart1,keypart2} tuple)-unique[0]-1
03257       ...
03258 
03259     For MI_STATS_METHOD_IGNORE_NULLS method, notnull_tuples is an array too:
03260       notnull_tuples[0]= (#of {keypart1} tuples such that keypart1 is not NULL)
03261       notnull_tuples[1]= (#of {keypart1,keypart2} tuples such that all
03262                           keypart{i} are not NULL)
03263       ...
03264     For all other statistics collection methods notnull_tuples==NULL.
03265 
03266     Output is an array:
03267     rec_per_key_part[k] =
03268      = E(#records in the table such that keypart_1=c_1 AND ... AND
03269          keypart_k=c_k for arbitrary constants c_1 ... c_k)
03270 
03271      = {assuming that values have uniform distribution and index contains all
03272         tuples from the domain (or that {c_1, ..., c_k} tuple is choosen from
03273         index tuples}
03274 
03275      = #tuples-in-the-index / #distinct-tuples-in-the-index.
03276 
03277     The #tuples-in-the-index and #distinct-tuples-in-the-index have different
03278     meaning depending on which statistics collection method is used:
03279 
03280     MI_STATS_METHOD_*  how are nulls compared?  which tuples are counted?
03281      NULLS_EQUAL            NULL == NULL           all tuples in table
03282      NULLS_NOT_EQUAL        NULL != NULL           all tuples in table
03283      IGNORE_NULLS               n/a             tuples that don't have NULLs
03284 */
03285 
03286 void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
03287                       uint64_t *unique, uint64_t *notnull,
03288                       uint64_t records)
03289 {
03290   uint64_t count=0,tmp, unique_tuples;
03291   uint64_t tuples= records;
03292   uint32_t parts;
03293   for (parts=0 ; parts < keyinfo->keysegs  ; parts++)
03294   {
03295     count+=unique[parts];
03296     unique_tuples= count + 1;
03297     if (notnull)
03298     {
03299       tuples= notnull[parts];
03300       /*
03301         #(unique_tuples not counting tuples with NULLs) =
03302           #(unique_tuples counting tuples with NULLs as different) -
03303           #(tuples with NULLs)
03304       */
03305       unique_tuples -= (records - notnull[parts]);
03306     }
03307 
03308     if (unique_tuples == 0)
03309       tmp= 1;
03310     else if (count == 0)
03311       tmp= tuples; /* 1 unique tuple */
03312     else
03313       tmp= (tuples + unique_tuples/2) / unique_tuples;
03314 
03315     /*
03316       for some weird keys (e.g. FULLTEXT) tmp can be <1 here.
03317       let's ensure it is not
03318     */
03319     if (tmp < 1)
03320       tmp= 1;
03321     if (tmp >= (uint64_t) ~(ulong) 0)
03322       tmp=(uint64_t) ~(ulong) 0;
03323 
03324     *rec_per_key_part=(ulong) tmp;
03325     rec_per_key_part++;
03326   }
03327 }
03328 
03329 
03330 static ha_checksum mi_byte_checksum(const unsigned char *buf, uint32_t length)
03331 {
03332   ha_checksum crc;
03333   const unsigned char *end=buf+length;
03334   for (crc=0; buf != end; buf++)
03335     crc=((crc << 1) + *((unsigned char*) buf)) +
03336       test(crc & (((ha_checksum) 1) << (8*sizeof(ha_checksum)-1)));
03337   return crc;
03338 }
03339 
03340 static bool mi_too_big_key_for_sort(MI_KEYDEF *key, ha_rows rows)
03341 {
03342   uint32_t key_maxlength=key->maxlength;
03343   return (key->flag & (HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY) &&
03344     ((uint64_t) rows * key_maxlength >
03345      (uint64_t) MAX_FILE_SIZE));
03346 }
03347 
03348 /*
03349   Deactivate all not unique index that can be recreated fast
03350   These include packed keys on which sorting will use more temporary
03351   space than the max allowed file length or for which the unpacked keys
03352   will take much more space than packed keys.
03353   Note that 'rows' may be zero for the case when we don't know how many
03354   rows we will put into the file.
03355  */
03356 
03357 void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows)
03358 {
03359   MYISAM_SHARE *share=info->s;
03360   MI_KEYDEF    *key=share->keyinfo;
03361   uint32_t          i;
03362 
03363   assert(info->state->records == 0 &&
03364               (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES));
03365   for (i=0 ; i < share->base.keys ; i++,key++)
03366   {
03367     if (!(key->flag & (HA_NOSAME | HA_AUTO_KEY)) &&
03368         ! mi_too_big_key_for_sort(key,rows) && info->s->base.auto_key != i+1)
03369     {
03370       mi_clear_key_active(share->state.key_map, i);
03371       info->update|= HA_STATE_CHANGED;
03372     }
03373   }
03374 }
03375 
03376 
03377 /*
03378   Return true if we can use repair by sorting
03379   One can set the force argument to force to use sorting
03380   even if the temporary file would be quite big!
03381 */
03382 
03383 bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows,
03384           uint64_t key_map, bool force)
03385 {
03386   MYISAM_SHARE *share=info->s;
03387   MI_KEYDEF *key=share->keyinfo;
03388   uint32_t i;
03389 
03390   /*
03391     mi_repair_by_sort only works if we have at least one key. If we don't
03392     have any keys, we should use the normal repair.
03393   */
03394   if (! mi_is_any_key_active(key_map))
03395     return false;       /* Can't use sort */
03396   for (i=0 ; i < share->base.keys ; i++,key++)
03397   {
03398     if (!force && mi_too_big_key_for_sort(key,rows))
03399       return false;
03400   }
03401   return true;
03402 }
03403 
03404 
03405 static void
03406 set_data_file_type(SORT_INFO *sort_info, MYISAM_SHARE *share)
03407 {
03408   if ((sort_info->new_data_file_type=share->data_file_type) ==
03409       COMPRESSED_RECORD && sort_info->param->testflag & T_UNPACK)
03410   {
03411     MYISAM_SHARE tmp;
03412 
03413     if (share->options & HA_OPTION_PACK_RECORD)
03414       sort_info->new_data_file_type = DYNAMIC_RECORD;
03415     else
03416       sort_info->new_data_file_type = STATIC_RECORD;
03417 
03418     /* Set delete_function for sort_delete_record() */
03419     memcpy(&tmp, share, sizeof(*share));
03420     tmp.options= ~HA_OPTION_COMPRESS_RECORD;
03421     mi_setup_functions(&tmp);
03422     share->delete_record=tmp.delete_record;
03423   }
03424 }