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