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