Drizzled Public API Documentation

mi_dynrec.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 /*
00017   Functions to handle space-packed-records and blobs
00018 
00019   A row may be stored in one or more linked blocks.
00020   The block size is between MI_MIN_BLOCK_LENGTH and MI_MAX_BLOCK_LENGTH.
00021   Each block is aligned on MI_DYN_ALIGN_SIZE.
00022   The reson for the max block size is to not have too many different types
00023   of blocks.  For the differnet block types, look at _mi_get_block_info()
00024 */
00025 
00026 #include "myisam_priv.h"
00027 
00028 #ifdef HAVE_SYS_TYPES
00029 #include <sys/types.h>
00030 #endif
00031 #ifdef HAVE_SYS_MMAN_H
00032 #include <sys/mman.h>
00033 #endif
00034 #include <drizzled/util/test.h>
00035 #include <drizzled/error.h>
00036 
00037 #include <cassert>
00038 #include <algorithm>
00039 
00040 using namespace drizzled;
00041 using namespace std;
00042 
00043 /* Enough for comparing if number is zero */
00044 static char zero_string[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
00045 
00046 static int write_dynamic_record(MI_INFO *info,const unsigned char *record,
00047         ulong reclength);
00048 static int _mi_find_writepos(MI_INFO *info,ulong reclength,internal::my_off_t *filepos,
00049            ulong *length);
00050 static int update_dynamic_record(MI_INFO *info,internal::my_off_t filepos,unsigned char *record,
00051          ulong reclength);
00052 static int delete_dynamic_record(MI_INFO *info,internal::my_off_t filepos,
00053          uint32_t second_read);
00054 static int _mi_cmp_buffer(int file, const unsigned char *buff, internal::my_off_t filepos,
00055         uint32_t length);
00056 
00057   /* Interface function from MI_INFO */
00058 
00059 
00060 /*
00061   Create mmaped area for MyISAM handler
00062 
00063   SYNOPSIS
00064     mi_dynmap_file()
00065     info    MyISAM handler
00066 
00067   RETURN
00068     0  ok
00069     1  error.
00070 */
00071 
00072 bool mi_dynmap_file(MI_INFO *info, internal::my_off_t size)
00073 {
00074   if (size > (internal::my_off_t) (~((size_t) 0)) - MEMMAP_EXTRA_MARGIN)
00075   {
00076     return(1);
00077   }
00078   /*
00079     I wonder if it is good to use MAP_NORESERVE. From the Linux man page:
00080     MAP_NORESERVE
00081       Do not reserve swap space for this mapping. When swap space is
00082       reserved, one has the guarantee that it is possible to modify the
00083       mapping. When swap space is not reserved one might get SIGSEGV
00084       upon a write if no physical memory is available.
00085   */
00086   info->s->file_map= (unsigned char*)
00087                   mmap(NULL, (size_t)(size + MEMMAP_EXTRA_MARGIN),
00088                        info->s->mode==O_RDONLY ? PROT_READ :
00089                        PROT_READ | PROT_WRITE,
00090                        MAP_SHARED | MAP_NORESERVE,
00091                        info->dfile, 0L);
00092   if (info->s->file_map == (unsigned char*) MAP_FAILED)
00093   {
00094     info->s->file_map= NULL;
00095     return(1);
00096   }
00097 /* per krow we should look at removing the following code */
00098 #if !defined(TARGET_OS_SOLARIS)
00099   madvise((char*) info->s->file_map, size, MADV_RANDOM);
00100 #endif
00101   info->s->mmaped_length= size;
00102   return(0);
00103 }
00104 
00105 
00106 /*
00107   Resize mmaped area for MyISAM handler
00108 
00109   SYNOPSIS
00110     mi_remap_file()
00111     info    MyISAM handler
00112 
00113   RETURN
00114 */
00115 
00116 void mi_remap_file(MI_INFO *info, internal::my_off_t size)
00117 {
00118   if (info->s->file_map)
00119   {
00120     munmap((char*) info->s->file_map,
00121            (size_t) info->s->mmaped_length + MEMMAP_EXTRA_MARGIN);
00122     mi_dynmap_file(info, size);
00123   }
00124 }
00125 
00126 
00127 /*
00128   Read bytes from MySAM handler, using mmap or pread
00129 
00130   SYNOPSIS
00131     mi_mmap_pread()
00132     info    MyISAM handler
00133     Buffer              Input buffer
00134     Count               Count of bytes for read
00135     offset              Start position
00136     MyFlags
00137 
00138   RETURN
00139     0  ok
00140 */
00141 
00142 size_t mi_mmap_pread(MI_INFO *info, unsigned char *Buffer,
00143                     size_t Count, internal::my_off_t offset, myf MyFlags)
00144 {
00145   /*
00146     The following test may fail in the following cases:
00147     - We failed to remap a memory area (fragmented memory?)
00148     - This thread has done some writes, but not yet extended the
00149     memory mapped area.
00150   */
00151 
00152   if (info->s->mmaped_length >= offset + Count)
00153   {
00154     memcpy(Buffer, info->s->file_map + offset, Count);
00155     return 0;
00156   }
00157   else
00158   {
00159     return my_pread(info->dfile, Buffer, Count, offset, MyFlags);
00160   }
00161 }
00162 
00163 
00164         /* wrapper for my_pread in case if mmap isn't used */
00165 
00166 size_t mi_nommap_pread(MI_INFO *info, unsigned char *Buffer,
00167                        size_t Count, internal::my_off_t offset, myf MyFlags)
00168 {
00169   return my_pread(info->dfile, Buffer, Count, offset, MyFlags);
00170 }
00171 
00172 
00173 /*
00174   Write bytes to MySAM handler, using mmap or pwrite
00175 
00176   SYNOPSIS
00177     mi_mmap_pwrite()
00178     info    MyISAM handler
00179     Buffer              Output buffer
00180     Count               Count of bytes for write
00181     offset              Start position
00182     MyFlags
00183 
00184   RETURN
00185     0  ok
00186     !=0  error.  In this case return error from pwrite
00187 */
00188 
00189 size_t mi_mmap_pwrite(MI_INFO *info, const unsigned char *Buffer,
00190                       size_t Count, internal::my_off_t offset, myf MyFlags)
00191 {
00192 
00193   /*
00194     The following test may fail in the following cases:
00195     - We failed to remap a memory area (fragmented memory?)
00196     - This thread has done some writes, but not yet extended the
00197     memory mapped area.
00198   */
00199 
00200   if (info->s->mmaped_length >= offset + Count)
00201   {
00202     memcpy(info->s->file_map + offset, Buffer, Count);
00203     return 0;
00204   }
00205   else
00206   {
00207     info->s->nonmmaped_inserts++;
00208     return my_pwrite(info->dfile, Buffer, Count, offset, MyFlags);
00209   }
00210 
00211 }
00212 
00213 
00214         /* wrapper for my_pwrite in case if mmap isn't used */
00215 
00216 size_t mi_nommap_pwrite(MI_INFO *info, const unsigned char *Buffer,
00217                       size_t Count, internal::my_off_t offset, myf MyFlags)
00218 {
00219   return my_pwrite(info->dfile, Buffer, Count, offset, MyFlags);
00220 }
00221 
00222 
00223 int _mi_write_dynamic_record(MI_INFO *info, const unsigned char *record)
00224 {
00225   ulong reclength=_mi_rec_pack(info,info->rec_buff,record);
00226   return (write_dynamic_record(info,info->rec_buff,reclength));
00227 }
00228 
00229 int _mi_update_dynamic_record(MI_INFO *info, internal::my_off_t pos, const unsigned char *record)
00230 {
00231   uint32_t length=_mi_rec_pack(info,info->rec_buff,record);
00232   return (update_dynamic_record(info,pos,info->rec_buff,length));
00233 }
00234 
00235 int _mi_write_blob_record(MI_INFO *info, const unsigned char *record)
00236 {
00237   unsigned char *rec_buff;
00238   int error;
00239   ulong reclength,reclength2,extra;
00240 
00241   extra= (ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
00242     MI_DYN_DELETE_BLOCK_HEADER+1);
00243   reclength= (info->s->base.pack_reclength +
00244         _my_calc_total_blob_length(info,record)+ extra);
00245 #ifdef NOT_USED         /* We now support big rows */
00246   if (reclength > MI_DYN_MAX_ROW_LENGTH)
00247   {
00248     errno=HA_ERR_TO_BIG_ROW;
00249     return -1;
00250   }
00251 #endif
00252   if (!(rec_buff=(unsigned char*) malloc(reclength)))
00253   {
00254     errno= HA_ERR_OUT_OF_MEM;
00255     return(-1);
00256   }
00257   reclength2= _mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
00258          record);
00259   assert(reclength2 <= reclength);
00260   error=write_dynamic_record(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
00261            reclength2);
00262   free(rec_buff);
00263   return(error);
00264 }
00265 
00266 
00267 int _mi_update_blob_record(MI_INFO *info, internal::my_off_t pos, const unsigned char *record)
00268 {
00269   unsigned char *rec_buff;
00270   int error;
00271   ulong reclength,extra;
00272 
00273   extra= (ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
00274     MI_DYN_DELETE_BLOCK_HEADER);
00275   reclength= (info->s->base.pack_reclength+
00276         _my_calc_total_blob_length(info,record)+ extra);
00277 #ifdef NOT_USED         /* We now support big rows */
00278   if (reclength > MI_DYN_MAX_ROW_LENGTH)
00279   {
00280     errno=HA_ERR_TO_BIG_ROW;
00281     return -1;
00282   }
00283 #endif
00284   if (!(rec_buff=(unsigned char*) malloc(reclength)))
00285   {
00286     errno= HA_ERR_OUT_OF_MEM;
00287     return(-1);
00288   }
00289   reclength=_mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
00290        record);
00291   error=update_dynamic_record(info,pos,
00292             rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
00293             reclength);
00294   free(rec_buff);
00295   return(error);
00296 }
00297 
00298 
00299 int _mi_delete_dynamic_record(MI_INFO *info)
00300 {
00301   return delete_dynamic_record(info,info->lastpos,0);
00302 }
00303 
00304 
00305   /* Write record to data-file */
00306 
00307 static int write_dynamic_record(MI_INFO *info, const unsigned char *record,
00308         ulong reclength)
00309 {
00310   int flag;
00311   ulong length;
00312   internal::my_off_t filepos;
00313 
00314   flag=0;
00315 
00316   /*
00317     Check if we have enough room for the new record.
00318     First we do simplified check to make usual case faster.
00319     Then we do more precise check for the space left.
00320     Though it still is not absolutely precise, as
00321     we always use MI_MAX_DYN_BLOCK_HEADER while it can be
00322     less in the most of the cases.
00323   */
00324 
00325   if (unlikely(info->s->base.max_data_file_length -
00326                info->state->data_file_length <
00327                reclength + MI_MAX_DYN_BLOCK_HEADER))
00328   {
00329     if (info->s->base.max_data_file_length - info->state->data_file_length +
00330         info->state->empty - info->state->del * MI_MAX_DYN_BLOCK_HEADER <
00331         reclength + MI_MAX_DYN_BLOCK_HEADER)
00332     {
00333       errno=HA_ERR_RECORD_FILE_FULL;
00334       return(1);
00335     }
00336   }
00337 
00338   do
00339   {
00340     if (_mi_find_writepos(info,reclength,&filepos,&length))
00341       goto err;
00342     if (_mi_write_part_record(info,filepos,length,
00343                               (info->append_insert_at_end ?
00344                                HA_OFFSET_ERROR : info->s->state.dellink),
00345             (unsigned char**) &record,&reclength,&flag))
00346       goto err;
00347   } while (reclength);
00348 
00349   return(0);
00350 err:
00351   return(1);
00352 }
00353 
00354 
00355   /* Get a block for data ; The given data-area must be used !! */
00356 
00357 static int _mi_find_writepos(MI_INFO *info,
00358            ulong reclength, /* record length */
00359            internal::my_off_t *filepos, /* Return file pos */
00360            ulong *length)   /* length of block at filepos */
00361 {
00362   MI_BLOCK_INFO block_info;
00363   ulong tmp;
00364 
00365   if (info->s->state.dellink != HA_OFFSET_ERROR &&
00366       !info->append_insert_at_end)
00367   {
00368     /* Deleted blocks exists;  Get last used block */
00369     *filepos=info->s->state.dellink;
00370     block_info.second_read=0;
00371     info->rec_cache.seek_not_done=1;
00372     if (!(_mi_get_block_info(&block_info,info->dfile,info->s->state.dellink) &
00373      BLOCK_DELETED))
00374     {
00375       errno=HA_ERR_WRONG_IN_RECORD;
00376       return(-1);
00377     }
00378     info->s->state.dellink=block_info.next_filepos;
00379     info->state->del--;
00380     info->state->empty-= block_info.block_len;
00381     *length= block_info.block_len;
00382   }
00383   else
00384   {
00385     /* No deleted blocks;  Allocate a new block */
00386     *filepos=info->state->data_file_length;
00387     if ((tmp=reclength+3 + test(reclength >= (65520-3))) <
00388   info->s->base.min_block_length)
00389       tmp= info->s->base.min_block_length;
00390     else
00391       tmp= ((tmp+MI_DYN_ALIGN_SIZE-1) &
00392       (~ (ulong) (MI_DYN_ALIGN_SIZE-1)));
00393     if (info->state->data_file_length >
00394   (info->s->base.max_data_file_length - tmp))
00395     {
00396       errno=HA_ERR_RECORD_FILE_FULL;
00397       return(-1);
00398     }
00399     if (tmp > MI_MAX_BLOCK_LENGTH)
00400       tmp=MI_MAX_BLOCK_LENGTH;
00401     *length= tmp;
00402     info->state->data_file_length+= tmp;
00403     info->s->state.split++;
00404     info->update|=HA_STATE_WRITE_AT_END;
00405   }
00406   return(0);
00407 } /* _mi_find_writepos */
00408 
00409 
00410 
00411 /*
00412   Unlink a deleted block from the deleted list.
00413   This block will be combined with the preceding or next block to form
00414   a big block.
00415 */
00416 
00417 static bool unlink_deleted_block(MI_INFO *info, MI_BLOCK_INFO *block_info)
00418 {
00419   if (block_info->filepos == info->s->state.dellink)
00420   {
00421     /* First deleted block;  We can just use this ! */
00422     info->s->state.dellink=block_info->next_filepos;
00423   }
00424   else
00425   {
00426     MI_BLOCK_INFO tmp;
00427     tmp.second_read=0;
00428     /* Unlink block from the previous block */
00429     if (!(_mi_get_block_info(&tmp,info->dfile,block_info->prev_filepos)
00430     & BLOCK_DELETED))
00431       return(1);        /* Something is wrong */
00432     mi_sizestore(tmp.header+4,block_info->next_filepos);
00433     if (info->s->file_write(info, tmp.header+4,8,
00434       block_info->prev_filepos+4, MYF(MY_NABP)))
00435       return(1);
00436     /* Unlink block from next block */
00437     if (block_info->next_filepos != HA_OFFSET_ERROR)
00438     {
00439       if (!(_mi_get_block_info(&tmp,info->dfile,block_info->next_filepos)
00440       & BLOCK_DELETED))
00441   return(1);        /* Something is wrong */
00442       mi_sizestore(tmp.header+12,block_info->prev_filepos);
00443       if (info->s->file_write(info, tmp.header+12,8,
00444         block_info->next_filepos+12,
00445         MYF(MY_NABP)))
00446   return(1);
00447     }
00448   }
00449   /* We now have one less deleted block */
00450   info->state->del--;
00451   info->state->empty-= block_info->block_len;
00452   info->s->state.split--;
00453 
00454   /*
00455     If this was a block that we where accessing through table scan
00456     (mi_rrnd() or mi_scan(), then ensure that we skip over this block
00457     when doing next mi_rrnd() or mi_scan().
00458   */
00459   if (info->nextpos == block_info->filepos)
00460     info->nextpos+=block_info->block_len;
00461   return(0);
00462 }
00463 
00464 
00465 /*
00466   Add a backward link to delete block
00467 
00468   SYNOPSIS
00469     update_backward_delete_link()
00470     info    MyISAM handler
00471     delete_block  Position to delete block to update.
00472       If this is 'HA_OFFSET_ERROR', nothing will be done
00473     filepos   Position to block that 'delete_block' should point to
00474 
00475   RETURN
00476     0  ok
00477     1  error.  In this case my_error is set.
00478 */
00479 
00480 static int update_backward_delete_link(MI_INFO *info, internal::my_off_t delete_block,
00481                internal::my_off_t filepos)
00482 {
00483   MI_BLOCK_INFO block_info;
00484 
00485   if (delete_block != HA_OFFSET_ERROR)
00486   {
00487     block_info.second_read=0;
00488     if (_mi_get_block_info(&block_info,info->dfile,delete_block)
00489   & BLOCK_DELETED)
00490     {
00491       unsigned char buff[8];
00492       mi_sizestore(buff,filepos);
00493       if (info->s->file_write(info,buff, 8, delete_block+12, MYF(MY_NABP)))
00494   return(1);        /* Error on write */
00495     }
00496     else
00497     {
00498       errno=HA_ERR_WRONG_IN_RECORD;
00499       return(1);        /* Wrong delete link */
00500     }
00501   }
00502   return(0);
00503 }
00504 
00505   /* Delete datarecord from database */
00506   /* info->rec_cache.seek_not_done is updated in cmp_record */
00507 
00508 static int delete_dynamic_record(MI_INFO *info, internal::my_off_t filepos,
00509          uint32_t second_read)
00510 {
00511   uint32_t length,b_type;
00512   MI_BLOCK_INFO block_info,del_block;
00513   int error;
00514   bool remove_next_block;
00515 
00516   /* First add a link from the last block to the new one */
00517   error= update_backward_delete_link(info, info->s->state.dellink, filepos);
00518 
00519   block_info.second_read=second_read;
00520   do
00521   {
00522     /* Remove block at 'filepos' */
00523     if ((b_type=_mi_get_block_info(&block_info,info->dfile,filepos))
00524   & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
00525      BLOCK_FATAL_ERROR) ||
00526   (length=(uint) (block_info.filepos-filepos) +block_info.block_len) <
00527   MI_MIN_BLOCK_LENGTH)
00528     {
00529       errno=HA_ERR_WRONG_IN_RECORD;
00530       return(1);
00531     }
00532     /* Check if next block is a delete block */
00533     del_block.second_read=0;
00534     remove_next_block=0;
00535     if (_mi_get_block_info(&del_block,info->dfile,filepos+length) &
00536   BLOCK_DELETED && del_block.block_len+length < MI_DYN_MAX_BLOCK_LENGTH)
00537     {
00538       /* We can't remove this yet as this block may be the head block */
00539       remove_next_block=1;
00540       length+=del_block.block_len;
00541     }
00542 
00543     block_info.header[0]=0;
00544     mi_int3store(block_info.header+1,length);
00545     mi_sizestore(block_info.header+4,info->s->state.dellink);
00546     if (b_type & BLOCK_LAST)
00547       memset(block_info.header+12, 255, 8);
00548     else
00549       mi_sizestore(block_info.header+12,block_info.next_filepos);
00550     if (info->s->file_write(info,(unsigned char*) block_info.header,20,filepos,
00551       MYF(MY_NABP)))
00552       return(1);
00553     info->s->state.dellink = filepos;
00554     info->state->del++;
00555     info->state->empty+=length;
00556     filepos=block_info.next_filepos;
00557 
00558     /* Now it's safe to unlink the deleted block directly after this one */
00559     if (remove_next_block && unlink_deleted_block(info,&del_block))
00560       error=1;
00561   } while (!(b_type & BLOCK_LAST));
00562 
00563   return(error);
00564 }
00565 
00566 
00567   /* Write a block to datafile */
00568 
00569 int _mi_write_part_record(MI_INFO *info,
00570         internal::my_off_t filepos, /* points at empty block */
00571         ulong length,   /* length of block */
00572         internal::my_off_t next_filepos,/* Next empty block */
00573         unsigned char **record, /* pointer to record ptr */
00574         ulong *reclength, /* length of *record */
00575         int *flag)    /* *flag == 0 if header */
00576 {
00577   ulong head_length,res_length,extra_length,long_block,del_length;
00578   unsigned char *pos,*record_end;
00579   internal::my_off_t  next_delete_block;
00580   unsigned char temp[MI_SPLIT_LENGTH+MI_DYN_DELETE_BLOCK_HEADER];
00581 
00582   next_delete_block=HA_OFFSET_ERROR;
00583 
00584   res_length=extra_length=0;
00585   if (length > *reclength + MI_SPLIT_LENGTH)
00586   {           /* Splitt big block */
00587     res_length=MY_ALIGN(length- *reclength - MI_EXTEND_BLOCK_LENGTH,
00588       MI_DYN_ALIGN_SIZE);
00589     length-= res_length;      /* Use this for first part */
00590   }
00591   long_block= (length < 65520L && *reclength < 65520L) ? 0 : 1;
00592   if (length == *reclength+ 3 + long_block)
00593   {
00594     /* Block is exactly of the right length */
00595     temp[0]=(unsigned char) (1+ *flag)+(unsigned char) long_block;  /* Flag is 0 or 6 */
00596     if (long_block)
00597     {
00598       mi_int3store(temp+1,*reclength);
00599       head_length=4;
00600     }
00601     else
00602     {
00603       mi_int2store(temp+1,*reclength);
00604       head_length=3;
00605     }
00606   }
00607   else if (length-long_block < *reclength+4)
00608   {           /* To short block */
00609     if (next_filepos == HA_OFFSET_ERROR)
00610       next_filepos= (info->s->state.dellink != HA_OFFSET_ERROR &&
00611                      !info->append_insert_at_end ?
00612                      info->s->state.dellink : info->state->data_file_length);
00613     if (*flag == 0)       /* First block */
00614     {
00615       if (*reclength > MI_MAX_BLOCK_LENGTH)
00616       {
00617   head_length= 16;
00618   temp[0]=13;
00619   mi_int4store(temp+1,*reclength);
00620   mi_int3store(temp+5,length-head_length);
00621   mi_sizestore((unsigned char*) temp+8,next_filepos);
00622       }
00623       else
00624       {
00625   head_length=5+8+long_block*2;
00626   temp[0]=5+(unsigned char) long_block;
00627   if (long_block)
00628   {
00629     mi_int3store(temp+1,*reclength);
00630     mi_int3store(temp+4,length-head_length);
00631     mi_sizestore((unsigned char*) temp+7,next_filepos);
00632   }
00633   else
00634   {
00635     mi_int2store(temp+1,*reclength);
00636     mi_int2store(temp+3,length-head_length);
00637     mi_sizestore((unsigned char*) temp+5,next_filepos);
00638   }
00639       }
00640     }
00641     else
00642     {
00643       head_length=3+8+long_block;
00644       temp[0]=11+(unsigned char) long_block;
00645       if (long_block)
00646       {
00647   mi_int3store(temp+1,length-head_length);
00648   mi_sizestore((unsigned char*) temp+4,next_filepos);
00649       }
00650       else
00651       {
00652   mi_int2store(temp+1,length-head_length);
00653   mi_sizestore((unsigned char*) temp+3,next_filepos);
00654       }
00655     }
00656   }
00657   else
00658   {         /* Block with empty info last */
00659     head_length=4+long_block;
00660     extra_length= length- *reclength-head_length;
00661     temp[0]= (unsigned char) (3+ *flag)+(unsigned char) long_block; /* 3,4 or 9,10 */
00662     if (long_block)
00663     {
00664       mi_int3store(temp+1,*reclength);
00665       temp[4]= (unsigned char) (extra_length);
00666     }
00667     else
00668     {
00669       mi_int2store(temp+1,*reclength);
00670       temp[3]= (unsigned char) (extra_length);
00671     }
00672     length=   *reclength+head_length; /* Write only what is needed */
00673   }
00674 
00675   /* Make a long block for one write */
00676   record_end= *record+length-head_length;
00677   del_length=(res_length ? MI_DYN_DELETE_BLOCK_HEADER : 0);
00678   memmove(*record - head_length, temp, head_length);
00679   memcpy(temp,record_end,(size_t) (extra_length+del_length));
00680   memset(record_end, 0, extra_length);
00681 
00682   if (res_length)
00683   {
00684     /* Check first if we can join this block with the next one */
00685     MI_BLOCK_INFO del_block;
00686     internal::my_off_t next_block=filepos+length+extra_length+res_length;
00687 
00688     del_block.second_read=0;
00689     if (next_block < info->state->data_file_length &&
00690   info->s->state.dellink != HA_OFFSET_ERROR)
00691     {
00692       if ((_mi_get_block_info(&del_block,info->dfile,next_block)
00693      & BLOCK_DELETED) &&
00694     res_length + del_block.block_len < MI_DYN_MAX_BLOCK_LENGTH)
00695       {
00696   if (unlink_deleted_block(info,&del_block))
00697     goto err;
00698   res_length+=del_block.block_len;
00699       }
00700     }
00701 
00702     /* Create a delete link of the last part of the block */
00703     pos=record_end+extra_length;
00704     pos[0]= '\0';
00705     mi_int3store(pos+1,res_length);
00706     mi_sizestore(pos+4,info->s->state.dellink);
00707     memset(pos+12, 255, 8);     /* End link */
00708     next_delete_block=info->s->state.dellink;
00709     info->s->state.dellink= filepos+length+extra_length;
00710     info->state->del++;
00711     info->state->empty+=res_length;
00712     info->s->state.split++;
00713   }
00714   if (info->opt_flag & WRITE_CACHE_USED &&
00715       info->update & HA_STATE_WRITE_AT_END)
00716   {
00717     if (info->update & HA_STATE_EXTEND_BLOCK)
00718     {
00719       info->update&= ~HA_STATE_EXTEND_BLOCK;
00720       if (info->rec_cache.block_write(*record - head_length,
00721        length+extra_length+del_length,filepos))
00722       goto err;
00723     }
00724     else if (info->rec_cache.write(*record-head_length, length+extra_length+del_length))
00725       goto err;
00726   }
00727   else
00728   {
00729     info->rec_cache.seek_not_done=1;
00730     if (info->s->file_write(info,(unsigned char*) *record-head_length,length+extra_length+
00731       del_length,filepos,info->s->write_flag))
00732       goto err;
00733   }
00734   memcpy(record_end, temp, extra_length + del_length);
00735   *record=record_end;
00736   *reclength-=(length-head_length);
00737   *flag=6;
00738 
00739   if (del_length)
00740   {
00741     /* link the next delete block to this */
00742     if (update_backward_delete_link(info, next_delete_block,
00743             info->s->state.dellink))
00744       goto err;
00745   }
00746 
00747   return(0);
00748 err:
00749   return(1);
00750 } /*_mi_write_part_record */
00751 
00752 
00753   /* update record from datafile */
00754 
00755 static int update_dynamic_record(MI_INFO *info, internal::my_off_t filepos, unsigned char *record,
00756          ulong reclength)
00757 {
00758   int flag;
00759   uint32_t error;
00760   ulong length;
00761   MI_BLOCK_INFO block_info;
00762 
00763   flag=block_info.second_read=0;
00764   /*
00765      Check if we have enough room for the record.
00766      First we do simplified check to make usual case faster.
00767      Then we do more precise check for the space left.
00768      Though it still is not absolutely precise, as
00769      we always use MI_MAX_DYN_BLOCK_HEADER while it can be
00770      less in the most of the cases.
00771   */
00772 
00773   /*
00774     compare with just the reclength as we're going
00775     to get some space from the old replaced record
00776   */
00777   if (unlikely(info->s->base.max_data_file_length -
00778         info->state->data_file_length < reclength))
00779   {
00780     /*
00781        let's read the old record's block to find out the length of the
00782        old record
00783     */
00784     if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
00785         & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR | BLOCK_FATAL_ERROR))
00786     {
00787       if (!(error & BLOCK_FATAL_ERROR))
00788         errno=HA_ERR_WRONG_IN_RECORD;
00789       goto err;
00790     }
00791 
00792     /*
00793       if new record isn't longer, we can go on safely
00794     */
00795     if (block_info.rec_len < reclength)
00796     {
00797       if (info->s->base.max_data_file_length - info->state->data_file_length +
00798           info->state->empty - info->state->del * MI_MAX_DYN_BLOCK_HEADER <
00799           reclength - block_info.rec_len + MI_MAX_DYN_BLOCK_HEADER)
00800       {
00801         errno=HA_ERR_RECORD_FILE_FULL;
00802         goto err;
00803       }
00804     }
00805     block_info.second_read=0;
00806   }
00807 
00808   while (reclength > 0)
00809   {
00810     if (filepos != info->s->state.dellink)
00811     {
00812       block_info.next_filepos= HA_OFFSET_ERROR;
00813       if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
00814     & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
00815        BLOCK_FATAL_ERROR))
00816       {
00817   if (!(error & BLOCK_FATAL_ERROR))
00818     errno=HA_ERR_WRONG_IN_RECORD;
00819   goto err;
00820       }
00821       length=(ulong) (block_info.filepos-filepos) + block_info.block_len;
00822       if (length < reclength)
00823       {
00824   uint32_t tmp=MY_ALIGN(reclength - length + 3 +
00825         test(reclength >= 65520L),MI_DYN_ALIGN_SIZE);
00826   /* Don't create a block bigger than MI_MAX_BLOCK_LENGTH */
00827   tmp= min(length+tmp, MI_MAX_BLOCK_LENGTH)-length;
00828   /* Check if we can extend this block */
00829   if (block_info.filepos + block_info.block_len ==
00830       info->state->data_file_length &&
00831       info->state->data_file_length <
00832       info->s->base.max_data_file_length-tmp)
00833   {
00834     /* extend file */
00835     if (info->nextpos == info->state->data_file_length)
00836       info->nextpos+= tmp;
00837     info->state->data_file_length+= tmp;
00838     info->update|= HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK;
00839     length+=tmp;
00840   }
00841   else if (length < MI_MAX_BLOCK_LENGTH - MI_MIN_BLOCK_LENGTH)
00842   {
00843     /*
00844       Check if next block is a deleted block
00845       Above we have MI_MIN_BLOCK_LENGTH to avoid the problem where
00846       the next block is so small it can't be splited which could
00847       casue problems
00848     */
00849 
00850     MI_BLOCK_INFO del_block;
00851     del_block.second_read=0;
00852     if (_mi_get_block_info(&del_block,info->dfile,
00853          block_info.filepos + block_info.block_len) &
00854         BLOCK_DELETED)
00855     {
00856       /* Use; Unlink it and extend the current block */
00857       if (unlink_deleted_block(info,&del_block))
00858         goto err;
00859       if ((length+=del_block.block_len) > MI_MAX_BLOCK_LENGTH)
00860       {
00861         /*
00862     New block was too big, link overflow part back to
00863     delete list
00864         */
00865         internal::my_off_t next_pos;
00866         ulong rest_length= length-MI_MAX_BLOCK_LENGTH;
00867         set_if_bigger(rest_length, (ulong)MI_MIN_BLOCK_LENGTH);
00868         next_pos= del_block.filepos+ del_block.block_len - rest_length;
00869 
00870         if (update_backward_delete_link(info, info->s->state.dellink,
00871                 next_pos))
00872     return(1);
00873 
00874         /* create delete link for data that didn't fit into the page */
00875         del_block.header[0]=0;
00876         mi_int3store(del_block.header+1, rest_length);
00877         mi_sizestore(del_block.header+4,info->s->state.dellink);
00878         memset(del_block.header+12, 255, 8);
00879         if (info->s->file_write(info,(unsigned char*) del_block.header,20, next_pos,
00880           MYF(MY_NABP)))
00881     return(1);
00882         info->s->state.dellink= next_pos;
00883         info->s->state.split++;
00884         info->state->del++;
00885         info->state->empty+= rest_length;
00886         length-= rest_length;
00887       }
00888     }
00889   }
00890       }
00891     }
00892     else
00893     {
00894       if (_mi_find_writepos(info,reclength,&filepos,&length))
00895   goto err;
00896     }
00897     if (_mi_write_part_record(info,filepos,length,block_info.next_filepos,
00898             &record,&reclength,&flag))
00899       goto err;
00900     if ((filepos=block_info.next_filepos) == HA_OFFSET_ERROR)
00901     {
00902       /* Start writing data on deleted blocks */
00903       filepos=info->s->state.dellink;
00904     }
00905   }
00906 
00907   if (block_info.next_filepos != HA_OFFSET_ERROR)
00908     if (delete_dynamic_record(info,block_info.next_filepos,1))
00909       goto err;
00910   return(0);
00911 err:
00912   return(1);
00913 }
00914 
00915 
00916   /* Pack a record. Return new reclength */
00917 
00918 uint32_t _mi_rec_pack(MI_INFO *info, register unsigned char *to,
00919                   register const unsigned char *from)
00920 {
00921   uint    length,new_length,flag,bit,i;
00922   unsigned char   *pos,*end,*startpos,*packpos;
00923   enum en_fieldtype type;
00924   register MI_COLUMNDEF *rec;
00925   MI_BLOB *blob;
00926 
00927   flag=0 ; bit=1;
00928   startpos=packpos=to; to+= info->s->base.pack_bits; blob=info->blobs;
00929   rec=info->s->rec;
00930 
00931   for (i=info->s->base.fields ; i-- > 0; from+= length,rec++)
00932   {
00933     length=(uint) rec->length;
00934     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
00935     {
00936       if (type == FIELD_BLOB)
00937       {
00938   if (!blob->length)
00939     flag|=bit;
00940   else
00941   {
00942     char *temp_pos;
00943     size_t tmp_length=length-portable_sizeof_char_ptr;
00944     memcpy(to,from,tmp_length);
00945     memcpy(&temp_pos,from+tmp_length,sizeof(char*));
00946     memcpy(to + tmp_length, temp_pos, blob->length);
00947     to+=tmp_length+blob->length;
00948   }
00949   blob++;
00950       }
00951       else if (type == FIELD_SKIP_ZERO)
00952       {
00953   if (memcmp(from,zero_string,length) == 0)
00954     flag|=bit;
00955   else
00956   {
00957     memcpy(to, from, length);
00958           to+=length;
00959   }
00960       }
00961       else if (type == FIELD_SKIP_ENDSPACE ||
00962          type == FIELD_SKIP_PRESPACE)
00963       {
00964   pos= (unsigned char*) from; end= (unsigned char*) from + length;
00965   if (type == FIELD_SKIP_ENDSPACE)
00966   {         /* Pack trailing spaces */
00967     while (end > from && *(end-1) == ' ')
00968       end--;
00969   }
00970   else
00971   {         /* Pack pref-spaces */
00972     while (pos < end && *pos == ' ')
00973       pos++;
00974   }
00975   new_length=(uint) (end-pos);
00976   if (new_length +1 + test(rec->length > 255 && new_length > 127)
00977       < length)
00978   {
00979     if (rec->length > 255 && new_length > 127)
00980     {
00981             to[0]= (unsigned char) ((new_length & 127) + 128);
00982             to[1]= (unsigned char) (new_length >> 7);
00983             to+=2;
00984           }
00985           else
00986             *to++= (unsigned char) new_length;
00987     memcpy(to, pos, new_length);
00988           to+=new_length;
00989     flag|=bit;
00990   }
00991   else
00992   {
00993     memcpy(to, from, length);
00994           to+=length;
00995   }
00996       }
00997       else if (type == FIELD_VARCHAR)
00998       {
00999         uint32_t pack_length= ha_varchar_packlength(rec->length -1);
01000   uint32_t tmp_length;
01001         if (pack_length == 1)
01002         {
01003           tmp_length= (uint) *(unsigned char*) from;
01004           *to++= *from;
01005         }
01006         else
01007         {
01008           tmp_length= uint2korr(from);
01009           store_key_length_inc(to,tmp_length);
01010         }
01011         memcpy(to, from+pack_length, tmp_length);
01012         to+= tmp_length;
01013         continue;
01014       }
01015       else
01016       {
01017   memcpy(to, from, length);
01018         to+=length;
01019   continue;       /* Normal field */
01020       }
01021       if ((bit= bit << 1) >= 256)
01022       {
01023         *packpos++= (unsigned char) flag;
01024   bit=1; flag=0;
01025       }
01026     }
01027     else
01028     {
01029       memcpy(to, from, length);
01030       to+=length;
01031     }
01032   }
01033   if (bit != 1)
01034     *packpos= (unsigned char) flag;
01035   if (info->s->calc_checksum)
01036     *to++= (unsigned char) info->checksum;
01037   return((uint) (to-startpos));
01038 } /* _mi_rec_pack */
01039 
01040 
01041 
01042 /*
01043   Check if a record was correctly packed. Used only by myisamchk
01044   Returns 0 if record is ok.
01045 */
01046 
01047 bool _mi_rec_check(MI_INFO *info,const unsigned char *record, unsigned char *rec_buff,
01048                       ulong packed_length, bool with_checksum)
01049 {
01050   uint    length,new_length,flag,bit,i;
01051   unsigned char   *pos,*end,*packpos,*to;
01052   enum en_fieldtype type;
01053   register MI_COLUMNDEF *rec;
01054 
01055   packpos=rec_buff; to= rec_buff+info->s->base.pack_bits;
01056   rec=info->s->rec;
01057   flag= *packpos; bit=1;
01058 
01059   for (i=info->s->base.fields ; i-- > 0; record+= length, rec++)
01060   {
01061     length=(uint) rec->length;
01062     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
01063     {
01064       if (type == FIELD_BLOB)
01065       {
01066   uint32_t blob_length=
01067     _mi_calc_blob_length(length-portable_sizeof_char_ptr,record);
01068   if (!blob_length && !(flag & bit))
01069     goto err;
01070   if (blob_length)
01071     to+=length - portable_sizeof_char_ptr+ blob_length;
01072       }
01073       else if (type == FIELD_SKIP_ZERO)
01074       {
01075   if (memcmp(record,zero_string,length) == 0)
01076   {
01077     if (!(flag & bit))
01078       goto err;
01079   }
01080   else
01081     to+=length;
01082       }
01083       else if (type == FIELD_SKIP_ENDSPACE ||
01084          type == FIELD_SKIP_PRESPACE)
01085       {
01086   pos= (unsigned char*) record; end= (unsigned char*) record + length;
01087   if (type == FIELD_SKIP_ENDSPACE)
01088   {         /* Pack trailing spaces */
01089     while (end > record && *(end-1) == ' ')
01090       end--;
01091   }
01092   else
01093   {         /* Pack pre-spaces */
01094     while (pos < end && *pos == ' ')
01095       pos++;
01096   }
01097   new_length=(uint) (end-pos);
01098   if (new_length +1 + test(rec->length > 255 && new_length > 127)
01099       < length)
01100   {
01101     if (!(flag & bit))
01102       goto err;
01103     if (rec->length > 255 && new_length > 127)
01104     {
01105             if (to[0] != (unsigned char) ((new_length & 127) + 128) ||
01106                 to[1] != (unsigned char) (new_length >> 7))
01107               goto err;
01108             to+=2;
01109           }
01110           else if (*to++ != (unsigned char) new_length)
01111       goto err;
01112     to+=new_length;
01113   }
01114   else
01115     to+=length;
01116       }
01117       else if (type == FIELD_VARCHAR)
01118       {
01119         uint32_t pack_length= ha_varchar_packlength(rec->length -1);
01120   uint32_t tmp_length;
01121         if (pack_length == 1)
01122         {
01123           tmp_length= (uint) *(unsigned char*) record;
01124           to+= 1+ tmp_length;
01125           continue;
01126         }
01127         else
01128         {
01129           tmp_length= uint2korr(record);
01130           to+= get_pack_length(tmp_length)+tmp_length;
01131         }
01132         continue;
01133       }
01134       else
01135       {
01136   to+=length;
01137   continue;       /* Normal field */
01138       }
01139       if ((bit= bit << 1) >= 256)
01140       {
01141   flag= *++packpos;
01142   bit=1;
01143       }
01144     }
01145     else
01146       to+= length;
01147   }
01148   if (packed_length != (uint) (to - rec_buff) + test(info->s->calc_checksum) ||
01149       (bit != 1 && (flag & ~(bit - 1))))
01150     goto err;
01151   if (with_checksum && ((unsigned char) info->checksum != (unsigned char) *to))
01152   {
01153     goto err;
01154   }
01155   return(0);
01156 
01157 err:
01158   return(1);
01159 }
01160 
01161 
01162 
01163   /* Unpacks a record */
01164   /* Returns -1 and errno =HA_ERR_RECORD_DELETED if reclength isn't */
01165   /* right. Returns reclength (>0) if ok */
01166 
01167 ulong _mi_rec_unpack(register MI_INFO *info, register unsigned char *to, unsigned char *from,
01168          ulong found_length)
01169 {
01170   uint32_t flag,bit,length,rec_length,min_pack_length;
01171   enum en_fieldtype type;
01172   unsigned char *from_end,*to_end,*packpos;
01173   register MI_COLUMNDEF *rec,*end_field;
01174 
01175   to_end=to + info->s->base.reclength;
01176   from_end=from+found_length;
01177   flag= (unsigned char) *from; bit=1; packpos=from;
01178   if (found_length < info->s->base.min_pack_length)
01179     goto err;
01180   from+= info->s->base.pack_bits;
01181   min_pack_length=info->s->base.min_pack_length - info->s->base.pack_bits;
01182 
01183   for (rec=info->s->rec , end_field=rec+info->s->base.fields ;
01184        rec < end_field ; to+= rec_length, rec++)
01185   {
01186     rec_length=rec->length;
01187     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL &&
01188   (type != FIELD_CHECK))
01189     {
01190       if (type == FIELD_VARCHAR)
01191       {
01192         uint32_t pack_length= ha_varchar_packlength(rec_length-1);
01193         if (pack_length == 1)
01194         {
01195           length= (uint) *(unsigned char*) from;
01196           if (length > rec_length-1)
01197             goto err;
01198           *to= *from++;
01199         }
01200         else
01201         {
01202           get_key_length(length, from);
01203           if (length > rec_length-2)
01204             goto err;
01205           int2store(to,length);
01206         }
01207         if (from+length > from_end)
01208           goto err;
01209         memcpy(to+pack_length, from, length);
01210         from+= length;
01211         min_pack_length--;
01212         continue;
01213       }
01214       if (flag & bit)
01215       {
01216   if (type == FIELD_BLOB || type == FIELD_SKIP_ZERO)
01217     memset(to, 0, rec_length);
01218   else if (type == FIELD_SKIP_ENDSPACE ||
01219      type == FIELD_SKIP_PRESPACE)
01220   {
01221     if (rec->length > 255 && *from & 128)
01222     {
01223       if (from + 1 >= from_end)
01224         goto err;
01225       length= (*from & 127)+ ((uint) (unsigned char) *(from+1) << 7); from+=2;
01226     }
01227     else
01228     {
01229       if (from == from_end)
01230         goto err;
01231       length= (unsigned char) *from++;
01232     }
01233     min_pack_length--;
01234     if (length >= rec_length ||
01235         min_pack_length + length > (uint) (from_end - from))
01236       goto err;
01237     if (type == FIELD_SKIP_ENDSPACE)
01238     {
01239       memcpy(to, from, length);
01240       memset(to+length, ' ', rec_length-length);
01241     }
01242     else
01243     {
01244       memset(to, ' ', rec_length-length);
01245       memcpy(to + rec_length - length, from, length);
01246     }
01247     from+=length;
01248   }
01249       }
01250       else if (type == FIELD_BLOB)
01251       {
01252   uint32_t size_length=rec_length- portable_sizeof_char_ptr;
01253   ulong blob_length=_mi_calc_blob_length(size_length,from);
01254         ulong from_left= (ulong) (from_end - from);
01255         if (from_left < size_length ||
01256             from_left - size_length < blob_length ||
01257             from_left - size_length - blob_length < min_pack_length)
01258           goto err;
01259   memcpy(to, from, size_length);
01260   from+=size_length;
01261   memcpy(to+size_length, &from, sizeof(char*));
01262   from+=blob_length;
01263       }
01264       else
01265       {
01266   if (type == FIELD_SKIP_ENDSPACE || type == FIELD_SKIP_PRESPACE)
01267     min_pack_length--;
01268   if (min_pack_length + rec_length > (uint) (from_end - from))
01269     goto err;
01270         memcpy(to, from, rec_length);
01271         from+=rec_length;
01272       }
01273       if ((bit= bit << 1) >= 256)
01274       {
01275   flag= (unsigned char) *++packpos; bit=1;
01276       }
01277     }
01278     else
01279     {
01280       if (min_pack_length > (uint) (from_end - from))
01281   goto err;
01282       min_pack_length-=rec_length;
01283       memcpy(to, from, rec_length);
01284       from+=rec_length;
01285     }
01286   }
01287   if (info->s->calc_checksum)
01288     from++;
01289   if (to == to_end && from == from_end && (bit == 1 || !(flag & ~(bit-1))))
01290     return(found_length);
01291 
01292 err:
01293   errno= HA_ERR_WRONG_IN_RECORD;
01294   return(MY_FILE_ERROR);
01295 } /* _mi_rec_unpack */
01296 
01297 
01298   /* Calc length of blob. Update info in blobs->length */
01299 
01300 ulong _my_calc_total_blob_length(MI_INFO *info, const unsigned char *record)
01301 {
01302   ulong length;
01303   MI_BLOB *blob,*end;
01304 
01305   for (length=0, blob= info->blobs, end=blob+info->s->base.blobs ;
01306        blob != end;
01307        blob++)
01308   {
01309     blob->length=_mi_calc_blob_length(blob->pack_length,record + blob->offset);
01310     length+=blob->length;
01311   }
01312   return length;
01313 }
01314 
01315 
01316 ulong _mi_calc_blob_length(uint32_t length, const unsigned char *pos)
01317 {
01318   switch (length) {
01319   case 1:
01320     return (uint) (unsigned char) *pos;
01321   case 2:
01322     return (uint) uint2korr(pos);
01323   case 3:
01324     return uint3korr(pos);
01325   case 4:
01326     return uint4korr(pos);
01327   default:
01328     break;
01329   }
01330   return 0; /* Impossible */
01331 }
01332 
01333 
01334 void _my_store_blob_length(unsigned char *pos,uint32_t pack_length,uint32_t length)
01335 {
01336   switch (pack_length) {
01337   case 1:
01338     *pos= (unsigned char) length;
01339     break;
01340   case 2:
01341     int2store(pos,length);
01342     break;
01343   case 3:
01344     int3store(pos,length);
01345     break;
01346   case 4:
01347     int4store(pos,length);
01348   default:
01349     break;
01350   }
01351   return;
01352 }
01353 
01354 
01355 /*
01356   Read record from datafile.
01357 
01358   SYNOPSIS
01359     _mi_read_dynamic_record()
01360       info                      MI_INFO pointer to table.
01361       filepos                   From where to read the record.
01362       buf                       Destination for record.
01363 
01364   NOTE
01365 
01366     If a write buffer is active, it needs to be flushed if its contents
01367     intersects with the record to read. We always check if the position
01368     of the first byte of the write buffer is lower than the position
01369     past the last byte to read. In theory this is also true if the write
01370     buffer is completely below the read segment. That is, if there is no
01371     intersection. But this case is unusual. We flush anyway. Only if the
01372     first byte in the write buffer is above the last byte to read, we do
01373     not flush.
01374 
01375     A dynamic record may need several reads. So this check must be done
01376     before every read. Reading a dynamic record starts with reading the
01377     block header. If the record does not fit into the free space of the
01378     header, the block may be longer than the header. In this case a
01379     second read is necessary. These one or two reads repeat for every
01380     part of the record.
01381 
01382   RETURN
01383     0           OK
01384     -1          Error
01385 */
01386 
01387 int _mi_read_dynamic_record(MI_INFO *info, internal::my_off_t filepos, unsigned char *buf)
01388 {
01389   int block_of_record;
01390   uint32_t b_type, left_length= 0;
01391   unsigned char *to= NULL;
01392   MI_BLOCK_INFO block_info;
01393   int file;
01394 
01395   if (filepos != HA_OFFSET_ERROR)
01396   {
01397     file=info->dfile;
01398     block_of_record= 0;   /* First block of record is numbered as zero. */
01399     block_info.second_read= 0;
01400     do
01401     {
01402       /* A corrupted table can have wrong pointers. (Bug# 19835) */
01403       if (filepos == HA_OFFSET_ERROR)
01404         goto panic;
01405       if (info->opt_flag & WRITE_CACHE_USED &&
01406     info->rec_cache.pos_in_file < filepos + MI_BLOCK_INFO_HEADER_LENGTH &&
01407     info->rec_cache.flush())
01408   goto err;
01409       info->rec_cache.seek_not_done=1;
01410       if ((b_type= _mi_get_block_info(&block_info, file, filepos))
01411     & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
01412        BLOCK_FATAL_ERROR))
01413       {
01414   if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
01415     errno=HA_ERR_RECORD_DELETED;
01416   goto err;
01417       }
01418       if (block_of_record++ == 0)     /* First block */
01419       {
01420   if (block_info.rec_len > (uint) info->s->base.max_pack_length)
01421     goto panic;
01422   if (info->s->base.blobs)
01423   {
01424     if (!(to=mi_alloc_rec_buff(info, block_info.rec_len,
01425              &info->rec_buff)))
01426       goto err;
01427   }
01428   else
01429     to= info->rec_buff;
01430   left_length=block_info.rec_len;
01431       }
01432       if (left_length < block_info.data_len || ! block_info.data_len)
01433   goto panic;     /* Wrong linked record */
01434       /* copy information that is already read */
01435       {
01436         uint32_t offset= (uint) (block_info.filepos - filepos);
01437         uint32_t prefetch_len= (sizeof(block_info.header) - offset);
01438         filepos+= sizeof(block_info.header);
01439 
01440         if (prefetch_len > block_info.data_len)
01441           prefetch_len= block_info.data_len;
01442         if (prefetch_len)
01443         {
01444           memcpy(to, block_info.header + offset, prefetch_len);
01445           block_info.data_len-= prefetch_len;
01446           left_length-= prefetch_len;
01447           to+= prefetch_len;
01448         }
01449       }
01450       /* read rest of record from file */
01451       if (block_info.data_len)
01452       {
01453         if (info->opt_flag & WRITE_CACHE_USED &&
01454             info->rec_cache.pos_in_file < filepos + block_info.data_len &&
01455             info->rec_cache.flush())
01456           goto err;
01457         /*
01458           What a pity that this method is not called 'file_pread' and that
01459           there is no equivalent without seeking. We are at the right
01460           position already. :(
01461         */
01462         if (info->s->file_read(info, (unsigned char*) to, block_info.data_len,
01463                                filepos, MYF(MY_NABP)))
01464           goto panic;
01465         left_length-=block_info.data_len;
01466         to+=block_info.data_len;
01467       }
01468       filepos= block_info.next_filepos;
01469     } while (left_length);
01470 
01471     info->update|= HA_STATE_AKTIV;  /* We have a aktive record */
01472     fast_mi_writeinfo(info);
01473     return(_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
01474     MY_FILE_ERROR ? 0 : -1);
01475   }
01476   fast_mi_writeinfo(info);
01477   return(-1);     /* Wrong data to read */
01478 
01479 panic:
01480   errno=HA_ERR_WRONG_IN_RECORD;
01481 err:
01482   _mi_writeinfo(info,0);
01483   return(-1);
01484 }
01485 
01486   /* compare unique constraint between stored rows */
01487 
01488 int _mi_cmp_dynamic_unique(MI_INFO *info, MI_UNIQUEDEF *def,
01489          const unsigned char *record, internal::my_off_t pos)
01490 {
01491   unsigned char *rec_buff,*old_record;
01492   int error;
01493 
01494   if (!(old_record=(unsigned char *)malloc(info->s->base.reclength)))
01495     return(1);
01496 
01497   /* Don't let the compare destroy blobs that may be in use */
01498   rec_buff=info->rec_buff;
01499   if (info->s->base.blobs)
01500     info->rec_buff=0;
01501   error=_mi_read_dynamic_record(info,pos,old_record);
01502   if (!error)
01503     error=mi_unique_comp(def, record, old_record, def->null_are_equal);
01504   if (info->s->base.blobs)
01505   {
01506     void * rec_buff_ptr= mi_get_rec_buff_ptr(info, info->rec_buff);
01507     free(rec_buff_ptr);
01508     info->rec_buff=rec_buff;
01509   }
01510   free(old_record);
01511   return(error);
01512 }
01513 
01514 
01515   /* Compare of record one disk with packed record in memory */
01516 
01517 int _mi_cmp_dynamic_record(register MI_INFO *info, register const unsigned char *record)
01518 {
01519   uint32_t flag,reclength,b_type;
01520   internal::my_off_t filepos;
01521   unsigned char *buffer;
01522   MI_BLOCK_INFO block_info;
01523 
01524   if (info->opt_flag & WRITE_CACHE_USED)
01525   {
01526     info->update&= ~(HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK);
01527     if (info->rec_cache.flush())
01528       return(-1);
01529   }
01530   info->rec_cache.seek_not_done=1;
01531 
01532   /* If nobody have touched the database we don't have to test rec */
01533 
01534   buffer=info->rec_buff;
01535   if ((info->opt_flag & READ_CHECK_USED))
01536   {           /* If check isn't disabled  */
01537     if (info->s->base.blobs)
01538     {
01539       if (!(buffer=(unsigned char*) malloc(info->s->base.pack_reclength+
01540              _my_calc_total_blob_length(info,record))))
01541   return(-1);
01542     }
01543     reclength=_mi_rec_pack(info,buffer,record);
01544     record= buffer;
01545 
01546     filepos=info->lastpos;
01547     flag=block_info.second_read=0;
01548     block_info.next_filepos=filepos;
01549     while (reclength > 0)
01550     {
01551       if ((b_type=_mi_get_block_info(&block_info,info->dfile,
01552             block_info.next_filepos))
01553     & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
01554        BLOCK_FATAL_ERROR))
01555       {
01556   if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
01557     errno=HA_ERR_RECORD_CHANGED;
01558   goto err;
01559       }
01560       if (flag == 0)        /* First block */
01561       {
01562   flag=1;
01563   if (reclength != block_info.rec_len)
01564   {
01565     errno=HA_ERR_RECORD_CHANGED;
01566     goto err;
01567   }
01568       } else if (reclength < block_info.data_len)
01569       {
01570   errno=HA_ERR_WRONG_IN_RECORD;
01571   goto err;
01572       }
01573       reclength-=block_info.data_len;
01574       if (_mi_cmp_buffer(info->dfile,record,block_info.filepos,
01575        block_info.data_len))
01576       {
01577   errno=HA_ERR_RECORD_CHANGED;
01578   goto err;
01579       }
01580       flag=1;
01581       record+=block_info.data_len;
01582     }
01583   }
01584   errno=0;
01585 err:
01586   if (buffer != info->rec_buff)
01587     free((unsigned char*) buffer);
01588   return(errno);
01589 }
01590 
01591 
01592   /* Compare file to buffert */
01593 
01594 static int _mi_cmp_buffer(int file, const unsigned char *buff, internal::my_off_t filepos,
01595         uint32_t length)
01596 {
01597   uint32_t next_length;
01598   unsigned char temp_buff[IO_SIZE*2];
01599 
01600   next_length= IO_SIZE*2 - (uint) (filepos & (IO_SIZE-1));
01601 
01602   while (length > IO_SIZE*2)
01603   {
01604     if (my_pread(file,temp_buff,next_length,filepos, MYF(MY_NABP)) ||
01605   memcmp(buff, temp_buff, next_length))
01606       goto err;
01607     filepos+=next_length;
01608     buff+=next_length;
01609     length-= next_length;
01610     next_length=IO_SIZE*2;
01611   }
01612   if (my_pread(file,temp_buff,length,filepos,MYF(MY_NABP)))
01613     goto err;
01614   return(memcmp(buff,temp_buff,length));
01615 err:
01616   return(1);
01617 }
01618 
01619 
01620 /*
01621   Read record from datafile.
01622 
01623   SYNOPSIS
01624     _mi_read_rnd_dynamic_record()
01625       info                      MI_INFO pointer to table.
01626       buf                       Destination for record.
01627       filepos                   From where to read the record.
01628       skip_deleted_blocks       If to repeat reading until a non-deleted
01629                                 record is found.
01630 
01631   NOTE
01632 
01633     If a write buffer is active, it needs to be flushed if its contents
01634     intersects with the record to read. We always check if the position
01635     of the first byte of the write buffer is lower than the position
01636     past the last byte to read. In theory this is also true if the write
01637     buffer is completely below the read segment. That is, if there is no
01638     intersection. But this case is unusual. We flush anyway. Only if the
01639     first byte in the write buffer is above the last byte to read, we do
01640     not flush.
01641 
01642     A dynamic record may need several reads. So this check must be done
01643     before every read. Reading a dynamic record starts with reading the
01644     block header. If the record does not fit into the free space of the
01645     header, the block may be longer than the header. In this case a
01646     second read is necessary. These one or two reads repeat for every
01647     part of the record.
01648 
01649   RETURN
01650     0           OK
01651     != 0        Error
01652 */
01653 
01654 int _mi_read_rnd_dynamic_record(MI_INFO *info, unsigned char *buf,
01655         register internal::my_off_t filepos,
01656         bool skip_deleted_blocks)
01657 {
01658   int block_of_record, info_read, save_errno;
01659   uint32_t left_len,b_type;
01660   unsigned char *to= NULL;
01661   MI_BLOCK_INFO block_info;
01662   MYISAM_SHARE *share=info->s;
01663 
01664   info_read=0;
01665 
01666   if (info->lock_type == F_UNLCK)
01667   {
01668     info->tmp_lock_type=F_RDLCK;
01669   }
01670   else
01671     info_read=1;        /* memory-keyinfoblock is ok */
01672 
01673   block_of_record= 0;   /* First block of record is numbered as zero. */
01674   block_info.second_read= 0;
01675   left_len=1;
01676   do
01677   {
01678     if (filepos >= info->state->data_file_length)
01679     {
01680       if (!info_read)
01681       {           /* Check if changed */
01682   info_read=1;
01683   info->rec_cache.seek_not_done=1;
01684   if (mi_state_info_read_dsk(share->kfile,&share->state,1))
01685     goto panic;
01686       }
01687       if (filepos >= info->state->data_file_length)
01688       {
01689   errno= HA_ERR_END_OF_FILE;
01690   goto err;
01691       }
01692     }
01693     if (info->opt_flag & READ_CACHE_USED)
01694     {
01695       if (_mi_read_cache(&info->rec_cache,(unsigned char*) block_info.header,filepos,
01696        sizeof(block_info.header),
01697        (!block_of_record && skip_deleted_blocks ?
01698                           READING_NEXT : 0) | READING_HEADER))
01699   goto panic;
01700       b_type=_mi_get_block_info(&block_info,-1,filepos);
01701     }
01702     else
01703     {
01704       if (info->opt_flag & WRITE_CACHE_USED &&
01705     info->rec_cache.pos_in_file < filepos + MI_BLOCK_INFO_HEADER_LENGTH &&
01706     info->rec_cache.flush())
01707   return(errno);
01708       info->rec_cache.seek_not_done=1;
01709       b_type=_mi_get_block_info(&block_info,info->dfile,filepos);
01710     }
01711 
01712     if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
01713       BLOCK_FATAL_ERROR))
01714     {
01715       if ((b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
01716     && skip_deleted_blocks)
01717       {
01718   filepos=block_info.filepos+block_info.block_len;
01719   block_info.second_read=0;
01720   continue;   /* Search after next_record */
01721       }
01722       if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
01723       {
01724   errno=HA_ERR_RECORD_DELETED;
01725   info->lastpos=block_info.filepos;
01726   info->nextpos=block_info.filepos+block_info.block_len;
01727       }
01728       goto err;
01729     }
01730     if (block_of_record == 0)       /* First block */
01731     {
01732       if (block_info.rec_len > (uint) share->base.max_pack_length)
01733   goto panic;
01734       info->lastpos=filepos;
01735       if (share->base.blobs)
01736       {
01737   if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
01738             &info->rec_buff)))
01739     goto err;
01740       }
01741       else
01742   to= info->rec_buff;
01743       left_len=block_info.rec_len;
01744     }
01745     if (left_len < block_info.data_len)
01746       goto panic;       /* Wrong linked record */
01747 
01748     /* copy information that is already read */
01749     {
01750       uint32_t offset=(uint) (block_info.filepos - filepos);
01751       uint32_t tmp_length= (sizeof(block_info.header) - offset);
01752       filepos=block_info.filepos;
01753 
01754       if (tmp_length > block_info.data_len)
01755   tmp_length= block_info.data_len;
01756       if (tmp_length)
01757       {
01758   memcpy(to, block_info.header+offset,tmp_length);
01759   block_info.data_len-=tmp_length;
01760   left_len-=tmp_length;
01761   to+=tmp_length;
01762   filepos+=tmp_length;
01763       }
01764     }
01765     /* read rest of record from file */
01766     if (block_info.data_len)
01767     {
01768       if (info->opt_flag & READ_CACHE_USED)
01769       {
01770   if (_mi_read_cache(&info->rec_cache,(unsigned char*) to,filepos,
01771          block_info.data_len,
01772          (!block_of_record && skip_deleted_blocks) ?
01773                            READING_NEXT : 0))
01774     goto panic;
01775       }
01776       else
01777       {
01778         if (info->opt_flag & WRITE_CACHE_USED &&
01779             info->rec_cache.pos_in_file <
01780             block_info.filepos + block_info.data_len &&
01781             info->rec_cache.flush())
01782           goto err;
01783   /* lseek(info->dfile,filepos,SEEK_SET); */
01784   if (internal::my_read(info->dfile,(unsigned char*) to,block_info.data_len,MYF(MY_NABP)))
01785   {
01786     if (errno == -1)
01787       errno= HA_ERR_WRONG_IN_RECORD;  /* Unexpected end of file */
01788     goto err;
01789   }
01790       }
01791     }
01792     /*
01793       Increment block-of-record counter. If it was the first block,
01794       remember the position behind the block for the next call.
01795     */
01796     if (block_of_record++ == 0)
01797     {
01798       info->nextpos= block_info.filepos + block_info.block_len;
01799       skip_deleted_blocks= 0;
01800     }
01801     left_len-=block_info.data_len;
01802     to+=block_info.data_len;
01803     filepos=block_info.next_filepos;
01804   } while (left_len);
01805 
01806   info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
01807   fast_mi_writeinfo(info);
01808   if (_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
01809       MY_FILE_ERROR)
01810     return(0);
01811   return(errno);      /* Wrong record */
01812 
01813 panic:
01814   errno=HA_ERR_WRONG_IN_RECORD;   /* Something is fatal wrong */
01815 err:
01816   save_errno=errno;
01817   _mi_writeinfo(info,0);
01818   return(errno=save_errno);
01819 }
01820 
01821 
01822   /* Read and process header from a dynamic-record-file */
01823 
01824 uint32_t _mi_get_block_info(MI_BLOCK_INFO *info, int file, internal::my_off_t filepos)
01825 {
01826   uint32_t return_val=0;
01827   unsigned char *header=info->header;
01828 
01829   if (file >= 0)
01830   {
01831     /*
01832       We do not use my_pread() here because we want to have the file
01833       pointer set to the end of the header after this function.
01834       my_pread() may leave the file pointer untouched.
01835     */
01836     lseek(file,filepos,SEEK_SET);
01837     if (internal::my_read(file, header, sizeof(info->header),MYF(0)) !=
01838   sizeof(info->header))
01839       goto err;
01840   }
01841   if (info->second_read)
01842   {
01843     if (info->header[0] <= 6 || info->header[0] == 13)
01844       return_val=BLOCK_SYNC_ERROR;
01845   }
01846   else
01847   {
01848     if (info->header[0] > 6 && info->header[0] != 13)
01849       return_val=BLOCK_SYNC_ERROR;
01850   }
01851   info->next_filepos= HA_OFFSET_ERROR; /* Dummy if no next block */
01852 
01853   switch (info->header[0]) {
01854   case 0:
01855     if ((info->block_len=(uint) mi_uint3korr(header+1)) <
01856   MI_MIN_BLOCK_LENGTH ||
01857   (info->block_len & (MI_DYN_ALIGN_SIZE -1)))
01858       goto err;
01859     info->filepos=filepos;
01860     info->next_filepos=mi_sizekorr(header+4);
01861     info->prev_filepos=mi_sizekorr(header+12);
01862 #if SIZEOF_OFF_T == 4
01863     if ((mi_uint4korr(header+4) != 0 &&
01864    (mi_uint4korr(header+4) != UINT32_MAX ||
01865     info->next_filepos != UINT32_MAX) ||
01866   (mi_uint4korr(header+12) != 0 &&
01867    (mi_uint4korr(header+12) != UINT32_MAX ||
01868     info->prev_filepos != UINT32_MAX))
01869       goto err;
01870 #endif
01871     return return_val | BLOCK_DELETED;    /* Deleted block */
01872 
01873   case 1:
01874     info->rec_len=info->data_len=info->block_len=mi_uint2korr(header+1);
01875     info->filepos=filepos+3;
01876     return return_val | BLOCK_FIRST | BLOCK_LAST;
01877   case 2:
01878     info->rec_len=info->data_len=info->block_len=mi_uint3korr(header+1);
01879     info->filepos=filepos+4;
01880     return return_val | BLOCK_FIRST | BLOCK_LAST;
01881 
01882   case 13:
01883     info->rec_len=mi_uint4korr(header+1);
01884     info->block_len=info->data_len=mi_uint3korr(header+5);
01885     info->next_filepos=mi_sizekorr(header+8);
01886     info->second_read=1;
01887     info->filepos=filepos+16;
01888     return return_val | BLOCK_FIRST;
01889 
01890   case 3:
01891     info->rec_len=info->data_len=mi_uint2korr(header+1);
01892     info->block_len=info->rec_len+ (uint) header[3];
01893     info->filepos=filepos+4;
01894     return return_val | BLOCK_FIRST | BLOCK_LAST;
01895   case 4:
01896     info->rec_len=info->data_len=mi_uint3korr(header+1);
01897     info->block_len=info->rec_len+ (uint) header[4];
01898     info->filepos=filepos+5;
01899     return return_val | BLOCK_FIRST | BLOCK_LAST;
01900 
01901   case 5:
01902     info->rec_len=mi_uint2korr(header+1);
01903     info->block_len=info->data_len=mi_uint2korr(header+3);
01904     info->next_filepos=mi_sizekorr(header+5);
01905     info->second_read=1;
01906     info->filepos=filepos+13;
01907     return return_val | BLOCK_FIRST;
01908   case 6:
01909     info->rec_len=mi_uint3korr(header+1);
01910     info->block_len=info->data_len=mi_uint3korr(header+4);
01911     info->next_filepos=mi_sizekorr(header+7);
01912     info->second_read=1;
01913     info->filepos=filepos+15;
01914     return return_val | BLOCK_FIRST;
01915 
01916     /* The following blocks are identical to 1-6 without rec_len */
01917   case 7:
01918     info->data_len=info->block_len=mi_uint2korr(header+1);
01919     info->filepos=filepos+3;
01920     return return_val | BLOCK_LAST;
01921   case 8:
01922     info->data_len=info->block_len=mi_uint3korr(header+1);
01923     info->filepos=filepos+4;
01924     return return_val | BLOCK_LAST;
01925 
01926   case 9:
01927     info->data_len=mi_uint2korr(header+1);
01928     info->block_len=info->data_len+ (uint) header[3];
01929     info->filepos=filepos+4;
01930     return return_val | BLOCK_LAST;
01931   case 10:
01932     info->data_len=mi_uint3korr(header+1);
01933     info->block_len=info->data_len+ (uint) header[4];
01934     info->filepos=filepos+5;
01935     return return_val | BLOCK_LAST;
01936 
01937   case 11:
01938     info->data_len=info->block_len=mi_uint2korr(header+1);
01939     info->next_filepos=mi_sizekorr(header+3);
01940     info->second_read=1;
01941     info->filepos=filepos+11;
01942     return return_val;
01943   case 12:
01944     info->data_len=info->block_len=mi_uint3korr(header+1);
01945     info->next_filepos=mi_sizekorr(header+4);
01946     info->second_read=1;
01947     info->filepos=filepos+12;
01948     return return_val;
01949   }
01950 
01951 err:
01952   errno=HA_ERR_WRONG_IN_RECORD;  /* Garbage */
01953   return BLOCK_ERROR;
01954 }