~drizzle-trunk/drizzle/development

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