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 (my_block_write(&info->rec_cache,(unsigned char*) *record-head_length,
00721        length+extra_length+del_length,filepos))
00722       goto err;
00723     }
00724     else if (my_b_write(&info->rec_cache,(unsigned char*) *record-head_length,
00725       length+extra_length+del_length))
00726       goto err;
00727   }
00728   else
00729   {
00730     info->rec_cache.seek_not_done=1;
00731     if (info->s->file_write(info,(unsigned char*) *record-head_length,length+extra_length+
00732       del_length,filepos,info->s->write_flag))
00733       goto err;
00734   }
00735   memcpy(record_end, temp, extra_length + del_length);
00736   *record=record_end;
00737   *reclength-=(length-head_length);
00738   *flag=6;
00739 
00740   if (del_length)
00741   {
00742     /* link the next delete block to this */
00743     if (update_backward_delete_link(info, next_delete_block,
00744             info->s->state.dellink))
00745       goto err;
00746   }
00747 
00748   return(0);
00749 err:
00750   return(1);
00751 } /*_mi_write_part_record */
00752 
00753 
00754   /* update record from datafile */
00755 
00756 static int update_dynamic_record(MI_INFO *info, internal::my_off_t filepos, unsigned char *record,
00757          ulong reclength)
00758 {
00759   int flag;
00760   uint32_t error;
00761   ulong length;
00762   MI_BLOCK_INFO block_info;
00763 
00764   flag=block_info.second_read=0;
00765   /*
00766      Check if we have enough room for the record.
00767      First we do simplified check to make usual case faster.
00768      Then we do more precise check for the space left.
00769      Though it still is not absolutely precise, as
00770      we always use MI_MAX_DYN_BLOCK_HEADER while it can be
00771      less in the most of the cases.
00772   */
00773 
00774   /*
00775     compare with just the reclength as we're going
00776     to get some space from the old replaced record
00777   */
00778   if (unlikely(info->s->base.max_data_file_length -
00779         info->state->data_file_length < reclength))
00780   {
00781     /*
00782        let's read the old record's block to find out the length of the
00783        old record
00784     */
00785     if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
00786         & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR | BLOCK_FATAL_ERROR))
00787     {
00788       if (!(error & BLOCK_FATAL_ERROR))
00789         errno=HA_ERR_WRONG_IN_RECORD;
00790       goto err;
00791     }
00792 
00793     /*
00794       if new record isn't longer, we can go on safely
00795     */
00796     if (block_info.rec_len < reclength)
00797     {
00798       if (info->s->base.max_data_file_length - info->state->data_file_length +
00799           info->state->empty - info->state->del * MI_MAX_DYN_BLOCK_HEADER <
00800           reclength - block_info.rec_len + MI_MAX_DYN_BLOCK_HEADER)
00801       {
00802         errno=HA_ERR_RECORD_FILE_FULL;
00803         goto err;
00804       }
00805     }
00806     block_info.second_read=0;
00807   }
00808 
00809   while (reclength > 0)
00810   {
00811     if (filepos != info->s->state.dellink)
00812     {
00813       block_info.next_filepos= HA_OFFSET_ERROR;
00814       if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
00815     & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
00816        BLOCK_FATAL_ERROR))
00817       {
00818   if (!(error & BLOCK_FATAL_ERROR))
00819     errno=HA_ERR_WRONG_IN_RECORD;
00820   goto err;
00821       }
00822       length=(ulong) (block_info.filepos-filepos) + block_info.block_len;
00823       if (length < reclength)
00824       {
00825   uint32_t tmp=MY_ALIGN(reclength - length + 3 +
00826         test(reclength >= 65520L),MI_DYN_ALIGN_SIZE);
00827   /* Don't create a block bigger than MI_MAX_BLOCK_LENGTH */
00828   tmp= min(length+tmp, MI_MAX_BLOCK_LENGTH)-length;
00829   /* Check if we can extend this block */
00830   if (block_info.filepos + block_info.block_len ==
00831       info->state->data_file_length &&
00832       info->state->data_file_length <
00833       info->s->base.max_data_file_length-tmp)
00834   {
00835     /* extend file */
00836     if (info->nextpos == info->state->data_file_length)
00837       info->nextpos+= tmp;
00838     info->state->data_file_length+= tmp;
00839     info->update|= HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK;
00840     length+=tmp;
00841   }
00842   else if (length < MI_MAX_BLOCK_LENGTH - MI_MIN_BLOCK_LENGTH)
00843   {
00844     /*
00845       Check if next block is a deleted block
00846       Above we have MI_MIN_BLOCK_LENGTH to avoid the problem where
00847       the next block is so small it can't be splited which could
00848       casue problems
00849     */
00850 
00851     MI_BLOCK_INFO del_block;
00852     del_block.second_read=0;
00853     if (_mi_get_block_info(&del_block,info->dfile,
00854          block_info.filepos + block_info.block_len) &
00855         BLOCK_DELETED)
00856     {
00857       /* Use; Unlink it and extend the current block */
00858       if (unlink_deleted_block(info,&del_block))
00859         goto err;
00860       if ((length+=del_block.block_len) > MI_MAX_BLOCK_LENGTH)
00861       {
00862         /*
00863     New block was too big, link overflow part back to
00864     delete list
00865         */
00866         internal::my_off_t next_pos;
00867         ulong rest_length= length-MI_MAX_BLOCK_LENGTH;
00868         set_if_bigger(rest_length, (ulong)MI_MIN_BLOCK_LENGTH);
00869         next_pos= del_block.filepos+ del_block.block_len - rest_length;
00870 
00871         if (update_backward_delete_link(info, info->s->state.dellink,
00872                 next_pos))
00873     return(1);
00874 
00875         /* create delete link for data that didn't fit into the page */
00876         del_block.header[0]=0;
00877         mi_int3store(del_block.header+1, rest_length);
00878         mi_sizestore(del_block.header+4,info->s->state.dellink);
00879         memset(del_block.header+12, 255, 8);
00880         if (info->s->file_write(info,(unsigned char*) del_block.header,20, next_pos,
00881           MYF(MY_NABP)))
00882     return(1);
00883         info->s->state.dellink= next_pos;
00884         info->s->state.split++;
00885         info->state->del++;
00886         info->state->empty+= rest_length;
00887         length-= rest_length;
00888       }
00889     }
00890   }
00891       }
00892     }
00893     else
00894     {
00895       if (_mi_find_writepos(info,reclength,&filepos,&length))
00896   goto err;
00897     }
00898     if (_mi_write_part_record(info,filepos,length,block_info.next_filepos,
00899             &record,&reclength,&flag))
00900       goto err;
00901     if ((filepos=block_info.next_filepos) == HA_OFFSET_ERROR)
00902     {
00903       /* Start writing data on deleted blocks */
00904       filepos=info->s->state.dellink;
00905     }
00906   }
00907 
00908   if (block_info.next_filepos != HA_OFFSET_ERROR)
00909     if (delete_dynamic_record(info,block_info.next_filepos,1))
00910       goto err;
00911   return(0);
00912 err:
00913   return(1);
00914 }
00915 
00916 
00917   /* Pack a record. Return new reclength */
00918 
00919 uint32_t _mi_rec_pack(MI_INFO *info, register unsigned char *to,
00920                   register const unsigned char *from)
00921 {
00922   uint    length,new_length,flag,bit,i;
00923   unsigned char   *pos,*end,*startpos,*packpos;
00924   enum en_fieldtype type;
00925   register MI_COLUMNDEF *rec;
00926   MI_BLOB *blob;
00927 
00928   flag=0 ; bit=1;
00929   startpos=packpos=to; to+= info->s->base.pack_bits; blob=info->blobs;
00930   rec=info->s->rec;
00931 
00932   for (i=info->s->base.fields ; i-- > 0; from+= length,rec++)
00933   {
00934     length=(uint) rec->length;
00935     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
00936     {
00937       if (type == FIELD_BLOB)
00938       {
00939   if (!blob->length)
00940     flag|=bit;
00941   else
00942   {
00943     char *temp_pos;
00944     size_t tmp_length=length-portable_sizeof_char_ptr;
00945     memcpy(to,from,tmp_length);
00946     memcpy(&temp_pos,from+tmp_length,sizeof(char*));
00947     memcpy(to + tmp_length, temp_pos, blob->length);
00948     to+=tmp_length+blob->length;
00949   }
00950   blob++;
00951       }
00952       else if (type == FIELD_SKIP_ZERO)
00953       {
00954   if (memcmp(from,zero_string,length) == 0)
00955     flag|=bit;
00956   else
00957   {
00958     memcpy(to, from, length);
00959           to+=length;
00960   }
00961       }
00962       else if (type == FIELD_SKIP_ENDSPACE ||
00963          type == FIELD_SKIP_PRESPACE)
00964       {
00965   pos= (unsigned char*) from; end= (unsigned char*) from + length;
00966   if (type == FIELD_SKIP_ENDSPACE)
00967   {         /* Pack trailing spaces */
00968     while (end > from && *(end-1) == ' ')
00969       end--;
00970   }
00971   else
00972   {         /* Pack pref-spaces */
00973     while (pos < end && *pos == ' ')
00974       pos++;
00975   }
00976   new_length=(uint) (end-pos);
00977   if (new_length +1 + test(rec->length > 255 && new_length > 127)
00978       < length)
00979   {
00980     if (rec->length > 255 && new_length > 127)
00981     {
00982             to[0]= (unsigned char) ((new_length & 127) + 128);
00983             to[1]= (unsigned char) (new_length >> 7);
00984             to+=2;
00985           }
00986           else
00987             *to++= (unsigned char) new_length;
00988     memcpy(to, pos, new_length);
00989           to+=new_length;
00990     flag|=bit;
00991   }
00992   else
00993   {
00994     memcpy(to, from, length);
00995           to+=length;
00996   }
00997       }
00998       else if (type == FIELD_VARCHAR)
00999       {
01000         uint32_t pack_length= ha_varchar_packlength(rec->length -1);
01001   uint32_t tmp_length;
01002         if (pack_length == 1)
01003         {
01004           tmp_length= (uint) *(unsigned char*) from;
01005           *to++= *from;
01006         }
01007         else
01008         {
01009           tmp_length= uint2korr(from);
01010           store_key_length_inc(to,tmp_length);
01011         }
01012         memcpy(to, from+pack_length, tmp_length);
01013         to+= tmp_length;
01014         continue;
01015       }
01016       else
01017       {
01018   memcpy(to, from, length);
01019         to+=length;
01020   continue;       /* Normal field */
01021       }
01022       if ((bit= bit << 1) >= 256)
01023       {
01024         *packpos++= (unsigned char) flag;
01025   bit=1; flag=0;
01026       }
01027     }
01028     else
01029     {
01030       memcpy(to, from, length);
01031       to+=length;
01032     }
01033   }
01034   if (bit != 1)
01035     *packpos= (unsigned char) flag;
01036   if (info->s->calc_checksum)
01037     *to++= (unsigned char) info->checksum;
01038   return((uint) (to-startpos));
01039 } /* _mi_rec_pack */
01040 
01041 
01042 
01043 /*
01044   Check if a record was correctly packed. Used only by myisamchk
01045   Returns 0 if record is ok.
01046 */
01047 
01048 bool _mi_rec_check(MI_INFO *info,const unsigned char *record, unsigned char *rec_buff,
01049                       ulong packed_length, bool with_checksum)
01050 {
01051   uint    length,new_length,flag,bit,i;
01052   unsigned char   *pos,*end,*packpos,*to;
01053   enum en_fieldtype type;
01054   register MI_COLUMNDEF *rec;
01055 
01056   packpos=rec_buff; to= rec_buff+info->s->base.pack_bits;
01057   rec=info->s->rec;
01058   flag= *packpos; bit=1;
01059 
01060   for (i=info->s->base.fields ; i-- > 0; record+= length, rec++)
01061   {
01062     length=(uint) rec->length;
01063     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
01064     {
01065       if (type == FIELD_BLOB)
01066       {
01067   uint32_t blob_length=
01068     _mi_calc_blob_length(length-portable_sizeof_char_ptr,record);
01069   if (!blob_length && !(flag & bit))
01070     goto err;
01071   if (blob_length)
01072     to+=length - portable_sizeof_char_ptr+ blob_length;
01073       }
01074       else if (type == FIELD_SKIP_ZERO)
01075       {
01076   if (memcmp(record,zero_string,length) == 0)
01077   {
01078     if (!(flag & bit))
01079       goto err;
01080   }
01081   else
01082     to+=length;
01083       }
01084       else if (type == FIELD_SKIP_ENDSPACE ||
01085          type == FIELD_SKIP_PRESPACE)
01086       {
01087   pos= (unsigned char*) record; end= (unsigned char*) record + length;
01088   if (type == FIELD_SKIP_ENDSPACE)
01089   {         /* Pack trailing spaces */
01090     while (end > record && *(end-1) == ' ')
01091       end--;
01092   }
01093   else
01094   {         /* Pack pre-spaces */
01095     while (pos < end && *pos == ' ')
01096       pos++;
01097   }
01098   new_length=(uint) (end-pos);
01099   if (new_length +1 + test(rec->length > 255 && new_length > 127)
01100       < length)
01101   {
01102     if (!(flag & bit))
01103       goto err;
01104     if (rec->length > 255 && new_length > 127)
01105     {
01106             if (to[0] != (unsigned char) ((new_length & 127) + 128) ||
01107                 to[1] != (unsigned char) (new_length >> 7))
01108               goto err;
01109             to+=2;
01110           }
01111           else if (*to++ != (unsigned char) new_length)
01112       goto err;
01113     to+=new_length;
01114   }
01115   else
01116     to+=length;
01117       }
01118       else if (type == FIELD_VARCHAR)
01119       {
01120         uint32_t pack_length= ha_varchar_packlength(rec->length -1);
01121   uint32_t tmp_length;
01122         if (pack_length == 1)
01123         {
01124           tmp_length= (uint) *(unsigned char*) record;
01125           to+= 1+ tmp_length;
01126           continue;
01127         }
01128         else
01129         {
01130           tmp_length= uint2korr(record);
01131           to+= get_pack_length(tmp_length)+tmp_length;
01132         }
01133         continue;
01134       }
01135       else
01136       {
01137   to+=length;
01138   continue;       /* Normal field */
01139       }
01140       if ((bit= bit << 1) >= 256)
01141       {
01142   flag= *++packpos;
01143   bit=1;
01144       }
01145     }
01146     else
01147       to+= length;
01148   }
01149   if (packed_length != (uint) (to - rec_buff) + test(info->s->calc_checksum) ||
01150       (bit != 1 && (flag & ~(bit - 1))))
01151     goto err;
01152   if (with_checksum && ((unsigned char) info->checksum != (unsigned char) *to))
01153   {
01154     goto err;
01155   }
01156   return(0);
01157 
01158 err:
01159   return(1);
01160 }
01161 
01162 
01163 
01164   /* Unpacks a record */
01165   /* Returns -1 and errno =HA_ERR_RECORD_DELETED if reclength isn't */
01166   /* right. Returns reclength (>0) if ok */
01167 
01168 ulong _mi_rec_unpack(register MI_INFO *info, register unsigned char *to, unsigned char *from,
01169          ulong found_length)
01170 {
01171   uint32_t flag,bit,length,rec_length,min_pack_length;
01172   enum en_fieldtype type;
01173   unsigned char *from_end,*to_end,*packpos;
01174   register MI_COLUMNDEF *rec,*end_field;
01175 
01176   to_end=to + info->s->base.reclength;
01177   from_end=from+found_length;
01178   flag= (unsigned char) *from; bit=1; packpos=from;
01179   if (found_length < info->s->base.min_pack_length)
01180     goto err;
01181   from+= info->s->base.pack_bits;
01182   min_pack_length=info->s->base.min_pack_length - info->s->base.pack_bits;
01183 
01184   for (rec=info->s->rec , end_field=rec+info->s->base.fields ;
01185        rec < end_field ; to+= rec_length, rec++)
01186   {
01187     rec_length=rec->length;
01188     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL &&
01189   (type != FIELD_CHECK))
01190     {
01191       if (type == FIELD_VARCHAR)
01192       {
01193         uint32_t pack_length= ha_varchar_packlength(rec_length-1);
01194         if (pack_length == 1)
01195         {
01196           length= (uint) *(unsigned char*) from;
01197           if (length > rec_length-1)
01198             goto err;
01199           *to= *from++;
01200         }
01201         else
01202         {
01203           get_key_length(length, from);
01204           if (length > rec_length-2)
01205             goto err;
01206           int2store(to,length);
01207         }
01208         if (from+length > from_end)
01209           goto err;
01210         memcpy(to+pack_length, from, length);
01211         from+= length;
01212         min_pack_length--;
01213         continue;
01214       }
01215       if (flag & bit)
01216       {
01217   if (type == FIELD_BLOB || type == FIELD_SKIP_ZERO)
01218     memset(to, 0, rec_length);
01219   else if (type == FIELD_SKIP_ENDSPACE ||
01220      type == FIELD_SKIP_PRESPACE)
01221   {
01222     if (rec->length > 255 && *from & 128)
01223     {
01224       if (from + 1 >= from_end)
01225         goto err;
01226       length= (*from & 127)+ ((uint) (unsigned char) *(from+1) << 7); from+=2;
01227     }
01228     else
01229     {
01230       if (from == from_end)
01231         goto err;
01232       length= (unsigned char) *from++;
01233     }
01234     min_pack_length--;
01235     if (length >= rec_length ||
01236         min_pack_length + length > (uint) (from_end - from))
01237       goto err;
01238     if (type == FIELD_SKIP_ENDSPACE)
01239     {
01240       memcpy(to, from, length);
01241       memset(to+length, ' ', rec_length-length);
01242     }
01243     else
01244     {
01245       memset(to, ' ', rec_length-length);
01246       memcpy(to + rec_length - length, from, length);
01247     }
01248     from+=length;
01249   }
01250       }
01251       else if (type == FIELD_BLOB)
01252       {
01253   uint32_t size_length=rec_length- portable_sizeof_char_ptr;
01254   ulong blob_length=_mi_calc_blob_length(size_length,from);
01255         ulong from_left= (ulong) (from_end - from);
01256         if (from_left < size_length ||
01257             from_left - size_length < blob_length ||
01258             from_left - size_length - blob_length < min_pack_length)
01259           goto err;
01260   memcpy(to, from, size_length);
01261   from+=size_length;
01262   memcpy(to+size_length, &from, sizeof(char*));
01263   from+=blob_length;
01264       }
01265       else
01266       {
01267   if (type == FIELD_SKIP_ENDSPACE || type == FIELD_SKIP_PRESPACE)
01268     min_pack_length--;
01269   if (min_pack_length + rec_length > (uint) (from_end - from))
01270     goto err;
01271         memcpy(to, from, rec_length);
01272         from+=rec_length;
01273       }
01274       if ((bit= bit << 1) >= 256)
01275       {
01276   flag= (unsigned char) *++packpos; bit=1;
01277       }
01278     }
01279     else
01280     {
01281       if (min_pack_length > (uint) (from_end - from))
01282   goto err;
01283       min_pack_length-=rec_length;
01284       memcpy(to, from, rec_length);
01285       from+=rec_length;
01286     }
01287   }
01288   if (info->s->calc_checksum)
01289     from++;
01290   if (to == to_end && from == from_end && (bit == 1 || !(flag & ~(bit-1))))
01291     return(found_length);
01292 
01293 err:
01294   errno= HA_ERR_WRONG_IN_RECORD;
01295   return(MY_FILE_ERROR);
01296 } /* _mi_rec_unpack */
01297 
01298 
01299   /* Calc length of blob. Update info in blobs->length */
01300 
01301 ulong _my_calc_total_blob_length(MI_INFO *info, const unsigned char *record)
01302 {
01303   ulong length;
01304   MI_BLOB *blob,*end;
01305 
01306   for (length=0, blob= info->blobs, end=blob+info->s->base.blobs ;
01307        blob != end;
01308        blob++)
01309   {
01310     blob->length=_mi_calc_blob_length(blob->pack_length,record + blob->offset);
01311     length+=blob->length;
01312   }
01313   return length;
01314 }
01315 
01316 
01317 ulong _mi_calc_blob_length(uint32_t length, const unsigned char *pos)
01318 {
01319   switch (length) {
01320   case 1:
01321     return (uint) (unsigned char) *pos;
01322   case 2:
01323     return (uint) uint2korr(pos);
01324   case 3:
01325     return uint3korr(pos);
01326   case 4:
01327     return uint4korr(pos);
01328   default:
01329     break;
01330   }
01331   return 0; /* Impossible */
01332 }
01333 
01334 
01335 void _my_store_blob_length(unsigned char *pos,uint32_t pack_length,uint32_t length)
01336 {
01337   switch (pack_length) {
01338   case 1:
01339     *pos= (unsigned char) length;
01340     break;
01341   case 2:
01342     int2store(pos,length);
01343     break;
01344   case 3:
01345     int3store(pos,length);
01346     break;
01347   case 4:
01348     int4store(pos,length);
01349   default:
01350     break;
01351   }
01352   return;
01353 }
01354 
01355 
01356 /*
01357   Read record from datafile.
01358 
01359   SYNOPSIS
01360     _mi_read_dynamic_record()
01361       info                      MI_INFO pointer to table.
01362       filepos                   From where to read the record.
01363       buf                       Destination for record.
01364 
01365   NOTE
01366 
01367     If a write buffer is active, it needs to be flushed if its contents
01368     intersects with the record to read. We always check if the position
01369     of the first byte of the write buffer is lower than the position
01370     past the last byte to read. In theory this is also true if the write
01371     buffer is completely below the read segment. That is, if there is no
01372     intersection. But this case is unusual. We flush anyway. Only if the
01373     first byte in the write buffer is above the last byte to read, we do
01374     not flush.
01375 
01376     A dynamic record may need several reads. So this check must be done
01377     before every read. Reading a dynamic record starts with reading the
01378     block header. If the record does not fit into the free space of the
01379     header, the block may be longer than the header. In this case a
01380     second read is necessary. These one or two reads repeat for every
01381     part of the record.
01382 
01383   RETURN
01384     0           OK
01385     -1          Error
01386 */
01387 
01388 int _mi_read_dynamic_record(MI_INFO *info, internal::my_off_t filepos, unsigned char *buf)
01389 {
01390   int block_of_record;
01391   uint32_t b_type, left_length= 0;
01392   unsigned char *to= NULL;
01393   MI_BLOCK_INFO block_info;
01394   int file;
01395 
01396   if (filepos != HA_OFFSET_ERROR)
01397   {
01398     file=info->dfile;
01399     block_of_record= 0;   /* First block of record is numbered as zero. */
01400     block_info.second_read= 0;
01401     do
01402     {
01403       /* A corrupted table can have wrong pointers. (Bug# 19835) */
01404       if (filepos == HA_OFFSET_ERROR)
01405         goto panic;
01406       if (info->opt_flag & WRITE_CACHE_USED &&
01407     info->rec_cache.pos_in_file < filepos + MI_BLOCK_INFO_HEADER_LENGTH &&
01408     flush_io_cache(&info->rec_cache))
01409   goto err;
01410       info->rec_cache.seek_not_done=1;
01411       if ((b_type= _mi_get_block_info(&block_info, file, filepos))
01412     & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
01413        BLOCK_FATAL_ERROR))
01414       {
01415   if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
01416     errno=HA_ERR_RECORD_DELETED;
01417   goto err;
01418       }
01419       if (block_of_record++ == 0)     /* First block */
01420       {
01421   if (block_info.rec_len > (uint) info->s->base.max_pack_length)
01422     goto panic;
01423   if (info->s->base.blobs)
01424   {
01425     if (!(to=mi_alloc_rec_buff(info, block_info.rec_len,
01426              &info->rec_buff)))
01427       goto err;
01428   }
01429   else
01430     to= info->rec_buff;
01431   left_length=block_info.rec_len;
01432       }
01433       if (left_length < block_info.data_len || ! block_info.data_len)
01434   goto panic;     /* Wrong linked record */
01435       /* copy information that is already read */
01436       {
01437         uint32_t offset= (uint) (block_info.filepos - filepos);
01438         uint32_t prefetch_len= (sizeof(block_info.header) - offset);
01439         filepos+= sizeof(block_info.header);
01440 
01441         if (prefetch_len > block_info.data_len)
01442           prefetch_len= block_info.data_len;
01443         if (prefetch_len)
01444         {
01445           memcpy(to, block_info.header + offset, prefetch_len);
01446           block_info.data_len-= prefetch_len;
01447           left_length-= prefetch_len;
01448           to+= prefetch_len;
01449         }
01450       }
01451       /* read rest of record from file */
01452       if (block_info.data_len)
01453       {
01454         if (info->opt_flag & WRITE_CACHE_USED &&
01455             info->rec_cache.pos_in_file < filepos + block_info.data_len &&
01456             flush_io_cache(&info->rec_cache))
01457           goto err;
01458         /*
01459           What a pity that this method is not called 'file_pread' and that
01460           there is no equivalent without seeking. We are at the right
01461           position already. :(
01462         */
01463         if (info->s->file_read(info, (unsigned char*) to, block_info.data_len,
01464                                filepos, MYF(MY_NABP)))
01465           goto panic;
01466         left_length-=block_info.data_len;
01467         to+=block_info.data_len;
01468       }
01469       filepos= block_info.next_filepos;
01470     } while (left_length);
01471 
01472     info->update|= HA_STATE_AKTIV;  /* We have a aktive record */
01473     fast_mi_writeinfo(info);
01474     return(_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
01475     MY_FILE_ERROR ? 0 : -1);
01476   }
01477   fast_mi_writeinfo(info);
01478   return(-1);     /* Wrong data to read */
01479 
01480 panic:
01481   errno=HA_ERR_WRONG_IN_RECORD;
01482 err:
01483   _mi_writeinfo(info,0);
01484   return(-1);
01485 }
01486 
01487   /* compare unique constraint between stored rows */
01488 
01489 int _mi_cmp_dynamic_unique(MI_INFO *info, MI_UNIQUEDEF *def,
01490          const unsigned char *record, internal::my_off_t pos)
01491 {
01492   unsigned char *rec_buff,*old_record;
01493   int error;
01494 
01495   if (!(old_record=(unsigned char *)malloc(info->s->base.reclength)))
01496     return(1);
01497 
01498   /* Don't let the compare destroy blobs that may be in use */
01499   rec_buff=info->rec_buff;
01500   if (info->s->base.blobs)
01501     info->rec_buff=0;
01502   error=_mi_read_dynamic_record(info,pos,old_record);
01503   if (!error)
01504     error=mi_unique_comp(def, record, old_record, def->null_are_equal);
01505   if (info->s->base.blobs)
01506   {
01507     void * rec_buff_ptr= mi_get_rec_buff_ptr(info, info->rec_buff);
01508     if (rec_buff_ptr != NULL)
01509       free(rec_buff_ptr);
01510     info->rec_buff=rec_buff;
01511   }
01512   free(old_record);
01513   return(error);
01514 }
01515 
01516 
01517   /* Compare of record one disk with packed record in memory */
01518 
01519 int _mi_cmp_dynamic_record(register MI_INFO *info, register const unsigned char *record)
01520 {
01521   uint32_t flag,reclength,b_type;
01522   internal::my_off_t filepos;
01523   unsigned char *buffer;
01524   MI_BLOCK_INFO block_info;
01525 
01526   if (info->opt_flag & WRITE_CACHE_USED)
01527   {
01528     info->update&= ~(HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK);
01529     if (flush_io_cache(&info->rec_cache))
01530       return(-1);
01531   }
01532   info->rec_cache.seek_not_done=1;
01533 
01534   /* If nobody have touched the database we don't have to test rec */
01535 
01536   buffer=info->rec_buff;
01537   if ((info->opt_flag & READ_CHECK_USED))
01538   {           /* If check isn't disabled  */
01539     if (info->s->base.blobs)
01540     {
01541       if (!(buffer=(unsigned char*) malloc(info->s->base.pack_reclength+
01542              _my_calc_total_blob_length(info,record))))
01543   return(-1);
01544     }
01545     reclength=_mi_rec_pack(info,buffer,record);
01546     record= buffer;
01547 
01548     filepos=info->lastpos;
01549     flag=block_info.second_read=0;
01550     block_info.next_filepos=filepos;
01551     while (reclength > 0)
01552     {
01553       if ((b_type=_mi_get_block_info(&block_info,info->dfile,
01554             block_info.next_filepos))
01555     & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
01556        BLOCK_FATAL_ERROR))
01557       {
01558   if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
01559     errno=HA_ERR_RECORD_CHANGED;
01560   goto err;
01561       }
01562       if (flag == 0)        /* First block */
01563       {
01564   flag=1;
01565   if (reclength != block_info.rec_len)
01566   {
01567     errno=HA_ERR_RECORD_CHANGED;
01568     goto err;
01569   }
01570       } else if (reclength < block_info.data_len)
01571       {
01572   errno=HA_ERR_WRONG_IN_RECORD;
01573   goto err;
01574       }
01575       reclength-=block_info.data_len;
01576       if (_mi_cmp_buffer(info->dfile,record,block_info.filepos,
01577        block_info.data_len))
01578       {
01579   errno=HA_ERR_RECORD_CHANGED;
01580   goto err;
01581       }
01582       flag=1;
01583       record+=block_info.data_len;
01584     }
01585   }
01586   errno=0;
01587 err:
01588   if (buffer != info->rec_buff)
01589     free((unsigned char*) buffer);
01590   return(errno);
01591 }
01592 
01593 
01594   /* Compare file to buffert */
01595 
01596 static int _mi_cmp_buffer(int file, const unsigned char *buff, internal::my_off_t filepos,
01597         uint32_t length)
01598 {
01599   uint32_t next_length;
01600   unsigned char temp_buff[IO_SIZE*2];
01601 
01602   next_length= IO_SIZE*2 - (uint) (filepos & (IO_SIZE-1));
01603 
01604   while (length > IO_SIZE*2)
01605   {
01606     if (my_pread(file,temp_buff,next_length,filepos, MYF(MY_NABP)) ||
01607   memcmp(buff, temp_buff, next_length))
01608       goto err;
01609     filepos+=next_length;
01610     buff+=next_length;
01611     length-= next_length;
01612     next_length=IO_SIZE*2;
01613   }
01614   if (my_pread(file,temp_buff,length,filepos,MYF(MY_NABP)))
01615     goto err;
01616   return(memcmp(buff,temp_buff,length));
01617 err:
01618   return(1);
01619 }
01620 
01621 
01622 /*
01623   Read record from datafile.
01624 
01625   SYNOPSIS
01626     _mi_read_rnd_dynamic_record()
01627       info                      MI_INFO pointer to table.
01628       buf                       Destination for record.
01629       filepos                   From where to read the record.
01630       skip_deleted_blocks       If to repeat reading until a non-deleted
01631                                 record is found.
01632 
01633   NOTE
01634 
01635     If a write buffer is active, it needs to be flushed if its contents
01636     intersects with the record to read. We always check if the position
01637     of the first byte of the write buffer is lower than the position
01638     past the last byte to read. In theory this is also true if the write
01639     buffer is completely below the read segment. That is, if there is no
01640     intersection. But this case is unusual. We flush anyway. Only if the
01641     first byte in the write buffer is above the last byte to read, we do
01642     not flush.
01643 
01644     A dynamic record may need several reads. So this check must be done
01645     before every read. Reading a dynamic record starts with reading the
01646     block header. If the record does not fit into the free space of the
01647     header, the block may be longer than the header. In this case a
01648     second read is necessary. These one or two reads repeat for every
01649     part of the record.
01650 
01651   RETURN
01652     0           OK
01653     != 0        Error
01654 */
01655 
01656 int _mi_read_rnd_dynamic_record(MI_INFO *info, unsigned char *buf,
01657         register internal::my_off_t filepos,
01658         bool skip_deleted_blocks)
01659 {
01660   int block_of_record, info_read, save_errno;
01661   uint32_t left_len,b_type;
01662   unsigned char *to= NULL;
01663   MI_BLOCK_INFO block_info;
01664   MYISAM_SHARE *share=info->s;
01665 
01666   info_read=0;
01667 
01668   if (info->lock_type == F_UNLCK)
01669   {
01670     info->tmp_lock_type=F_RDLCK;
01671   }
01672   else
01673     info_read=1;        /* memory-keyinfoblock is ok */
01674 
01675   block_of_record= 0;   /* First block of record is numbered as zero. */
01676   block_info.second_read= 0;
01677   left_len=1;
01678   do
01679   {
01680     if (filepos >= info->state->data_file_length)
01681     {
01682       if (!info_read)
01683       {           /* Check if changed */
01684   info_read=1;
01685   info->rec_cache.seek_not_done=1;
01686   if (mi_state_info_read_dsk(share->kfile,&share->state,1))
01687     goto panic;
01688       }
01689       if (filepos >= info->state->data_file_length)
01690       {
01691   errno= HA_ERR_END_OF_FILE;
01692   goto err;
01693       }
01694     }
01695     if (info->opt_flag & READ_CACHE_USED)
01696     {
01697       if (_mi_read_cache(&info->rec_cache,(unsigned char*) block_info.header,filepos,
01698        sizeof(block_info.header),
01699        (!block_of_record && skip_deleted_blocks ?
01700                           READING_NEXT : 0) | READING_HEADER))
01701   goto panic;
01702       b_type=_mi_get_block_info(&block_info,-1,filepos);
01703     }
01704     else
01705     {
01706       if (info->opt_flag & WRITE_CACHE_USED &&
01707     info->rec_cache.pos_in_file < filepos + MI_BLOCK_INFO_HEADER_LENGTH &&
01708     flush_io_cache(&info->rec_cache))
01709   return(errno);
01710       info->rec_cache.seek_not_done=1;
01711       b_type=_mi_get_block_info(&block_info,info->dfile,filepos);
01712     }
01713 
01714     if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
01715       BLOCK_FATAL_ERROR))
01716     {
01717       if ((b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
01718     && skip_deleted_blocks)
01719       {
01720   filepos=block_info.filepos+block_info.block_len;
01721   block_info.second_read=0;
01722   continue;   /* Search after next_record */
01723       }
01724       if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
01725       {
01726   errno=HA_ERR_RECORD_DELETED;
01727   info->lastpos=block_info.filepos;
01728   info->nextpos=block_info.filepos+block_info.block_len;
01729       }
01730       goto err;
01731     }
01732     if (block_of_record == 0)       /* First block */
01733     {
01734       if (block_info.rec_len > (uint) share->base.max_pack_length)
01735   goto panic;
01736       info->lastpos=filepos;
01737       if (share->base.blobs)
01738       {
01739   if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
01740             &info->rec_buff)))
01741     goto err;
01742       }
01743       else
01744   to= info->rec_buff;
01745       left_len=block_info.rec_len;
01746     }
01747     if (left_len < block_info.data_len)
01748       goto panic;       /* Wrong linked record */
01749 
01750     /* copy information that is already read */
01751     {
01752       uint32_t offset=(uint) (block_info.filepos - filepos);
01753       uint32_t tmp_length= (sizeof(block_info.header) - offset);
01754       filepos=block_info.filepos;
01755 
01756       if (tmp_length > block_info.data_len)
01757   tmp_length= block_info.data_len;
01758       if (tmp_length)
01759       {
01760   memcpy(to, block_info.header+offset,tmp_length);
01761   block_info.data_len-=tmp_length;
01762   left_len-=tmp_length;
01763   to+=tmp_length;
01764   filepos+=tmp_length;
01765       }
01766     }
01767     /* read rest of record from file */
01768     if (block_info.data_len)
01769     {
01770       if (info->opt_flag & READ_CACHE_USED)
01771       {
01772   if (_mi_read_cache(&info->rec_cache,(unsigned char*) to,filepos,
01773          block_info.data_len,
01774          (!block_of_record && skip_deleted_blocks) ?
01775                            READING_NEXT : 0))
01776     goto panic;
01777       }
01778       else
01779       {
01780         if (info->opt_flag & WRITE_CACHE_USED &&
01781             info->rec_cache.pos_in_file <
01782             block_info.filepos + block_info.data_len &&
01783             flush_io_cache(&info->rec_cache))
01784           goto err;
01785   /* lseek(info->dfile,filepos,SEEK_SET); */
01786   if (internal::my_read(info->dfile,(unsigned char*) to,block_info.data_len,MYF(MY_NABP)))
01787   {
01788     if (errno == -1)
01789       errno= HA_ERR_WRONG_IN_RECORD;  /* Unexpected end of file */
01790     goto err;
01791   }
01792       }
01793     }
01794     /*
01795       Increment block-of-record counter. If it was the first block,
01796       remember the position behind the block for the next call.
01797     */
01798     if (block_of_record++ == 0)
01799     {
01800       info->nextpos= block_info.filepos + block_info.block_len;
01801       skip_deleted_blocks= 0;
01802     }
01803     left_len-=block_info.data_len;
01804     to+=block_info.data_len;
01805     filepos=block_info.next_filepos;
01806   } while (left_len);
01807 
01808   info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
01809   fast_mi_writeinfo(info);
01810   if (_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
01811       MY_FILE_ERROR)
01812     return(0);
01813   return(errno);      /* Wrong record */
01814 
01815 panic:
01816   errno=HA_ERR_WRONG_IN_RECORD;   /* Something is fatal wrong */
01817 err:
01818   save_errno=errno;
01819   _mi_writeinfo(info,0);
01820   return(errno=save_errno);
01821 }
01822 
01823 
01824   /* Read and process header from a dynamic-record-file */
01825 
01826 uint32_t _mi_get_block_info(MI_BLOCK_INFO *info, int file, internal::my_off_t filepos)
01827 {
01828   uint32_t return_val=0;
01829   unsigned char *header=info->header;
01830 
01831   if (file >= 0)
01832   {
01833     /*
01834       We do not use my_pread() here because we want to have the file
01835       pointer set to the end of the header after this function.
01836       my_pread() may leave the file pointer untouched.
01837     */
01838     lseek(file,filepos,SEEK_SET);
01839     if (internal::my_read(file, header, sizeof(info->header),MYF(0)) !=
01840   sizeof(info->header))
01841       goto err;
01842   }
01843   if (info->second_read)
01844   {
01845     if (info->header[0] <= 6 || info->header[0] == 13)
01846       return_val=BLOCK_SYNC_ERROR;
01847   }
01848   else
01849   {
01850     if (info->header[0] > 6 && info->header[0] != 13)
01851       return_val=BLOCK_SYNC_ERROR;
01852   }
01853   info->next_filepos= HA_OFFSET_ERROR; /* Dummy if no next block */
01854 
01855   switch (info->header[0]) {
01856   case 0:
01857     if ((info->block_len=(uint) mi_uint3korr(header+1)) <
01858   MI_MIN_BLOCK_LENGTH ||
01859   (info->block_len & (MI_DYN_ALIGN_SIZE -1)))
01860       goto err;
01861     info->filepos=filepos;
01862     info->next_filepos=mi_sizekorr(header+4);
01863     info->prev_filepos=mi_sizekorr(header+12);
01864 #if SIZEOF_OFF_T == 4
01865     if ((mi_uint4korr(header+4) != 0 &&
01866    (mi_uint4korr(header+4) != UINT32_MAX ||
01867     info->next_filepos != UINT32_MAX) ||
01868   (mi_uint4korr(header+12) != 0 &&
01869    (mi_uint4korr(header+12) != UINT32_MAX ||
01870     info->prev_filepos != UINT32_MAX))
01871       goto err;
01872 #endif
01873     return return_val | BLOCK_DELETED;    /* Deleted block */
01874 
01875   case 1:
01876     info->rec_len=info->data_len=info->block_len=mi_uint2korr(header+1);
01877     info->filepos=filepos+3;
01878     return return_val | BLOCK_FIRST | BLOCK_LAST;
01879   case 2:
01880     info->rec_len=info->data_len=info->block_len=mi_uint3korr(header+1);
01881     info->filepos=filepos+4;
01882     return return_val | BLOCK_FIRST | BLOCK_LAST;
01883 
01884   case 13:
01885     info->rec_len=mi_uint4korr(header+1);
01886     info->block_len=info->data_len=mi_uint3korr(header+5);
01887     info->next_filepos=mi_sizekorr(header+8);
01888     info->second_read=1;
01889     info->filepos=filepos+16;
01890     return return_val | BLOCK_FIRST;
01891 
01892   case 3:
01893     info->rec_len=info->data_len=mi_uint2korr(header+1);
01894     info->block_len=info->rec_len+ (uint) header[3];
01895     info->filepos=filepos+4;
01896     return return_val | BLOCK_FIRST | BLOCK_LAST;
01897   case 4:
01898     info->rec_len=info->data_len=mi_uint3korr(header+1);
01899     info->block_len=info->rec_len+ (uint) header[4];
01900     info->filepos=filepos+5;
01901     return return_val | BLOCK_FIRST | BLOCK_LAST;
01902 
01903   case 5:
01904     info->rec_len=mi_uint2korr(header+1);
01905     info->block_len=info->data_len=mi_uint2korr(header+3);
01906     info->next_filepos=mi_sizekorr(header+5);
01907     info->second_read=1;
01908     info->filepos=filepos+13;
01909     return return_val | BLOCK_FIRST;
01910   case 6:
01911     info->rec_len=mi_uint3korr(header+1);
01912     info->block_len=info->data_len=mi_uint3korr(header+4);
01913     info->next_filepos=mi_sizekorr(header+7);
01914     info->second_read=1;
01915     info->filepos=filepos+15;
01916     return return_val | BLOCK_FIRST;
01917 
01918     /* The following blocks are identical to 1-6 without rec_len */
01919   case 7:
01920     info->data_len=info->block_len=mi_uint2korr(header+1);
01921     info->filepos=filepos+3;
01922     return return_val | BLOCK_LAST;
01923   case 8:
01924     info->data_len=info->block_len=mi_uint3korr(header+1);
01925     info->filepos=filepos+4;
01926     return return_val | BLOCK_LAST;
01927 
01928   case 9:
01929     info->data_len=mi_uint2korr(header+1);
01930     info->block_len=info->data_len+ (uint) header[3];
01931     info->filepos=filepos+4;
01932     return return_val | BLOCK_LAST;
01933   case 10:
01934     info->data_len=mi_uint3korr(header+1);
01935     info->block_len=info->data_len+ (uint) header[4];
01936     info->filepos=filepos+5;
01937     return return_val | BLOCK_LAST;
01938 
01939   case 11:
01940     info->data_len=info->block_len=mi_uint2korr(header+1);
01941     info->next_filepos=mi_sizekorr(header+3);
01942     info->second_read=1;
01943     info->filepos=filepos+11;
01944     return return_val;
01945   case 12:
01946     info->data_len=info->block_len=mi_uint3korr(header+1);
01947     info->next_filepos=mi_sizekorr(header+4);
01948     info->second_read=1;
01949     info->filepos=filepos+12;
01950     return return_val;
01951   }
01952 
01953 err:
01954   errno=HA_ERR_WRONG_IN_RECORD;  /* Garbage */
01955   return BLOCK_ERROR;
01956 }