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