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