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
mi_dynmap_file(MI_INFO * info,my_off_t size)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
mi_munmap_file(MI_INFO * info)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
mi_remap_file(MI_INFO * info,my_off_t size)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
mi_mmap_pread(MI_INFO * info,uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)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
mi_nommap_pread(MI_INFO * info,uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)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
mi_mmap_pwrite(MI_INFO * info,const uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)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
mi_nommap_pwrite(MI_INFO * info,const uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)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
_mi_write_dynamic_record(MI_INFO * info,const uchar * record)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
_mi_update_dynamic_record(MI_INFO * info,my_off_t pos,const uchar * record)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
_mi_write_blob_record(MI_INFO * info,const uchar * record)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
_mi_update_blob_record(MI_INFO * info,my_off_t pos,const uchar * record)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
_mi_delete_dynamic_record(MI_INFO * info)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
write_dynamic_record(MI_INFO * info,const uchar * record,ulong reclength)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
_mi_find_writepos(MI_INFO * info,ulong reclength,my_off_t * filepos,ulong * length)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
unlink_deleted_block(MI_INFO * info,MI_BLOCK_INFO * block_info)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
update_backward_delete_link(MI_INFO * info,my_off_t delete_block,my_off_t filepos)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
delete_dynamic_record(MI_INFO * info,my_off_t filepos,uint second_read)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 bfill(block_info.header+12,8,255);
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
_mi_write_part_record(MI_INFO * info,my_off_t filepos,ulong length,my_off_t next_filepos,uchar ** record,ulong * reclength,int * flag)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 bzero((uchar*) record_end,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 bfill(pos+12,8,255); /* 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
update_dynamic_record(MI_INFO * info,my_off_t filepos,uchar * record,ulong reclength)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= 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 bfill(del_block.header+12,8,255);
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
_mi_rec_pack(MI_INFO * info,register uchar * to,register const uchar * from)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
_mi_rec_check(MI_INFO * info,const uchar * record,uchar * rec_buff,ulong packed_length,my_bool with_checksum)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
_mi_rec_unpack(register MI_INFO * info,register uchar * to,uchar * from,ulong found_length)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 bzero((uchar*) to,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 bfill((uchar*) to+length,rec_length-length,' ');
1293 }
1294 else
1295 {
1296 bfill((uchar*) 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
_my_calc_total_blob_length(MI_INFO * info,const uchar * record)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
_mi_calc_blob_length(uint length,const uchar * pos)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
_my_store_blob_length(uchar * pos,uint pack_length,uint length)1388 void _my_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
_mi_read_dynamic_record(MI_INFO * info,my_off_t filepos,uchar * buf)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
_mi_cmp_dynamic_unique(MI_INFO * info,MI_UNIQUEDEF * def,const uchar * record,my_off_t pos)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
_mi_cmp_dynamic_record(register MI_INFO * info,register const uchar * record)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
_mi_cmp_buffer(File file,const uchar * buff,my_off_t filepos,uint length)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
_mi_read_rnd_dynamic_record(MI_INFO * info,uchar * buf,register my_off_t filepos,my_bool skip_deleted_blocks)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 my_errno=HA_ERR_RECORD_DELETED;
1792 info->lastpos=block_info.filepos;
1793 info->nextpos=block_info.filepos+block_info.block_len;
1794 }
1795 goto err;
1796 }
1797 if (block_of_record == 0) /* First block */
1798 {
1799 if (block_info.rec_len > (uint) share->base.max_pack_length)
1800 goto panic;
1801 info->lastpos=filepos;
1802 if (share->base.blobs)
1803 {
1804 if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
1805 &info->rec_buff)))
1806 goto err;
1807 }
1808 else
1809 to= info->rec_buff;
1810 left_len=block_info.rec_len;
1811 }
1812 if (left_len < block_info.data_len)
1813 goto panic; /* Wrong linked record */
1814
1815 /* copy information that is already read */
1816 {
1817 uint offset=(uint) (block_info.filepos - filepos);
1818 uint tmp_length= (sizeof(block_info.header) - offset);
1819 filepos=block_info.filepos;
1820
1821 if (tmp_length > block_info.data_len)
1822 tmp_length= block_info.data_len;
1823 if (tmp_length)
1824 {
1825 memcpy((uchar*) to, block_info.header+offset,tmp_length);
1826 block_info.data_len-=tmp_length;
1827 left_len-=tmp_length;
1828 to+=tmp_length;
1829 filepos+=tmp_length;
1830 }
1831 }
1832 /* read rest of record from file */
1833 if (block_info.data_len)
1834 {
1835 if (info->opt_flag & READ_CACHE_USED)
1836 {
1837 if (_mi_read_cache(&info->rec_cache,(uchar*) to,filepos,
1838 block_info.data_len,
1839 (!block_of_record && skip_deleted_blocks) ?
1840 READING_NEXT : 0))
1841 goto panic;
1842 }
1843 else
1844 {
1845 if (info->opt_flag & WRITE_CACHE_USED &&
1846 info->rec_cache.pos_in_file <
1847 block_info.filepos + block_info.data_len &&
1848 flush_io_cache(&info->rec_cache))
1849 goto err;
1850 /* mysql_file_seek(info->dfile, filepos, MY_SEEK_SET, MYF(0)); */
1851 if (mysql_file_read(info->dfile, (uchar*) to, block_info.data_len,
1852 MYF(MY_NABP)))
1853 {
1854 if (my_errno == -1)
1855 my_errno= HA_ERR_WRONG_IN_RECORD; /* Unexpected end of file */
1856 goto err;
1857 }
1858 }
1859 }
1860 /*
1861 Increment block-of-record counter. If it was the first block,
1862 remember the position behind the block for the next call.
1863 */
1864 if (block_of_record++ == 0)
1865 {
1866 info->nextpos= block_info.filepos + block_info.block_len;
1867 skip_deleted_blocks= 0;
1868 }
1869 left_len-=block_info.data_len;
1870 to+=block_info.data_len;
1871 filepos=block_info.next_filepos;
1872 } while (left_len);
1873
1874 info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
1875 fast_mi_writeinfo(info);
1876 if (_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
1877 MY_FILE_ERROR)
1878 DBUG_RETURN(0);
1879 DBUG_RETURN(my_errno); /* Wrong record */
1880
1881 panic:
1882 my_errno=HA_ERR_WRONG_IN_RECORD; /* Something is fatal wrong */
1883 err:
1884 save_errno=my_errno;
1885 (void) _mi_writeinfo(info,0);
1886 DBUG_RETURN(my_errno=save_errno);
1887 }
1888
1889
1890 /* Read and process header from a dynamic-record-file */
1891
_mi_get_block_info(MI_BLOCK_INFO * info,File file,my_off_t filepos)1892 uint _mi_get_block_info(MI_BLOCK_INFO *info, File file, my_off_t filepos)
1893 {
1894 uint return_val=0;
1895 uchar *header=info->header;
1896
1897 if (file >= 0)
1898 {
1899 /*
1900 We do not use mysql_file_pread() here because we want to have the file
1901 pointer set to the end of the header after this function.
1902 mysql_file_pread() may leave the file pointer untouched.
1903 */
1904 mysql_file_seek(file, filepos, MY_SEEK_SET, MYF(0));
1905 if (mysql_file_read(file, header, sizeof(info->header), MYF(0)) !=
1906 sizeof(info->header))
1907 goto err;
1908 }
1909 DBUG_DUMP("header",header,MI_BLOCK_INFO_HEADER_LENGTH);
1910 if (info->second_read)
1911 {
1912 if (info->header[0] <= 6 || info->header[0] == 13)
1913 return_val=BLOCK_SYNC_ERROR;
1914 }
1915 else
1916 {
1917 if (info->header[0] > 6 && info->header[0] != 13)
1918 return_val=BLOCK_SYNC_ERROR;
1919 }
1920 info->next_filepos= HA_OFFSET_ERROR; /* Dummy if no next block */
1921
1922 switch (info->header[0]) {
1923 case 0:
1924 if ((info->block_len=(uint) mi_uint3korr(header+1)) <
1925 MI_MIN_BLOCK_LENGTH ||
1926 (info->block_len & (MI_DYN_ALIGN_SIZE -1)))
1927 goto err;
1928 info->filepos=filepos;
1929 info->next_filepos=mi_sizekorr(header+4);
1930 info->prev_filepos=mi_sizekorr(header+12);
1931 #if SIZEOF_OFF_T == 4
1932 if ((mi_uint4korr(header+4) != 0 &&
1933 (mi_uint4korr(header+4) != (ulong) ~0 ||
1934 info->next_filepos != (ulong) ~0)) ||
1935 (mi_uint4korr(header+12) != 0 &&
1936 (mi_uint4korr(header+12) != (ulong) ~0 ||
1937 info->prev_filepos != (ulong) ~0)))
1938 goto err;
1939 #endif
1940 return return_val | BLOCK_DELETED; /* Deleted block */
1941
1942 case 1:
1943 info->rec_len=info->data_len=info->block_len=mi_uint2korr(header+1);
1944 info->filepos=filepos+3;
1945 return return_val | BLOCK_FIRST | BLOCK_LAST;
1946 case 2:
1947 info->rec_len=info->data_len=info->block_len=mi_uint3korr(header+1);
1948 info->filepos=filepos+4;
1949 return return_val | BLOCK_FIRST | BLOCK_LAST;
1950
1951 case 13:
1952 info->rec_len=mi_uint4korr(header+1);
1953 info->block_len=info->data_len=mi_uint3korr(header+5);
1954 info->next_filepos=mi_sizekorr(header+8);
1955 info->second_read=1;
1956 info->filepos=filepos+16;
1957 return return_val | BLOCK_FIRST;
1958
1959 case 3:
1960 info->rec_len=info->data_len=mi_uint2korr(header+1);
1961 info->block_len=info->rec_len+ (uint) header[3];
1962 info->filepos=filepos+4;
1963 return return_val | BLOCK_FIRST | BLOCK_LAST;
1964 case 4:
1965 info->rec_len=info->data_len=mi_uint3korr(header+1);
1966 info->block_len=info->rec_len+ (uint) header[4];
1967 info->filepos=filepos+5;
1968 return return_val | BLOCK_FIRST | BLOCK_LAST;
1969
1970 case 5:
1971 info->rec_len=mi_uint2korr(header+1);
1972 info->block_len=info->data_len=mi_uint2korr(header+3);
1973 info->next_filepos=mi_sizekorr(header+5);
1974 info->second_read=1;
1975 info->filepos=filepos+13;
1976 return return_val | BLOCK_FIRST;
1977 case 6:
1978 info->rec_len=mi_uint3korr(header+1);
1979 info->block_len=info->data_len=mi_uint3korr(header+4);
1980 info->next_filepos=mi_sizekorr(header+7);
1981 info->second_read=1;
1982 info->filepos=filepos+15;
1983 return return_val | BLOCK_FIRST;
1984
1985 /* The following blocks are identical to 1-6 without rec_len */
1986 case 7:
1987 info->data_len=info->block_len=mi_uint2korr(header+1);
1988 info->filepos=filepos+3;
1989 return return_val | BLOCK_LAST;
1990 case 8:
1991 info->data_len=info->block_len=mi_uint3korr(header+1);
1992 info->filepos=filepos+4;
1993 return return_val | BLOCK_LAST;
1994
1995 case 9:
1996 info->data_len=mi_uint2korr(header+1);
1997 info->block_len=info->data_len+ (uint) header[3];
1998 info->filepos=filepos+4;
1999 return return_val | BLOCK_LAST;
2000 case 10:
2001 info->data_len=mi_uint3korr(header+1);
2002 info->block_len=info->data_len+ (uint) header[4];
2003 info->filepos=filepos+5;
2004 return return_val | BLOCK_LAST;
2005
2006 case 11:
2007 info->data_len=info->block_len=mi_uint2korr(header+1);
2008 info->next_filepos=mi_sizekorr(header+3);
2009 info->second_read=1;
2010 info->filepos=filepos+11;
2011 return return_val;
2012 case 12:
2013 info->data_len=info->block_len=mi_uint3korr(header+1);
2014 info->next_filepos=mi_sizekorr(header+4);
2015 info->second_read=1;
2016 info->filepos=filepos+12;
2017 return return_val;
2018 }
2019
2020 err:
2021 my_errno=HA_ERR_WRONG_IN_RECORD; /* Garbage */
2022 return BLOCK_ERROR;
2023 }
2024