~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/mi_dynrec.c

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

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