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