MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
mi_dynrec.c
1 /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 /*
17  Functions to handle space-packed-records and blobs
18 
19  A row may be stored in one or more linked blocks.
20  The block size is between MI_MIN_BLOCK_LENGTH and MI_MAX_BLOCK_LENGTH.
21  Each block is aligned on MI_DYN_ALIGN_SIZE.
22  The reson for the max block size is to not have too many different types
23  of blocks. For the differnet block types, look at _mi_get_block_info()
24 */
25 
26 #include "myisamdef.h"
27 
28 /* Enough for comparing if number is zero */
29 static char zero_string[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
30 
31 static int write_dynamic_record(MI_INFO *info,const uchar *record,
32  ulong reclength);
33 static int _mi_find_writepos(MI_INFO *info,ulong reclength,my_off_t *filepos,
34  ulong *length);
35 static int update_dynamic_record(MI_INFO *info,my_off_t filepos,uchar *record,
36  ulong reclength);
37 static int delete_dynamic_record(MI_INFO *info,my_off_t filepos,
38  uint second_read);
39 static int _mi_cmp_buffer(File file, const uchar *buff, my_off_t filepos,
40  uint length);
41 
42 /* Play it safe; We have a small stack when using threads */
43 #undef my_alloca
44 #undef my_afree
45 #define my_alloca(A) my_malloc((A),MYF(0))
46 #define my_afree(A) my_free((A))
47 
48  /* Interface function from MI_INFO */
49 
50 #ifdef HAVE_MMAP
51 
52 /*
53  Create mmaped area for MyISAM handler
54 
55  SYNOPSIS
56  mi_dynmap_file()
57  info MyISAM handler
58 
59  RETURN
60  0 ok
61  1 error.
62 */
63 
64 my_bool mi_dynmap_file(MI_INFO *info, my_off_t size)
65 {
66  DBUG_ENTER("mi_dynmap_file");
67  if (size == 0 || size > (my_off_t) (~((size_t) 0)))
68  {
69  if (size)
70  DBUG_PRINT("warning", ("File is too large for mmap"));
71  else
72  DBUG_PRINT("warning", ("Do not mmap zero-length"));
73  DBUG_RETURN(1);
74  }
75  /*
76  I wonder if it is good to use MAP_NORESERVE. From the Linux man page:
77  MAP_NORESERVE
78  Do not reserve swap space for this mapping. When swap space is
79  reserved, one has the guarantee that it is possible to modify the
80  mapping. When swap space is not reserved one might get SIGSEGV
81  upon a write if no physical memory is available.
82  */
83  info->s->file_map= (uchar*)
84  my_mmap(0, (size_t) size,
85  info->s->mode==O_RDONLY ? PROT_READ :
86  PROT_READ | PROT_WRITE,
87  MAP_SHARED | MAP_NORESERVE,
88  info->dfile, 0L);
89  if (info->s->file_map == (uchar*) MAP_FAILED)
90  {
91  info->s->file_map= NULL;
92  DBUG_RETURN(1);
93  }
94 #if defined(HAVE_MADVISE)
95  madvise((char*) info->s->file_map, size, MADV_RANDOM);
96 #endif
97  info->s->mmaped_length= size;
98  info->s->file_read= mi_mmap_pread;
99  info->s->file_write= mi_mmap_pwrite;
100  DBUG_RETURN(0);
101 }
102 
103 
104 /*
105  Destroy mmaped area for MyISAM handler
106 
107  SYNOPSIS
108  mi_munmap_file()
109  info MyISAM handler
110 
111  RETURN
112  0 ok
113  !0 error.
114 */
115 
116 int mi_munmap_file(MI_INFO *info)
117 {
118  int ret;
119  DBUG_ENTER("mi_unmap_file");
120  if ((ret= my_munmap((void*) info->s->file_map, info->s->mmaped_length)))
121  DBUG_RETURN(ret);
122  info->s->file_read= mi_nommap_pread;
123  info->s->file_write= mi_nommap_pwrite;
124  info->s->file_map= 0;
125  info->s->mmaped_length= 0;
126  DBUG_RETURN(0);
127 }
128 
129 
130 /*
131  Resize mmaped area for MyISAM handler
132 
133  SYNOPSIS
134  mi_remap_file()
135  info MyISAM handler
136 
137  RETURN
138 */
139 
140 void mi_remap_file(MI_INFO *info, my_off_t size)
141 {
142  if (info->s->file_map)
143  {
144  mi_munmap_file(info);
145  mi_dynmap_file(info, size);
146  }
147 }
148 #endif
149 
150 
151 /*
152  Read bytes from MySAM handler, using mmap or pread
153 
154  SYNOPSIS
155  mi_mmap_pread()
156  info MyISAM handler
157  Buffer Input buffer
158  Count Count of bytes for read
159  offset Start position
160  MyFlags
161 
162  RETURN
163  0 ok
164 */
165 
166 size_t mi_mmap_pread(MI_INFO *info, uchar *Buffer,
167  size_t Count, my_off_t offset, myf MyFlags)
168 {
169  DBUG_PRINT("info", ("mi_read with mmap %d\n", info->dfile));
170  if (info->s->concurrent_insert)
171  mysql_rwlock_rdlock(&info->s->mmap_lock);
172 
173  /*
174  The following test may fail in the following cases:
175  - We failed to remap a memory area (fragmented memory?)
176  - This thread has done some writes, but not yet extended the
177  memory mapped area.
178  */
179 
180  if (info->s->mmaped_length >= offset + Count)
181  {
182  memcpy(Buffer, info->s->file_map + offset, Count);
183  if (info->s->concurrent_insert)
184  mysql_rwlock_unlock(&info->s->mmap_lock);
185  return 0;
186  }
187  else
188  {
189  if (info->s->concurrent_insert)
190  mysql_rwlock_unlock(&info->s->mmap_lock);
191  return mysql_file_pread(info->dfile, Buffer, Count, offset, MyFlags);
192  }
193 }
194 
195 
196  /* wrapper for mysql_file_pread in case if mmap isn't used */
197 
198 size_t mi_nommap_pread(MI_INFO *info, uchar *Buffer,
199  size_t Count, my_off_t offset, myf MyFlags)
200 {
201  return mysql_file_pread(info->dfile, Buffer, Count, offset, MyFlags);
202 }
203 
204 
205 /*
206  Write bytes to MySAM handler, using mmap or pwrite
207 
208  SYNOPSIS
209  mi_mmap_pwrite()
210  info MyISAM handler
211  Buffer Output buffer
212  Count Count of bytes for write
213  offset Start position
214  MyFlags
215 
216  RETURN
217  0 ok
218  !=0 error. In this case return error from pwrite
219 */
220 
221 size_t mi_mmap_pwrite(MI_INFO *info, const uchar *Buffer,
222  size_t Count, my_off_t offset, myf MyFlags)
223 {
224  DBUG_PRINT("info", ("mi_write with mmap %d\n", info->dfile));
225  if (info->s->concurrent_insert)
226  mysql_rwlock_rdlock(&info->s->mmap_lock);
227 
228  /*
229  The following test may fail in the following cases:
230  - We failed to remap a memory area (fragmented memory?)
231  - This thread has done some writes, but not yet extended the
232  memory mapped area.
233  */
234 
235  if (info->s->mmaped_length >= offset + Count)
236  {
237  memcpy(info->s->file_map + offset, Buffer, Count);
238  if (info->s->concurrent_insert)
239  mysql_rwlock_unlock(&info->s->mmap_lock);
240  return 0;
241  }
242  else
243  {
244  info->s->nonmmaped_inserts++;
245  if (info->s->concurrent_insert)
246  mysql_rwlock_unlock(&info->s->mmap_lock);
247  return mysql_file_pwrite(info->dfile, Buffer, Count, offset, MyFlags);
248  }
249 
250 }
251 
252 
253  /* wrapper for mysql_file_pwrite in case if mmap isn't used */
254 
255 size_t mi_nommap_pwrite(MI_INFO *info, const uchar *Buffer,
256  size_t Count, my_off_t offset, myf MyFlags)
257 {
258  return mysql_file_pwrite(info->dfile, Buffer, Count, offset, MyFlags);
259 }
260 
261 
262 int _mi_write_dynamic_record(MI_INFO *info, const uchar *record)
263 {
264  ulong reclength=_mi_rec_pack(info,info->rec_buff,record);
265  return (write_dynamic_record(info,info->rec_buff,reclength));
266 }
267 
268 int _mi_update_dynamic_record(MI_INFO *info, my_off_t pos, const uchar *record)
269 {
270  uint length=_mi_rec_pack(info,info->rec_buff,record);
271  return (update_dynamic_record(info,pos,info->rec_buff,length));
272 }
273 
274 int _mi_write_blob_record(MI_INFO *info, const uchar *record)
275 {
276  uchar *rec_buff;
277  int error;
278  ulong reclength,reclength2,extra;
279 
280  extra= (ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
281  MI_DYN_DELETE_BLOCK_HEADER+1);
282  reclength= (info->s->base.pack_reclength +
283  _my_calc_total_blob_length(info,record)+ extra);
284  if (!(rec_buff=(uchar*) my_alloca(reclength)))
285  {
286  my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */
287  return(-1);
288  }
289  reclength2= _mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
290  record);
291  DBUG_PRINT("info",("reclength: %lu reclength2: %lu",
292  reclength, reclength2));
293  DBUG_ASSERT(reclength2 <= reclength);
294  error=write_dynamic_record(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
295  reclength2);
296  my_afree(rec_buff);
297  return(error);
298 }
299 
300 
301 int _mi_update_blob_record(MI_INFO *info, my_off_t pos, const uchar *record)
302 {
303  uchar *rec_buff;
304  int error;
305  ulong reclength,extra;
306 
307  extra= (ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
308  MI_DYN_DELETE_BLOCK_HEADER);
309  reclength= (info->s->base.pack_reclength+
310  _my_calc_total_blob_length(info,record)+ extra);
311  if (!(rec_buff=(uchar*) my_alloca(reclength)))
312  {
313  my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */
314  return(-1);
315  }
316  reclength=_mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
317  record);
318  error=update_dynamic_record(info,pos,
319  rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
320  reclength);
321  my_afree(rec_buff);
322  return(error);
323 }
324 
325 
326 int _mi_delete_dynamic_record(MI_INFO *info)
327 {
328  return delete_dynamic_record(info,info->lastpos,0);
329 }
330 
331 
332  /* Write record to data-file */
333 
334 static int write_dynamic_record(MI_INFO *info, const uchar *record,
335  ulong reclength)
336 {
337  int flag;
338  ulong length;
339  my_off_t filepos;
340  DBUG_ENTER("write_dynamic_record");
341 
342  flag=0;
343 
344  /*
345  Check if we have enough room for the new record.
346  First we do simplified check to make usual case faster.
347  Then we do more precise check for the space left.
348  Though it still is not absolutely precise, as
349  we always use MI_MAX_DYN_BLOCK_HEADER while it can be
350  less in the most of the cases.
351  */
352 
353  if (unlikely(info->s->base.max_data_file_length -
354  info->state->data_file_length <
355  reclength + MI_MAX_DYN_BLOCK_HEADER))
356  {
357  if (info->s->base.max_data_file_length - info->state->data_file_length +
358  info->state->empty - info->state->del * MI_MAX_DYN_BLOCK_HEADER <
359  reclength + MI_MAX_DYN_BLOCK_HEADER)
360  {
361  my_errno=HA_ERR_RECORD_FILE_FULL;
362  DBUG_RETURN(1);
363  }
364  }
365 
366  do
367  {
368  if (_mi_find_writepos(info,reclength,&filepos,&length))
369  goto err;
370  if (_mi_write_part_record(info,filepos,length,
371  (info->append_insert_at_end ?
372  HA_OFFSET_ERROR : info->s->state.dellink),
373  (uchar**) &record,&reclength,&flag))
374  goto err;
375  } while (reclength);
376 
377  DBUG_RETURN(0);
378 err:
379  DBUG_RETURN(1);
380 }
381 
382 
383  /* Get a block for data ; The given data-area must be used !! */
384 
385 static int _mi_find_writepos(MI_INFO *info,
386  ulong reclength, /* record length */
387  my_off_t *filepos, /* Return file pos */
388  ulong *length) /* length of block at filepos */
389 {
390  MI_BLOCK_INFO block_info;
391  ulong tmp;
392  DBUG_ENTER("_mi_find_writepos");
393 
394  if (info->s->state.dellink != HA_OFFSET_ERROR &&
395  !info->append_insert_at_end)
396  {
397  /* Deleted blocks exists; Get last used block */
398  *filepos=info->s->state.dellink;
399  block_info.second_read=0;
400  info->rec_cache.seek_not_done=1;
401  if (!(_mi_get_block_info(&block_info,info->dfile,info->s->state.dellink) &
402  BLOCK_DELETED))
403  {
404  DBUG_PRINT("error",("Delete link crashed"));
405  my_errno=HA_ERR_WRONG_IN_RECORD;
406  DBUG_RETURN(-1);
407  }
408  info->s->state.dellink=block_info.next_filepos;
409  info->state->del--;
410  info->state->empty-= block_info.block_len;
411  *length= block_info.block_len;
412  }
413  else
414  {
415  /* No deleted blocks; Allocate a new block */
416  *filepos=info->state->data_file_length;
417  if ((tmp=reclength+3 + test(reclength >= (65520-3))) <
418  info->s->base.min_block_length)
419  tmp= info->s->base.min_block_length;
420  else
421  tmp= ((tmp+MI_DYN_ALIGN_SIZE-1) &
422  (~ (ulong) (MI_DYN_ALIGN_SIZE-1)));
423  if (info->state->data_file_length >
424  (info->s->base.max_data_file_length - tmp))
425  {
426  my_errno=HA_ERR_RECORD_FILE_FULL;
427  DBUG_RETURN(-1);
428  }
429  if (tmp > MI_MAX_BLOCK_LENGTH)
430  tmp=MI_MAX_BLOCK_LENGTH;
431  *length= tmp;
432  info->state->data_file_length+= tmp;
433  info->s->state.split++;
434  info->update|=HA_STATE_WRITE_AT_END;
435  }
436  DBUG_RETURN(0);
437 } /* _mi_find_writepos */
438 
439 
440 
441 /*
442  Unlink a deleted block from the deleted list.
443  This block will be combined with the preceding or next block to form
444  a big block.
445 */
446 
447 static my_bool unlink_deleted_block(MI_INFO *info, MI_BLOCK_INFO *block_info)
448 {
449  DBUG_ENTER("unlink_deleted_block");
450  if (block_info->filepos == info->s->state.dellink)
451  {
452  /* First deleted block; We can just use this ! */
453  info->s->state.dellink=block_info->next_filepos;
454  }
455  else
456  {
457  MI_BLOCK_INFO tmp;
458  tmp.second_read=0;
459  /* Unlink block from the previous block */
460  if (!(_mi_get_block_info(&tmp,info->dfile,block_info->prev_filepos)
461  & BLOCK_DELETED))
462  DBUG_RETURN(1); /* Something is wrong */
463  mi_sizestore(tmp.header+4,block_info->next_filepos);
464  if (info->s->file_write(info, tmp.header+4,8,
465  block_info->prev_filepos+4, MYF(MY_NABP)))
466  DBUG_RETURN(1);
467  /* Unlink block from next block */
468  if (block_info->next_filepos != HA_OFFSET_ERROR)
469  {
470  if (!(_mi_get_block_info(&tmp,info->dfile,block_info->next_filepos)
471  & BLOCK_DELETED))
472  DBUG_RETURN(1); /* Something is wrong */
473  mi_sizestore(tmp.header+12,block_info->prev_filepos);
474  if (info->s->file_write(info, tmp.header+12,8,
475  block_info->next_filepos+12,
476  MYF(MY_NABP)))
477  DBUG_RETURN(1);
478  }
479  }
480  /* We now have one less deleted block */
481  info->state->del--;
482  info->state->empty-= block_info->block_len;
483  info->s->state.split--;
484 
485  /*
486  If this was a block that we where accessing through table scan
487  (mi_rrnd() or mi_scan(), then ensure that we skip over this block
488  when doing next mi_rrnd() or mi_scan().
489  */
490  if (info->nextpos == block_info->filepos)
491  info->nextpos+=block_info->block_len;
492  DBUG_RETURN(0);
493 }
494 
495 
496 /*
497  Add a backward link to delete block
498 
499  SYNOPSIS
500  update_backward_delete_link()
501  info MyISAM handler
502  delete_block Position to delete block to update.
503  If this is 'HA_OFFSET_ERROR', nothing will be done
504  filepos Position to block that 'delete_block' should point to
505 
506  RETURN
507  0 ok
508  1 error. In this case my_error is set.
509 */
510 
511 static int update_backward_delete_link(MI_INFO *info, my_off_t delete_block,
512  my_off_t filepos)
513 {
514  MI_BLOCK_INFO block_info;
515  DBUG_ENTER("update_backward_delete_link");
516 
517  if (delete_block != HA_OFFSET_ERROR)
518  {
519  block_info.second_read=0;
520  if (_mi_get_block_info(&block_info,info->dfile,delete_block)
521  & BLOCK_DELETED)
522  {
523  uchar buff[8];
524  mi_sizestore(buff,filepos);
525  if (info->s->file_write(info,buff, 8, delete_block+12, MYF(MY_NABP)))
526  DBUG_RETURN(1); /* Error on write */
527  }
528  else
529  {
530  my_errno=HA_ERR_WRONG_IN_RECORD;
531  DBUG_RETURN(1); /* Wrong delete link */
532  }
533  }
534  DBUG_RETURN(0);
535 }
536 
537  /* Delete datarecord from database */
538  /* info->rec_cache.seek_not_done is updated in cmp_record */
539 
540 static int delete_dynamic_record(MI_INFO *info, my_off_t filepos,
541  uint second_read)
542 {
543  uint length,b_type;
544  MI_BLOCK_INFO block_info,del_block;
545  int error;
546  my_bool remove_next_block;
547  DBUG_ENTER("delete_dynamic_record");
548 
549  /* First add a link from the last block to the new one */
550  error= update_backward_delete_link(info, info->s->state.dellink, filepos);
551 
552  block_info.second_read=second_read;
553  do
554  {
555  /* Remove block at 'filepos' */
556  if ((b_type=_mi_get_block_info(&block_info,info->dfile,filepos))
557  & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
558  BLOCK_FATAL_ERROR) ||
559  (length=(uint) (block_info.filepos-filepos) +block_info.block_len) <
560  MI_MIN_BLOCK_LENGTH)
561  {
562  my_errno=HA_ERR_WRONG_IN_RECORD;
563  DBUG_RETURN(1);
564  }
565  /* Check if next block is a delete block */
566  del_block.second_read=0;
567  remove_next_block=0;
568  if (_mi_get_block_info(&del_block,info->dfile,filepos+length) &
569  BLOCK_DELETED && del_block.block_len+length < MI_DYN_MAX_BLOCK_LENGTH)
570  {
571  /* We can't remove this yet as this block may be the head block */
572  remove_next_block=1;
573  length+=del_block.block_len;
574  }
575 
576  block_info.header[0]=0;
577  mi_int3store(block_info.header+1,length);
578  mi_sizestore(block_info.header+4,info->s->state.dellink);
579  if (b_type & BLOCK_LAST)
580  memset(block_info.header + 12, 255, 8);
581  else
582  mi_sizestore(block_info.header+12,block_info.next_filepos);
583  if (info->s->file_write(info,(uchar*) block_info.header,20,filepos,
584  MYF(MY_NABP)))
585  DBUG_RETURN(1);
586  info->s->state.dellink = filepos;
587  info->state->del++;
588  info->state->empty+=length;
589  filepos=block_info.next_filepos;
590 
591  /* Now it's safe to unlink the deleted block directly after this one */
592  if (remove_next_block && unlink_deleted_block(info,&del_block))
593  error=1;
594  } while (!(b_type & BLOCK_LAST));
595 
596  DBUG_RETURN(error);
597 }
598 
599 
600  /* Write a block to datafile */
601 
602 int _mi_write_part_record(MI_INFO *info,
603  my_off_t filepos, /* points at empty block */
604  ulong length, /* length of block */
605  my_off_t next_filepos,/* Next empty block */
606  uchar **record, /* pointer to record ptr */
607  ulong *reclength, /* length of *record */
608  int *flag) /* *flag == 0 if header */
609 {
610  ulong head_length,res_length,extra_length,long_block,del_length;
611  uchar *pos,*record_end;
612  my_off_t next_delete_block;
613  uchar temp[MI_SPLIT_LENGTH+MI_DYN_DELETE_BLOCK_HEADER];
614  DBUG_ENTER("_mi_write_part_record");
615 
616  next_delete_block=HA_OFFSET_ERROR;
617 
618  res_length=extra_length=0;
619  if (length > *reclength + MI_SPLIT_LENGTH)
620  { /* Splitt big block */
621  res_length=MY_ALIGN(length- *reclength - MI_EXTEND_BLOCK_LENGTH,
622  MI_DYN_ALIGN_SIZE);
623  length-= res_length; /* Use this for first part */
624  }
625  long_block= (length < 65520L && *reclength < 65520L) ? 0 : 1;
626  if (length == *reclength+ 3 + long_block)
627  {
628  /* Block is exactly of the right length */
629  temp[0]=(uchar) (1+ *flag)+(uchar) long_block; /* Flag is 0 or 6 */
630  if (long_block)
631  {
632  mi_int3store(temp+1,*reclength);
633  head_length=4;
634  }
635  else
636  {
637  mi_int2store(temp+1,*reclength);
638  head_length=3;
639  }
640  }
641  else if (length-long_block < *reclength+4)
642  { /* To short block */
643  if (next_filepos == HA_OFFSET_ERROR)
644  next_filepos= (info->s->state.dellink != HA_OFFSET_ERROR &&
645  !info->append_insert_at_end ?
646  info->s->state.dellink : info->state->data_file_length);
647  if (*flag == 0) /* First block */
648  {
649  if (*reclength > MI_MAX_BLOCK_LENGTH)
650  {
651  head_length= 16;
652  temp[0]=13;
653  mi_int4store(temp+1,*reclength);
654  mi_int3store(temp+5,length-head_length);
655  mi_sizestore((uchar*) temp+8,next_filepos);
656  }
657  else
658  {
659  head_length=5+8+long_block*2;
660  temp[0]=5+(uchar) long_block;
661  if (long_block)
662  {
663  mi_int3store(temp+1,*reclength);
664  mi_int3store(temp+4,length-head_length);
665  mi_sizestore((uchar*) temp+7,next_filepos);
666  }
667  else
668  {
669  mi_int2store(temp+1,*reclength);
670  mi_int2store(temp+3,length-head_length);
671  mi_sizestore((uchar*) temp+5,next_filepos);
672  }
673  }
674  }
675  else
676  {
677  head_length=3+8+long_block;
678  temp[0]=11+(uchar) long_block;
679  if (long_block)
680  {
681  mi_int3store(temp+1,length-head_length);
682  mi_sizestore((uchar*) temp+4,next_filepos);
683  }
684  else
685  {
686  mi_int2store(temp+1,length-head_length);
687  mi_sizestore((uchar*) temp+3,next_filepos);
688  }
689  }
690  }
691  else
692  { /* Block with empty info last */
693  head_length=4+long_block;
694  extra_length= length- *reclength-head_length;
695  temp[0]= (uchar) (3+ *flag)+(uchar) long_block; /* 3,4 or 9,10 */
696  if (long_block)
697  {
698  mi_int3store(temp+1,*reclength);
699  temp[4]= (uchar) (extra_length);
700  }
701  else
702  {
703  mi_int2store(temp+1,*reclength);
704  temp[3]= (uchar) (extra_length);
705  }
706  length= *reclength+head_length; /* Write only what is needed */
707  }
708  DBUG_DUMP("header",(uchar*) temp,head_length);
709 
710  /* Make a long block for one write */
711  record_end= *record+length-head_length;
712  del_length=(res_length ? MI_DYN_DELETE_BLOCK_HEADER : 0);
713  bmove((uchar*) (*record-head_length),(uchar*) temp,head_length);
714  memcpy(temp,record_end,(size_t) (extra_length+del_length));
715  memset(record_end, 0, extra_length);
716 
717  if (res_length)
718  {
719  /* Check first if we can join this block with the next one */
720  MI_BLOCK_INFO del_block;
721  my_off_t next_block=filepos+length+extra_length+res_length;
722 
723  del_block.second_read=0;
724  if (next_block < info->state->data_file_length &&
725  info->s->state.dellink != HA_OFFSET_ERROR)
726  {
727  if ((_mi_get_block_info(&del_block,info->dfile,next_block)
728  & BLOCK_DELETED) &&
729  res_length + del_block.block_len < MI_DYN_MAX_BLOCK_LENGTH)
730  {
731  if (unlink_deleted_block(info,&del_block))
732  goto err;
733  res_length+=del_block.block_len;
734  }
735  }
736 
737  /* Create a delete link of the last part of the block */
738  pos=record_end+extra_length;
739  pos[0]= '\0';
740  mi_int3store(pos+1,res_length);
741  mi_sizestore(pos+4,info->s->state.dellink);
742  memset(pos + 12, 255, 8); /* End link */
743  next_delete_block=info->s->state.dellink;
744  info->s->state.dellink= filepos+length+extra_length;
745  info->state->del++;
746  info->state->empty+=res_length;
747  info->s->state.split++;
748  }
749  if (info->opt_flag & WRITE_CACHE_USED &&
750  info->update & HA_STATE_WRITE_AT_END)
751  {
752  if (info->update & HA_STATE_EXTEND_BLOCK)
753  {
754  info->update&= ~HA_STATE_EXTEND_BLOCK;
755  if (my_block_write(&info->rec_cache,(uchar*) *record-head_length,
756  length+extra_length+del_length,filepos))
757  goto err;
758  }
759  else if (my_b_write(&info->rec_cache,(uchar*) *record-head_length,
760  length+extra_length+del_length))
761  goto err;
762  }
763  else
764  {
765  info->rec_cache.seek_not_done=1;
766  if (info->s->file_write(info,(uchar*) *record-head_length,length+extra_length+
767  del_length,filepos,info->s->write_flag))
768  goto err;
769  }
770  memcpy(record_end,temp,(size_t) (extra_length+del_length));
771  *record=record_end;
772  *reclength-=(length-head_length);
773  *flag=6;
774 
775  if (del_length)
776  {
777  /* link the next delete block to this */
778  if (update_backward_delete_link(info, next_delete_block,
779  info->s->state.dellink))
780  goto err;
781  }
782 
783  DBUG_RETURN(0);
784 err:
785  DBUG_PRINT("exit",("errno: %d",my_errno));
786  DBUG_RETURN(1);
787 } /*_mi_write_part_record */
788 
789 
790  /* update record from datafile */
791 
792 static int update_dynamic_record(MI_INFO *info, my_off_t filepos, uchar *record,
793  ulong reclength)
794 {
795  int flag;
796  uint error;
797  ulong length;
798  MI_BLOCK_INFO block_info;
799  DBUG_ENTER("update_dynamic_record");
800 
801  flag=block_info.second_read=0;
802  /*
803  Check if we have enough room for the record.
804  First we do simplified check to make usual case faster.
805  Then we do more precise check for the space left.
806  Though it still is not absolutely precise, as
807  we always use MI_MAX_DYN_BLOCK_HEADER while it can be
808  less in the most of the cases.
809  */
810 
811  /*
812  compare with just the reclength as we're going
813  to get some space from the old replaced record
814  */
815  if (unlikely(info->s->base.max_data_file_length -
816  info->state->data_file_length < reclength))
817  {
818  /*
819  let's read the old record's block to find out the length of the
820  old record
821  */
822  if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
823  & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR | BLOCK_FATAL_ERROR))
824  {
825  DBUG_PRINT("error",("Got wrong block info"));
826  if (!(error & BLOCK_FATAL_ERROR))
827  my_errno=HA_ERR_WRONG_IN_RECORD;
828  goto err;
829  }
830 
831  /*
832  if new record isn't longer, we can go on safely
833  */
834  if (block_info.rec_len < reclength)
835  {
836  if (info->s->base.max_data_file_length - info->state->data_file_length +
837  info->state->empty - info->state->del * MI_MAX_DYN_BLOCK_HEADER <
838  reclength - block_info.rec_len + MI_MAX_DYN_BLOCK_HEADER)
839  {
840  my_errno=HA_ERR_RECORD_FILE_FULL;
841  goto err;
842  }
843  }
844  block_info.second_read=0;
845  }
846 
847  while (reclength > 0)
848  {
849  if (filepos != info->s->state.dellink)
850  {
851  block_info.next_filepos= HA_OFFSET_ERROR;
852  if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
853  & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
854  BLOCK_FATAL_ERROR))
855  {
856  DBUG_PRINT("error",("Got wrong block info"));
857  if (!(error & BLOCK_FATAL_ERROR))
858  my_errno=HA_ERR_WRONG_IN_RECORD;
859  goto err;
860  }
861  length=(ulong) (block_info.filepos-filepos) + block_info.block_len;
862  if (length < reclength)
863  {
864  uint tmp=MY_ALIGN(reclength - length + 3 +
865  test(reclength >= 65520L),MI_DYN_ALIGN_SIZE);
866  /* Don't create a block bigger than MI_MAX_BLOCK_LENGTH */
867  tmp= MY_MIN(length+tmp, MI_MAX_BLOCK_LENGTH)-length;
868  /* Check if we can extend this block */
869  if (block_info.filepos + block_info.block_len ==
870  info->state->data_file_length &&
871  info->state->data_file_length <
872  info->s->base.max_data_file_length-tmp)
873  {
874  /* extend file */
875  DBUG_PRINT("info",("Extending file with %d bytes",tmp));
876  if (info->nextpos == info->state->data_file_length)
877  info->nextpos+= tmp;
878  info->state->data_file_length+= tmp;
879  info->update|= HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK;
880  length+=tmp;
881  }
882  else if (length < MI_MAX_BLOCK_LENGTH - MI_MIN_BLOCK_LENGTH)
883  {
884  /*
885  Check if next block is a deleted block
886  Above we have MI_MIN_BLOCK_LENGTH to avoid the problem where
887  the next block is so small it can't be splited which could
888  casue problems
889  */
890 
891  MI_BLOCK_INFO del_block;
892  del_block.second_read=0;
893  if (_mi_get_block_info(&del_block,info->dfile,
894  block_info.filepos + block_info.block_len) &
895  BLOCK_DELETED)
896  {
897  /* Use; Unlink it and extend the current block */
898  DBUG_PRINT("info",("Extending current block"));
899  if (unlink_deleted_block(info,&del_block))
900  goto err;
901  if ((length+=del_block.block_len) > MI_MAX_BLOCK_LENGTH)
902  {
903  /*
904  New block was too big, link overflow part back to
905  delete list
906  */
907  my_off_t next_pos;
908  ulong rest_length= length-MI_MAX_BLOCK_LENGTH;
909  set_if_bigger(rest_length, MI_MIN_BLOCK_LENGTH);
910  next_pos= del_block.filepos+ del_block.block_len - rest_length;
911 
912  if (update_backward_delete_link(info, info->s->state.dellink,
913  next_pos))
914  DBUG_RETURN(1);
915 
916  /* create delete link for data that didn't fit into the page */
917  del_block.header[0]=0;
918  mi_int3store(del_block.header+1, rest_length);
919  mi_sizestore(del_block.header+4,info->s->state.dellink);
920  memset(del_block.header + 12, 255, 8);
921  if (info->s->file_write(info,(uchar*) del_block.header,20, next_pos,
922  MYF(MY_NABP)))
923  DBUG_RETURN(1);
924  info->s->state.dellink= next_pos;
925  info->s->state.split++;
926  info->state->del++;
927  info->state->empty+= rest_length;
928  length-= rest_length;
929  }
930  }
931  }
932  }
933  }
934  else
935  {
936  if (_mi_find_writepos(info,reclength,&filepos,&length))
937  goto err;
938  }
939  if (_mi_write_part_record(info,filepos,length,block_info.next_filepos,
940  &record,&reclength,&flag))
941  goto err;
942  if ((filepos=block_info.next_filepos) == HA_OFFSET_ERROR)
943  {
944  /* Start writing data on deleted blocks */
945  filepos=info->s->state.dellink;
946  }
947  }
948 
949  if (block_info.next_filepos != HA_OFFSET_ERROR)
950  {
951  /*
952  delete_dynamic_record() may change data file position.
953  IO cache must be notified as it may still have cached
954  data, which has to be flushed later.
955  */
956  info->rec_cache.seek_not_done= 1;
957  if (delete_dynamic_record(info,block_info.next_filepos,1))
958  goto err;
959  }
960  DBUG_RETURN(0);
961 err:
962  DBUG_RETURN(1);
963 }
964 
965 
966  /* Pack a record. Return new reclength */
967 
968 uint _mi_rec_pack(MI_INFO *info, register uchar *to,
969  register const uchar *from)
970 {
971  uint length,new_length,flag,bit,i;
972  uchar *pos,*end,*startpos,*packpos;
973  enum en_fieldtype type;
974  reg3 MI_COLUMNDEF *rec;
975  MI_BLOB *blob;
976  DBUG_ENTER("_mi_rec_pack");
977 
978  flag=0 ; bit=1;
979  startpos=packpos=to; to+= info->s->base.pack_bits; blob=info->blobs;
980  rec=info->s->rec;
981 
982  for (i=info->s->base.fields ; i-- > 0; from+= length,rec++)
983  {
984  length=(uint) rec->length;
985  if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
986  {
987  if (type == FIELD_BLOB)
988  {
989  if (!blob->length)
990  flag|=bit;
991  else
992  {
993  char *temp_pos;
994  size_t tmp_length=length-portable_sizeof_char_ptr;
995  memcpy((uchar*) to,from,tmp_length);
996  memcpy(&temp_pos,from+tmp_length,sizeof(char*));
997  memcpy(to+tmp_length,temp_pos,(size_t) blob->length);
998  to+=tmp_length+blob->length;
999  }
1000  blob++;
1001  }
1002  else if (type == FIELD_SKIP_ZERO)
1003  {
1004  if (memcmp((uchar*) from,zero_string,length) == 0)
1005  flag|=bit;
1006  else
1007  {
1008  memcpy((uchar*) to,from,(size_t) length); to+=length;
1009  }
1010  }
1011  else if (type == FIELD_SKIP_ENDSPACE ||
1012  type == FIELD_SKIP_PRESPACE)
1013  {
1014  pos= (uchar*) from; end= (uchar*) from + length;
1015  if (type == FIELD_SKIP_ENDSPACE)
1016  { /* Pack trailing spaces */
1017  while (end > from && *(end-1) == ' ')
1018  end--;
1019  }
1020  else
1021  { /* Pack pref-spaces */
1022  while (pos < end && *pos == ' ')
1023  pos++;
1024  }
1025  new_length=(uint) (end-pos);
1026  if (new_length +1 + test(rec->length > 255 && new_length > 127)
1027  < length)
1028  {
1029  if (rec->length > 255 && new_length > 127)
1030  {
1031  to[0]= (uchar) ((new_length & 127) + 128);
1032  to[1]= (uchar) (new_length >> 7);
1033  to+=2;
1034  }
1035  else
1036  *to++= (uchar) new_length;
1037  memcpy((uchar*) to,pos,(size_t) new_length); to+=new_length;
1038  flag|=bit;
1039  }
1040  else
1041  {
1042  memcpy(to,from,(size_t) length); to+=length;
1043  }
1044  }
1045  else if (type == FIELD_VARCHAR)
1046  {
1047  uint pack_length= HA_VARCHAR_PACKLENGTH(rec->length -1);
1048  uint tmp_length;
1049  if (pack_length == 1)
1050  {
1051  tmp_length= (uint) *(uchar*) from;
1052  *to++= *from;
1053  }
1054  else
1055  {
1056  tmp_length= uint2korr(from);
1057  store_key_length_inc(to,tmp_length);
1058  }
1059  memcpy(to, from+pack_length,tmp_length);
1060  to+= tmp_length;
1061  continue;
1062  }
1063  else
1064  {
1065  memcpy(to,from,(size_t) length); to+=length;
1066  continue; /* Normal field */
1067  }
1068  if ((bit= bit << 1) >= 256)
1069  {
1070  *packpos++= (uchar) flag;
1071  bit=1; flag=0;
1072  }
1073  }
1074  else
1075  {
1076  memcpy(to,from,(size_t) length); to+=length;
1077  }
1078  }
1079  if (bit != 1)
1080  *packpos= (uchar) flag;
1081  if (info->s->calc_checksum)
1082  *to++= (uchar) info->checksum;
1083  DBUG_PRINT("exit",("packed length: %d",(int) (to-startpos)));
1084  DBUG_RETURN((uint) (to-startpos));
1085 } /* _mi_rec_pack */
1086 
1087 
1088 
1089 /*
1090  Check if a record was correctly packed. Used only by myisamchk
1091  Returns 0 if record is ok.
1092 */
1093 
1094 my_bool _mi_rec_check(MI_INFO *info,const uchar *record, uchar *rec_buff,
1095  ulong packed_length, my_bool with_checksum)
1096 {
1097  uint length,new_length,flag,bit,i;
1098  uchar *pos,*end,*packpos,*to;
1099  enum en_fieldtype type;
1100  reg3 MI_COLUMNDEF *rec;
1101  DBUG_ENTER("_mi_rec_check");
1102 
1103  packpos=rec_buff; to= rec_buff+info->s->base.pack_bits;
1104  rec=info->s->rec;
1105  flag= *packpos; bit=1;
1106 
1107  for (i=info->s->base.fields ; i-- > 0; record+= length, rec++)
1108  {
1109  length=(uint) rec->length;
1110  if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
1111  {
1112  if (type == FIELD_BLOB)
1113  {
1114  uint blob_length=
1115  _mi_calc_blob_length(length-portable_sizeof_char_ptr,record);
1116  if (!blob_length && !(flag & bit))
1117  goto err;
1118  if (blob_length)
1119  to+=length - portable_sizeof_char_ptr+ blob_length;
1120  }
1121  else if (type == FIELD_SKIP_ZERO)
1122  {
1123  if (memcmp((uchar*) record,zero_string,length) == 0)
1124  {
1125  if (!(flag & bit))
1126  goto err;
1127  }
1128  else
1129  to+=length;
1130  }
1131  else if (type == FIELD_SKIP_ENDSPACE ||
1132  type == FIELD_SKIP_PRESPACE)
1133  {
1134  pos= (uchar*) record; end= (uchar*) record + length;
1135  if (type == FIELD_SKIP_ENDSPACE)
1136  { /* Pack trailing spaces */
1137  while (end > record && *(end-1) == ' ')
1138  end--;
1139  }
1140  else
1141  { /* Pack pre-spaces */
1142  while (pos < end && *pos == ' ')
1143  pos++;
1144  }
1145  new_length=(uint) (end-pos);
1146  if (new_length +1 + test(rec->length > 255 && new_length > 127)
1147  < length)
1148  {
1149  if (!(flag & bit))
1150  goto err;
1151  if (rec->length > 255 && new_length > 127)
1152  {
1153  /* purecov: begin inspected */
1154  if (to[0] != (uchar) ((new_length & 127) + 128) ||
1155  to[1] != (uchar) (new_length >> 7))
1156  goto err;
1157  to+=2;
1158  /* purecov: end */
1159  }
1160  else if (*to++ != (uchar) new_length)
1161  goto err;
1162  to+=new_length;
1163  }
1164  else
1165  to+=length;
1166  }
1167  else if (type == FIELD_VARCHAR)
1168  {
1169  uint pack_length= HA_VARCHAR_PACKLENGTH(rec->length -1);
1170  uint tmp_length;
1171  if (pack_length == 1)
1172  {
1173  tmp_length= (uint) *(uchar*) record;
1174  to+= 1+ tmp_length;
1175  continue;
1176  }
1177  else
1178  {
1179  tmp_length= uint2korr(record);
1180  to+= get_pack_length(tmp_length)+tmp_length;
1181  }
1182  continue;
1183  }
1184  else
1185  {
1186  to+=length;
1187  continue; /* Normal field */
1188  }
1189  if ((bit= bit << 1) >= 256)
1190  {
1191  flag= *++packpos;
1192  bit=1;
1193  }
1194  }
1195  else
1196  to+= length;
1197  }
1198  if (packed_length != (uint) (to - rec_buff) + test(info->s->calc_checksum) ||
1199  (bit != 1 && (flag & ~(bit - 1))))
1200  goto err;
1201  if (with_checksum && ((uchar) info->checksum != (uchar) *to))
1202  {
1203  DBUG_PRINT("error",("wrong checksum for row"));
1204  goto err;
1205  }
1206  DBUG_RETURN(0);
1207 
1208 err:
1209  DBUG_RETURN(1);
1210 }
1211 
1212 
1213 
1214  /* Unpacks a record */
1215  /* Returns -1 and my_errno =HA_ERR_RECORD_DELETED if reclength isn't */
1216  /* right. Returns reclength (>0) if ok */
1217 
1218 ulong _mi_rec_unpack(register MI_INFO *info, register uchar *to, uchar *from,
1219  ulong found_length)
1220 {
1221  uint flag,bit,length,rec_length,min_pack_length;
1222  enum en_fieldtype type;
1223  uchar *from_end,*to_end,*packpos;
1224  reg3 MI_COLUMNDEF *rec,*end_field;
1225  DBUG_ENTER("_mi_rec_unpack");
1226 
1227  to_end=to + info->s->base.reclength;
1228  from_end=from+found_length;
1229  flag= (uchar) *from; bit=1; packpos=from;
1230  if (found_length < info->s->base.min_pack_length)
1231  goto err;
1232  from+= info->s->base.pack_bits;
1233  min_pack_length=info->s->base.min_pack_length - info->s->base.pack_bits;
1234 
1235  for (rec=info->s->rec , end_field=rec+info->s->base.fields ;
1236  rec < end_field ; to+= rec_length, rec++)
1237  {
1238  rec_length=rec->length;
1239  if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL &&
1240  (type != FIELD_CHECK))
1241  {
1242  if (type == FIELD_VARCHAR)
1243  {
1244  uint pack_length= HA_VARCHAR_PACKLENGTH(rec_length-1);
1245  if (pack_length == 1)
1246  {
1247  length= (uint) *(uchar*) from;
1248  if (length > rec_length-1)
1249  goto err;
1250  *to= *from++;
1251  }
1252  else
1253  {
1254  get_key_length(length, from);
1255  if (length > rec_length-2)
1256  goto err;
1257  int2store(to,length);
1258  }
1259  if (from+length > from_end)
1260  goto err;
1261  memcpy(to+pack_length, from, length);
1262  from+= length;
1263  min_pack_length--;
1264  continue;
1265  }
1266  if (flag & bit)
1267  {
1268  if (type == FIELD_BLOB || type == FIELD_SKIP_ZERO)
1269  memset(to, 0, rec_length);
1270  else if (type == FIELD_SKIP_ENDSPACE ||
1271  type == FIELD_SKIP_PRESPACE)
1272  {
1273  if (rec->length > 255 && *from & 128)
1274  {
1275  if (from + 1 >= from_end)
1276  goto err;
1277  length= (*from & 127)+ ((uint) (uchar) *(from+1) << 7); from+=2;
1278  }
1279  else
1280  {
1281  if (from == from_end)
1282  goto err;
1283  length= (uchar) *from++;
1284  }
1285  min_pack_length--;
1286  if (length >= rec_length ||
1287  min_pack_length + length > (uint) (from_end - from))
1288  goto err;
1289  if (type == FIELD_SKIP_ENDSPACE)
1290  {
1291  memcpy(to,(uchar*) from,(size_t) length);
1292  memset(to + length, ' ', rec_length-length);
1293  }
1294  else
1295  {
1296  memset(to, ' ', rec_length-length);
1297  memcpy(to+rec_length-length,(uchar*) from,(size_t) length);
1298  }
1299  from+=length;
1300  }
1301  }
1302  else if (type == FIELD_BLOB)
1303  {
1304  uint size_length=rec_length- portable_sizeof_char_ptr;
1305  ulong blob_length=_mi_calc_blob_length(size_length,from);
1306  ulong from_left= (ulong) (from_end - from);
1307  if (from_left < size_length ||
1308  from_left - size_length < blob_length ||
1309  from_left - size_length - blob_length < min_pack_length)
1310  goto err;
1311  memcpy(to, from, (size_t) size_length);
1312  from+=size_length;
1313  memcpy(to+size_length, &from, sizeof(char*));
1314  from+=blob_length;
1315  }
1316  else
1317  {
1318  if (type == FIELD_SKIP_ENDSPACE || type == FIELD_SKIP_PRESPACE)
1319  min_pack_length--;
1320  if (min_pack_length + rec_length > (uint) (from_end - from))
1321  goto err;
1322  memcpy(to,(uchar*) from,(size_t) rec_length); from+=rec_length;
1323  }
1324  if ((bit= bit << 1) >= 256)
1325  {
1326  flag= (uchar) *++packpos; bit=1;
1327  }
1328  }
1329  else
1330  {
1331  if (min_pack_length > (uint) (from_end - from))
1332  goto err;
1333  min_pack_length-=rec_length;
1334  memcpy(to, (uchar*) from, (size_t) rec_length);
1335  from+=rec_length;
1336  }
1337  }
1338  if (info->s->calc_checksum)
1339  from++;
1340  if (to == to_end && from == from_end && (bit == 1 || !(flag & ~(bit-1))))
1341  DBUG_RETURN(found_length);
1342 
1343 err:
1344  my_errno= HA_ERR_WRONG_IN_RECORD;
1345  DBUG_PRINT("error",("to_end: 0x%lx -> 0x%lx from_end: 0x%lx -> 0x%lx",
1346  (long) to, (long) to_end, (long) from, (long) from_end));
1347  DBUG_DUMP("from",(uchar*) info->rec_buff,info->s->base.min_pack_length);
1348  DBUG_RETURN(MY_FILE_ERROR);
1349 } /* _mi_rec_unpack */
1350 
1351 
1352  /* Calc length of blob. Update info in blobs->length */
1353 
1354 ulong _my_calc_total_blob_length(MI_INFO *info, const uchar *record)
1355 {
1356  ulong length;
1357  MI_BLOB *blob,*end;
1358 
1359  for (length=0, blob= info->blobs, end=blob+info->s->base.blobs ;
1360  blob != end;
1361  blob++)
1362  {
1363  blob->length=_mi_calc_blob_length(blob->pack_length,record + blob->offset);
1364  length+=blob->length;
1365  }
1366  return length;
1367 }
1368 
1369 
1370 ulong _mi_calc_blob_length(uint length, const uchar *pos)
1371 {
1372  switch (length) {
1373  case 1:
1374  return (uint) (uchar) *pos;
1375  case 2:
1376  return (uint) uint2korr(pos);
1377  case 3:
1378  return uint3korr(pos);
1379  case 4:
1380  return uint4korr(pos);
1381  default:
1382  break;
1383  }
1384  return 0; /* Impossible */
1385 }
1386 
1387 
1388 void _mi_store_blob_length(uchar *pos,uint pack_length,uint length)
1389 {
1390  switch (pack_length) {
1391  case 1:
1392  *pos= (uchar) length;
1393  break;
1394  case 2:
1395  int2store(pos,length);
1396  break;
1397  case 3:
1398  int3store(pos,length);
1399  break;
1400  case 4:
1401  int4store(pos,length);
1402  default:
1403  break;
1404  }
1405  return;
1406 }
1407 
1408 
1409 /*
1410  Read record from datafile.
1411 
1412  SYNOPSIS
1413  _mi_read_dynamic_record()
1414  info MI_INFO pointer to table.
1415  filepos From where to read the record.
1416  buf Destination for record.
1417 
1418  NOTE
1419 
1420  If a write buffer is active, it needs to be flushed if its contents
1421  intersects with the record to read. We always check if the position
1422  of the first byte of the write buffer is lower than the position
1423  past the last byte to read. In theory this is also true if the write
1424  buffer is completely below the read segment. That is, if there is no
1425  intersection. But this case is unusual. We flush anyway. Only if the
1426  first byte in the write buffer is above the last byte to read, we do
1427  not flush.
1428 
1429  A dynamic record may need several reads. So this check must be done
1430  before every read. Reading a dynamic record starts with reading the
1431  block header. If the record does not fit into the free space of the
1432  header, the block may be longer than the header. In this case a
1433  second read is necessary. These one or two reads repeat for every
1434  part of the record.
1435 
1436  RETURN
1437  0 OK
1438  -1 Error
1439 */
1440 
1441 int _mi_read_dynamic_record(MI_INFO *info, my_off_t filepos, uchar *buf)
1442 {
1443  int block_of_record;
1444  uint b_type,UNINIT_VAR(left_length);
1445  uchar *UNINIT_VAR(to);
1446  MI_BLOCK_INFO block_info;
1447  File file;
1448  DBUG_ENTER("mi_read_dynamic_record");
1449 
1450  if (filepos != HA_OFFSET_ERROR)
1451  {
1452  file=info->dfile;
1453  block_of_record= 0; /* First block of record is numbered as zero. */
1454  block_info.second_read= 0;
1455  do
1456  {
1457  /* A corrupted table can have wrong pointers. (Bug# 19835) */
1458  if (filepos == HA_OFFSET_ERROR)
1459  goto panic;
1460  if (info->opt_flag & WRITE_CACHE_USED &&
1461  info->rec_cache.pos_in_file < filepos + MI_BLOCK_INFO_HEADER_LENGTH &&
1462  flush_io_cache(&info->rec_cache))
1463  goto err;
1464  info->rec_cache.seek_not_done=1;
1465  if ((b_type= _mi_get_block_info(&block_info, file, filepos))
1466  & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
1467  BLOCK_FATAL_ERROR))
1468  {
1469  if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
1470  my_errno=HA_ERR_RECORD_DELETED;
1471  goto err;
1472  }
1473  if (block_of_record++ == 0) /* First block */
1474  {
1475  if (block_info.rec_len > (uint) info->s->base.max_pack_length)
1476  goto panic;
1477  if (info->s->base.blobs)
1478  {
1479  if (!(to=mi_alloc_rec_buff(info, block_info.rec_len,
1480  &info->rec_buff)))
1481  goto err;
1482  }
1483  else
1484  to= info->rec_buff;
1485  left_length=block_info.rec_len;
1486  }
1487  if (left_length < block_info.data_len || ! block_info.data_len)
1488  goto panic; /* Wrong linked record */
1489  /* copy information that is already read */
1490  {
1491  uint offset= (uint) (block_info.filepos - filepos);
1492  uint prefetch_len= (sizeof(block_info.header) - offset);
1493  filepos+= sizeof(block_info.header);
1494 
1495  if (prefetch_len > block_info.data_len)
1496  prefetch_len= block_info.data_len;
1497  if (prefetch_len)
1498  {
1499  memcpy((uchar*) to, block_info.header + offset, prefetch_len);
1500  block_info.data_len-= prefetch_len;
1501  left_length-= prefetch_len;
1502  to+= prefetch_len;
1503  }
1504  }
1505  /* read rest of record from file */
1506  if (block_info.data_len)
1507  {
1508  if (info->opt_flag & WRITE_CACHE_USED &&
1509  info->rec_cache.pos_in_file < filepos + block_info.data_len &&
1510  flush_io_cache(&info->rec_cache))
1511  goto err;
1512  /*
1513  What a pity that this method is not called 'file_pread' and that
1514  there is no equivalent without seeking. We are at the right
1515  position already. :(
1516  */
1517  if (info->s->file_read(info, (uchar*) to, block_info.data_len,
1518  filepos, MYF(MY_NABP)))
1519  goto panic;
1520  left_length-=block_info.data_len;
1521  to+=block_info.data_len;
1522  }
1523  filepos= block_info.next_filepos;
1524  } while (left_length);
1525 
1526  info->update|= HA_STATE_AKTIV; /* We have a aktive record */
1527  fast_mi_writeinfo(info);
1528  DBUG_RETURN(_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
1529  MY_FILE_ERROR ? 0 : -1);
1530  }
1531  fast_mi_writeinfo(info);
1532  DBUG_RETURN(-1); /* Wrong data to read */
1533 
1534 panic:
1535  my_errno=HA_ERR_WRONG_IN_RECORD;
1536 err:
1537  (void) _mi_writeinfo(info,0);
1538  DBUG_RETURN(-1);
1539 }
1540 
1541  /* compare unique constraint between stored rows */
1542 
1543 int _mi_cmp_dynamic_unique(MI_INFO *info, MI_UNIQUEDEF *def,
1544  const uchar *record, my_off_t pos)
1545 {
1546  uchar *rec_buff,*old_record;
1547  int error;
1548  DBUG_ENTER("_mi_cmp_dynamic_unique");
1549 
1550  if (!(old_record=my_alloca(info->s->base.reclength)))
1551  DBUG_RETURN(1);
1552 
1553  /* Don't let the compare destroy blobs that may be in use */
1554  rec_buff=info->rec_buff;
1555  if (info->s->base.blobs)
1556  info->rec_buff=0;
1557  error=_mi_read_dynamic_record(info,pos,old_record);
1558  if (!error)
1559  error=mi_unique_comp(def, record, old_record, def->null_are_equal);
1560  if (info->s->base.blobs)
1561  {
1562  my_free(mi_get_rec_buff_ptr(info, info->rec_buff));
1563  info->rec_buff=rec_buff;
1564  }
1565  my_afree(old_record);
1566  DBUG_RETURN(error);
1567 }
1568 
1569 
1570  /* Compare of record one disk with packed record in memory */
1571 
1572 int _mi_cmp_dynamic_record(register MI_INFO *info, register const uchar *record)
1573 {
1574  uint flag,reclength,b_type;
1575  my_off_t filepos;
1576  uchar *buffer;
1577  MI_BLOCK_INFO block_info;
1578  DBUG_ENTER("_mi_cmp_dynamic_record");
1579 
1580  if (info->opt_flag & WRITE_CACHE_USED)
1581  {
1582  info->update&= ~(HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK);
1583  if (flush_io_cache(&info->rec_cache))
1584  DBUG_RETURN(-1);
1585  }
1586  info->rec_cache.seek_not_done=1;
1587 
1588  /* If nobody have touched the database we don't have to test rec */
1589 
1590  buffer=info->rec_buff;
1591  if ((info->opt_flag & READ_CHECK_USED))
1592  { /* If check isn't disabled */
1593  if (info->s->base.blobs)
1594  {
1595  if (!(buffer=(uchar*) my_alloca(info->s->base.pack_reclength+
1596  _my_calc_total_blob_length(info,record))))
1597  DBUG_RETURN(-1);
1598  }
1599  reclength=_mi_rec_pack(info,buffer,record);
1600  record= buffer;
1601 
1602  filepos=info->lastpos;
1603  flag=block_info.second_read=0;
1604  block_info.next_filepos=filepos;
1605  while (reclength > 0)
1606  {
1607  if ((b_type=_mi_get_block_info(&block_info,info->dfile,
1608  block_info.next_filepos))
1609  & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
1610  BLOCK_FATAL_ERROR))
1611  {
1612  if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
1613  my_errno=HA_ERR_RECORD_CHANGED;
1614  goto err;
1615  }
1616  if (flag == 0) /* First block */
1617  {
1618  flag=1;
1619  if (reclength != block_info.rec_len)
1620  {
1621  my_errno=HA_ERR_RECORD_CHANGED;
1622  goto err;
1623  }
1624  } else if (reclength < block_info.data_len)
1625  {
1626  my_errno=HA_ERR_WRONG_IN_RECORD;
1627  goto err;
1628  }
1629  reclength-=block_info.data_len;
1630  if (_mi_cmp_buffer(info->dfile,record,block_info.filepos,
1631  block_info.data_len))
1632  {
1633  my_errno=HA_ERR_RECORD_CHANGED;
1634  goto err;
1635  }
1636  flag=1;
1637  record+=block_info.data_len;
1638  }
1639  }
1640  my_errno=0;
1641 err:
1642  if (buffer != info->rec_buff)
1643  my_afree((uchar*) buffer);
1644  DBUG_RETURN(my_errno);
1645 }
1646 
1647 
1648  /* Compare file to buffert */
1649 
1650 static int _mi_cmp_buffer(File file, const uchar *buff, my_off_t filepos,
1651  uint length)
1652 {
1653  uint next_length;
1654  uchar temp_buff[IO_SIZE*2];
1655  DBUG_ENTER("_mi_cmp_buffer");
1656 
1657  next_length= IO_SIZE*2 - (uint) (filepos & (IO_SIZE-1));
1658 
1659  while (length > IO_SIZE*2)
1660  {
1661  if (mysql_file_pread(file, temp_buff, next_length, filepos, MYF(MY_NABP)) ||
1662  memcmp(buff, temp_buff, next_length))
1663  goto err;
1664  filepos+=next_length;
1665  buff+=next_length;
1666  length-= next_length;
1667  next_length=IO_SIZE*2;
1668  }
1669  if (mysql_file_pread(file, temp_buff, length, filepos, MYF(MY_NABP)))
1670  goto err;
1671  DBUG_RETURN(memcmp(buff,temp_buff,length));
1672 err:
1673  DBUG_RETURN(1);
1674 }
1675 
1676 
1677 /*
1678  Read record from datafile.
1679 
1680  SYNOPSIS
1681  _mi_read_rnd_dynamic_record()
1682  info MI_INFO pointer to table.
1683  buf Destination for record.
1684  filepos From where to read the record.
1685  skip_deleted_blocks If to repeat reading until a non-deleted
1686  record is found.
1687 
1688  NOTE
1689 
1690  If a write buffer is active, it needs to be flushed if its contents
1691  intersects with the record to read. We always check if the position
1692  of the first byte of the write buffer is lower than the position
1693  past the last byte to read. In theory this is also true if the write
1694  buffer is completely below the read segment. That is, if there is no
1695  intersection. But this case is unusual. We flush anyway. Only if the
1696  first byte in the write buffer is above the last byte to read, we do
1697  not flush.
1698 
1699  A dynamic record may need several reads. So this check must be done
1700  before every read. Reading a dynamic record starts with reading the
1701  block header. If the record does not fit into the free space of the
1702  header, the block may be longer than the header. In this case a
1703  second read is necessary. These one or two reads repeat for every
1704  part of the record.
1705 
1706  RETURN
1707  0 OK
1708  != 0 Error
1709 */
1710 
1711 int _mi_read_rnd_dynamic_record(MI_INFO *info, uchar *buf,
1712  register my_off_t filepos,
1713  my_bool skip_deleted_blocks)
1714 {
1715  int block_of_record, info_read, save_errno;
1716  uint left_len,b_type;
1717  uchar *UNINIT_VAR(to);
1718  MI_BLOCK_INFO block_info;
1719  MYISAM_SHARE *share=info->s;
1720  DBUG_ENTER("_mi_read_rnd_dynamic_record");
1721 
1722  info_read=0;
1723 
1724  if (info->lock_type == F_UNLCK)
1725  {
1726 #ifndef UNSAFE_LOCKING
1727  if (share->tot_locks == 0)
1728  {
1729  if (my_lock(share->kfile,F_RDLCK,0L,F_TO_EOF,
1730  MYF(MY_SEEK_NOT_DONE) | info->lock_wait))
1731  DBUG_RETURN(my_errno);
1732  }
1733 #else
1734  info->tmp_lock_type=F_RDLCK;
1735 #endif
1736  }
1737  else
1738  info_read=1; /* memory-keyinfoblock is ok */
1739 
1740  block_of_record= 0; /* First block of record is numbered as zero. */
1741  block_info.second_read= 0;
1742  left_len=1;
1743  do
1744  {
1745  if (filepos >= info->state->data_file_length)
1746  {
1747  if (!info_read)
1748  { /* Check if changed */
1749  info_read=1;
1750  info->rec_cache.seek_not_done=1;
1751  if (mi_state_info_read_dsk(share->kfile,&share->state,1))
1752  goto panic;
1753  }
1754  if (filepos >= info->state->data_file_length)
1755  {
1756  my_errno= HA_ERR_END_OF_FILE;
1757  goto err;
1758  }
1759  }
1760  if (info->opt_flag & READ_CACHE_USED)
1761  {
1762  if (_mi_read_cache(&info->rec_cache,(uchar*) block_info.header,filepos,
1763  sizeof(block_info.header),
1764  (!block_of_record && skip_deleted_blocks ?
1765  READING_NEXT : 0) | READING_HEADER))
1766  goto panic;
1767  b_type=_mi_get_block_info(&block_info,-1,filepos);
1768  }
1769  else
1770  {
1771  if (info->opt_flag & WRITE_CACHE_USED &&
1772  info->rec_cache.pos_in_file < filepos + MI_BLOCK_INFO_HEADER_LENGTH &&
1773  flush_io_cache(&info->rec_cache))
1774  DBUG_RETURN(my_errno);
1775  info->rec_cache.seek_not_done=1;
1776  b_type=_mi_get_block_info(&block_info,info->dfile,filepos);
1777  }
1778 
1779  if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
1780  BLOCK_FATAL_ERROR))
1781  {
1782  if ((b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
1783  && skip_deleted_blocks)
1784  {
1785  filepos=block_info.filepos+block_info.block_len;
1786  block_info.second_read=0;
1787  continue; /* Search after next_record */
1788  }
1789  if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
1790  {
1791  /*
1792  If we're not on the first block of a record and
1793  the block is marked as deleted or out of sync,
1794  something's gone wrong: the record is damaged.
1795  */
1796  if (block_of_record != 0)
1797  goto panic;
1798 
1799  my_errno=HA_ERR_RECORD_DELETED;
1800  info->lastpos=block_info.filepos;
1801  info->nextpos=block_info.filepos+block_info.block_len;
1802  }
1803  goto err;
1804  }
1805  if (block_of_record == 0) /* First block */
1806  {
1807  if (block_info.rec_len > (uint) share->base.max_pack_length)
1808  goto panic;
1809  info->lastpos=filepos;
1810  if (share->base.blobs)
1811  {
1812  if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
1813  &info->rec_buff)))
1814  goto err;
1815  }
1816  else
1817  to= info->rec_buff;
1818  left_len=block_info.rec_len;
1819  }
1820  if (left_len < block_info.data_len)
1821  goto panic; /* Wrong linked record */
1822 
1823  /* copy information that is already read */
1824  {
1825  uint offset=(uint) (block_info.filepos - filepos);
1826  uint tmp_length= (sizeof(block_info.header) - offset);
1827  filepos=block_info.filepos;
1828 
1829  if (tmp_length > block_info.data_len)
1830  tmp_length= block_info.data_len;
1831  if (tmp_length)
1832  {
1833  memcpy((uchar*) to, block_info.header+offset,tmp_length);
1834  block_info.data_len-=tmp_length;
1835  left_len-=tmp_length;
1836  to+=tmp_length;
1837  filepos+=tmp_length;
1838  }
1839  }
1840  /* read rest of record from file */
1841  if (block_info.data_len)
1842  {
1843  if (info->opt_flag & READ_CACHE_USED)
1844  {
1845  if (_mi_read_cache(&info->rec_cache,(uchar*) to,filepos,
1846  block_info.data_len,
1847  (!block_of_record && skip_deleted_blocks) ?
1848  READING_NEXT : 0))
1849  goto panic;
1850  }
1851  else
1852  {
1853  if (info->opt_flag & WRITE_CACHE_USED &&
1854  info->rec_cache.pos_in_file <
1855  block_info.filepos + block_info.data_len &&
1856  flush_io_cache(&info->rec_cache))
1857  goto err;
1858  /* mysql_file_seek(info->dfile, filepos, MY_SEEK_SET, MYF(0)); */
1859  if (mysql_file_read(info->dfile, (uchar*) to, block_info.data_len,
1860  MYF(MY_NABP)))
1861  {
1862  if (my_errno == -1)
1863  my_errno= HA_ERR_WRONG_IN_RECORD; /* Unexpected end of file */
1864  goto err;
1865  }
1866  }
1867  }
1868  /*
1869  Increment block-of-record counter. If it was the first block,
1870  remember the position behind the block for the next call.
1871  */
1872  if (block_of_record++ == 0)
1873  {
1874  info->nextpos= block_info.filepos + block_info.block_len;
1875  skip_deleted_blocks= 0;
1876  }
1877  left_len-=block_info.data_len;
1878  to+=block_info.data_len;
1879  filepos=block_info.next_filepos;
1880  } while (left_len);
1881 
1882  info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
1883  fast_mi_writeinfo(info);
1884  if (_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
1885  MY_FILE_ERROR)
1886  DBUG_RETURN(0);
1887  DBUG_RETURN(my_errno); /* Wrong record */
1888 
1889 panic:
1890  my_errno=HA_ERR_WRONG_IN_RECORD; /* Something is fatal wrong */
1891 err:
1892  save_errno=my_errno;
1893  (void) _mi_writeinfo(info,0);
1894  DBUG_RETURN(my_errno=save_errno);
1895 }
1896 
1897 
1898  /* Read and process header from a dynamic-record-file */
1899 
1900 uint _mi_get_block_info(MI_BLOCK_INFO *info, File file, my_off_t filepos)
1901 {
1902  uint return_val=0;
1903  uchar *header=info->header;
1904 
1905  if (file >= 0)
1906  {
1907  /*
1908  We do not use mysql_file_pread() here because we want to have the file
1909  pointer set to the end of the header after this function.
1910  mysql_file_pread() may leave the file pointer untouched.
1911  */
1912  mysql_file_seek(file, filepos, MY_SEEK_SET, MYF(0));
1913  if (mysql_file_read(file, header, sizeof(info->header), MYF(0)) !=
1914  sizeof(info->header))
1915  goto err;
1916  }
1917  DBUG_DUMP("header",header,MI_BLOCK_INFO_HEADER_LENGTH);
1918  if (info->second_read)
1919  {
1920  if (info->header[0] <= 6 || info->header[0] == 13)
1921  return_val=BLOCK_SYNC_ERROR;
1922  }
1923  else
1924  {
1925  if (info->header[0] > 6 && info->header[0] != 13)
1926  return_val=BLOCK_SYNC_ERROR;
1927  }
1928  info->next_filepos= HA_OFFSET_ERROR; /* Dummy if no next block */
1929 
1930  switch (info->header[0]) {
1931  case 0:
1932  if ((info->block_len=(uint) mi_uint3korr(header+1)) <
1933  MI_MIN_BLOCK_LENGTH ||
1934  (info->block_len & (MI_DYN_ALIGN_SIZE -1)))
1935  goto err;
1936  info->filepos=filepos;
1937  info->next_filepos=mi_sizekorr(header+4);
1938  info->prev_filepos=mi_sizekorr(header+12);
1939 #if SIZEOF_OFF_T == 4
1940  if ((mi_uint4korr(header+4) != 0 &&
1941  (mi_uint4korr(header+4) != (ulong) ~0 ||
1942  info->next_filepos != (ulong) ~0)) ||
1943  (mi_uint4korr(header+12) != 0 &&
1944  (mi_uint4korr(header+12) != (ulong) ~0 ||
1945  info->prev_filepos != (ulong) ~0)))
1946  goto err;
1947 #endif
1948  return return_val | BLOCK_DELETED; /* Deleted block */
1949 
1950  case 1:
1951  info->rec_len=info->data_len=info->block_len=mi_uint2korr(header+1);
1952  info->filepos=filepos+3;
1953  return return_val | BLOCK_FIRST | BLOCK_LAST;
1954  case 2:
1955  info->rec_len=info->data_len=info->block_len=mi_uint3korr(header+1);
1956  info->filepos=filepos+4;
1957  return return_val | BLOCK_FIRST | BLOCK_LAST;
1958 
1959  case 13:
1960  info->rec_len=mi_uint4korr(header+1);
1961  info->block_len=info->data_len=mi_uint3korr(header+5);
1962  info->next_filepos=mi_sizekorr(header+8);
1963  info->second_read=1;
1964  info->filepos=filepos+16;
1965  return return_val | BLOCK_FIRST;
1966 
1967  case 3:
1968  info->rec_len=info->data_len=mi_uint2korr(header+1);
1969  info->block_len=info->rec_len+ (uint) header[3];
1970  info->filepos=filepos+4;
1971  return return_val | BLOCK_FIRST | BLOCK_LAST;
1972  case 4:
1973  info->rec_len=info->data_len=mi_uint3korr(header+1);
1974  info->block_len=info->rec_len+ (uint) header[4];
1975  info->filepos=filepos+5;
1976  return return_val | BLOCK_FIRST | BLOCK_LAST;
1977 
1978  case 5:
1979  info->rec_len=mi_uint2korr(header+1);
1980  info->block_len=info->data_len=mi_uint2korr(header+3);
1981  info->next_filepos=mi_sizekorr(header+5);
1982  info->second_read=1;
1983  info->filepos=filepos+13;
1984  return return_val | BLOCK_FIRST;
1985  case 6:
1986  info->rec_len=mi_uint3korr(header+1);
1987  info->block_len=info->data_len=mi_uint3korr(header+4);
1988  info->next_filepos=mi_sizekorr(header+7);
1989  info->second_read=1;
1990  info->filepos=filepos+15;
1991  return return_val | BLOCK_FIRST;
1992 
1993  /* The following blocks are identical to 1-6 without rec_len */
1994  case 7:
1995  info->data_len=info->block_len=mi_uint2korr(header+1);
1996  info->filepos=filepos+3;
1997  return return_val | BLOCK_LAST;
1998  case 8:
1999  info->data_len=info->block_len=mi_uint3korr(header+1);
2000  info->filepos=filepos+4;
2001  return return_val | BLOCK_LAST;
2002 
2003  case 9:
2004  info->data_len=mi_uint2korr(header+1);
2005  info->block_len=info->data_len+ (uint) header[3];
2006  info->filepos=filepos+4;
2007  return return_val | BLOCK_LAST;
2008  case 10:
2009  info->data_len=mi_uint3korr(header+1);
2010  info->block_len=info->data_len+ (uint) header[4];
2011  info->filepos=filepos+5;
2012  return return_val | BLOCK_LAST;
2013 
2014  case 11:
2015  info->data_len=info->block_len=mi_uint2korr(header+1);
2016  info->next_filepos=mi_sizekorr(header+3);
2017  info->second_read=1;
2018  info->filepos=filepos+11;
2019  return return_val;
2020  case 12:
2021  info->data_len=info->block_len=mi_uint3korr(header+1);
2022  info->next_filepos=mi_sizekorr(header+4);
2023  info->second_read=1;
2024  info->filepos=filepos+12;
2025  return return_val;
2026  }
2027 
2028 err:
2029  my_errno=HA_ERR_WRONG_IN_RECORD; /* Garbage */
2030  return BLOCK_ERROR;
2031 }