~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to storage/myisam/mi_check.c

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

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
/* Describe, check and repair of MyISAM tables */
 
17
 
 
18
/*
 
19
  About checksum calculation.
 
20
 
 
21
  There are two types of checksums. Table checksum and row checksum.
 
22
 
 
23
  Row checksum is an additional byte at the end of dynamic length
 
24
  records. It must be calculated if the table is configured for them.
 
25
  Otherwise they must not be used. The variable
 
26
  MYISAM_SHARE::calc_checksum determines if row checksums are used.
 
27
  MI_INFO::checksum is used as temporary storage during row handling.
 
28
  For parallel repair we must assure that only one thread can use this
 
29
  variable. There is no problem on the write side as this is done by one
 
30
  thread only. But when checking a record after read this could go
 
31
  wrong. But since all threads read through a common read buffer, it is
 
32
  sufficient if only one thread checks it.
 
33
 
 
34
  Table checksum is an eight byte value in the header of the index file.
 
35
  It can be calculated even if row checksums are not used. The variable
 
36
  MI_CHECK::glob_crc is calculated over all records.
 
37
  MI_SORT_PARAM::calc_checksum determines if this should be done. This
 
38
  variable is not part of MI_CHECK because it must be set per thread for
 
39
  parallel repair. The global glob_crc must be changed by one thread
 
40
  only. And it is sufficient to calculate the checksum once only.
 
41
*/
 
42
 
 
43
#include "ftdefs.h"
 
44
#include <m_ctype.h>
 
45
#include <stdarg.h>
 
46
#include <my_getopt.h>
 
47
#ifdef HAVE_SYS_VADVISE_H
 
48
#include <sys/vadvise.h>
 
49
#endif
 
50
#ifdef HAVE_SYS_MMAN_H
 
51
#include <sys/mman.h>
 
52
#endif
 
53
#include "rt_index.h"
 
54
 
 
55
#ifndef USE_RAID
 
56
#define my_raid_create(A,B,C,D,E,F,G) my_create(A,B,C,G)
 
57
#define my_raid_delete(A,B,C) my_delete(A,B)
 
58
#endif
 
59
 
 
60
        /* Functions defined in this file */
 
61
 
 
62
static int check_k_link(MI_CHECK *param, MI_INFO *info,uint nr);
 
63
static int chk_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo,
 
64
                     my_off_t page, uchar *buff, ha_rows *keys,
 
65
                     ha_checksum *key_checksum, uint level);
 
66
static uint isam_key_length(MI_INFO *info,MI_KEYDEF *keyinfo);
 
67
static ha_checksum calc_checksum(ha_rows count);
 
68
static int writekeys(MI_SORT_PARAM *sort_param);
 
69
static int sort_one_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo,
 
70
                          my_off_t pagepos, File new_file);
 
71
static int sort_key_read(MI_SORT_PARAM *sort_param,void *key);
 
72
static int sort_ft_key_read(MI_SORT_PARAM *sort_param,void *key);
 
73
static int sort_get_next_record(MI_SORT_PARAM *sort_param);
 
74
static int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,const void *b);
 
75
static int sort_ft_key_write(MI_SORT_PARAM *sort_param, const void *a);
 
76
static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a);
 
77
static my_off_t get_record_for_key(MI_INFO *info,MI_KEYDEF *keyinfo,
 
78
                                uchar *key);
 
79
static int sort_insert_key(MI_SORT_PARAM  *sort_param,
 
80
                           reg1 SORT_KEY_BLOCKS *key_block,
 
81
                           uchar *key, my_off_t prev_block);
 
82
static int sort_delete_record(MI_SORT_PARAM *sort_param);
 
83
/*static int flush_pending_blocks(MI_CHECK *param);*/
 
84
static SORT_KEY_BLOCKS  *alloc_key_blocks(MI_CHECK *param, uint blocks,
 
85
                                          uint buffer_length);
 
86
static ha_checksum mi_byte_checksum(const uchar *buf, uint length);
 
87
static void set_data_file_type(SORT_INFO *sort_info, MYISAM_SHARE *share);
 
88
 
 
89
void myisamchk_init(MI_CHECK *param)
 
90
{
 
91
  bzero((uchar*) param,sizeof(*param));
 
92
  param->opt_follow_links=1;
 
93
  param->keys_in_use= ~(ulonglong) 0;
 
94
  param->search_after_block=HA_OFFSET_ERROR;
 
95
  param->auto_increment_value= 0;
 
96
  param->use_buffers=USE_BUFFER_INIT;
 
97
  param->read_buffer_length=READ_BUFFER_INIT;
 
98
  param->write_buffer_length=READ_BUFFER_INIT;
 
99
  param->sort_buffer_length=SORT_BUFFER_INIT;
 
100
  param->sort_key_blocks=BUFFERS_WHEN_SORTING;
 
101
  param->tmpfile_createflag=O_RDWR | O_TRUNC | O_EXCL;
 
102
  param->myf_rw=MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL);
 
103
  param->start_check_pos=0;
 
104
  param->max_record_length= LONGLONG_MAX;
 
105
  param->key_cache_block_size= KEY_CACHE_BLOCK_SIZE;
 
106
  param->stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
 
107
#ifdef THREAD
 
108
  param->need_print_msg_lock= 0;
 
109
#endif
 
110
}
 
111
 
 
112
        /* Check the status flags for the table */
 
113
 
 
114
int chk_status(MI_CHECK *param, register MI_INFO *info)
 
115
{
 
116
  MYISAM_SHARE *share=info->s;
 
117
 
 
118
  if (mi_is_crashed_on_repair(info))
 
119
    mi_check_print_warning(param,
 
120
                           "Table is marked as crashed and last repair failed");
 
121
  else if (mi_is_crashed(info))
 
122
    mi_check_print_warning(param,
 
123
                           "Table is marked as crashed");
 
124
  if (share->state.open_count != (uint) (info->s->global_changed ? 1 : 0))
 
125
  {
 
126
    /* Don't count this as a real warning, as check can correct this ! */
 
127
    uint save=param->warning_printed;
 
128
    mi_check_print_warning(param,
 
129
                           share->state.open_count==1 ? 
 
130
                           "%d client is using or hasn't closed the table properly" : 
 
131
                           "%d clients are using or haven't closed the table properly",
 
132
                           share->state.open_count);
 
133
    /* If this will be fixed by the check, forget the warning */
 
134
    if (param->testflag & T_UPDATE_STATE)
 
135
      param->warning_printed=save;
 
136
  }
 
137
  return 0;
 
138
}
 
139
 
 
140
        /* Check delete links */
 
141
 
 
142
int chk_del(MI_CHECK *param, register MI_INFO *info, uint test_flag)
 
143
{
 
144
  reg2 ha_rows i;
 
145
  uint delete_link_length;
 
146
  my_off_t empty,next_link,UNINIT_VAR(old_link);
 
147
  char buff[22],buff2[22];
 
148
  DBUG_ENTER("chk_del");
 
149
 
 
150
  param->record_checksum=0;
 
151
  delete_link_length=((info->s->options & HA_OPTION_PACK_RECORD) ? 20 :
 
152
                      info->s->rec_reflength+1);
 
153
 
 
154
  if (!(test_flag & T_SILENT))
 
155
    puts("- check record delete-chain");
 
156
 
 
157
  next_link=info->s->state.dellink;
 
158
  if (info->state->del == 0)
 
159
  {
 
160
    if (test_flag & T_VERBOSE)
 
161
    {
 
162
      puts("No recordlinks");
 
163
    }
 
164
  }
 
165
  else
 
166
  {
 
167
    if (test_flag & T_VERBOSE)
 
168
      printf("Recordlinks:    ");
 
169
    empty=0;
 
170
    for (i= info->state->del ; i > 0L && next_link != HA_OFFSET_ERROR ; i--)
 
171
    {
 
172
      if (*killed_ptr(param))
 
173
        DBUG_RETURN(1);
 
174
      if (test_flag & T_VERBOSE)
 
175
        printf(" %9s",llstr(next_link,buff));
 
176
      if (next_link >= info->state->data_file_length)
 
177
        goto wrong;
 
178
      if (my_pread(info->dfile, (uchar*) buff,delete_link_length,
 
179
                   next_link,MYF(MY_NABP)))
 
180
      {
 
181
        if (test_flag & T_VERBOSE) puts("");
 
182
        mi_check_print_error(param,"Can't read delete-link at filepos: %s",
 
183
                    llstr(next_link,buff));
 
184
        DBUG_RETURN(1);
 
185
      }
 
186
      if (*buff != '\0')
 
187
      {
 
188
        if (test_flag & T_VERBOSE) puts("");
 
189
        mi_check_print_error(param,"Record at pos: %s is not remove-marked",
 
190
                    llstr(next_link,buff));
 
191
        goto wrong;
 
192
      }
 
193
      if (info->s->options & HA_OPTION_PACK_RECORD)
 
194
      {
 
195
        my_off_t prev_link=mi_sizekorr(buff+12);
 
196
        if (empty && prev_link != old_link)
 
197
        {
 
198
          if (test_flag & T_VERBOSE) puts("");
 
199
          mi_check_print_error(param,"Deleted block at %s doesn't point back at previous delete link",llstr(next_link,buff2));
 
200
          goto wrong;
 
201
        }
 
202
        old_link=next_link;
 
203
        next_link=mi_sizekorr(buff+4);
 
204
        empty+=mi_uint3korr(buff+1);
 
205
      }
 
206
      else
 
207
      {
 
208
        param->record_checksum+=(ha_checksum) next_link;
 
209
        next_link=_mi_rec_pos(info->s,(uchar*) buff+1);
 
210
        empty+=info->s->base.pack_reclength;
 
211
      }
 
212
    }
 
213
    if (test_flag & T_VERBOSE)
 
214
      puts("\n");
 
215
    if (empty != info->state->empty)
 
216
    {
 
217
      mi_check_print_warning(param,
 
218
                             "Found %s deleted space in delete link chain. Should be %s",
 
219
                             llstr(empty,buff2),
 
220
                             llstr(info->state->empty,buff));
 
221
    }
 
222
    if (next_link != HA_OFFSET_ERROR)
 
223
    {
 
224
      mi_check_print_error(param,
 
225
                           "Found more than the expected %s deleted rows in delete link chain",
 
226
                           llstr(info->state->del, buff));
 
227
      goto wrong;
 
228
    }
 
229
    if (i != 0)
 
230
    {
 
231
      mi_check_print_error(param,
 
232
                           "Found %s deleted rows in delete link chain. Should be %s",
 
233
                           llstr(info->state->del - i, buff2),
 
234
                           llstr(info->state->del, buff));
 
235
      goto wrong;
 
236
    }
 
237
  }
 
238
  DBUG_RETURN(0);
 
239
 
 
240
wrong:
 
241
  param->testflag|=T_RETRY_WITHOUT_QUICK;
 
242
  if (test_flag & T_VERBOSE) puts("");
 
243
  mi_check_print_error(param,"record delete-link-chain corrupted");
 
244
  DBUG_RETURN(1);
 
245
} /* chk_del */
 
246
 
 
247
 
 
248
        /* Check delete links in index file */
 
249
 
 
250
static int check_k_link(MI_CHECK *param, register MI_INFO *info, uint nr)
 
251
{
 
252
  my_off_t next_link;
 
253
  uint block_size=(nr+1)*MI_MIN_KEY_BLOCK_LENGTH;
 
254
  ha_rows records;
 
255
  char llbuff[21], llbuff2[21];
 
256
  uchar *buff;
 
257
  DBUG_ENTER("check_k_link");
 
258
  DBUG_PRINT("enter", ("block_size: %u", block_size));
 
259
 
 
260
  if (param->testflag & T_VERBOSE)
 
261
    printf("block_size %4u:", block_size); /* purecov: tested */
 
262
 
 
263
  next_link=info->s->state.key_del[nr];
 
264
  records= (ha_rows) (info->state->key_file_length / block_size);
 
265
  while (next_link != HA_OFFSET_ERROR && records > 0)
 
266
  {
 
267
    if (*killed_ptr(param))
 
268
      DBUG_RETURN(1);
 
269
    if (param->testflag & T_VERBOSE)
 
270
      printf("%16s",llstr(next_link,llbuff));
 
271
 
 
272
    /* Key blocks must lay within the key file length entirely. */
 
273
    if (next_link + block_size > info->state->key_file_length)
 
274
    {
 
275
      /* purecov: begin tested */
 
276
      mi_check_print_error(param, "Invalid key block position: %s  "
 
277
                           "key block size: %u  file_length: %s",
 
278
                           llstr(next_link, llbuff), block_size,
 
279
                           llstr(info->state->key_file_length, llbuff2));
 
280
      DBUG_RETURN(1);
 
281
      /* purecov: end */
 
282
    }
 
283
 
 
284
    /* Key blocks must be aligned at MI_MIN_KEY_BLOCK_LENGTH. */
 
285
    if (next_link & (MI_MIN_KEY_BLOCK_LENGTH - 1))
 
286
    {
 
287
      /* purecov: begin tested */
 
288
      mi_check_print_error(param, "Mis-aligned key block: %s  "
 
289
                           "minimum key block length: %u",
 
290
                           llstr(next_link, llbuff), MI_MIN_KEY_BLOCK_LENGTH);
 
291
      DBUG_RETURN(1);
 
292
      /* purecov: end */
 
293
    }
 
294
 
 
295
    /*
 
296
      Read the key block with MI_MIN_KEY_BLOCK_LENGTH to find next link.
 
297
      If the key cache block size is smaller than block_size, we can so
 
298
      avoid unecessary eviction of cache block.
 
299
    */
 
300
    if (!(buff=key_cache_read(info->s->key_cache,
 
301
                              info->s->kfile, next_link, DFLT_INIT_HITS,
 
302
                              (uchar*) info->buff, MI_MIN_KEY_BLOCK_LENGTH,
 
303
                              MI_MIN_KEY_BLOCK_LENGTH, 1)))
 
304
    {
 
305
      /* purecov: begin tested */
 
306
      mi_check_print_error(param, "key cache read error for block: %s",
 
307
                           llstr(next_link,llbuff));
 
308
      DBUG_RETURN(1);
 
309
      /* purecov: end */
 
310
    }
 
311
    next_link=mi_sizekorr(buff);
 
312
    records--;
 
313
    param->key_file_blocks+=block_size;
 
314
  }
 
315
  if (param->testflag & T_VERBOSE)
 
316
  {
 
317
    if (next_link != HA_OFFSET_ERROR)
 
318
      printf("%16s\n",llstr(next_link,llbuff));
 
319
    else
 
320
      puts("");
 
321
  }
 
322
  DBUG_RETURN (next_link != HA_OFFSET_ERROR);
 
323
} /* check_k_link */
 
324
 
 
325
 
 
326
        /* Check sizes of files */
 
327
 
 
328
int chk_size(MI_CHECK *param, register MI_INFO *info)
 
329
{
 
330
  int error=0;
 
331
  register my_off_t skr,size;
 
332
  char buff[22],buff2[22];
 
333
  DBUG_ENTER("chk_size");
 
334
 
 
335
  if (!(param->testflag & T_SILENT)) puts("- check file-size");
 
336
 
 
337
  /* The following is needed if called externally (not from myisamchk) */
 
338
  flush_key_blocks(info->s->key_cache,
 
339
                   info->s->kfile, FLUSH_FORCE_WRITE);
 
340
 
 
341
  size= my_seek(info->s->kfile, 0L, MY_SEEK_END, MYF(MY_THREADSAFE));
 
342
  if ((skr=(my_off_t) info->state->key_file_length) != size)
 
343
  {
 
344
    /* Don't give error if file generated by myisampack */
 
345
    if (skr > size && mi_is_any_key_active(info->s->state.key_map))
 
346
    {
 
347
      error=1;
 
348
      mi_check_print_error(param,
 
349
                           "Size of indexfile is: %-8s        Should be: %s",
 
350
                           llstr(size,buff), llstr(skr,buff2));
 
351
    }
 
352
    else
 
353
      mi_check_print_warning(param,
 
354
                             "Size of indexfile is: %-8s      Should be: %s",
 
355
                             llstr(size,buff), llstr(skr,buff2));
 
356
  }
 
357
  if (!(param->testflag & T_VERY_SILENT) &&
 
358
      ! (info->s->options & HA_OPTION_COMPRESS_RECORD) &&
 
359
      ulonglong2double(info->state->key_file_length) >
 
360
      ulonglong2double(info->s->base.margin_key_file_length)*0.9)
 
361
    mi_check_print_warning(param,"Keyfile is almost full, %10s of %10s used",
 
362
                           llstr(info->state->key_file_length,buff),
 
363
                           llstr(info->s->base.max_key_file_length-1,buff));
 
364
 
 
365
  size=my_seek(info->dfile,0L,MY_SEEK_END,MYF(0));
 
366
  skr=(my_off_t) info->state->data_file_length;
 
367
  if (info->s->options & HA_OPTION_COMPRESS_RECORD)
 
368
    skr+= MEMMAP_EXTRA_MARGIN;
 
369
#ifdef USE_RELOC
 
370
  if (info->data_file_type == STATIC_RECORD &&
 
371
      skr < (my_off_t) info->s->base.reloc*info->s->base.min_pack_length)
 
372
    skr=(my_off_t) info->s->base.reloc*info->s->base.min_pack_length;
 
373
#endif
 
374
  if (skr != size)
 
375
  {
 
376
    info->state->data_file_length=size; /* Skip other errors */
 
377
    if (skr > size && skr != size + MEMMAP_EXTRA_MARGIN)
 
378
    {
 
379
      error=1;
 
380
      mi_check_print_error(param,"Size of datafile is: %-9s         Should be: %s",
 
381
                    llstr(size,buff), llstr(skr,buff2));
 
382
      param->testflag|=T_RETRY_WITHOUT_QUICK;
 
383
    }
 
384
    else
 
385
    {
 
386
      mi_check_print_warning(param,
 
387
                             "Size of datafile is: %-9s       Should be: %s",
 
388
                             llstr(size,buff), llstr(skr,buff2));
 
389
    }
 
390
  }
 
391
  if (!(param->testflag & T_VERY_SILENT) &&
 
392
      !(info->s->options & HA_OPTION_COMPRESS_RECORD) &&
 
393
      ulonglong2double(info->state->data_file_length) >
 
394
      (ulonglong2double(info->s->base.max_data_file_length)*0.9))
 
395
    mi_check_print_warning(param, "Datafile is almost full, %10s of %10s used",
 
396
                           llstr(info->state->data_file_length,buff),
 
397
                           llstr(info->s->base.max_data_file_length-1,buff2));
 
398
  DBUG_RETURN(error);
 
399
} /* chk_size */
 
400
 
 
401
 
 
402
        /* Check keys */
 
403
 
 
404
int chk_key(MI_CHECK *param, register MI_INFO *info)
 
405
{
 
406
  uint key,found_keys=0,full_text_keys=0,result=0;
 
407
  ha_rows keys;
 
408
  ha_checksum old_record_checksum,init_checksum;
 
409
  my_off_t all_keydata,all_totaldata,key_totlength,length;
 
410
  ulong   *rec_per_key_part;
 
411
  MYISAM_SHARE *share=info->s;
 
412
  MI_KEYDEF *keyinfo;
 
413
  char buff[22],buff2[22];
 
414
  DBUG_ENTER("chk_key");
 
415
 
 
416
  if (!(param->testflag & T_SILENT))
 
417
    puts("- check key delete-chain");
 
418
 
 
419
  param->key_file_blocks=info->s->base.keystart;
 
420
  for (key=0 ; key < info->s->state.header.max_block_size_index ; key++)
 
421
    if (check_k_link(param,info,key))
 
422
    {
 
423
      if (param->testflag & T_VERBOSE) puts("");
 
424
      mi_check_print_error(param,"key delete-link-chain corrupted");
 
425
      DBUG_RETURN(-1);
 
426
    }
 
427
 
 
428
  if (!(param->testflag & T_SILENT)) puts("- check index reference");
 
429
 
 
430
  all_keydata=all_totaldata=key_totlength=0;
 
431
  old_record_checksum=0;
 
432
  init_checksum=param->record_checksum;
 
433
  if (!(share->options &
 
434
        (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
 
435
    old_record_checksum=calc_checksum(info->state->records+info->state->del-1)*
 
436
      share->base.pack_reclength;
 
437
  rec_per_key_part= param->rec_per_key_part;
 
438
  for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
 
439
       rec_per_key_part+=keyinfo->keysegs, key++, keyinfo++)
 
440
  {
 
441
    param->key_crc[key]=0;
 
442
    if (! mi_is_key_active(share->state.key_map, key))
 
443
    {
 
444
      /* Remember old statistics for key */
 
445
      memcpy((char*) rec_per_key_part,
 
446
             (char*) (share->state.rec_per_key_part +
 
447
                      (uint) (rec_per_key_part - param->rec_per_key_part)),
 
448
             keyinfo->keysegs*sizeof(*rec_per_key_part));
 
449
      continue;
 
450
    }
 
451
    found_keys++;
 
452
 
 
453
    param->record_checksum=init_checksum;
 
454
    
 
455
    bzero((char*) &param->unique_count,sizeof(param->unique_count));
 
456
    bzero((char*) &param->notnull_count,sizeof(param->notnull_count));
 
457
 
 
458
    if ((!(param->testflag & T_SILENT)))
 
459
      printf ("- check data record references index: %d\n",key+1);
 
460
    if (keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL))
 
461
      full_text_keys++;
 
462
    if (share->state.key_root[key] == HA_OFFSET_ERROR &&
 
463
        (info->state->records == 0 || keyinfo->flag & HA_FULLTEXT))
 
464
      goto do_stat;
 
465
    if (!_mi_fetch_keypage(info,keyinfo,share->state.key_root[key],
 
466
                           DFLT_INIT_HITS,info->buff,0))
 
467
    {
 
468
      mi_check_print_error(param,"Can't read indexpage from filepos: %s",
 
469
                  llstr(share->state.key_root[key],buff));
 
470
      if (!(param->testflag & T_INFO))
 
471
        DBUG_RETURN(-1);
 
472
      result= -1;
 
473
      continue;
 
474
    }
 
475
    param->key_file_blocks+=keyinfo->block_length;
 
476
    keys=0;
 
477
    param->keydata=param->totaldata=0;
 
478
    param->key_blocks=0;
 
479
    param->max_level=0;
 
480
    if (chk_index(param,info,keyinfo,share->state.key_root[key],info->buff,
 
481
                  &keys, param->key_crc+key,1))
 
482
      DBUG_RETURN(-1);
 
483
    if(!(keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL)))
 
484
    {
 
485
      if (keys != info->state->records)
 
486
      {
 
487
        mi_check_print_error(param,"Found %s keys of %s",llstr(keys,buff),
 
488
                    llstr(info->state->records,buff2));
 
489
        if (!(param->testflag & T_INFO))
 
490
        DBUG_RETURN(-1);
 
491
        result= -1;
 
492
        continue;
 
493
      }
 
494
      if (found_keys - full_text_keys == 1 &&
 
495
          ((share->options &
 
496
            (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
 
497
           (param->testflag & T_DONT_CHECK_CHECKSUM)))
 
498
        old_record_checksum=param->record_checksum;
 
499
      else if (old_record_checksum != param->record_checksum)
 
500
      {
 
501
        if (key)
 
502
          mi_check_print_error(param,"Key %u doesn't point at same records that key 1",
 
503
                      key+1);
 
504
        else
 
505
          mi_check_print_error(param,"Key 1 doesn't point at all records");
 
506
        if (!(param->testflag & T_INFO))
 
507
          DBUG_RETURN(-1);
 
508
        result= -1;
 
509
        continue;
 
510
      }
 
511
    }
 
512
    if ((uint) share->base.auto_key -1 == key)
 
513
    {
 
514
      /* Check that auto_increment key is bigger than max key value */
 
515
      ulonglong auto_increment;
 
516
      info->lastinx=key;
 
517
      _mi_read_key_record(info, 0L, info->rec_buff);
 
518
      auto_increment= retrieve_auto_increment(info, info->rec_buff);
 
519
      if (auto_increment > info->s->state.auto_increment)
 
520
      {
 
521
        mi_check_print_warning(param, "Auto-increment value: %s is smaller "
 
522
                               "than max used value: %s",
 
523
                               llstr(info->s->state.auto_increment,buff2),
 
524
                               llstr(auto_increment, buff));
 
525
      }
 
526
      if (param->testflag & T_AUTO_INC)
 
527
      {
 
528
        set_if_bigger(info->s->state.auto_increment,
 
529
                      auto_increment);
 
530
        set_if_bigger(info->s->state.auto_increment,
 
531
                      param->auto_increment_value);
 
532
      }
 
533
 
 
534
      /* Check that there isn't a row with auto_increment = 0 in the table */
 
535
      mi_extra(info,HA_EXTRA_KEYREAD,0);
 
536
      bzero(info->lastkey,keyinfo->seg->length);
 
537
      if (!mi_rkey(info, info->rec_buff, key, (const uchar*) info->lastkey,
 
538
                   (key_part_map)1, HA_READ_KEY_EXACT))
 
539
      {
 
540
        /* Don't count this as a real warning, as myisamchk can't correct it */
 
541
        uint save=param->warning_printed;
 
542
        mi_check_print_warning(param, "Found row where the auto_increment "
 
543
                               "column has the value 0");
 
544
        param->warning_printed=save;
 
545
      }
 
546
      mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
 
547
    }
 
548
 
 
549
    length=(my_off_t) isam_key_length(info,keyinfo)*keys + param->key_blocks*2;
 
550
    if (param->testflag & T_INFO && param->totaldata != 0L && keys != 0L)
 
551
      printf("Key: %2d:  Keyblocks used: %3d%%  Packed: %4d%%  Max levels: %2d\n",
 
552
             key+1,
 
553
             (int) (my_off_t2double(param->keydata)*100.0/my_off_t2double(param->totaldata)),
 
554
             (int) ((my_off_t2double(length) - my_off_t2double(param->keydata))*100.0/
 
555
                    my_off_t2double(length)),
 
556
             param->max_level);
 
557
    all_keydata+=param->keydata; all_totaldata+=param->totaldata; key_totlength+=length;
 
558
 
 
559
do_stat:
 
560
    if (param->testflag & T_STATISTICS)
 
561
      update_key_parts(keyinfo, rec_per_key_part, param->unique_count,
 
562
                       param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
 
563
                       param->notnull_count: NULL, 
 
564
                       (ulonglong)info->state->records);
 
565
  }
 
566
  if (param->testflag & T_INFO)
 
567
  {
 
568
    if (all_totaldata != 0L && found_keys > 0)
 
569
      printf("Total:    Keyblocks used: %3d%%  Packed: %4d%%\n\n",
 
570
             (int) (my_off_t2double(all_keydata)*100.0/
 
571
                    my_off_t2double(all_totaldata)),
 
572
             (int) ((my_off_t2double(key_totlength) -
 
573
                     my_off_t2double(all_keydata))*100.0/
 
574
                     my_off_t2double(key_totlength)));
 
575
    else if (all_totaldata != 0L && mi_is_any_key_active(share->state.key_map))
 
576
      puts("");
 
577
  }
 
578
  if (param->key_file_blocks != info->state->key_file_length &&
 
579
      param->keys_in_use != ~(ulonglong) 0)
 
580
    mi_check_print_warning(param, "Some data are unreferenced in keyfile");
 
581
  if (found_keys != full_text_keys)
 
582
    param->record_checksum=old_record_checksum-init_checksum;   /* Remove delete links */
 
583
  else
 
584
    param->record_checksum=0;
 
585
  DBUG_RETURN(result);
 
586
} /* chk_key */
 
587
 
 
588
 
 
589
static int chk_index_down(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
 
590
                     my_off_t page, uchar *buff, ha_rows *keys,
 
591
                     ha_checksum *key_checksum, uint level)
 
592
{
 
593
  char llbuff[22],llbuff2[22];
 
594
  DBUG_ENTER("chk_index_down");
 
595
 
 
596
  /* Key blocks must lay within the key file length entirely. */
 
597
  if (page + keyinfo->block_length > info->state->key_file_length)
 
598
  {
 
599
    /* purecov: begin tested */
 
600
    /* Give it a chance to fit in the real file size. */
 
601
    my_off_t max_length= my_seek(info->s->kfile, 0L, MY_SEEK_END,
 
602
                                 MYF(MY_THREADSAFE));
 
603
    mi_check_print_error(param, "Invalid key block position: %s  "
 
604
                         "key block size: %u  file_length: %s",
 
605
                         llstr(page, llbuff), keyinfo->block_length,
 
606
                         llstr(info->state->key_file_length, llbuff2));
 
607
    if (page + keyinfo->block_length > max_length)
 
608
      goto err;
 
609
    /* Fix the remebered key file length. */
 
610
    info->state->key_file_length= (max_length &
 
611
                                   ~ (my_off_t) (keyinfo->block_length - 1));
 
612
    /* purecov: end */
 
613
  }
 
614
 
 
615
  /* Key blocks must be aligned at MI_MIN_KEY_BLOCK_LENGTH. */
 
616
  if (page & (MI_MIN_KEY_BLOCK_LENGTH - 1))
 
617
  {
 
618
    /* purecov: begin tested */
 
619
    mi_check_print_error(param, "Mis-aligned key block: %s  "
 
620
                         "minimum key block length: %u",
 
621
                         llstr(page, llbuff), MI_MIN_KEY_BLOCK_LENGTH);
 
622
    goto err;
 
623
    /* purecov: end */
 
624
  }
 
625
 
 
626
  if (!_mi_fetch_keypage(info,keyinfo,page, DFLT_INIT_HITS,buff,0))
 
627
  {
 
628
    mi_check_print_error(param,"Can't read key from filepos: %s",
 
629
        llstr(page,llbuff));
 
630
    goto err;
 
631
  }
 
632
  param->key_file_blocks+=keyinfo->block_length;
 
633
  if (chk_index(param,info,keyinfo,page,buff,keys,key_checksum,level))
 
634
    goto err;
 
635
 
 
636
  DBUG_RETURN(0);
 
637
 
 
638
  /* purecov: begin tested */
 
639
err:
 
640
  DBUG_RETURN(1);
 
641
  /* purecov: end */
 
642
}
 
643
 
 
644
 
 
645
/*
 
646
  "Ignore NULLs" statistics collection method: process first index tuple.
 
647
 
 
648
  SYNOPSIS
 
649
    mi_collect_stats_nonulls_first()
 
650
      keyseg   IN     Array of key part descriptions
 
651
      notnull  INOUT  Array, notnull[i] = (number of {keypart1...keypart_i}
 
652
                                           tuples that don't contain NULLs)
 
653
      key      IN     Key values tuple
 
654
 
 
655
  DESCRIPTION
 
656
    Process the first index tuple - find out which prefix tuples don't
 
657
    contain NULLs, and update the array of notnull counters accordingly.
 
658
*/
 
659
 
 
660
static
 
661
void mi_collect_stats_nonulls_first(HA_KEYSEG *keyseg, ulonglong *notnull,
 
662
                                    uchar *key)
 
663
{
 
664
  uint first_null, kp;
 
665
  first_null= (uint) (ha_find_null(keyseg, key) - keyseg);
 
666
  /*
 
667
    All prefix tuples that don't include keypart_{first_null} are not-null
 
668
    tuples (and all others aren't), increment counters for them.
 
669
  */
 
670
  for (kp= 0; kp < first_null; kp++)
 
671
    notnull[kp]++;
 
672
}
 
673
 
 
674
 
 
675
/*
 
676
  "Ignore NULLs" statistics collection method: process next index tuple.
 
677
 
 
678
  SYNOPSIS
 
679
    mi_collect_stats_nonulls_next()
 
680
      keyseg   IN     Array of key part descriptions
 
681
      notnull  INOUT  Array, notnull[i] = (number of {keypart1...keypart_i}
 
682
                                           tuples that don't contain NULLs)
 
683
      prev_key IN     Previous key values tuple
 
684
      last_key IN     Next key values tuple
 
685
 
 
686
  DESCRIPTION
 
687
    Process the next index tuple:
 
688
    1. Find out which prefix tuples of last_key don't contain NULLs, and
 
689
       update the array of notnull counters accordingly.
 
690
    2. Find the first keypart number where the prev_key and last_key tuples
 
691
       are different(A), or last_key has NULL value(B), and return it, so the 
 
692
       caller can count number of unique tuples for each key prefix. We don't 
 
693
       need (B) to be counted, and that is compensated back in 
 
694
       update_key_parts().
 
695
 
 
696
  RETURN
 
697
    1 + number of first keypart where values differ or last_key tuple has NULL
 
698
*/
 
699
 
 
700
static
 
701
int mi_collect_stats_nonulls_next(HA_KEYSEG *keyseg, ulonglong *notnull,
 
702
                                  uchar *prev_key, uchar *last_key)
 
703
{
 
704
  uint diffs[2];
 
705
  uint first_null_seg, kp;
 
706
  HA_KEYSEG *seg;
 
707
 
 
708
  /* 
 
709
     Find the first keypart where values are different or either of them is
 
710
     NULL. We get results in diffs array:
 
711
     diffs[0]= 1 + number of first different keypart
 
712
     diffs[1]=offset: (last_key + diffs[1]) points to first value in
 
713
                      last_key that is NULL or different from corresponding
 
714
                      value in prev_key.
 
715
  */
 
716
  ha_key_cmp(keyseg, prev_key, last_key, USE_WHOLE_KEY, 
 
717
             SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diffs);
 
718
  seg= keyseg + diffs[0] - 1;
 
719
 
 
720
  /* Find first NULL in last_key */
 
721
  first_null_seg= (uint) (ha_find_null(seg, last_key + diffs[1]) - keyseg);
 
722
  for (kp= 0; kp < first_null_seg; kp++)
 
723
    notnull[kp]++;
 
724
 
 
725
  /* 
 
726
    Return 1+ number of first key part where values differ. Don't care if
 
727
    these were NULLs and not .... We compensate for that in
 
728
    update_key_parts.
 
729
  */
 
730
  return diffs[0];
 
731
}
 
732
 
 
733
 
 
734
        /* Check if index is ok */
 
735
 
 
736
static int chk_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
 
737
                     my_off_t page, uchar *buff, ha_rows *keys,
 
738
                     ha_checksum *key_checksum, uint level)
 
739
{
 
740
  int flag;
 
741
  uint used_length,comp_flag,nod_flag,key_length=0;
 
742
  uchar key[HA_MAX_POSSIBLE_KEY_BUFF],*temp_buff,*keypos,*old_keypos,*endpos;
 
743
  my_off_t next_page,record;
 
744
  char llbuff[22];
 
745
  uint diff_pos[2];
 
746
  DBUG_ENTER("chk_index");
 
747
  DBUG_DUMP("buff",(uchar*) buff,mi_getint(buff));
 
748
 
 
749
  /* TODO: implement appropriate check for RTree keys */
 
750
  if (keyinfo->flag & HA_SPATIAL)
 
751
    DBUG_RETURN(0);
 
752
 
 
753
  if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
 
754
  {
 
755
    mi_check_print_error(param,"Not enough memory for keyblock");
 
756
    DBUG_RETURN(-1);
 
757
  }
 
758
 
 
759
  if (keyinfo->flag & HA_NOSAME)
 
760
    comp_flag=SEARCH_FIND | SEARCH_UPDATE;      /* Not real duplicates */
 
761
  else
 
762
    comp_flag=SEARCH_SAME;                      /* Keys in positionorder */
 
763
  nod_flag=mi_test_if_nod(buff);
 
764
  used_length=mi_getint(buff);
 
765
  keypos=buff+2+nod_flag;
 
766
  endpos=buff+used_length;
 
767
 
 
768
  param->keydata+=used_length; param->totaldata+=keyinfo->block_length; /* INFO */
 
769
  param->key_blocks++;
 
770
  if (level > param->max_level)
 
771
    param->max_level=level;
 
772
 
 
773
  if (used_length > keyinfo->block_length)
 
774
  {
 
775
    mi_check_print_error(param,"Wrong pageinfo at page: %s",
 
776
                         llstr(page,llbuff));
 
777
    goto err;
 
778
  }
 
779
  for ( ;; )
 
780
  {
 
781
    if (*killed_ptr(param))
 
782
      goto err;
 
783
    memcpy((char*) info->lastkey,(char*) key,key_length);
 
784
    info->lastkey_length=key_length;
 
785
    if (nod_flag)
 
786
    {
 
787
      next_page=_mi_kpos(nod_flag,keypos);
 
788
      if (chk_index_down(param,info,keyinfo,next_page,
 
789
                         temp_buff,keys,key_checksum,level+1))
 
790
        goto err;
 
791
    }
 
792
    old_keypos=keypos;
 
793
    if (keypos >= endpos ||
 
794
        (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0)
 
795
      break;
 
796
    if (keypos > endpos)
 
797
    {
 
798
      mi_check_print_error(param,"Wrong key block length at page: %s",llstr(page,llbuff));
 
799
      goto err;
 
800
    }
 
801
    if ((*keys)++ &&
 
802
        (flag=ha_key_cmp(keyinfo->seg,info->lastkey,key,key_length,
 
803
                         comp_flag, diff_pos)) >=0)
 
804
    {
 
805
      DBUG_DUMP("old",(uchar*) info->lastkey, info->lastkey_length);
 
806
      DBUG_DUMP("new",(uchar*) key, key_length);
 
807
      DBUG_DUMP("new_in_page",(uchar*) old_keypos,(uint) (keypos-old_keypos));
 
808
 
 
809
      if (comp_flag & SEARCH_FIND && flag == 0)
 
810
        mi_check_print_error(param,"Found duplicated key at page %s",llstr(page,llbuff));
 
811
      else
 
812
        mi_check_print_error(param,"Key in wrong position at page %s",llstr(page,llbuff));
 
813
      goto err;
 
814
    }
 
815
    if (param->testflag & T_STATISTICS)
 
816
    {
 
817
      if (*keys != 1L)                          /* not first_key */
 
818
      {
 
819
        if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL)
 
820
          ha_key_cmp(keyinfo->seg,info->lastkey,key,USE_WHOLE_KEY,
 
821
                     SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL,
 
822
                     diff_pos);
 
823
        else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
 
824
        {
 
825
          diff_pos[0]= mi_collect_stats_nonulls_next(keyinfo->seg, 
 
826
                                                  param->notnull_count,
 
827
                                                  info->lastkey, key);
 
828
        }
 
829
        param->unique_count[diff_pos[0]-1]++;
 
830
      }
 
831
      else
 
832
      {  
 
833
        if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
 
834
          mi_collect_stats_nonulls_first(keyinfo->seg, param->notnull_count,
 
835
                                         key);
 
836
      }
 
837
    }
 
838
    (*key_checksum)+= mi_byte_checksum((uchar*) key,
 
839
                                       key_length- info->s->rec_reflength);
 
840
    record= _mi_dpos(info,0,key+key_length);
 
841
    if (keyinfo->flag & HA_FULLTEXT) /* special handling for ft2 */
 
842
    {
 
843
      uint off;
 
844
      int  subkeys;
 
845
      get_key_full_length_rdonly(off, key);
 
846
      subkeys=ft_sintXkorr(key+off);
 
847
      if (subkeys < 0)
 
848
      {
 
849
        ha_rows tmp_keys=0;
 
850
        if (chk_index_down(param,info,&info->s->ft2_keyinfo,record,
 
851
                           temp_buff,&tmp_keys,key_checksum,1))
 
852
          goto err;
 
853
        if (tmp_keys + subkeys)
 
854
        {
 
855
          mi_check_print_error(param,
 
856
                               "Number of words in the 2nd level tree "
 
857
                               "does not match the number in the header. "
 
858
                               "Parent word in on the page %s, offset %u",
 
859
                               llstr(page,llbuff), (uint) (old_keypos-buff));
 
860
          goto err;
 
861
        }
 
862
        (*keys)+=tmp_keys-1;
 
863
        continue;
 
864
      }
 
865
      /* fall through */
 
866
    }
 
867
    if (record >= info->state->data_file_length)
 
868
    {
 
869
#ifndef DBUG_OFF
 
870
      char llbuff2[22], llbuff3[22];
 
871
#endif
 
872
      mi_check_print_error(param,"Found key at page %s that points to record outside datafile",llstr(page,llbuff));
 
873
      DBUG_PRINT("test",("page: %s  record: %s  filelength: %s",
 
874
                         llstr(page,llbuff),llstr(record,llbuff2),
 
875
                         llstr(info->state->data_file_length,llbuff3)));
 
876
      DBUG_DUMP("key",(uchar*) key,key_length);
 
877
      DBUG_DUMP("new_in_page",(uchar*) old_keypos,(uint) (keypos-old_keypos));
 
878
      goto err;
 
879
    }
 
880
    param->record_checksum+=(ha_checksum) record;
 
881
  }
 
882
  if (keypos != endpos)
 
883
  {
 
884
    mi_check_print_error(param,"Keyblock size at page %s is not correct.  Block length: %d  key length: %d",
 
885
                llstr(page,llbuff), used_length, (keypos - buff));
 
886
    goto err;
 
887
  }
 
888
  my_afree((uchar*) temp_buff);
 
889
  DBUG_RETURN(0);
 
890
 err:
 
891
  my_afree((uchar*) temp_buff);
 
892
  DBUG_RETURN(1);
 
893
} /* chk_index */
 
894
 
 
895
 
 
896
        /* Calculate a checksum of 1+2+3+4...N = N*(N+1)/2 without overflow */
 
897
 
 
898
static ha_checksum calc_checksum(ha_rows count)
 
899
{
 
900
  ulonglong sum,a,b;
 
901
  DBUG_ENTER("calc_checksum");
 
902
 
 
903
  sum=0;
 
904
  a=count; b=count+1;
 
905
  if (a & 1)
 
906
    b>>=1;
 
907
  else
 
908
    a>>=1;
 
909
  while (b)
 
910
  {
 
911
    if (b & 1)
 
912
      sum+=a;
 
913
    a<<=1; b>>=1;
 
914
  }
 
915
  DBUG_PRINT("exit",("sum: %lx",(ulong) sum));
 
916
  DBUG_RETURN((ha_checksum) sum);
 
917
} /* calc_checksum */
 
918
 
 
919
 
 
920
        /* Calc length of key in normal isam */
 
921
 
 
922
static uint isam_key_length(MI_INFO *info, register MI_KEYDEF *keyinfo)
 
923
{
 
924
  uint length;
 
925
  HA_KEYSEG *keyseg;
 
926
  DBUG_ENTER("isam_key_length");
 
927
 
 
928
  length= info->s->rec_reflength;
 
929
  for (keyseg=keyinfo->seg ; keyseg->type ; keyseg++)
 
930
    length+= keyseg->length;
 
931
 
 
932
  DBUG_PRINT("exit",("length: %d",length));
 
933
  DBUG_RETURN(length);
 
934
} /* key_length */
 
935
 
 
936
 
 
937
        /* Check that record-link is ok */
 
938
 
 
939
int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend)
 
940
{
 
941
  int   error,got_error,flag;
 
942
  uint  key,UNINIT_VAR(left_length),b_type,field;
 
943
  ha_rows records,del_blocks;
 
944
  my_off_t used,empty,pos,splits,UNINIT_VAR(start_recpos),
 
945
           del_length,link_used,start_block;
 
946
  uchar *record= 0, *UNINIT_VAR(to);
 
947
  char llbuff[22],llbuff2[22],llbuff3[22];
 
948
  ha_checksum intern_record_checksum;
 
949
  ha_checksum key_checksum[HA_MAX_POSSIBLE_KEY];
 
950
  my_bool static_row_size;
 
951
  MI_KEYDEF *keyinfo;
 
952
  MI_BLOCK_INFO block_info;
 
953
  DBUG_ENTER("chk_data_link");
 
954
 
 
955
  if (!(param->testflag & T_SILENT))
 
956
  {
 
957
    if (extend)
 
958
      puts("- check records and index references");
 
959
    else
 
960
      puts("- check record links");
 
961
  }
 
962
 
 
963
  if (!mi_alloc_rec_buff(info, -1, &record))
 
964
  {
 
965
    mi_check_print_error(param,"Not enough memory for record");
 
966
    DBUG_RETURN(-1);
 
967
  }
 
968
  records=del_blocks=0;
 
969
  used=link_used=splits=del_length=0;
 
970
  intern_record_checksum=param->glob_crc=0;
 
971
  got_error=error=0;
 
972
  empty=info->s->pack.header_length;
 
973
 
 
974
  /* Check how to calculate checksum of rows */
 
975
  static_row_size=1;
 
976
  if (info->s->data_file_type == COMPRESSED_RECORD)
 
977
  {
 
978
    for (field=0 ; field < info->s->base.fields ; field++)
 
979
    {
 
980
      if (info->s->rec[field].base_type == FIELD_BLOB ||
 
981
          info->s->rec[field].base_type == FIELD_VARCHAR)
 
982
      {
 
983
        static_row_size=0;
 
984
        break;
 
985
      }
 
986
    }
 
987
  }
 
988
 
 
989
  pos=my_b_tell(&param->read_cache);
 
990
  bzero((char*) key_checksum, info->s->base.keys * sizeof(key_checksum[0]));
 
991
  while (pos < info->state->data_file_length)
 
992
  {
 
993
    if (*killed_ptr(param))
 
994
      goto err2;
 
995
    switch (info->s->data_file_type) {
 
996
    case STATIC_RECORD:
 
997
      if (my_b_read(&param->read_cache,(uchar*) record,
 
998
                    info->s->base.pack_reclength))
 
999
        goto err;
 
1000
      start_recpos=pos;
 
1001
      pos+=info->s->base.pack_reclength;
 
1002
      splits++;
 
1003
      if (*record == '\0')
 
1004
      {
 
1005
        del_blocks++;
 
1006
        del_length+=info->s->base.pack_reclength;
 
1007
        continue;                                       /* Record removed */
 
1008
      }
 
1009
      param->glob_crc+= mi_static_checksum(info,record);
 
1010
      used+=info->s->base.pack_reclength;
 
1011
      break;
 
1012
    case DYNAMIC_RECORD:
 
1013
      flag=block_info.second_read=0;
 
1014
      block_info.next_filepos=pos;
 
1015
      do
 
1016
      {
 
1017
        if (_mi_read_cache(&param->read_cache,(uchar*) block_info.header,
 
1018
                           (start_block=block_info.next_filepos),
 
1019
                           sizeof(block_info.header),
 
1020
                           (flag ? 0 : READING_NEXT) | READING_HEADER))
 
1021
          goto err;
 
1022
        if (start_block & (MI_DYN_ALIGN_SIZE-1))
 
1023
        {
 
1024
          mi_check_print_error(param,"Wrong aligned block at %s",
 
1025
                               llstr(start_block,llbuff));
 
1026
          goto err2;
 
1027
        }
 
1028
        b_type=_mi_get_block_info(&block_info,-1,start_block);
 
1029
        if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
 
1030
                      BLOCK_FATAL_ERROR))
 
1031
        {
 
1032
          if (b_type & BLOCK_SYNC_ERROR)
 
1033
          {
 
1034
            if (flag)
 
1035
            {
 
1036
              mi_check_print_error(param,"Unexpected byte: %d at link: %s",
 
1037
                          (int) block_info.header[0],
 
1038
                          llstr(start_block,llbuff));
 
1039
              goto err2;
 
1040
            }
 
1041
            pos=block_info.filepos+block_info.block_len;
 
1042
            goto next;
 
1043
          }
 
1044
          if (b_type & BLOCK_DELETED)
 
1045
          {
 
1046
            if (block_info.block_len < info->s->base.min_block_length)
 
1047
            {
 
1048
              mi_check_print_error(param,
 
1049
                                   "Deleted block with impossible length %lu at %s",
 
1050
                                   block_info.block_len,llstr(pos,llbuff));
 
1051
              goto err2;
 
1052
            }
 
1053
            if ((block_info.next_filepos != HA_OFFSET_ERROR &&
 
1054
                 block_info.next_filepos >= info->state->data_file_length) ||
 
1055
                (block_info.prev_filepos != HA_OFFSET_ERROR &&
 
1056
                 block_info.prev_filepos >= info->state->data_file_length))
 
1057
            {
 
1058
              mi_check_print_error(param,"Delete link points outside datafile at %s",
 
1059
                          llstr(pos,llbuff));
 
1060
              goto err2;
 
1061
            }
 
1062
            del_blocks++;
 
1063
            del_length+=block_info.block_len;
 
1064
            pos=block_info.filepos+block_info.block_len;
 
1065
            splits++;
 
1066
            goto next;
 
1067
          }
 
1068
          mi_check_print_error(param,"Wrong bytesec: %d-%d-%d at linkstart: %s",
 
1069
                               block_info.header[0],block_info.header[1],
 
1070
                               block_info.header[2],
 
1071
                               llstr(start_block,llbuff));
 
1072
          goto err2;
 
1073
        }
 
1074
        if (info->state->data_file_length < block_info.filepos+
 
1075
            block_info.block_len)
 
1076
        {
 
1077
          mi_check_print_error(param,
 
1078
                               "Recordlink that points outside datafile at %s",
 
1079
                               llstr(pos,llbuff));
 
1080
          got_error=1;
 
1081
          break;
 
1082
        }
 
1083
        splits++;
 
1084
        if (!flag++)                            /* First block */
 
1085
        {
 
1086
          start_recpos=pos;
 
1087
          pos=block_info.filepos+block_info.block_len;
 
1088
          if (block_info.rec_len > (uint) info->s->base.max_pack_length)
 
1089
          {
 
1090
            mi_check_print_error(param,"Found too long record (%lu) at %s",
 
1091
                                 (ulong) block_info.rec_len,
 
1092
                                 llstr(start_recpos,llbuff));
 
1093
            got_error=1;
 
1094
            break;
 
1095
          }
 
1096
          if (info->s->base.blobs)
 
1097
          {
 
1098
            if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
 
1099
                                        &info->rec_buff)))
 
1100
            {
 
1101
              mi_check_print_error(param,
 
1102
                                   "Not enough memory (%lu) for blob at %s",
 
1103
                                   (ulong) block_info.rec_len,
 
1104
                                   llstr(start_recpos,llbuff));
 
1105
              got_error=1;
 
1106
              break;
 
1107
            }
 
1108
          }
 
1109
          else
 
1110
            to= info->rec_buff;
 
1111
          left_length=block_info.rec_len;
 
1112
        }
 
1113
        if (left_length < block_info.data_len)
 
1114
        {
 
1115
          mi_check_print_error(param,"Found too long record (%lu) at %s",
 
1116
                               (ulong) block_info.data_len,
 
1117
                               llstr(start_recpos,llbuff));
 
1118
          got_error=1;
 
1119
          break;
 
1120
        }
 
1121
        if (_mi_read_cache(&param->read_cache,(uchar*) to,block_info.filepos,
 
1122
                           (uint) block_info.data_len,
 
1123
                           flag == 1 ? READING_NEXT : 0))
 
1124
          goto err;
 
1125
        to+=block_info.data_len;
 
1126
        link_used+= block_info.filepos-start_block;
 
1127
        used+= block_info.filepos - start_block + block_info.data_len;
 
1128
        empty+=block_info.block_len-block_info.data_len;
 
1129
        left_length-=block_info.data_len;
 
1130
        if (left_length)
 
1131
        {
 
1132
          if (b_type & BLOCK_LAST)
 
1133
          {
 
1134
            mi_check_print_error(param,
 
1135
                                 "Wrong record length %s of %s at %s",
 
1136
                                 llstr(block_info.rec_len-left_length,llbuff),
 
1137
                                 llstr(block_info.rec_len, llbuff2),
 
1138
                                 llstr(start_recpos,llbuff3));
 
1139
            got_error=1;
 
1140
            break;
 
1141
          }
 
1142
          if (info->state->data_file_length < block_info.next_filepos)
 
1143
          {
 
1144
            mi_check_print_error(param,
 
1145
                                 "Found next-recordlink that points outside datafile at %s",
 
1146
                                 llstr(block_info.filepos,llbuff));
 
1147
            got_error=1;
 
1148
            break;
 
1149
          }
 
1150
        }
 
1151
      } while (left_length);
 
1152
      if (! got_error)
 
1153
      {
 
1154
        if (_mi_rec_unpack(info,record,info->rec_buff,block_info.rec_len) ==
 
1155
            MY_FILE_ERROR)
 
1156
        {
 
1157
          mi_check_print_error(param,"Found wrong record at %s",
 
1158
                               llstr(start_recpos,llbuff));
 
1159
          got_error=1;
 
1160
        }
 
1161
        else
 
1162
        {
 
1163
          info->checksum=mi_checksum(info,record);
 
1164
          if (param->testflag & (T_EXTEND | T_MEDIUM | T_VERBOSE))
 
1165
          {
 
1166
            if (_mi_rec_check(info,record, info->rec_buff,block_info.rec_len,
 
1167
                              test(info->s->calc_checksum)))
 
1168
            {
 
1169
              mi_check_print_error(param,"Found wrong packed record at %s",
 
1170
                          llstr(start_recpos,llbuff));
 
1171
              got_error=1;
 
1172
            }
 
1173
          }
 
1174
          if (!got_error)
 
1175
            param->glob_crc+= info->checksum;
 
1176
        }
 
1177
      }
 
1178
      else if (!flag)
 
1179
        pos=block_info.filepos+block_info.block_len;
 
1180
      break;
 
1181
    case COMPRESSED_RECORD:
 
1182
      if (_mi_read_cache(&param->read_cache,(uchar*) block_info.header, pos,
 
1183
                         info->s->pack.ref_length, READING_NEXT))
 
1184
        goto err;
 
1185
      start_recpos=pos;
 
1186
      splits++;
 
1187
      VOID(_mi_pack_get_block_info(info, &info->bit_buff, &block_info,
 
1188
                                   &info->rec_buff, -1, start_recpos));
 
1189
      pos=block_info.filepos+block_info.rec_len;
 
1190
      if (block_info.rec_len < (uint) info->s->min_pack_length ||
 
1191
          block_info.rec_len > (uint) info->s->max_pack_length)
 
1192
      {
 
1193
        mi_check_print_error(param,
 
1194
                             "Found block with wrong recordlength: %d at %s",
 
1195
                             block_info.rec_len, llstr(start_recpos,llbuff));
 
1196
        got_error=1;
 
1197
        break;
 
1198
      }
 
1199
      if (_mi_read_cache(&param->read_cache,(uchar*) info->rec_buff,
 
1200
                        block_info.filepos, block_info.rec_len, READING_NEXT))
 
1201
        goto err;
 
1202
      if (_mi_pack_rec_unpack(info, &info->bit_buff, record,
 
1203
                              info->rec_buff, block_info.rec_len))
 
1204
      {
 
1205
        mi_check_print_error(param,"Found wrong record at %s",
 
1206
                             llstr(start_recpos,llbuff));
 
1207
        got_error=1;
 
1208
      }
 
1209
      if (static_row_size)
 
1210
        param->glob_crc+= mi_static_checksum(info,record);
 
1211
      else
 
1212
        param->glob_crc+= mi_checksum(info,record);
 
1213
      link_used+= (block_info.filepos - start_recpos);
 
1214
      used+= (pos-start_recpos);
 
1215
      break;
 
1216
    case BLOCK_RECORD:
 
1217
      assert(0);                                /* Impossible */
 
1218
    } /* switch */
 
1219
    if (! got_error)
 
1220
    {
 
1221
      intern_record_checksum+=(ha_checksum) start_recpos;
 
1222
      records++;
 
1223
      if (param->testflag & T_WRITE_LOOP && records % WRITE_COUNT == 0)
 
1224
      {
 
1225
        printf("%s\r", llstr(records,llbuff)); VOID(fflush(stdout));
 
1226
      }
 
1227
 
 
1228
      /* Check if keys match the record */
 
1229
 
 
1230
      for (key=0,keyinfo= info->s->keyinfo; key < info->s->base.keys;
 
1231
           key++,keyinfo++)
 
1232
      {
 
1233
        if (mi_is_key_active(info->s->state.key_map, key))
 
1234
        {
 
1235
          if(!(keyinfo->flag & HA_FULLTEXT))
 
1236
          {
 
1237
            uint key_length=_mi_make_key(info,key,info->lastkey,record,
 
1238
                                         start_recpos);
 
1239
            if (extend)
 
1240
            {
 
1241
              /* We don't need to lock the key tree here as we don't allow
 
1242
                 concurrent threads when running myisamchk
 
1243
              */
 
1244
              int search_result=
 
1245
#ifdef HAVE_RTREE_KEYS
 
1246
                (keyinfo->flag & HA_SPATIAL) ?
 
1247
                rtree_find_first(info, key, info->lastkey, key_length,
 
1248
                                 MBR_EQUAL | MBR_DATA) : 
 
1249
#endif
 
1250
                _mi_search(info,keyinfo,info->lastkey,key_length,
 
1251
                           SEARCH_SAME, info->s->state.key_root[key]);
 
1252
              if (search_result)
 
1253
              {
 
1254
                mi_check_print_error(param,"Record at: %10s  "
 
1255
                                     "Can't find key for index: %2d",
 
1256
                                     llstr(start_recpos,llbuff),key+1);
 
1257
                if (error++ > MAXERR || !(param->testflag & T_VERBOSE))
 
1258
                  goto err2;
 
1259
              }
 
1260
            }
 
1261
            else
 
1262
              key_checksum[key]+=mi_byte_checksum((uchar*) info->lastkey,
 
1263
                                                  key_length);
 
1264
          }
 
1265
        }
 
1266
      }
 
1267
    }
 
1268
    else
 
1269
    {
 
1270
      got_error=0;
 
1271
      if (error++ > MAXERR || !(param->testflag & T_VERBOSE))
 
1272
        goto err2;
 
1273
    }
 
1274
  next:;                                /* Next record */
 
1275
  }
 
1276
  if (param->testflag & T_WRITE_LOOP)
 
1277
  {
 
1278
    VOID(fputs("          \r",stdout)); VOID(fflush(stdout));
 
1279
  }
 
1280
  if (records != info->state->records)
 
1281
  {
 
1282
    mi_check_print_error(param,"Record-count is not ok; is %-10s   Should be: %s",
 
1283
                llstr(records,llbuff), llstr(info->state->records,llbuff2));
 
1284
    error=1;
 
1285
  }
 
1286
  else if (param->record_checksum &&
 
1287
           param->record_checksum != intern_record_checksum)
 
1288
  {
 
1289
    mi_check_print_error(param,
 
1290
                         "Keypointers and record positions doesn't match");
 
1291
    error=1;
 
1292
  }
 
1293
  else if (param->glob_crc != info->state->checksum &&
 
1294
           (info->s->options &
 
1295
            (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)))
 
1296
  {
 
1297
    mi_check_print_warning(param,
 
1298
                           "Record checksum is not the same as checksum stored in the index file\n");
 
1299
    error=1;
 
1300
  }
 
1301
  else if (!extend)
 
1302
  {
 
1303
    for (key=0 ; key < info->s->base.keys;  key++)
 
1304
    {
 
1305
      if (key_checksum[key] != param->key_crc[key] &&
 
1306
          !(info->s->keyinfo[key].flag & (HA_FULLTEXT | HA_SPATIAL)))
 
1307
      {
 
1308
        mi_check_print_error(param,"Checksum for key: %2d doesn't match checksum for records",
 
1309
                    key+1);
 
1310
        error=1;
 
1311
      }
 
1312
    }
 
1313
  }
 
1314
 
 
1315
  if (del_length != info->state->empty)
 
1316
  {
 
1317
    mi_check_print_warning(param,
 
1318
                           "Found %s deleted space.   Should be %s",
 
1319
                           llstr(del_length,llbuff2),
 
1320
                           llstr(info->state->empty,llbuff));
 
1321
  }
 
1322
  if (used+empty+del_length != info->state->data_file_length)
 
1323
  {
 
1324
    mi_check_print_warning(param,
 
1325
                           "Found %s record-data and %s unused data and %s deleted-data",
 
1326
                           llstr(used,llbuff),llstr(empty,llbuff2),
 
1327
                           llstr(del_length,llbuff3));
 
1328
    mi_check_print_warning(param,
 
1329
                           "Total %s, Should be: %s",
 
1330
                           llstr((used+empty+del_length),llbuff),
 
1331
                           llstr(info->state->data_file_length,llbuff2));
 
1332
  }
 
1333
  if (del_blocks != info->state->del)
 
1334
  {
 
1335
    mi_check_print_warning(param,
 
1336
                           "Found %10s deleted blocks       Should be: %s",
 
1337
                           llstr(del_blocks,llbuff),
 
1338
                           llstr(info->state->del,llbuff2));
 
1339
  }
 
1340
  if (splits != info->s->state.split)
 
1341
  {
 
1342
    mi_check_print_warning(param,
 
1343
                           "Found %10s key parts. Should be: %s",
 
1344
                           llstr(splits,llbuff),
 
1345
                           llstr(info->s->state.split,llbuff2));
 
1346
  }
 
1347
  if (param->testflag & T_INFO)
 
1348
  {
 
1349
    if (param->warning_printed || param->error_printed)
 
1350
      puts("");
 
1351
    if (used != 0 && ! param->error_printed)
 
1352
    {
 
1353
      printf("Records:%18s    M.recordlength:%9lu   Packed:%14.0f%%\n",
 
1354
             llstr(records,llbuff), (long)((used-link_used)/records),
 
1355
             (info->s->base.blobs ? 0.0 :
 
1356
              (ulonglong2double((ulonglong) info->s->base.reclength*records)-
 
1357
               my_off_t2double(used))/
 
1358
              ulonglong2double((ulonglong) info->s->base.reclength*records)*100.0));
 
1359
      printf("Recordspace used:%9.0f%%   Empty space:%12d%%  Blocks/Record: %6.2f\n",
 
1360
             (ulonglong2double(used-link_used)/ulonglong2double(used-link_used+empty)*100.0),
 
1361
             (!records ? 100 : (int) (ulonglong2double(del_length+empty)/
 
1362
                                      my_off_t2double(used)*100.0)),
 
1363
             ulonglong2double(splits - del_blocks) / records);
 
1364
    }
 
1365
    printf("Record blocks:%12s    Delete blocks:%10s\n",
 
1366
           llstr(splits-del_blocks,llbuff),llstr(del_blocks,llbuff2));
 
1367
    printf("Record data:  %12s    Deleted data: %10s\n",
 
1368
           llstr(used-link_used,llbuff),llstr(del_length,llbuff2));
 
1369
    printf("Lost space:   %12s    Linkdata:     %10s\n",
 
1370
           llstr(empty,llbuff),llstr(link_used,llbuff2));
 
1371
  }
 
1372
  my_free(mi_get_rec_buff_ptr(info, record), MYF(0));
 
1373
  DBUG_RETURN (error);
 
1374
 err:
 
1375
  mi_check_print_error(param,"got error: %d when reading datafile at record: %s",my_errno, llstr(records,llbuff));
 
1376
 err2:
 
1377
  my_free(mi_get_rec_buff_ptr(info, record), MYF(0));
 
1378
  param->testflag|=T_RETRY_WITHOUT_QUICK;
 
1379
  DBUG_RETURN(1);
 
1380
} /* chk_data_link */
 
1381
 
 
1382
 
 
1383
/**
 
1384
  @brief Drop all indexes
 
1385
 
 
1386
  @param[in]    param           check parameters
 
1387
  @param[in]    info            MI_INFO handle
 
1388
  @param[in]    force           if to force drop all indexes
 
1389
 
 
1390
  @return       status
 
1391
    @retval     0               OK
 
1392
    @retval     != 0            Error
 
1393
 
 
1394
  @note
 
1395
    Once allocated, index blocks remain part of the key file forever.
 
1396
    When indexes are disabled, no block is freed. When enabling indexes,
 
1397
    no block is freed either. The new indexes are create from new
 
1398
    blocks. (Bug #4692)
 
1399
 
 
1400
    Before recreating formerly disabled indexes, the unused blocks
 
1401
    must be freed. There are two options to do this:
 
1402
    - Follow the tree of disabled indexes, add all blocks to the
 
1403
      deleted blocks chain. Would require a lot of random I/O.
 
1404
    - Drop all blocks by clearing all index root pointers and all
 
1405
      delete chain pointers and resetting key_file_length to the end
 
1406
      of the index file header. This requires to recreate all indexes,
 
1407
      even those that may still be intact.
 
1408
    The second method is probably faster in most cases.
 
1409
 
 
1410
    When disabling indexes, MySQL disables either all indexes or all
 
1411
    non-unique indexes. When MySQL [re-]enables disabled indexes
 
1412
    (T_CREATE_MISSING_KEYS), then we either have "lost" blocks in the
 
1413
    index file, or there are no non-unique indexes. In the latter case,
 
1414
    mi_repair*() would not be called as there would be no disabled
 
1415
    indexes.
 
1416
 
 
1417
    If there would be more unique indexes than disabled (non-unique)
 
1418
    indexes, we could do the first method. But this is not implemented
 
1419
    yet. By now we drop and recreate all indexes when repair is called.
 
1420
 
 
1421
    However, there is an exception. Sometimes MySQL disables non-unique
 
1422
    indexes when the table is empty (e.g. when copying a table in
 
1423
    mysql_alter_table()). When enabling the non-unique indexes, they
 
1424
    are still empty. So there is no index block that can be lost. This
 
1425
    optimization is implemented in this function.
 
1426
 
 
1427
    Note that in normal repair (T_CREATE_MISSING_KEYS not set) we
 
1428
    recreate all enabled indexes unconditonally. We do not change the
 
1429
    key_map. Otherwise we invert the key map temporarily (outside of
 
1430
    this function) and recreate the then "seemingly" enabled indexes.
 
1431
    When we cannot use the optimization, and drop all indexes, we
 
1432
    pretend that all indexes were disabled. By the inversion, we will
 
1433
    then recrate all indexes.
 
1434
*/
 
1435
 
 
1436
static int mi_drop_all_indexes(MI_CHECK *param, MI_INFO *info, my_bool force)
 
1437
{
 
1438
  MYISAM_SHARE *share= info->s;
 
1439
  MI_STATE_INFO *state= &share->state;
 
1440
  uint i;
 
1441
  int error;
 
1442
  DBUG_ENTER("mi_drop_all_indexes");
 
1443
 
 
1444
  /*
 
1445
    If any of the disabled indexes has a key block assigned, we must
 
1446
    drop and recreate all indexes to avoid losing index blocks.
 
1447
 
 
1448
    If we want to recreate disabled indexes only _and_ all of these
 
1449
    indexes are empty, we don't need to recreate the existing indexes.
 
1450
  */
 
1451
  if (!force && (param->testflag & T_CREATE_MISSING_KEYS))
 
1452
  {
 
1453
    DBUG_PRINT("repair", ("creating missing indexes"));
 
1454
    for (i= 0; i < share->base.keys; i++)
 
1455
    {
 
1456
      DBUG_PRINT("repair", ("index #: %u  key_root: 0x%lx  active: %d",
 
1457
                            i, (long) state->key_root[i],
 
1458
                            mi_is_key_active(state->key_map, i)));
 
1459
      if ((state->key_root[i] != HA_OFFSET_ERROR) &&
 
1460
          !mi_is_key_active(state->key_map, i))
 
1461
      {
 
1462
        /*
 
1463
          This index has at least one key block and it is disabled.
 
1464
          We would lose its block(s) if would just recreate it.
 
1465
          So we need to drop and recreate all indexes.
 
1466
        */
 
1467
        DBUG_PRINT("repair", ("nonempty and disabled: recreate all"));
 
1468
        break;
 
1469
      }
 
1470
    }
 
1471
    if (i >= share->base.keys)
 
1472
    {
 
1473
      /*
 
1474
        All of the disabled indexes are empty. We can just recreate them.
 
1475
        Flush dirty blocks of this index file from key cache and remove
 
1476
        all blocks of this index file from key cache.
 
1477
      */
 
1478
      DBUG_PRINT("repair", ("all disabled are empty: create missing"));
 
1479
      error= flush_key_blocks(share->key_cache, share->kfile,
 
1480
                              FLUSH_FORCE_WRITE);
 
1481
      goto end;
 
1482
    }
 
1483
    /*
 
1484
      We do now drop all indexes and declare them disabled. With the
 
1485
      T_CREATE_MISSING_KEYS flag, mi_repair*() will recreate all
 
1486
      disabled indexes and enable them.
 
1487
    */
 
1488
    mi_clear_all_keys_active(state->key_map);
 
1489
    DBUG_PRINT("repair", ("declared all indexes disabled"));
 
1490
  }
 
1491
 
 
1492
  /* Remove all key blocks of this index file from key cache. */
 
1493
  if ((error= flush_key_blocks(share->key_cache, share->kfile,
 
1494
                               FLUSH_IGNORE_CHANGED)))
 
1495
    goto end; /* purecov: inspected */
 
1496
 
 
1497
  /* Clear index root block pointers. */
 
1498
  for (i= 0; i < share->base.keys; i++)
 
1499
    state->key_root[i]= HA_OFFSET_ERROR;
 
1500
 
 
1501
  /* Clear the delete chains. */
 
1502
  for (i= 0; i < state->header.max_block_size_index; i++)
 
1503
    state->key_del[i]= HA_OFFSET_ERROR;
 
1504
 
 
1505
  /* Reset index file length to end of index file header. */
 
1506
  info->state->key_file_length= share->base.keystart;
 
1507
 
 
1508
  DBUG_PRINT("repair", ("dropped all indexes"));
 
1509
  /* error= 0; set by last (error= flush_key_bocks()). */
 
1510
 
 
1511
 end:
 
1512
  DBUG_RETURN(error);
 
1513
}
 
1514
 
 
1515
 
 
1516
        /* Recover old table by reading each record and writing all keys */
 
1517
        /* Save new datafile-name in temp_filename */
 
1518
 
 
1519
int mi_repair(MI_CHECK *param, register MI_INFO *info,
 
1520
              char * name, int rep_quick)
 
1521
{
 
1522
  int error,got_error;
 
1523
  ha_rows start_records,new_header_length;
 
1524
  my_off_t del;
 
1525
  File new_file;
 
1526
  MYISAM_SHARE *share=info->s;
 
1527
  char llbuff[22],llbuff2[22];
 
1528
  SORT_INFO sort_info;
 
1529
  MI_SORT_PARAM sort_param;
 
1530
  DBUG_ENTER("mi_repair");
 
1531
 
 
1532
  bzero((char *)&sort_info, sizeof(sort_info));
 
1533
  bzero((char *)&sort_param, sizeof(sort_param));
 
1534
  start_records=info->state->records;
 
1535
  new_header_length=(param->testflag & T_UNPACK) ? 0L :
 
1536
    share->pack.header_length;
 
1537
  got_error=1;
 
1538
  new_file= -1;
 
1539
  sort_param.sort_info=&sort_info;
 
1540
 
 
1541
  if (!(param->testflag & T_SILENT))
 
1542
  {
 
1543
    printf("- recovering (with keycache) MyISAM-table '%s'\n",name);
 
1544
    printf("Data records: %s\n", llstr(info->state->records,llbuff));
 
1545
  }
 
1546
  param->testflag|=T_REP; /* for easy checking */
 
1547
 
 
1548
  if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
 
1549
    param->testflag|=T_CALC_CHECKSUM;
 
1550
 
 
1551
  DBUG_ASSERT(param->use_buffers < SIZE_T_MAX);
 
1552
 
 
1553
  if (!param->using_global_keycache)
 
1554
    VOID(init_key_cache(dflt_key_cache, param->key_cache_block_size,
 
1555
                        (size_t) param->use_buffers, 0, 0));
 
1556
 
 
1557
  if (init_io_cache(&param->read_cache,info->dfile,
 
1558
                    (uint) param->read_buffer_length,
 
1559
                    READ_CACHE,share->pack.header_length,1,MYF(MY_WME)))
 
1560
  {
 
1561
    bzero(&info->rec_cache,sizeof(info->rec_cache));
 
1562
    goto err;
 
1563
  }
 
1564
  if (!rep_quick)
 
1565
    if (init_io_cache(&info->rec_cache,-1,(uint) param->write_buffer_length,
 
1566
                      WRITE_CACHE, new_header_length, 1,
 
1567
                      MYF(MY_WME | MY_WAIT_IF_FULL)))
 
1568
      goto err;
 
1569
  info->opt_flag|=WRITE_CACHE_USED;
 
1570
  if (!mi_alloc_rec_buff(info, -1, &sort_param.record) ||
 
1571
      !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff))
 
1572
  {
 
1573
    mi_check_print_error(param, "Not enough memory for extra record");
 
1574
    goto err;
 
1575
  }
 
1576
 
 
1577
  if (!rep_quick)
 
1578
  {
 
1579
    /* Get real path for data file */
 
1580
    if ((new_file=my_raid_create(fn_format(param->temp_filename,
 
1581
                                           share->data_file_name, "",
 
1582
                                           DATA_TMP_EXT, 2+4),
 
1583
                                 0,param->tmpfile_createflag,
 
1584
                                 share->base.raid_type,
 
1585
                                 share->base.raid_chunks,
 
1586
                                 share->base.raid_chunksize,
 
1587
                                 MYF(0))) < 0)
 
1588
    {
 
1589
      mi_check_print_error(param,"Can't create new tempfile: '%s'",
 
1590
                           param->temp_filename);
 
1591
      goto err;
 
1592
    }
 
1593
    if (new_header_length &&
 
1594
        filecopy(param,new_file,info->dfile,0L,new_header_length,
 
1595
                 "datafile-header"))
 
1596
      goto err;
 
1597
    info->s->state.dellink= HA_OFFSET_ERROR;
 
1598
    info->rec_cache.file=new_file;
 
1599
    if (param->testflag & T_UNPACK)
 
1600
    {
 
1601
      share->options&= ~HA_OPTION_COMPRESS_RECORD;
 
1602
      mi_int2store(share->state.header.options,share->options);
 
1603
    }
 
1604
  }
 
1605
  sort_info.info=info;
 
1606
  sort_info.param = param;
 
1607
  sort_param.read_cache=param->read_cache;
 
1608
  sort_param.pos=sort_param.max_pos=share->pack.header_length;
 
1609
  sort_param.filepos=new_header_length;
 
1610
  param->read_cache.end_of_file=sort_info.filelength=
 
1611
    my_seek(info->dfile,0L,MY_SEEK_END,MYF(0));
 
1612
  sort_info.dupp=0;
 
1613
  sort_param.fix_datafile= (my_bool) (! rep_quick);
 
1614
  sort_param.master=1;
 
1615
  sort_info.max_records= ~(ha_rows) 0;
 
1616
 
 
1617
  set_data_file_type(&sort_info, share);
 
1618
  del=info->state->del;
 
1619
  info->state->records=info->state->del=share->state.split=0;
 
1620
  info->state->empty=0;
 
1621
  param->glob_crc=0;
 
1622
  if (param->testflag & T_CALC_CHECKSUM)
 
1623
    sort_param.calc_checksum= 1;
 
1624
 
 
1625
  info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
 
1626
 
 
1627
  /* This function always recreates all enabled indexes. */
 
1628
  if (param->testflag & T_CREATE_MISSING_KEYS)
 
1629
    mi_set_all_keys_active(share->state.key_map, share->base.keys);
 
1630
  mi_drop_all_indexes(param, info, TRUE);
 
1631
 
 
1632
  lock_memory(param);                   /* Everything is alloced */
 
1633
 
 
1634
  /* Re-create all keys, which are set in key_map. */
 
1635
  while (!(error=sort_get_next_record(&sort_param)))
 
1636
  {
 
1637
    if (writekeys(&sort_param))
 
1638
    {
 
1639
      if (my_errno != HA_ERR_FOUND_DUPP_KEY)
 
1640
        goto err;
 
1641
      DBUG_DUMP("record",(uchar*) sort_param.record,share->base.pack_reclength);
 
1642
      mi_check_print_info(param,"Duplicate key %2d for record at %10s against new record at %10s",
 
1643
                          info->errkey+1,
 
1644
                          llstr(sort_param.start_recpos,llbuff),
 
1645
                          llstr(info->dupp_key_pos,llbuff2));
 
1646
      if (param->testflag & T_VERBOSE)
 
1647
      {
 
1648
        VOID(_mi_make_key(info,(uint) info->errkey,info->lastkey,
 
1649
                          sort_param.record,0L));
 
1650
        _mi_print_key(stdout,share->keyinfo[info->errkey].seg,info->lastkey,
 
1651
                      USE_WHOLE_KEY);
 
1652
      }
 
1653
      sort_info.dupp++;
 
1654
      if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
 
1655
      {
 
1656
        param->testflag|=T_RETRY_WITHOUT_QUICK;
 
1657
        param->error_printed=1;
 
1658
        goto err;
 
1659
      }
 
1660
      continue;
 
1661
    }
 
1662
    if (sort_write_record(&sort_param))
 
1663
      goto err;
 
1664
  }
 
1665
  if (error > 0 || write_data_suffix(&sort_info, (my_bool)!rep_quick) ||
 
1666
      flush_io_cache(&info->rec_cache) || param->read_cache.error < 0)
 
1667
    goto err;
 
1668
 
 
1669
  if (param->testflag & T_WRITE_LOOP)
 
1670
  {
 
1671
    VOID(fputs("          \r",stdout)); VOID(fflush(stdout));
 
1672
  }
 
1673
  if (my_chsize(share->kfile,info->state->key_file_length,0,MYF(0)))
 
1674
  {
 
1675
    mi_check_print_warning(param,
 
1676
                           "Can't change size of indexfile, error: %d",
 
1677
                           my_errno);
 
1678
    goto err;
 
1679
  }
 
1680
 
 
1681
  if (rep_quick && del+sort_info.dupp != info->state->del)
 
1682
  {
 
1683
    mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
 
1684
    mi_check_print_error(param,"Run recovery again without -q");
 
1685
    got_error=1;
 
1686
    param->retry_repair=1;
 
1687
    param->testflag|=T_RETRY_WITHOUT_QUICK;
 
1688
    goto err;
 
1689
  }
 
1690
  if (param->testflag & T_SAFE_REPAIR)
 
1691
  {
 
1692
    /* Don't repair if we loosed more than one row */
 
1693
    if (info->state->records+1 < start_records)
 
1694
    {
 
1695
      info->state->records=start_records;
 
1696
      got_error=1;
 
1697
      goto err;
 
1698
    }
 
1699
  }
 
1700
 
 
1701
  if (!rep_quick)
 
1702
  {
 
1703
    my_close(info->dfile,MYF(0));
 
1704
    info->dfile=new_file;
 
1705
    info->state->data_file_length=sort_param.filepos;
 
1706
    share->state.version=(ulong) time((time_t*) 0);     /* Force reopen */
 
1707
  }
 
1708
  else
 
1709
  {
 
1710
    info->state->data_file_length=sort_param.max_pos;
 
1711
  }
 
1712
  if (param->testflag & T_CALC_CHECKSUM)
 
1713
    info->state->checksum=param->glob_crc;
 
1714
 
 
1715
  if (!(param->testflag & T_SILENT))
 
1716
  {
 
1717
    if (start_records != info->state->records)
 
1718
      printf("Data records: %s\n", llstr(info->state->records,llbuff));
 
1719
    if (sort_info.dupp)
 
1720
      mi_check_print_warning(param,
 
1721
                             "%s records have been removed",
 
1722
                             llstr(sort_info.dupp,llbuff));
 
1723
  }
 
1724
 
 
1725
  got_error=0;
 
1726
  /* If invoked by external program that uses thr_lock */
 
1727
  if (&share->state.state != info->state)
 
1728
    memcpy( &share->state.state, info->state, sizeof(*info->state));
 
1729
 
 
1730
err:
 
1731
  if (!got_error)
 
1732
  {
 
1733
    /* Replace the actual file with the temporary file */
 
1734
    if (new_file >= 0)
 
1735
    {
 
1736
      my_close(new_file,MYF(0));
 
1737
      info->dfile=new_file= -1;
 
1738
      if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
 
1739
                            DATA_TMP_EXT, share->base.raid_chunks,
 
1740
                            (param->testflag & T_BACKUP_DATA ?
 
1741
                             MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
 
1742
          mi_open_datafile(info,share,name,-1))
 
1743
        got_error=1;
 
1744
    }
 
1745
  }
 
1746
  if (got_error)
 
1747
  {
 
1748
    if (! param->error_printed)
 
1749
      mi_check_print_error(param,"%d for record at pos %s",my_errno,
 
1750
                  llstr(sort_param.start_recpos,llbuff));
 
1751
    if (new_file >= 0)
 
1752
    {
 
1753
      VOID(my_close(new_file,MYF(0)));
 
1754
      VOID(my_raid_delete(param->temp_filename,info->s->base.raid_chunks,
 
1755
                          MYF(MY_WME)));
 
1756
      info->rec_cache.file=-1; /* don't flush data to new_file, it's closed */
 
1757
    }
 
1758
    mi_mark_crashed_on_repair(info);
 
1759
  }
 
1760
  my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff),
 
1761
                            MYF(MY_ALLOW_ZERO_PTR));
 
1762
  my_free(mi_get_rec_buff_ptr(info, sort_param.record),
 
1763
          MYF(MY_ALLOW_ZERO_PTR));
 
1764
  my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
 
1765
  VOID(end_io_cache(&param->read_cache));
 
1766
  info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
 
1767
  VOID(end_io_cache(&info->rec_cache));
 
1768
  got_error|=flush_blocks(param, share->key_cache, share->kfile);
 
1769
  if (!got_error && param->testflag & T_UNPACK)
 
1770
  {
 
1771
    share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
 
1772
    share->pack.header_length=0;
 
1773
    share->data_file_type=sort_info.new_data_file_type;
 
1774
  }
 
1775
  share->state.changed|= (STATE_NOT_OPTIMIZED_KEYS | STATE_NOT_SORTED_PAGES |
 
1776
                          STATE_NOT_ANALYZED);
 
1777
  DBUG_RETURN(got_error);
 
1778
}
 
1779
 
 
1780
 
 
1781
/* Uppate keyfile when doing repair */
 
1782
 
 
1783
static int writekeys(MI_SORT_PARAM *sort_param)
 
1784
{
 
1785
  register uint i;
 
1786
  uchar    *key;
 
1787
  MI_INFO  *info=   sort_param->sort_info->info;
 
1788
  uchar    *buff=   sort_param->record;
 
1789
  my_off_t filepos= sort_param->filepos;
 
1790
  DBUG_ENTER("writekeys");
 
1791
 
 
1792
  key=info->lastkey+info->s->base.max_key_length;
 
1793
  for (i=0 ; i < info->s->base.keys ; i++)
 
1794
  {
 
1795
    if (mi_is_key_active(info->s->state.key_map, i))
 
1796
    {
 
1797
      if (info->s->keyinfo[i].flag & HA_FULLTEXT )
 
1798
      {
 
1799
        if (_mi_ft_add(info, i, key, buff, filepos))
 
1800
          goto err;
 
1801
      }
 
1802
#ifdef HAVE_SPATIAL
 
1803
      else if (info->s->keyinfo[i].flag & HA_SPATIAL)
 
1804
      {
 
1805
        uint key_length=_mi_make_key(info,i,key,buff,filepos);
 
1806
        if (rtree_insert(info, i, key, key_length))
 
1807
          goto err;
 
1808
      }
 
1809
#endif /*HAVE_SPATIAL*/
 
1810
      else
 
1811
      {
 
1812
        uint key_length=_mi_make_key(info,i,key,buff,filepos);
 
1813
        if (_mi_ck_write(info,i,key,key_length))
 
1814
          goto err;
 
1815
      }
 
1816
    }
 
1817
  }
 
1818
  DBUG_RETURN(0);
 
1819
 
 
1820
 err:
 
1821
  if (my_errno == HA_ERR_FOUND_DUPP_KEY)
 
1822
  {
 
1823
    info->errkey=(int) i;                       /* This key was found */
 
1824
    while ( i-- > 0 )
 
1825
    {
 
1826
      if (mi_is_key_active(info->s->state.key_map, i))
 
1827
      {
 
1828
        if (info->s->keyinfo[i].flag & HA_FULLTEXT)
 
1829
        {
 
1830
          if (_mi_ft_del(info,i, key,buff,filepos))
 
1831
            break;
 
1832
        }
 
1833
        else
 
1834
        {
 
1835
          uint key_length=_mi_make_key(info,i,key,buff,filepos);
 
1836
          if (_mi_ck_delete(info,i,key,key_length))
 
1837
            break;
 
1838
        }
 
1839
      }
 
1840
    }
 
1841
  }
 
1842
  /* Remove checksum that was added to glob_crc in sort_get_next_record */
 
1843
  if (sort_param->calc_checksum)
 
1844
    sort_param->sort_info->param->glob_crc-= info->checksum;
 
1845
  DBUG_PRINT("error",("errno: %d",my_errno));
 
1846
  DBUG_RETURN(-1);
 
1847
} /* writekeys */
 
1848
 
 
1849
 
 
1850
        /* Change all key-pointers that points to a records */
 
1851
 
 
1852
int movepoint(register MI_INFO *info, uchar *record, my_off_t oldpos,
 
1853
              my_off_t newpos, uint prot_key)
 
1854
{
 
1855
  register uint i;
 
1856
  uchar *key;
 
1857
  uint key_length;
 
1858
  DBUG_ENTER("movepoint");
 
1859
 
 
1860
  key=info->lastkey+info->s->base.max_key_length;
 
1861
  for (i=0 ; i < info->s->base.keys; i++)
 
1862
  {
 
1863
    if (i != prot_key && mi_is_key_active(info->s->state.key_map, i))
 
1864
    {
 
1865
      key_length=_mi_make_key(info,i,key,record,oldpos);
 
1866
      if (info->s->keyinfo[i].flag & HA_NOSAME)
 
1867
      {                                 /* Change pointer direct */
 
1868
        uint nod_flag;
 
1869
        MI_KEYDEF *keyinfo;
 
1870
        keyinfo=info->s->keyinfo+i;
 
1871
        if (_mi_search(info,keyinfo,key,USE_WHOLE_KEY,
 
1872
                       (uint) (SEARCH_SAME | SEARCH_SAVE_BUFF),
 
1873
                       info->s->state.key_root[i]))
 
1874
          DBUG_RETURN(-1);
 
1875
        nod_flag=mi_test_if_nod(info->buff);
 
1876
        _mi_dpointer(info,info->int_keypos-nod_flag-
 
1877
                     info->s->rec_reflength,newpos);
 
1878
        if (_mi_write_keypage(info,keyinfo,info->last_keypage,
 
1879
                              DFLT_INIT_HITS,info->buff))
 
1880
          DBUG_RETURN(-1);
 
1881
      }
 
1882
      else
 
1883
      {                                 /* Change old key to new */
 
1884
        if (_mi_ck_delete(info,i,key,key_length))
 
1885
          DBUG_RETURN(-1);
 
1886
        key_length=_mi_make_key(info,i,key,record,newpos);
 
1887
        if (_mi_ck_write(info,i,key,key_length))
 
1888
          DBUG_RETURN(-1);
 
1889
      }
 
1890
    }
 
1891
  }
 
1892
  DBUG_RETURN(0);
 
1893
} /* movepoint */
 
1894
 
 
1895
 
 
1896
        /* Tell system that we want all memory for our cache */
 
1897
 
 
1898
void lock_memory(MI_CHECK *param __attribute__((unused)))
 
1899
{
 
1900
#ifdef SUN_OS                           /* Key-cacheing thrases on sun 4.1 */
 
1901
  if (param->opt_lock_memory)
 
1902
  {
 
1903
    int success = mlockall(MCL_CURRENT);        /* or plock(DATLOCK); */
 
1904
    if (geteuid() == 0 && success != 0)
 
1905
      mi_check_print_warning(param,
 
1906
                             "Failed to lock memory. errno %d",my_errno);
 
1907
  }
 
1908
#endif
 
1909
} /* lock_memory */
 
1910
 
 
1911
 
 
1912
        /* Flush all changed blocks to disk */
 
1913
 
 
1914
int flush_blocks(MI_CHECK *param, KEY_CACHE *key_cache, File file)
 
1915
{
 
1916
  if (flush_key_blocks(key_cache, file, FLUSH_RELEASE))
 
1917
  {
 
1918
    mi_check_print_error(param,"%d when trying to write bufferts",my_errno);
 
1919
    return(1);
 
1920
  }
 
1921
  if (!param->using_global_keycache)
 
1922
    end_key_cache(key_cache,1);
 
1923
  return 0;
 
1924
} /* flush_blocks */
 
1925
 
 
1926
 
 
1927
        /* Sort index for more efficent reads */
 
1928
 
 
1929
int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name)
 
1930
{
 
1931
  reg2 uint key;
 
1932
  reg1 MI_KEYDEF *keyinfo;
 
1933
  File new_file;
 
1934
  my_off_t index_pos[HA_MAX_POSSIBLE_KEY];
 
1935
  uint r_locks,w_locks;
 
1936
  int old_lock;
 
1937
  MYISAM_SHARE *share=info->s;
 
1938
  MI_STATE_INFO old_state;
 
1939
  DBUG_ENTER("mi_sort_index");
 
1940
 
 
1941
  /* cannot sort index files with R-tree indexes */
 
1942
  for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
 
1943
       key++,keyinfo++)
 
1944
    if (keyinfo->key_alg == HA_KEY_ALG_RTREE)
 
1945
      DBUG_RETURN(0);
 
1946
 
 
1947
  if (!(param->testflag & T_SILENT))
 
1948
    printf("- Sorting index for MyISAM-table '%s'\n",name);
 
1949
 
 
1950
  /* Get real path for index file */
 
1951
  fn_format(param->temp_filename,name,"", MI_NAME_IEXT,2+4+32);
 
1952
  if ((new_file=my_create(fn_format(param->temp_filename,param->temp_filename,
 
1953
                                    "", INDEX_TMP_EXT,2+4),
 
1954
                          0,param->tmpfile_createflag,MYF(0))) <= 0)
 
1955
  {
 
1956
    mi_check_print_error(param,"Can't create new tempfile: '%s'",
 
1957
                         param->temp_filename);
 
1958
    DBUG_RETURN(-1);
 
1959
  }
 
1960
  if (filecopy(param, new_file,share->kfile,0L,
 
1961
               (ulong) share->base.keystart, "headerblock"))
 
1962
    goto err;
 
1963
 
 
1964
  param->new_file_pos=share->base.keystart;
 
1965
  for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
 
1966
       key++,keyinfo++)
 
1967
  {
 
1968
    if (! mi_is_key_active(info->s->state.key_map, key))
 
1969
      continue;
 
1970
 
 
1971
    if (share->state.key_root[key] != HA_OFFSET_ERROR)
 
1972
    {
 
1973
      index_pos[key]=param->new_file_pos;       /* Write first block here */
 
1974
      if (sort_one_index(param,info,keyinfo,share->state.key_root[key],
 
1975
                         new_file))
 
1976
        goto err;
 
1977
    }
 
1978
    else
 
1979
      index_pos[key]= HA_OFFSET_ERROR;          /* No blocks */
 
1980
  }
 
1981
 
 
1982
  /* Flush key cache for this file if we are calling this outside myisamchk */
 
1983
  flush_key_blocks(share->key_cache,share->kfile, FLUSH_IGNORE_CHANGED);
 
1984
 
 
1985
  share->state.version=(ulong) time((time_t*) 0);
 
1986
  old_state= share->state;                      /* save state if not stored */
 
1987
  r_locks=   share->r_locks;
 
1988
  w_locks=   share->w_locks;
 
1989
  old_lock=  info->lock_type;
 
1990
 
 
1991
        /* Put same locks as old file */
 
1992
  share->r_locks= share->w_locks= share->tot_locks= 0;
 
1993
  (void) _mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE);
 
1994
  VOID(my_close(share->kfile,MYF(MY_WME)));
 
1995
  share->kfile = -1;
 
1996
  VOID(my_close(new_file,MYF(MY_WME)));
 
1997
  if (change_to_newfile(share->index_file_name,MI_NAME_IEXT,INDEX_TMP_EXT,0,
 
1998
                        MYF(0)) ||
 
1999
      mi_open_keyfile(share))
 
2000
    goto err2;
 
2001
  info->lock_type= F_UNLCK;                     /* Force mi_readinfo to lock */
 
2002
  _mi_readinfo(info,F_WRLCK,0);                 /* Will lock the table */
 
2003
  info->lock_type=  old_lock;
 
2004
  share->r_locks=   r_locks;
 
2005
  share->w_locks=   w_locks;
 
2006
  share->tot_locks= r_locks+w_locks;
 
2007
  share->state=     old_state;                  /* Restore old state */
 
2008
 
 
2009
  info->state->key_file_length=param->new_file_pos;
 
2010
  info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
 
2011
  for (key=0 ; key < info->s->base.keys ; key++)
 
2012
    info->s->state.key_root[key]=index_pos[key];
 
2013
  for (key=0 ; key < info->s->state.header.max_block_size_index ; key++)
 
2014
    info->s->state.key_del[key]=  HA_OFFSET_ERROR;
 
2015
 
 
2016
  info->s->state.changed&= ~STATE_NOT_SORTED_PAGES;
 
2017
  DBUG_RETURN(0);
 
2018
 
 
2019
err:
 
2020
  VOID(my_close(new_file,MYF(MY_WME)));
 
2021
err2:
 
2022
  VOID(my_delete(param->temp_filename,MYF(MY_WME)));
 
2023
  DBUG_RETURN(-1);
 
2024
} /* mi_sort_index */
 
2025
 
 
2026
 
 
2027
         /* Sort records recursive using one index */
 
2028
 
 
2029
static int sort_one_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
 
2030
                          my_off_t pagepos, File new_file)
 
2031
{
 
2032
  uint length,nod_flag,used_length, key_length;
 
2033
  uchar *buff,*keypos,*endpos;
 
2034
  uchar key[HA_MAX_POSSIBLE_KEY_BUFF];
 
2035
  my_off_t new_page_pos,next_page;
 
2036
  char llbuff[22];
 
2037
  DBUG_ENTER("sort_one_index");
 
2038
 
 
2039
  /* cannot walk over R-tree indices */
 
2040
  DBUG_ASSERT(keyinfo->key_alg != HA_KEY_ALG_RTREE);
 
2041
  new_page_pos=param->new_file_pos;
 
2042
  param->new_file_pos+=keyinfo->block_length;
 
2043
 
 
2044
  if (!(buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
 
2045
  {
 
2046
    mi_check_print_error(param,"Not enough memory for key block");
 
2047
    DBUG_RETURN(-1);
 
2048
  }
 
2049
  if (!_mi_fetch_keypage(info,keyinfo,pagepos,DFLT_INIT_HITS,buff,0))
 
2050
  {
 
2051
    mi_check_print_error(param,"Can't read key block from filepos: %s",
 
2052
                llstr(pagepos,llbuff));
 
2053
    goto err;
 
2054
  }
 
2055
  if ((nod_flag=mi_test_if_nod(buff)) || keyinfo->flag & HA_FULLTEXT)
 
2056
  {
 
2057
    used_length=mi_getint(buff);
 
2058
    keypos=buff+2+nod_flag;
 
2059
    endpos=buff+used_length;
 
2060
    for ( ;; )
 
2061
    {
 
2062
      if (nod_flag)
 
2063
      {
 
2064
        next_page=_mi_kpos(nod_flag,keypos);
 
2065
        _mi_kpointer(info,keypos-nod_flag,param->new_file_pos); /* Save new pos */
 
2066
        if (sort_one_index(param,info,keyinfo,next_page,new_file))
 
2067
        {
 
2068
          DBUG_PRINT("error",
 
2069
                     ("From page: %ld, keyoffset: %lu  used_length: %d",
 
2070
                      (ulong) pagepos, (ulong) (keypos - buff),
 
2071
                      (int) used_length));
 
2072
          DBUG_DUMP("buff",(uchar*) buff,used_length);
 
2073
          goto err;
 
2074
        }
 
2075
      }
 
2076
      if (keypos >= endpos ||
 
2077
          (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0)
 
2078
        break;
 
2079
      DBUG_ASSERT(keypos <= endpos);
 
2080
      if (keyinfo->flag & HA_FULLTEXT)
 
2081
      {
 
2082
        uint off;
 
2083
        int  subkeys;
 
2084
        get_key_full_length_rdonly(off, key);
 
2085
        subkeys=ft_sintXkorr(key+off);
 
2086
        if (subkeys < 0)
 
2087
        {
 
2088
          next_page= _mi_dpos(info,0,key+key_length);
 
2089
          _mi_dpointer(info,keypos-nod_flag-info->s->rec_reflength,
 
2090
                       param->new_file_pos); /* Save new pos */
 
2091
          if (sort_one_index(param,info,&info->s->ft2_keyinfo,
 
2092
                             next_page,new_file))
 
2093
            goto err;
 
2094
        }
 
2095
      }
 
2096
    }
 
2097
  }
 
2098
 
 
2099
  /* Fill block with zero and write it to the new index file */
 
2100
  length=mi_getint(buff);
 
2101
  bzero((uchar*) buff+length,keyinfo->block_length-length);
 
2102
  if (my_pwrite(new_file,(uchar*) buff,(uint) keyinfo->block_length,
 
2103
                new_page_pos,MYF(MY_NABP | MY_WAIT_IF_FULL)))
 
2104
  {
 
2105
    mi_check_print_error(param,"Can't write indexblock, error: %d",my_errno);
 
2106
    goto err;
 
2107
  }
 
2108
  my_afree((uchar*) buff);
 
2109
  DBUG_RETURN(0);
 
2110
err:
 
2111
  my_afree((uchar*) buff);
 
2112
  DBUG_RETURN(1);
 
2113
} /* sort_one_index */
 
2114
 
 
2115
 
 
2116
        /*
 
2117
          Let temporary file replace old file.
 
2118
          This assumes that the new file was created in the same
 
2119
          directory as given by realpath(filename).
 
2120
          This will ensure that any symlinks that are used will still work.
 
2121
          Copy stats from old file to new file, deletes orignal and
 
2122
          changes new file name to old file name
 
2123
        */
 
2124
 
 
2125
int change_to_newfile(const char * filename, const char * old_ext,
 
2126
                      const char * new_ext,
 
2127
                      uint raid_chunks __attribute__((unused)),
 
2128
                      myf MyFlags)
 
2129
{
 
2130
  char old_filename[FN_REFLEN],new_filename[FN_REFLEN];
 
2131
#ifdef USE_RAID
 
2132
  if (raid_chunks)
 
2133
    return my_raid_redel(fn_format(old_filename,filename,"",old_ext,2+4),
 
2134
                         fn_format(new_filename,filename,"",new_ext,2+4),
 
2135
                         raid_chunks,
 
2136
                         MYF(MY_WME | MY_LINK_WARNING | MyFlags));
 
2137
#endif
 
2138
  /* Get real path to filename */
 
2139
  (void) fn_format(old_filename,filename,"",old_ext,2+4+32);
 
2140
  return my_redel(old_filename,
 
2141
                  fn_format(new_filename,old_filename,"",new_ext,2+4),
 
2142
                  MYF(MY_WME | MY_LINK_WARNING | MyFlags));
 
2143
} /* change_to_newfile */
 
2144
 
 
2145
 
 
2146
        /* Locks a whole file */
 
2147
        /* Gives an error-message if file can't be locked */
 
2148
 
 
2149
int lock_file(MI_CHECK *param, File file, my_off_t start, int lock_type,
 
2150
              const char *filetype, const char *filename)
 
2151
{
 
2152
  if (my_lock(file,lock_type,start,F_TO_EOF,
 
2153
              param->testflag & T_WAIT_FOREVER ? MYF(MY_SEEK_NOT_DONE) :
 
2154
              MYF(MY_SEEK_NOT_DONE |  MY_DONT_WAIT)))
 
2155
  {
 
2156
    mi_check_print_error(param," %d when locking %s '%s'",my_errno,filetype,filename);
 
2157
    param->error_printed=2;             /* Don't give that data is crashed */
 
2158
    return 1;
 
2159
  }
 
2160
  return 0;
 
2161
} /* lock_file */
 
2162
 
 
2163
 
 
2164
        /* Copy a block between two files */
 
2165
 
 
2166
int filecopy(MI_CHECK *param, File to,File from,my_off_t start,
 
2167
             my_off_t length, const char *type)
 
2168
{
 
2169
  char tmp_buff[IO_SIZE],*buff;
 
2170
  ulong buff_length;
 
2171
  DBUG_ENTER("filecopy");
 
2172
 
 
2173
  buff_length=(ulong) min(param->write_buffer_length,length);
 
2174
  if (!(buff=my_malloc(buff_length,MYF(0))))
 
2175
  {
 
2176
    buff=tmp_buff; buff_length=IO_SIZE;
 
2177
  }
 
2178
 
 
2179
  VOID(my_seek(from,start,MY_SEEK_SET,MYF(0)));
 
2180
  while (length > buff_length)
 
2181
  {
 
2182
    if (my_read(from,(uchar*) buff,buff_length,MYF(MY_NABP)) ||
 
2183
        my_write(to,(uchar*) buff,buff_length,param->myf_rw))
 
2184
      goto err;
 
2185
    length-= buff_length;
 
2186
  }
 
2187
  if (my_read(from,(uchar*) buff,(uint) length,MYF(MY_NABP)) ||
 
2188
      my_write(to,(uchar*) buff,(uint) length,param->myf_rw))
 
2189
    goto err;
 
2190
  if (buff != tmp_buff)
 
2191
    my_free(buff,MYF(0));
 
2192
  DBUG_RETURN(0);
 
2193
err:
 
2194
  if (buff != tmp_buff)
 
2195
    my_free(buff,MYF(0));
 
2196
  mi_check_print_error(param,"Can't copy %s to tempfile, error %d",
 
2197
                       type,my_errno);
 
2198
  DBUG_RETURN(1);
 
2199
}
 
2200
 
 
2201
 
 
2202
/*
 
2203
  Repair table or given index using sorting
 
2204
 
 
2205
  SYNOPSIS
 
2206
    mi_repair_by_sort()
 
2207
    param               Repair parameters
 
2208
    info                MyISAM handler to repair
 
2209
    name                Name of table (for warnings)
 
2210
    rep_quick           set to <> 0 if we should not change data file
 
2211
 
 
2212
  RESULT
 
2213
    0   ok
 
2214
    <>0 Error
 
2215
*/
 
2216
 
 
2217
int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
 
2218
                      const char * name, int rep_quick)
 
2219
{
 
2220
  int got_error;
 
2221
  uint i;
 
2222
  ulong length;
 
2223
  ha_rows start_records;
 
2224
  my_off_t new_header_length,del;
 
2225
  File new_file;
 
2226
  MI_SORT_PARAM sort_param;
 
2227
  MYISAM_SHARE *share=info->s;
 
2228
  HA_KEYSEG *keyseg;
 
2229
  ulong   *rec_per_key_part;
 
2230
  char llbuff[22];
 
2231
  SORT_INFO sort_info;
 
2232
  ulonglong UNINIT_VAR(key_map);
 
2233
  DBUG_ENTER("mi_repair_by_sort");
 
2234
 
 
2235
  start_records=info->state->records;
 
2236
  got_error=1;
 
2237
  new_file= -1;
 
2238
  new_header_length=(param->testflag & T_UNPACK) ? 0 :
 
2239
    share->pack.header_length;
 
2240
  if (!(param->testflag & T_SILENT))
 
2241
  {
 
2242
    printf("- recovering (with sort) MyISAM-table '%s'\n",name);
 
2243
    printf("Data records: %s\n", llstr(start_records,llbuff));
 
2244
  }
 
2245
  param->testflag|=T_REP; /* for easy checking */
 
2246
 
 
2247
  if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
 
2248
    param->testflag|=T_CALC_CHECKSUM;
 
2249
 
 
2250
  bzero((char*)&sort_info,sizeof(sort_info));
 
2251
  bzero((char *)&sort_param, sizeof(sort_param));
 
2252
  if (!(sort_info.key_block=
 
2253
        alloc_key_blocks(param,
 
2254
                         (uint) param->sort_key_blocks,
 
2255
                         share->base.max_key_block_length))
 
2256
      || init_io_cache(&param->read_cache,info->dfile,
 
2257
                       (uint) param->read_buffer_length,
 
2258
                       READ_CACHE,share->pack.header_length,1,MYF(MY_WME)) ||
 
2259
      (! rep_quick &&
 
2260
       init_io_cache(&info->rec_cache,info->dfile,
 
2261
                     (uint) param->write_buffer_length,
 
2262
                     WRITE_CACHE,new_header_length,1,
 
2263
                     MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw)))
 
2264
    goto err;
 
2265
  sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
 
2266
  info->opt_flag|=WRITE_CACHE_USED;
 
2267
  info->rec_cache.file=info->dfile;             /* for sort_delete_record */
 
2268
 
 
2269
  if (!mi_alloc_rec_buff(info, -1, &sort_param.record) ||
 
2270
      !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff))
 
2271
  {
 
2272
    mi_check_print_error(param, "Not enough memory for extra record");
 
2273
    goto err;
 
2274
  }
 
2275
  if (!rep_quick)
 
2276
  {
 
2277
    /* Get real path for data file */
 
2278
    if ((new_file=my_raid_create(fn_format(param->temp_filename,
 
2279
                                           share->data_file_name, "",
 
2280
                                           DATA_TMP_EXT, 2+4),
 
2281
                                 0,param->tmpfile_createflag,
 
2282
                                 share->base.raid_type,
 
2283
                                 share->base.raid_chunks,
 
2284
                                 share->base.raid_chunksize,
 
2285
                                 MYF(0))) < 0)
 
2286
    {
 
2287
      mi_check_print_error(param,"Can't create new tempfile: '%s'",
 
2288
                           param->temp_filename);
 
2289
      goto err;
 
2290
    }
 
2291
    if (new_header_length &&
 
2292
        filecopy(param, new_file,info->dfile,0L,new_header_length,
 
2293
                 "datafile-header"))
 
2294
      goto err;
 
2295
    if (param->testflag & T_UNPACK)
 
2296
    {
 
2297
      share->options&= ~HA_OPTION_COMPRESS_RECORD;
 
2298
      mi_int2store(share->state.header.options,share->options);
 
2299
    }
 
2300
    share->state.dellink= HA_OFFSET_ERROR;
 
2301
    info->rec_cache.file=new_file;
 
2302
  }
 
2303
 
 
2304
  info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
 
2305
 
 
2306
  /* Optionally drop indexes and optionally modify the key_map. */
 
2307
  mi_drop_all_indexes(param, info, FALSE);
 
2308
  key_map= share->state.key_map;
 
2309
  if (param->testflag & T_CREATE_MISSING_KEYS)
 
2310
  {
 
2311
    /* Invert the copied key_map to recreate all disabled indexes. */
 
2312
    key_map= ~key_map;
 
2313
  }
 
2314
 
 
2315
  sort_info.info=info;
 
2316
  sort_info.param = param;
 
2317
 
 
2318
  set_data_file_type(&sort_info, share);
 
2319
  sort_param.filepos=new_header_length;
 
2320
  sort_info.dupp=0;
 
2321
  sort_info.buff=0;
 
2322
  param->read_cache.end_of_file=sort_info.filelength=
 
2323
    my_seek(param->read_cache.file,0L,MY_SEEK_END,MYF(0));
 
2324
 
 
2325
  sort_param.wordlist=NULL;
 
2326
  init_alloc_root(&sort_param.wordroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0);
 
2327
 
 
2328
  if (share->data_file_type == DYNAMIC_RECORD)
 
2329
    length=max(share->base.min_pack_length+1,share->base.min_block_length);
 
2330
  else if (share->data_file_type == COMPRESSED_RECORD)
 
2331
    length=share->base.min_block_length;
 
2332
  else
 
2333
    length=share->base.pack_reclength;
 
2334
  sort_info.max_records=
 
2335
    ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records :
 
2336
     (ha_rows) (sort_info.filelength/length+1));
 
2337
  sort_param.key_cmp=sort_key_cmp;
 
2338
  sort_param.lock_in_memory=lock_memory;
 
2339
  sort_param.tmpdir=param->tmpdir;
 
2340
  sort_param.sort_info=&sort_info;
 
2341
  sort_param.fix_datafile= (my_bool) (! rep_quick);
 
2342
  sort_param.master =1;
 
2343
  
 
2344
  del=info->state->del;
 
2345
  param->glob_crc=0;
 
2346
  if (param->testflag & T_CALC_CHECKSUM)
 
2347
    sort_param.calc_checksum= 1;
 
2348
 
 
2349
  rec_per_key_part= param->rec_per_key_part;
 
2350
  for (sort_param.key=0 ; sort_param.key < share->base.keys ;
 
2351
       rec_per_key_part+=sort_param.keyinfo->keysegs, sort_param.key++)
 
2352
  {
 
2353
    sort_param.read_cache=param->read_cache;
 
2354
    sort_param.keyinfo=share->keyinfo+sort_param.key;
 
2355
    sort_param.seg=sort_param.keyinfo->seg;
 
2356
    /*
 
2357
      Skip this index if it is marked disabled in the copied
 
2358
      (and possibly inverted) key_map.
 
2359
    */
 
2360
    if (! mi_is_key_active(key_map, sort_param.key))
 
2361
    {
 
2362
      /* Remember old statistics for key */
 
2363
      memcpy((char*) rec_per_key_part,
 
2364
             (char*) (share->state.rec_per_key_part +
 
2365
                      (uint) (rec_per_key_part - param->rec_per_key_part)),
 
2366
             sort_param.keyinfo->keysegs*sizeof(*rec_per_key_part));
 
2367
      DBUG_PRINT("repair", ("skipping seemingly disabled index #: %u",
 
2368
                            sort_param.key));
 
2369
      continue;
 
2370
    }
 
2371
 
 
2372
    if ((!(param->testflag & T_SILENT)))
 
2373
      printf ("- Fixing index %d\n",sort_param.key+1);
 
2374
    sort_param.max_pos=sort_param.pos=share->pack.header_length;
 
2375
    keyseg=sort_param.seg;
 
2376
    bzero((char*) sort_param.unique,sizeof(sort_param.unique));
 
2377
    sort_param.key_length=share->rec_reflength;
 
2378
    for (i=0 ; keyseg[i].type != HA_KEYTYPE_END; i++)
 
2379
    {
 
2380
      sort_param.key_length+=keyseg[i].length;
 
2381
      if (keyseg[i].flag & HA_SPACE_PACK)
 
2382
        sort_param.key_length+=get_pack_length(keyseg[i].length);
 
2383
      if (keyseg[i].flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
 
2384
        sort_param.key_length+=2 + test(keyseg[i].length >= 127);
 
2385
      if (keyseg[i].flag & HA_NULL_PART)
 
2386
        sort_param.key_length++;
 
2387
    }
 
2388
    info->state->records=info->state->del=share->state.split=0;
 
2389
    info->state->empty=0;
 
2390
 
 
2391
    if (sort_param.keyinfo->flag & HA_FULLTEXT)
 
2392
    {
 
2393
      uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT*
 
2394
                                    sort_param.keyinfo->seg->charset->mbmaxlen;
 
2395
      sort_param.key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
 
2396
      /*
 
2397
        fulltext indexes may have much more entries than the
 
2398
        number of rows in the table. We estimate the number here.
 
2399
 
 
2400
        Note, built-in parser is always nr. 0 - see ftparser_call_initializer()
 
2401
      */
 
2402
      if (sort_param.keyinfo->ftkey_nr == 0)
 
2403
      {
 
2404
        /*
 
2405
          for built-in parser the number of generated index entries
 
2406
          cannot be larger than the size of the data file divided
 
2407
          by the minimal word's length
 
2408
        */
 
2409
        sort_info.max_records=
 
2410
          (ha_rows) (sort_info.filelength/ft_min_word_len+1);
 
2411
      }
 
2412
      else
 
2413
      {
 
2414
        /*
 
2415
          for external plugin parser we cannot tell anything at all :(
 
2416
          so, we'll use all the sort memory and start from ~10 buffpeks.
 
2417
          (see _create_index_by_sort)
 
2418
        */
 
2419
        sort_info.max_records=
 
2420
          10*param->sort_buffer_length/sort_param.key_length;
 
2421
      }
 
2422
 
 
2423
      sort_param.key_read=sort_ft_key_read;
 
2424
      sort_param.key_write=sort_ft_key_write;
 
2425
    }
 
2426
    else
 
2427
    {
 
2428
      sort_param.key_read=sort_key_read;
 
2429
      sort_param.key_write=sort_key_write;
 
2430
    }
 
2431
 
 
2432
    if (_create_index_by_sort(&sort_param,
 
2433
                              (my_bool) (!(param->testflag & T_VERBOSE)),
 
2434
                              (uint) param->sort_buffer_length))
 
2435
    {
 
2436
      param->retry_repair=1;
 
2437
      goto err;
 
2438
    }
 
2439
    /* No need to calculate checksum again. */
 
2440
    sort_param.calc_checksum= 0;
 
2441
    free_root(&sort_param.wordroot, MYF(0));
 
2442
 
 
2443
    /* Set for next loop */
 
2444
    sort_info.max_records= (ha_rows) info->state->records;
 
2445
 
 
2446
    if (param->testflag & T_STATISTICS)
 
2447
      update_key_parts(sort_param.keyinfo, rec_per_key_part, sort_param.unique,
 
2448
                       param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
 
2449
                       sort_param.notnull: NULL,
 
2450
                       (ulonglong) info->state->records);
 
2451
    /* Enable this index in the permanent (not the copied) key_map. */
 
2452
    mi_set_key_active(share->state.key_map, sort_param.key);
 
2453
    DBUG_PRINT("repair", ("set enabled index #: %u", sort_param.key));
 
2454
 
 
2455
    if (sort_param.fix_datafile)
 
2456
    {
 
2457
      param->read_cache.end_of_file=sort_param.filepos;
 
2458
      if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache))
 
2459
        goto err;
 
2460
      if (param->testflag & T_SAFE_REPAIR)
 
2461
      {
 
2462
        /* Don't repair if we loosed more than one row */
 
2463
        if (info->state->records+1 < start_records)
 
2464
        {
 
2465
          info->state->records=start_records;
 
2466
          goto err;
 
2467
        }
 
2468
      }
 
2469
      share->state.state.data_file_length = info->state->data_file_length=
 
2470
        sort_param.filepos;
 
2471
      /* Only whole records */
 
2472
      share->state.version=(ulong) time((time_t*) 0);
 
2473
      my_close(info->dfile,MYF(0));
 
2474
      info->dfile=new_file;
 
2475
      share->data_file_type=sort_info.new_data_file_type;
 
2476
      share->pack.header_length=(ulong) new_header_length;
 
2477
      sort_param.fix_datafile=0;
 
2478
    }
 
2479
    else
 
2480
      info->state->data_file_length=sort_param.max_pos;
 
2481
 
 
2482
    param->read_cache.file=info->dfile;         /* re-init read cache */
 
2483
    reinit_io_cache(&param->read_cache,READ_CACHE,share->pack.header_length,
 
2484
                    1,1);
 
2485
  }
 
2486
 
 
2487
  if (param->testflag & T_WRITE_LOOP)
 
2488
  {
 
2489
    VOID(fputs("          \r",stdout)); VOID(fflush(stdout));
 
2490
  }
 
2491
 
 
2492
  if (rep_quick && del+sort_info.dupp != info->state->del)
 
2493
  {
 
2494
    mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
 
2495
    mi_check_print_error(param,"Run recovery again without -q");
 
2496
    got_error=1;
 
2497
    param->retry_repair=1;
 
2498
    param->testflag|=T_RETRY_WITHOUT_QUICK;
 
2499
    goto err;
 
2500
  }
 
2501
 
 
2502
  if (rep_quick & T_FORCE_UNIQUENESS)
 
2503
  {
 
2504
    my_off_t skr=info->state->data_file_length+
 
2505
      (share->options & HA_OPTION_COMPRESS_RECORD ?
 
2506
       MEMMAP_EXTRA_MARGIN : 0);
 
2507
#ifdef USE_RELOC
 
2508
    if (share->data_file_type == STATIC_RECORD &&
 
2509
        skr < share->base.reloc*share->base.min_pack_length)
 
2510
      skr=share->base.reloc*share->base.min_pack_length;
 
2511
#endif
 
2512
    if (skr != sort_info.filelength && !info->s->base.raid_type)
 
2513
      if (my_chsize(info->dfile,skr,0,MYF(0)))
 
2514
        mi_check_print_warning(param,
 
2515
                               "Can't change size of datafile,  error: %d",
 
2516
                               my_errno);
 
2517
  }
 
2518
  if (param->testflag & T_CALC_CHECKSUM)
 
2519
    info->state->checksum=param->glob_crc;
 
2520
 
 
2521
  if (my_chsize(share->kfile,info->state->key_file_length,0,MYF(0)))
 
2522
    mi_check_print_warning(param,
 
2523
                           "Can't change size of indexfile, error: %d",
 
2524
                           my_errno);
 
2525
 
 
2526
  if (!(param->testflag & T_SILENT))
 
2527
  {
 
2528
    if (start_records != info->state->records)
 
2529
      printf("Data records: %s\n", llstr(info->state->records,llbuff));
 
2530
    if (sort_info.dupp)
 
2531
      mi_check_print_warning(param,
 
2532
                             "%s records have been removed",
 
2533
                             llstr(sort_info.dupp,llbuff));
 
2534
  }
 
2535
  got_error=0;
 
2536
 
 
2537
  if (&share->state.state != info->state)
 
2538
    memcpy( &share->state.state, info->state, sizeof(*info->state));
 
2539
 
 
2540
err:
 
2541
  got_error|= flush_blocks(param, share->key_cache, share->kfile);
 
2542
  VOID(end_io_cache(&info->rec_cache));
 
2543
  if (!got_error)
 
2544
  {
 
2545
    /* Replace the actual file with the temporary file */
 
2546
    if (new_file >= 0)
 
2547
    {
 
2548
      my_close(new_file,MYF(0));
 
2549
      info->dfile=new_file= -1;
 
2550
      if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
 
2551
                            DATA_TMP_EXT, share->base.raid_chunks,
 
2552
                            (param->testflag & T_BACKUP_DATA ?
 
2553
                             MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
 
2554
          mi_open_datafile(info,share,name,-1))
 
2555
        got_error=1;
 
2556
    }
 
2557
  }
 
2558
  if (got_error)
 
2559
  {
 
2560
    if (! param->error_printed)
 
2561
      mi_check_print_error(param,"%d when fixing table",my_errno);
 
2562
    if (new_file >= 0)
 
2563
    {
 
2564
      VOID(my_close(new_file,MYF(0)));
 
2565
      VOID(my_raid_delete(param->temp_filename,share->base.raid_chunks,
 
2566
                          MYF(MY_WME)));
 
2567
      if (info->dfile == new_file) /* Retry with key cache */
 
2568
        if (unlikely(mi_open_datafile(info, share, name, -1)))
 
2569
          param->retry_repair= 0; /* Safety */
 
2570
    }
 
2571
    mi_mark_crashed_on_repair(info);
 
2572
  }
 
2573
  else if (key_map == share->state.key_map)
 
2574
    share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
 
2575
  share->state.changed|=STATE_NOT_SORTED_PAGES;
 
2576
 
 
2577
  my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff),
 
2578
                            MYF(MY_ALLOW_ZERO_PTR));
 
2579
  my_free(mi_get_rec_buff_ptr(info, sort_param.record),
 
2580
          MYF(MY_ALLOW_ZERO_PTR));
 
2581
  my_free((uchar*) sort_info.key_block,MYF(MY_ALLOW_ZERO_PTR));
 
2582
  my_free((uchar*) sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR));
 
2583
  my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
 
2584
  VOID(end_io_cache(&param->read_cache));
 
2585
  info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
 
2586
  if (!got_error && (param->testflag & T_UNPACK))
 
2587
  {
 
2588
    share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
 
2589
    share->pack.header_length=0;
 
2590
  }
 
2591
  DBUG_RETURN(got_error);
 
2592
}
 
2593
 
 
2594
/*
 
2595
  Threaded repair of table using sorting
 
2596
 
 
2597
  SYNOPSIS
 
2598
    mi_repair_parallel()
 
2599
    param               Repair parameters
 
2600
    info                MyISAM handler to repair
 
2601
    name                Name of table (for warnings)
 
2602
    rep_quick           set to <> 0 if we should not change data file
 
2603
 
 
2604
  DESCRIPTION
 
2605
    Same as mi_repair_by_sort but do it multithreaded
 
2606
    Each key is handled by a separate thread.
 
2607
    TODO: make a number of threads a parameter
 
2608
 
 
2609
    In parallel repair we use one thread per index. There are two modes:
 
2610
 
 
2611
    Quick
 
2612
 
 
2613
      Only the indexes are rebuilt. All threads share a read buffer.
 
2614
      Every thread that needs fresh data in the buffer enters the shared
 
2615
      cache lock. The last thread joining the lock reads the buffer from
 
2616
      the data file and wakes all other threads.
 
2617
 
 
2618
    Non-quick
 
2619
 
 
2620
      The data file is rebuilt and all indexes are rebuilt to point to
 
2621
      the new record positions. One thread is the master thread. It
 
2622
      reads from the old data file and writes to the new data file. It
 
2623
      also creates one of the indexes. The other threads read from a
 
2624
      buffer which is filled by the master. If they need fresh data,
 
2625
      they enter the shared cache lock. If the masters write buffer is
 
2626
      full, it flushes it to the new data file and enters the shared
 
2627
      cache lock too. When all threads joined in the lock, the master
 
2628
      copies its write buffer to the read buffer for the other threads
 
2629
      and wakes them.
 
2630
 
 
2631
  RESULT
 
2632
    0   ok
 
2633
    <>0 Error
 
2634
*/
 
2635
 
 
2636
int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
 
2637
                        const char * name, int rep_quick)
 
2638
{
 
2639
#ifndef THREAD
 
2640
  return mi_repair_by_sort(param, info, name, rep_quick);
 
2641
#else
 
2642
  int got_error;
 
2643
  uint i,key, total_key_length, istep;
 
2644
  ulong rec_length;
 
2645
  ha_rows start_records;
 
2646
  my_off_t new_header_length,del;
 
2647
  File new_file;
 
2648
  MI_SORT_PARAM *sort_param=0;
 
2649
  MYISAM_SHARE *share=info->s;
 
2650
  ulong   *rec_per_key_part;
 
2651
  HA_KEYSEG *keyseg;
 
2652
  char llbuff[22];
 
2653
  IO_CACHE new_data_cache; /* For non-quick repair. */
 
2654
  IO_CACHE_SHARE io_share;
 
2655
  SORT_INFO sort_info;
 
2656
  ulonglong UNINIT_VAR(key_map);
 
2657
  pthread_attr_t thr_attr;
 
2658
  ulong max_pack_reclength;
 
2659
  DBUG_ENTER("mi_repair_parallel");
 
2660
 
 
2661
  start_records=info->state->records;
 
2662
  got_error=1;
 
2663
  new_file= -1;
 
2664
  new_header_length=(param->testflag & T_UNPACK) ? 0 :
 
2665
    share->pack.header_length;
 
2666
  if (!(param->testflag & T_SILENT))
 
2667
  {
 
2668
    printf("- parallel recovering (with sort) MyISAM-table '%s'\n",name);
 
2669
    printf("Data records: %s\n", llstr(start_records,llbuff));
 
2670
  }
 
2671
  param->testflag|=T_REP; /* for easy checking */
 
2672
 
 
2673
  if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
 
2674
    param->testflag|=T_CALC_CHECKSUM;
 
2675
 
 
2676
  /*
 
2677
    Quick repair (not touching data file, rebuilding indexes):
 
2678
    {
 
2679
      Read  cache is (MI_CHECK *param)->read_cache using info->dfile.
 
2680
    }
 
2681
 
 
2682
    Non-quick repair (rebuilding data file and indexes):
 
2683
    {
 
2684
      Master thread:
 
2685
 
 
2686
        Read  cache is (MI_CHECK *param)->read_cache using info->dfile.
 
2687
        Write cache is (MI_INFO   *info)->rec_cache  using new_file.
 
2688
 
 
2689
      Slave threads:
 
2690
 
 
2691
        Read  cache is new_data_cache synced to master rec_cache.
 
2692
 
 
2693
      The final assignment of the filedescriptor for rec_cache is done
 
2694
      after the cache creation.
 
2695
 
 
2696
      Don't check file size on new_data_cache, as the resulting file size
 
2697
      is not known yet.
 
2698
 
 
2699
      As rec_cache and new_data_cache are synced, write_buffer_length is
 
2700
      used for the read cache 'new_data_cache'. Both start at the same
 
2701
      position 'new_header_length'.
 
2702
    }
 
2703
  */
 
2704
  DBUG_PRINT("info", ("is quick repair: %d", rep_quick));
 
2705
  bzero((char*)&sort_info,sizeof(sort_info));
 
2706
  /* Initialize pthread structures before goto err. */
 
2707
  pthread_mutex_init(&sort_info.mutex, MY_MUTEX_INIT_FAST);
 
2708
  pthread_cond_init(&sort_info.cond, 0);
 
2709
  pthread_mutex_init(&param->print_msg_mutex, MY_MUTEX_INIT_FAST);
 
2710
  param->need_print_msg_lock= 1;
 
2711
 
 
2712
  if (!(sort_info.key_block=
 
2713
        alloc_key_blocks(param, (uint) param->sort_key_blocks,
 
2714
                         share->base.max_key_block_length)) ||
 
2715
      init_io_cache(&param->read_cache, info->dfile,
 
2716
                    (uint) param->read_buffer_length,
 
2717
                    READ_CACHE, share->pack.header_length, 1, MYF(MY_WME)) ||
 
2718
      (!rep_quick &&
 
2719
       (init_io_cache(&info->rec_cache, info->dfile,
 
2720
                      (uint) param->write_buffer_length,
 
2721
                      WRITE_CACHE, new_header_length, 1,
 
2722
                      MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw) ||
 
2723
        init_io_cache(&new_data_cache, -1,
 
2724
                      (uint) param->write_buffer_length,
 
2725
                      READ_CACHE, new_header_length, 1,
 
2726
                      MYF(MY_WME | MY_DONT_CHECK_FILESIZE)))))
 
2727
    goto err;
 
2728
  sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
 
2729
  info->opt_flag|=WRITE_CACHE_USED;
 
2730
  info->rec_cache.file=info->dfile;         /* for sort_delete_record */
 
2731
 
 
2732
  if (!rep_quick)
 
2733
  {
 
2734
    /* Get real path for data file */
 
2735
    if ((new_file=my_raid_create(fn_format(param->temp_filename,
 
2736
                                           share->data_file_name, "",
 
2737
                                           DATA_TMP_EXT,
 
2738
                                           2+4),
 
2739
                                 0,param->tmpfile_createflag,
 
2740
                                 share->base.raid_type,
 
2741
                                 share->base.raid_chunks,
 
2742
                                 share->base.raid_chunksize,
 
2743
                                 MYF(0))) < 0)
 
2744
    {
 
2745
      mi_check_print_error(param,"Can't create new tempfile: '%s'",
 
2746
                           param->temp_filename);
 
2747
      goto err;
 
2748
    }
 
2749
    if (new_header_length &&
 
2750
        filecopy(param, new_file,info->dfile,0L,new_header_length,
 
2751
                 "datafile-header"))
 
2752
      goto err;
 
2753
    if (param->testflag & T_UNPACK)
 
2754
    {
 
2755
      share->options&= ~HA_OPTION_COMPRESS_RECORD;
 
2756
      mi_int2store(share->state.header.options,share->options);
 
2757
    }
 
2758
    share->state.dellink= HA_OFFSET_ERROR;
 
2759
    info->rec_cache.file=new_file;
 
2760
  }
 
2761
 
 
2762
  info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
 
2763
 
 
2764
  /* Optionally drop indexes and optionally modify the key_map. */
 
2765
  mi_drop_all_indexes(param, info, FALSE);
 
2766
  key_map= share->state.key_map;
 
2767
  if (param->testflag & T_CREATE_MISSING_KEYS)
 
2768
  {
 
2769
    /* Invert the copied key_map to recreate all disabled indexes. */
 
2770
    key_map= ~key_map;
 
2771
  }
 
2772
 
 
2773
  sort_info.info=info;
 
2774
  sort_info.param = param;
 
2775
 
 
2776
  set_data_file_type(&sort_info, share);
 
2777
  sort_info.dupp=0;
 
2778
  sort_info.buff=0;
 
2779
  param->read_cache.end_of_file=sort_info.filelength=
 
2780
    my_seek(param->read_cache.file,0L,MY_SEEK_END,MYF(0));
 
2781
 
 
2782
  if (share->data_file_type == DYNAMIC_RECORD)
 
2783
    rec_length=max(share->base.min_pack_length+1,share->base.min_block_length);
 
2784
  else if (share->data_file_type == COMPRESSED_RECORD)
 
2785
    rec_length=share->base.min_block_length;
 
2786
  else
 
2787
    rec_length=share->base.pack_reclength;
 
2788
  /*
 
2789
    +1 below is required hack for parallel repair mode.
 
2790
    The info->state->records value, that is compared later
 
2791
    to sort_info.max_records and cannot exceed it, is
 
2792
    increased in sort_key_write. In mi_repair_by_sort, sort_key_write
 
2793
    is called after sort_key_read, where the comparison is performed,
 
2794
    but in parallel mode master thread can call sort_key_write
 
2795
    before some other repair thread calls sort_key_read.
 
2796
    Furthermore I'm not even sure +1 would be enough.
 
2797
    May be sort_info.max_records shold be always set to max value in
 
2798
    parallel mode.
 
2799
  */
 
2800
  sort_info.max_records=
 
2801
    ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records + 1:
 
2802
     (ha_rows) (sort_info.filelength/rec_length+1));
 
2803
 
 
2804
  del=info->state->del;
 
2805
  param->glob_crc=0;
 
2806
  /* for compressed tables */
 
2807
  max_pack_reclength= share->base.pack_reclength;
 
2808
  if (share->options & HA_OPTION_COMPRESS_RECORD)
 
2809
    set_if_bigger(max_pack_reclength, share->max_pack_length);
 
2810
  if (!(sort_param=(MI_SORT_PARAM *)
 
2811
        my_malloc((uint) share->base.keys *
 
2812
                  (sizeof(MI_SORT_PARAM) + max_pack_reclength),
 
2813
                  MYF(MY_ZEROFILL))))
 
2814
  {
 
2815
    mi_check_print_error(param,"Not enough memory for key!");
 
2816
    goto err;
 
2817
  }
 
2818
  total_key_length=0;
 
2819
  rec_per_key_part= param->rec_per_key_part;
 
2820
  info->state->records=info->state->del=share->state.split=0;
 
2821
  info->state->empty=0;
 
2822
 
 
2823
  for (i=key=0, istep=1 ; key < share->base.keys ;
 
2824
       rec_per_key_part+=sort_param[i].keyinfo->keysegs, i+=istep, key++)
 
2825
  {
 
2826
    sort_param[i].key=key;
 
2827
    sort_param[i].keyinfo=share->keyinfo+key;
 
2828
    sort_param[i].seg=sort_param[i].keyinfo->seg;
 
2829
    /*
 
2830
      Skip this index if it is marked disabled in the copied
 
2831
      (and possibly inverted) key_map.
 
2832
    */
 
2833
    if (! mi_is_key_active(key_map, key))
 
2834
    {
 
2835
      /* Remember old statistics for key */
 
2836
      memcpy((char*) rec_per_key_part,
 
2837
             (char*) (share->state.rec_per_key_part+
 
2838
                      (uint) (rec_per_key_part - param->rec_per_key_part)),
 
2839
             sort_param[i].keyinfo->keysegs*sizeof(*rec_per_key_part));
 
2840
      istep=0;
 
2841
      continue;
 
2842
    }
 
2843
    istep=1;
 
2844
    if ((!(param->testflag & T_SILENT)))
 
2845
      printf ("- Fixing index %d\n",key+1);
 
2846
    if (sort_param[i].keyinfo->flag & HA_FULLTEXT)
 
2847
    {
 
2848
      sort_param[i].key_read=sort_ft_key_read;
 
2849
      sort_param[i].key_write=sort_ft_key_write;
 
2850
    }
 
2851
    else
 
2852
    {
 
2853
      sort_param[i].key_read=sort_key_read;
 
2854
      sort_param[i].key_write=sort_key_write;
 
2855
    }
 
2856
    sort_param[i].key_cmp=sort_key_cmp;
 
2857
    sort_param[i].lock_in_memory=lock_memory;
 
2858
    sort_param[i].tmpdir=param->tmpdir;
 
2859
    sort_param[i].sort_info=&sort_info;
 
2860
    sort_param[i].master=0;
 
2861
    sort_param[i].fix_datafile=0;
 
2862
    sort_param[i].calc_checksum= 0;
 
2863
 
 
2864
    sort_param[i].filepos=new_header_length;
 
2865
    sort_param[i].max_pos=sort_param[i].pos=share->pack.header_length;
 
2866
 
 
2867
    sort_param[i].record= (((uchar *)(sort_param+share->base.keys))+
 
2868
                           (max_pack_reclength * i));
 
2869
    if (!mi_alloc_rec_buff(info, -1, &sort_param[i].rec_buff))
 
2870
    {
 
2871
      mi_check_print_error(param,"Not enough memory!");
 
2872
      goto err;
 
2873
    }
 
2874
 
 
2875
    sort_param[i].key_length=share->rec_reflength;
 
2876
    for (keyseg=sort_param[i].seg; keyseg->type != HA_KEYTYPE_END;
 
2877
         keyseg++)
 
2878
    {
 
2879
      sort_param[i].key_length+=keyseg->length;
 
2880
      if (keyseg->flag & HA_SPACE_PACK)
 
2881
        sort_param[i].key_length+=get_pack_length(keyseg->length);
 
2882
      if (keyseg->flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
 
2883
        sort_param[i].key_length+=2 + test(keyseg->length >= 127);
 
2884
      if (keyseg->flag & HA_NULL_PART)
 
2885
        sort_param[i].key_length++;
 
2886
    }
 
2887
    total_key_length+=sort_param[i].key_length;
 
2888
 
 
2889
    if (sort_param[i].keyinfo->flag & HA_FULLTEXT)
 
2890
    {
 
2891
      uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT*
 
2892
                                    sort_param[i].keyinfo->seg->charset->mbmaxlen;
 
2893
      sort_param[i].key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
 
2894
      init_alloc_root(&sort_param[i].wordroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0);
 
2895
    }
 
2896
  }
 
2897
  sort_info.total_keys=i;
 
2898
  sort_param[0].master= 1;
 
2899
  sort_param[0].fix_datafile= (my_bool)(! rep_quick);
 
2900
  sort_param[0].calc_checksum= test(param->testflag & T_CALC_CHECKSUM);
 
2901
 
 
2902
  if (!ftparser_alloc_param(info))
 
2903
    goto err;
 
2904
 
 
2905
  sort_info.got_error=0;
 
2906
  pthread_mutex_lock(&sort_info.mutex);
 
2907
 
 
2908
  /*
 
2909
    Initialize the I/O cache share for use with the read caches and, in
 
2910
    case of non-quick repair, the write cache. When all threads join on
 
2911
    the cache lock, the writer copies the write cache contents to the
 
2912
    read caches.
 
2913
  */
 
2914
  if (i > 1)
 
2915
  {
 
2916
    if (rep_quick)
 
2917
      init_io_cache_share(&param->read_cache, &io_share, NULL, i);
 
2918
    else
 
2919
      init_io_cache_share(&new_data_cache, &io_share, &info->rec_cache, i);
 
2920
  }
 
2921
  else
 
2922
    io_share.total_threads= 0; /* share not used */
 
2923
 
 
2924
  (void) pthread_attr_init(&thr_attr);
 
2925
  (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
 
2926
 
 
2927
  for (i=0 ; i < sort_info.total_keys ; i++)
 
2928
  {
 
2929
    /*
 
2930
      Copy the properly initialized IO_CACHE structure so that every
 
2931
      thread has its own copy. In quick mode param->read_cache is shared
 
2932
      for use by all threads. In non-quick mode all threads but the
 
2933
      first copy the shared new_data_cache, which is synchronized to the
 
2934
      write cache of the first thread. The first thread copies
 
2935
      param->read_cache, which is not shared.
 
2936
    */
 
2937
    sort_param[i].read_cache= ((rep_quick || !i) ? param->read_cache :
 
2938
                               new_data_cache);
 
2939
    DBUG_PRINT("io_cache_share", ("thread: %u  read_cache: 0x%lx",
 
2940
                                  i, (long) &sort_param[i].read_cache));
 
2941
 
 
2942
    /*
 
2943
      two approaches: the same amount of memory for each thread
 
2944
      or the memory for the same number of keys for each thread...
 
2945
      In the second one all the threads will fill their sort_buffers
 
2946
      (and call write_keys) at the same time, putting more stress on i/o.
 
2947
    */
 
2948
    sort_param[i].sortbuff_size=
 
2949
#ifndef USING_SECOND_APPROACH
 
2950
      param->sort_buffer_length/sort_info.total_keys;
 
2951
#else
 
2952
      param->sort_buffer_length*sort_param[i].key_length/total_key_length;
 
2953
#endif
 
2954
    if (pthread_create(&sort_param[i].thr, &thr_attr,
 
2955
                       thr_find_all_keys,
 
2956
                       (void *) (sort_param+i)))
 
2957
    {
 
2958
      mi_check_print_error(param,"Cannot start a repair thread");
 
2959
      /* Cleanup: Detach from the share. Avoid others to be blocked. */
 
2960
      if (io_share.total_threads)
 
2961
        remove_io_thread(&sort_param[i].read_cache);
 
2962
      DBUG_PRINT("error", ("Cannot start a repair thread"));
 
2963
      sort_info.got_error=1;
 
2964
    }
 
2965
    else
 
2966
      sort_info.threads_running++;
 
2967
  }
 
2968
  (void) pthread_attr_destroy(&thr_attr);
 
2969
 
 
2970
  /* waiting for all threads to finish */
 
2971
  while (sort_info.threads_running)
 
2972
    pthread_cond_wait(&sort_info.cond, &sort_info.mutex);
 
2973
  pthread_mutex_unlock(&sort_info.mutex);
 
2974
 
 
2975
  if ((got_error= thr_write_keys(sort_param)))
 
2976
  {
 
2977
    param->retry_repair=1;
 
2978
    goto err;
 
2979
  }
 
2980
  got_error=1;                          /* Assume the following may go wrong */
 
2981
 
 
2982
  if (sort_param[0].fix_datafile)
 
2983
  {
 
2984
    /*
 
2985
      Append some nuls to the end of a memory mapped file. Destroy the
 
2986
      write cache. The master thread did already detach from the share
 
2987
      by remove_io_thread() in sort.c:thr_find_all_keys().
 
2988
    */
 
2989
    if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache))
 
2990
      goto err;
 
2991
    if (param->testflag & T_SAFE_REPAIR)
 
2992
    {
 
2993
      /* Don't repair if we loosed more than one row */
 
2994
      if (info->state->records+1 < start_records)
 
2995
      {
 
2996
        info->state->records=start_records;
 
2997
        goto err;
 
2998
      }
 
2999
    }
 
3000
    share->state.state.data_file_length= info->state->data_file_length=
 
3001
      sort_param->filepos;
 
3002
    /* Only whole records */
 
3003
    share->state.version=(ulong) time((time_t*) 0);
 
3004
 
 
3005
    /*
 
3006
      Exchange the data file descriptor of the table, so that we use the
 
3007
      new file from now on.
 
3008
     */
 
3009
    my_close(info->dfile,MYF(0));
 
3010
    info->dfile=new_file;
 
3011
 
 
3012
    share->data_file_type=sort_info.new_data_file_type;
 
3013
    share->pack.header_length=(ulong) new_header_length;
 
3014
  }
 
3015
  else
 
3016
    info->state->data_file_length=sort_param->max_pos;
 
3017
 
 
3018
  if (rep_quick && del+sort_info.dupp != info->state->del)
 
3019
  {
 
3020
    mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
 
3021
    mi_check_print_error(param,"Run recovery again without -q");
 
3022
    param->retry_repair=1;
 
3023
    param->testflag|=T_RETRY_WITHOUT_QUICK;
 
3024
    goto err;
 
3025
  }
 
3026
 
 
3027
  if (rep_quick & T_FORCE_UNIQUENESS)
 
3028
  {
 
3029
    my_off_t skr=info->state->data_file_length+
 
3030
      (share->options & HA_OPTION_COMPRESS_RECORD ?
 
3031
       MEMMAP_EXTRA_MARGIN : 0);
 
3032
#ifdef USE_RELOC
 
3033
    if (share->data_file_type == STATIC_RECORD &&
 
3034
        skr < share->base.reloc*share->base.min_pack_length)
 
3035
      skr=share->base.reloc*share->base.min_pack_length;
 
3036
#endif
 
3037
    if (skr != sort_info.filelength && !info->s->base.raid_type)
 
3038
      if (my_chsize(info->dfile,skr,0,MYF(0)))
 
3039
        mi_check_print_warning(param,
 
3040
                               "Can't change size of datafile,  error: %d",
 
3041
                               my_errno);
 
3042
  }
 
3043
  if (param->testflag & T_CALC_CHECKSUM)
 
3044
    info->state->checksum=param->glob_crc;
 
3045
 
 
3046
  if (my_chsize(share->kfile,info->state->key_file_length,0,MYF(0)))
 
3047
    mi_check_print_warning(param,
 
3048
                           "Can't change size of indexfile, error: %d", my_errno);
 
3049
 
 
3050
  if (!(param->testflag & T_SILENT))
 
3051
  {
 
3052
    if (start_records != info->state->records)
 
3053
      printf("Data records: %s\n", llstr(info->state->records,llbuff));
 
3054
    if (sort_info.dupp)
 
3055
      mi_check_print_warning(param,
 
3056
                             "%s records have been removed",
 
3057
                             llstr(sort_info.dupp,llbuff));
 
3058
  }
 
3059
  got_error=0;
 
3060
 
 
3061
  if (&share->state.state != info->state)
 
3062
    memcpy(&share->state.state, info->state, sizeof(*info->state));
 
3063
 
 
3064
err:
 
3065
  got_error|= flush_blocks(param, share->key_cache, share->kfile);
 
3066
  /*
 
3067
    Destroy the write cache. The master thread did already detach from
 
3068
    the share by remove_io_thread() or it was not yet started (if the
 
3069
    error happend before creating the thread).
 
3070
  */
 
3071
  VOID(end_io_cache(&info->rec_cache));
 
3072
  /*
 
3073
    Destroy the new data cache in case of non-quick repair. All slave
 
3074
    threads did either detach from the share by remove_io_thread()
 
3075
    already or they were not yet started (if the error happend before
 
3076
    creating the threads).
 
3077
  */
 
3078
  if (!rep_quick)
 
3079
    VOID(end_io_cache(&new_data_cache));
 
3080
  if (!got_error)
 
3081
  {
 
3082
    /* Replace the actual file with the temporary file */
 
3083
    if (new_file >= 0)
 
3084
    {
 
3085
      my_close(new_file,MYF(0));
 
3086
      info->dfile=new_file= -1;
 
3087
      if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
 
3088
                            DATA_TMP_EXT, share->base.raid_chunks,
 
3089
                            (param->testflag & T_BACKUP_DATA ?
 
3090
                             MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
 
3091
          mi_open_datafile(info,share,name,-1))
 
3092
        got_error=1;
 
3093
    }
 
3094
  }
 
3095
  if (got_error)
 
3096
  {
 
3097
    if (! param->error_printed)
 
3098
      mi_check_print_error(param,"%d when fixing table",my_errno);
 
3099
    if (new_file >= 0)
 
3100
    {
 
3101
      VOID(my_close(new_file,MYF(0)));
 
3102
      VOID(my_raid_delete(param->temp_filename,share->base.raid_chunks,
 
3103
                          MYF(MY_WME)));
 
3104
      if (info->dfile == new_file) /* Retry with key cache */
 
3105
        if (unlikely(mi_open_datafile(info, share, name, -1)))
 
3106
          param->retry_repair= 0; /* Safety */
 
3107
    }
 
3108
    mi_mark_crashed_on_repair(info);
 
3109
  }
 
3110
  else if (key_map == share->state.key_map)
 
3111
    share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
 
3112
  share->state.changed|=STATE_NOT_SORTED_PAGES;
 
3113
 
 
3114
  pthread_cond_destroy (&sort_info.cond);
 
3115
  pthread_mutex_destroy(&sort_info.mutex);
 
3116
  pthread_mutex_destroy(&param->print_msg_mutex);
 
3117
  param->need_print_msg_lock= 0;
 
3118
 
 
3119
  my_free((uchar*) sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR));
 
3120
  my_free((uchar*) sort_info.key_block,MYF(MY_ALLOW_ZERO_PTR));
 
3121
  my_free((uchar*) sort_param,MYF(MY_ALLOW_ZERO_PTR));
 
3122
  my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
 
3123
  VOID(end_io_cache(&param->read_cache));
 
3124
  info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
 
3125
  if (!got_error && (param->testflag & T_UNPACK))
 
3126
  {
 
3127
    share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
 
3128
    share->pack.header_length=0;
 
3129
  }
 
3130
  DBUG_RETURN(got_error);
 
3131
#endif /* THREAD */
 
3132
}
 
3133
 
 
3134
        /* Read next record and return next key */
 
3135
 
 
3136
static int sort_key_read(MI_SORT_PARAM *sort_param, void *key)
 
3137
{
 
3138
  int error;
 
3139
  SORT_INFO *sort_info=sort_param->sort_info;
 
3140
  MI_INFO *info=sort_info->info;
 
3141
  DBUG_ENTER("sort_key_read");
 
3142
 
 
3143
  if ((error=sort_get_next_record(sort_param)))
 
3144
    DBUG_RETURN(error);
 
3145
  if (info->state->records == sort_info->max_records)
 
3146
  {
 
3147
    mi_check_print_error(sort_info->param,
 
3148
                         "Key %d - Found too many records; Can't continue",
 
3149
                         sort_param->key+1);
 
3150
    DBUG_RETURN(1);
 
3151
  }
 
3152
  sort_param->real_key_length=
 
3153
    (info->s->rec_reflength+
 
3154
     _mi_make_key(info, sort_param->key, (uchar*) key,
 
3155
                  sort_param->record, sort_param->filepos));
 
3156
#ifdef HAVE_purify
 
3157
  bzero(key+sort_param->real_key_length,
 
3158
        (sort_param->key_length-sort_param->real_key_length));
 
3159
#endif
 
3160
  DBUG_RETURN(sort_write_record(sort_param));
 
3161
} /* sort_key_read */
 
3162
 
 
3163
static int sort_ft_key_read(MI_SORT_PARAM *sort_param, void *key)
 
3164
{
 
3165
  int error;
 
3166
  SORT_INFO *sort_info=sort_param->sort_info;
 
3167
  MI_INFO *info=sort_info->info;
 
3168
  FT_WORD *wptr=0;
 
3169
  DBUG_ENTER("sort_ft_key_read");
 
3170
 
 
3171
  if (!sort_param->wordlist)
 
3172
  {
 
3173
    for (;;)
 
3174
    {
 
3175
      free_root(&sort_param->wordroot, MYF(MY_MARK_BLOCKS_FREE));
 
3176
      if ((error=sort_get_next_record(sort_param)))
 
3177
        DBUG_RETURN(error);
 
3178
      if (!(wptr=_mi_ft_parserecord(info,sort_param->key,sort_param->record,
 
3179
                                    &sort_param->wordroot)))
 
3180
        DBUG_RETURN(1);
 
3181
      if (wptr->pos)
 
3182
        break;
 
3183
      error=sort_write_record(sort_param);
 
3184
    }
 
3185
    sort_param->wordptr=sort_param->wordlist=wptr;
 
3186
  }
 
3187
  else
 
3188
  {
 
3189
    error=0;
 
3190
    wptr=(FT_WORD*)(sort_param->wordptr);
 
3191
  }
 
3192
 
 
3193
  sort_param->real_key_length=(info->s->rec_reflength+
 
3194
                               _ft_make_key(info, sort_param->key,
 
3195
                                            key, wptr++, sort_param->filepos));
 
3196
#ifdef HAVE_purify
 
3197
  if (sort_param->key_length > sort_param->real_key_length)
 
3198
    bzero(key+sort_param->real_key_length,
 
3199
          (sort_param->key_length-sort_param->real_key_length));
 
3200
#endif
 
3201
  if (!wptr->pos)
 
3202
  {
 
3203
    free_root(&sort_param->wordroot, MYF(MY_MARK_BLOCKS_FREE));
 
3204
    sort_param->wordlist=0;
 
3205
    error=sort_write_record(sort_param);
 
3206
  }
 
3207
  else
 
3208
    sort_param->wordptr=(void*)wptr;
 
3209
 
 
3210
  DBUG_RETURN(error);
 
3211
} /* sort_ft_key_read */
 
3212
 
 
3213
 
 
3214
/*
 
3215
  Read next record from file using parameters in sort_info.
 
3216
 
 
3217
  SYNOPSIS
 
3218
    sort_get_next_record()
 
3219
      sort_param                Information about and for the sort process
 
3220
 
 
3221
  NOTE
 
3222
 
 
3223
    Dynamic Records With Non-Quick Parallel Repair
 
3224
 
 
3225
      For non-quick parallel repair we use a synchronized read/write
 
3226
      cache. This means that one thread is the master who fixes the data
 
3227
      file by reading each record from the old data file and writing it
 
3228
      to the new data file. By doing this the records in the new data
 
3229
      file are written contiguously. Whenever the write buffer is full,
 
3230
      it is copied to the read buffer. The slaves read from the read
 
3231
      buffer, which is not associated with a file. Thus read_cache.file
 
3232
      is -1. When using _mi_read_cache(), the slaves must always set
 
3233
      flag to READING_NEXT so that the function never tries to read from
 
3234
      file. This is safe because the records are contiguous. There is no
 
3235
      need to read outside the cache. This condition is evaluated in the
 
3236
      variable 'parallel_flag' for quick reference. read_cache.file must
 
3237
      be >= 0 in every other case.
 
3238
 
 
3239
  RETURN
 
3240
    -1          end of file
 
3241
    0           ok
 
3242
    > 0         error
 
3243
*/
 
3244
 
 
3245
static int sort_get_next_record(MI_SORT_PARAM *sort_param)
 
3246
{
 
3247
  int searching;
 
3248
  int parallel_flag;
 
3249
  uint found_record,b_type,left_length;
 
3250
  my_off_t pos;
 
3251
  uchar *UNINIT_VAR(to);
 
3252
  MI_BLOCK_INFO block_info;
 
3253
  SORT_INFO *sort_info=sort_param->sort_info;
 
3254
  MI_CHECK *param=sort_info->param;
 
3255
  MI_INFO *info=sort_info->info;
 
3256
  MYISAM_SHARE *share=info->s;
 
3257
  char llbuff[22],llbuff2[22];
 
3258
  DBUG_ENTER("sort_get_next_record");
 
3259
 
 
3260
  if (*killed_ptr(param))
 
3261
    DBUG_RETURN(1);
 
3262
 
 
3263
  switch (share->data_file_type) {
 
3264
  case STATIC_RECORD:
 
3265
    for (;;)
 
3266
    {
 
3267
      if (my_b_read(&sort_param->read_cache,sort_param->record,
 
3268
                    share->base.pack_reclength))
 
3269
      {
 
3270
        if (sort_param->read_cache.error)
 
3271
          param->out_flag |= O_DATA_LOST;
 
3272
        param->retry_repair=1;
 
3273
        param->testflag|=T_RETRY_WITHOUT_QUICK;
 
3274
        DBUG_RETURN(-1);
 
3275
      }
 
3276
      sort_param->start_recpos=sort_param->pos;
 
3277
      if (!sort_param->fix_datafile)
 
3278
      {
 
3279
        sort_param->filepos=sort_param->pos;
 
3280
        if (sort_param->master)
 
3281
          share->state.split++;
 
3282
      }
 
3283
      sort_param->max_pos=(sort_param->pos+=share->base.pack_reclength);
 
3284
      if (*sort_param->record)
 
3285
      {
 
3286
        if (sort_param->calc_checksum)
 
3287
          param->glob_crc+= (info->checksum=
 
3288
                             mi_static_checksum(info,sort_param->record));
 
3289
        DBUG_RETURN(0);
 
3290
      }
 
3291
      if (!sort_param->fix_datafile && sort_param->master)
 
3292
      {
 
3293
        info->state->del++;
 
3294
        info->state->empty+=share->base.pack_reclength;
 
3295
      }
 
3296
    }
 
3297
  case DYNAMIC_RECORD:
 
3298
    LINT_INIT(to);
 
3299
    pos=sort_param->pos;
 
3300
    searching=(sort_param->fix_datafile && (param->testflag & T_EXTEND));
 
3301
    parallel_flag= (sort_param->read_cache.file < 0) ? READING_NEXT : 0;
 
3302
    for (;;)
 
3303
    {
 
3304
      found_record=block_info.second_read= 0;
 
3305
      left_length=1;
 
3306
      if (searching)
 
3307
      {
 
3308
        pos=MY_ALIGN(pos,MI_DYN_ALIGN_SIZE);
 
3309
        param->testflag|=T_RETRY_WITHOUT_QUICK;
 
3310
        sort_param->start_recpos=pos;
 
3311
      }
 
3312
      do
 
3313
      {
 
3314
        if (pos > sort_param->max_pos)
 
3315
          sort_param->max_pos=pos;
 
3316
        if (pos & (MI_DYN_ALIGN_SIZE-1))
 
3317
        {
 
3318
          if ((param->testflag & T_VERBOSE) || searching == 0)
 
3319
            mi_check_print_info(param,"Wrong aligned block at %s",
 
3320
                                llstr(pos,llbuff));
 
3321
          if (searching)
 
3322
            goto try_next;
 
3323
        }
 
3324
        if (found_record && pos == param->search_after_block)
 
3325
          mi_check_print_info(param,"Block: %s used by record at %s",
 
3326
                     llstr(param->search_after_block,llbuff),
 
3327
                     llstr(sort_param->start_recpos,llbuff2));
 
3328
        if (_mi_read_cache(&sort_param->read_cache,
 
3329
                           (uchar*) block_info.header,pos,
 
3330
                           MI_BLOCK_INFO_HEADER_LENGTH,
 
3331
                           (! found_record ? READING_NEXT : 0) |
 
3332
                           parallel_flag | READING_HEADER))
 
3333
        {
 
3334
          if (found_record)
 
3335
          {
 
3336
            mi_check_print_info(param,
 
3337
                                "Can't read whole record at %s (errno: %d)",
 
3338
                                llstr(sort_param->start_recpos,llbuff),errno);
 
3339
            goto try_next;
 
3340
          }
 
3341
          DBUG_RETURN(-1);
 
3342
        }
 
3343
        if (searching && ! sort_param->fix_datafile)
 
3344
        {
 
3345
          param->error_printed=1;
 
3346
          param->retry_repair=1;
 
3347
          param->testflag|=T_RETRY_WITHOUT_QUICK;
 
3348
          DBUG_RETURN(1);       /* Something wrong with data */
 
3349
        }
 
3350
        b_type=_mi_get_block_info(&block_info,-1,pos);
 
3351
        if ((b_type & (BLOCK_ERROR | BLOCK_FATAL_ERROR)) ||
 
3352
           ((b_type & BLOCK_FIRST) &&
 
3353
             (block_info.rec_len < (uint) share->base.min_pack_length ||
 
3354
              block_info.rec_len > (uint) share->base.max_pack_length)))
 
3355
        {
 
3356
          uint i;
 
3357
          if (param->testflag & T_VERBOSE || searching == 0)
 
3358
            mi_check_print_info(param,
 
3359
                                "Wrong bytesec: %3d-%3d-%3d at %10s; Skipped",
 
3360
                       block_info.header[0],block_info.header[1],
 
3361
                       block_info.header[2],llstr(pos,llbuff));
 
3362
          if (found_record)
 
3363
            goto try_next;
 
3364
          block_info.second_read=0;
 
3365
          searching=1;
 
3366
          /* Search after block in read header string */
 
3367
          for (i=MI_DYN_ALIGN_SIZE ;
 
3368
               i < MI_BLOCK_INFO_HEADER_LENGTH ;
 
3369
               i+= MI_DYN_ALIGN_SIZE)
 
3370
            if (block_info.header[i] >= 1 &&
 
3371
                block_info.header[i] <= MI_MAX_DYN_HEADER_BYTE)
 
3372
              break;
 
3373
          pos+=(ulong) i;
 
3374
          sort_param->start_recpos=pos;
 
3375
          continue;
 
3376
        }
 
3377
        if (b_type & BLOCK_DELETED)
 
3378
        {
 
3379
          my_bool error=0;
 
3380
          if (block_info.block_len+ (uint) (block_info.filepos-pos) <
 
3381
              share->base.min_block_length)
 
3382
          {
 
3383
            if (!searching)
 
3384
              mi_check_print_info(param,
 
3385
                                  "Deleted block with impossible length %u at %s",
 
3386
                                  block_info.block_len,llstr(pos,llbuff));
 
3387
            error=1;
 
3388
          }
 
3389
          else
 
3390
          {
 
3391
            if ((block_info.next_filepos != HA_OFFSET_ERROR &&
 
3392
                 block_info.next_filepos >=
 
3393
                 info->state->data_file_length) ||
 
3394
                (block_info.prev_filepos != HA_OFFSET_ERROR &&
 
3395
                 block_info.prev_filepos >= info->state->data_file_length))
 
3396
            {
 
3397
              if (!searching)
 
3398
                mi_check_print_info(param,
 
3399
                                    "Delete link points outside datafile at %s",
 
3400
                                    llstr(pos,llbuff));
 
3401
              error=1;
 
3402
            }
 
3403
          }
 
3404
          if (error)
 
3405
          {
 
3406
            if (found_record)
 
3407
              goto try_next;
 
3408
            searching=1;
 
3409
            pos+= MI_DYN_ALIGN_SIZE;
 
3410
            sort_param->start_recpos=pos;
 
3411
            block_info.second_read=0;
 
3412
            continue;
 
3413
          }
 
3414
        }
 
3415
        else
 
3416
        {
 
3417
          if (block_info.block_len+ (uint) (block_info.filepos-pos) <
 
3418
              share->base.min_block_length ||
 
3419
              block_info.block_len > (uint) share->base.max_pack_length+
 
3420
              MI_SPLIT_LENGTH)
 
3421
          {
 
3422
            if (!searching)
 
3423
              mi_check_print_info(param,
 
3424
                                  "Found block with impossible length %u at %s; Skipped",
 
3425
                                  block_info.block_len+ (uint) (block_info.filepos-pos),
 
3426
                                  llstr(pos,llbuff));
 
3427
            if (found_record)
 
3428
              goto try_next;
 
3429
            searching=1;
 
3430
            pos+= MI_DYN_ALIGN_SIZE;
 
3431
            sort_param->start_recpos=pos;
 
3432
            block_info.second_read=0;
 
3433
            continue;
 
3434
          }
 
3435
        }
 
3436
        if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
 
3437
        {
 
3438
          if (!sort_param->fix_datafile && sort_param->master &&
 
3439
              (b_type & BLOCK_DELETED))
 
3440
          {
 
3441
            info->state->empty+=block_info.block_len;
 
3442
            info->state->del++;
 
3443
            share->state.split++;
 
3444
          }
 
3445
          if (found_record)
 
3446
            goto try_next;
 
3447
          if (searching)
 
3448
          {
 
3449
            pos+=MI_DYN_ALIGN_SIZE;
 
3450
            sort_param->start_recpos=pos;
 
3451
          }
 
3452
          else
 
3453
            pos=block_info.filepos+block_info.block_len;
 
3454
          block_info.second_read=0;
 
3455
          continue;
 
3456
        }
 
3457
 
 
3458
        if (!sort_param->fix_datafile && sort_param->master)
 
3459
          share->state.split++;
 
3460
        if (! found_record++)
 
3461
        {
 
3462
          sort_param->find_length=left_length=block_info.rec_len;
 
3463
          sort_param->start_recpos=pos;
 
3464
          if (!sort_param->fix_datafile)
 
3465
            sort_param->filepos=sort_param->start_recpos;
 
3466
          if (sort_param->fix_datafile && (param->testflag & T_EXTEND))
 
3467
            sort_param->pos=block_info.filepos+1;
 
3468
          else
 
3469
            sort_param->pos=block_info.filepos+block_info.block_len;
 
3470
          if (share->base.blobs)
 
3471
          {
 
3472
            if (!(to=mi_alloc_rec_buff(info,block_info.rec_len,
 
3473
                                       &(sort_param->rec_buff))))
 
3474
            {
 
3475
              if (param->max_record_length >= block_info.rec_len)
 
3476
              {
 
3477
                mi_check_print_error(param,"Not enough memory for blob at %s (need %lu)",
 
3478
                                     llstr(sort_param->start_recpos,llbuff),
 
3479
                                     (ulong) block_info.rec_len);
 
3480
                DBUG_RETURN(1);
 
3481
              }
 
3482
              else
 
3483
              {
 
3484
                mi_check_print_info(param,"Not enough memory for blob at %s (need %lu); Row skipped",
 
3485
                                    llstr(sort_param->start_recpos,llbuff),
 
3486
                                    (ulong) block_info.rec_len);
 
3487
                goto try_next;
 
3488
              }
 
3489
            }
 
3490
          }
 
3491
          else
 
3492
            to= sort_param->rec_buff;
 
3493
        }
 
3494
        if (left_length < block_info.data_len || ! block_info.data_len)
 
3495
        {
 
3496
          mi_check_print_info(param,
 
3497
                              "Found block with too small length at %s; Skipped",
 
3498
                              llstr(sort_param->start_recpos,llbuff));
 
3499
          goto try_next;
 
3500
        }
 
3501
        if (block_info.filepos + block_info.data_len >
 
3502
            sort_param->read_cache.end_of_file)
 
3503
        {
 
3504
          mi_check_print_info(param,
 
3505
                              "Found block that points outside data file at %s",
 
3506
                              llstr(sort_param->start_recpos,llbuff));
 
3507
          goto try_next;
 
3508
        }
 
3509
        /*
 
3510
          Copy information that is already read. Avoid accessing data
 
3511
          below the cache start. This could happen if the header
 
3512
          streched over the end of the previous buffer contents.
 
3513
        */
 
3514
        {
 
3515
          uint header_len= (uint) (block_info.filepos - pos);
 
3516
          uint prefetch_len= (MI_BLOCK_INFO_HEADER_LENGTH - header_len);
 
3517
 
 
3518
          if (prefetch_len > block_info.data_len)
 
3519
            prefetch_len= block_info.data_len;
 
3520
          if (prefetch_len)
 
3521
          {
 
3522
            memcpy(to, block_info.header + header_len, prefetch_len);
 
3523
            block_info.filepos+= prefetch_len;
 
3524
            block_info.data_len-= prefetch_len;
 
3525
            left_length-= prefetch_len;
 
3526
            to+= prefetch_len;
 
3527
          }
 
3528
        }
 
3529
        if (block_info.data_len &&
 
3530
            _mi_read_cache(&sort_param->read_cache,to,block_info.filepos,
 
3531
                           block_info.data_len,
 
3532
                           (found_record == 1 ? READING_NEXT : 0) |
 
3533
                           parallel_flag))
 
3534
        {
 
3535
          mi_check_print_info(param,
 
3536
                              "Read error for block at: %s (error: %d); Skipped",
 
3537
                              llstr(block_info.filepos,llbuff),my_errno);
 
3538
          goto try_next;
 
3539
        }
 
3540
        left_length-=block_info.data_len;
 
3541
        to+=block_info.data_len;
 
3542
        pos=block_info.next_filepos;
 
3543
        if (pos == HA_OFFSET_ERROR && left_length)
 
3544
        {
 
3545
          mi_check_print_info(param,"Wrong block with wrong total length starting at %s",
 
3546
                              llstr(sort_param->start_recpos,llbuff));
 
3547
          goto try_next;
 
3548
        }
 
3549
        if (pos + MI_BLOCK_INFO_HEADER_LENGTH > sort_param->read_cache.end_of_file)
 
3550
        {
 
3551
          mi_check_print_info(param,"Found link that points at %s (outside data file) at %s",
 
3552
                              llstr(pos,llbuff2),
 
3553
                              llstr(sort_param->start_recpos,llbuff));
 
3554
          goto try_next;
 
3555
        }
 
3556
      } while (left_length);
 
3557
 
 
3558
      if (_mi_rec_unpack(info,sort_param->record,sort_param->rec_buff,
 
3559
                         sort_param->find_length) != MY_FILE_ERROR)
 
3560
      {
 
3561
        if (sort_param->read_cache.error < 0)
 
3562
          DBUG_RETURN(1);
 
3563
        if (sort_param->calc_checksum)
 
3564
          info->checksum= mi_checksum(info, sort_param->record);
 
3565
        if ((param->testflag & (T_EXTEND | T_REP)) || searching)
 
3566
        {
 
3567
          if (_mi_rec_check(info, sort_param->record, sort_param->rec_buff,
 
3568
                            sort_param->find_length,
 
3569
                            (param->testflag & T_QUICK) &&
 
3570
                            sort_param->calc_checksum &&
 
3571
                            test(info->s->calc_checksum)))
 
3572
          {
 
3573
            mi_check_print_info(param,"Found wrong packed record at %s",
 
3574
                                llstr(sort_param->start_recpos,llbuff));
 
3575
            goto try_next;
 
3576
          }
 
3577
        }
 
3578
        if (sort_param->calc_checksum)
 
3579
          param->glob_crc+= info->checksum;
 
3580
        DBUG_RETURN(0);
 
3581
      }
 
3582
      if (!searching)
 
3583
        mi_check_print_info(param,"Key %d - Found wrong stored record at %s",
 
3584
                            sort_param->key+1,
 
3585
                            llstr(sort_param->start_recpos,llbuff));
 
3586
    try_next:
 
3587
      pos=(sort_param->start_recpos+=MI_DYN_ALIGN_SIZE);
 
3588
      searching=1;
 
3589
    }
 
3590
  case COMPRESSED_RECORD:
 
3591
    for (searching=0 ;; searching=1, sort_param->pos++)
 
3592
    {
 
3593
      if (_mi_read_cache(&sort_param->read_cache,(uchar*) block_info.header,
 
3594
                         sort_param->pos,
 
3595
                         share->pack.ref_length,READING_NEXT))
 
3596
        DBUG_RETURN(-1);
 
3597
      if (searching && ! sort_param->fix_datafile)
 
3598
      {
 
3599
        param->error_printed=1;
 
3600
        param->retry_repair=1;
 
3601
        param->testflag|=T_RETRY_WITHOUT_QUICK;
 
3602
        DBUG_RETURN(1);         /* Something wrong with data */
 
3603
      }
 
3604
      sort_param->start_recpos=sort_param->pos;
 
3605
      if (_mi_pack_get_block_info(info, &sort_param->bit_buff, &block_info,
 
3606
                                  &sort_param->rec_buff, -1, sort_param->pos))
 
3607
        DBUG_RETURN(-1);
 
3608
      if (!block_info.rec_len &&
 
3609
          sort_param->pos + MEMMAP_EXTRA_MARGIN ==
 
3610
          sort_param->read_cache.end_of_file)
 
3611
        DBUG_RETURN(-1);
 
3612
      if (block_info.rec_len < (uint) share->min_pack_length ||
 
3613
          block_info.rec_len > (uint) share->max_pack_length)
 
3614
      {
 
3615
        if (! searching)
 
3616
          mi_check_print_info(param,"Found block with wrong recordlength: %d at %s\n",
 
3617
                              block_info.rec_len,
 
3618
                              llstr(sort_param->pos,llbuff));
 
3619
        continue;
 
3620
      }
 
3621
      if (_mi_read_cache(&sort_param->read_cache,(uchar*) sort_param->rec_buff,
 
3622
                         block_info.filepos, block_info.rec_len,
 
3623
                         READING_NEXT))
 
3624
      {
 
3625
        if (! searching)
 
3626
          mi_check_print_info(param,"Couldn't read whole record from %s",
 
3627
                              llstr(sort_param->pos,llbuff));
 
3628
        continue;
 
3629
      }
 
3630
      if (_mi_pack_rec_unpack(info, &sort_param->bit_buff, sort_param->record,
 
3631
                              sort_param->rec_buff, block_info.rec_len))
 
3632
      {
 
3633
        if (! searching)
 
3634
          mi_check_print_info(param,"Found wrong record at %s",
 
3635
                              llstr(sort_param->pos,llbuff));
 
3636
        continue;
 
3637
      }
 
3638
      if (!sort_param->fix_datafile)
 
3639
      {
 
3640
        sort_param->filepos=sort_param->pos;
 
3641
        if (sort_param->master)
 
3642
          share->state.split++;
 
3643
      }
 
3644
      sort_param->max_pos=(sort_param->pos=block_info.filepos+
 
3645
                         block_info.rec_len);
 
3646
      info->packed_length=block_info.rec_len;
 
3647
      if (sort_param->calc_checksum)
 
3648
        param->glob_crc+= (info->checksum=
 
3649
                           mi_checksum(info, sort_param->record));
 
3650
      DBUG_RETURN(0);
 
3651
    }
 
3652
  case BLOCK_RECORD:
 
3653
    assert(0);                                  /* Impossible */
 
3654
  }
 
3655
  DBUG_RETURN(1);                               /* Impossible */
 
3656
}
 
3657
 
 
3658
 
 
3659
/*
 
3660
  Write record to new file.
 
3661
 
 
3662
  SYNOPSIS
 
3663
    sort_write_record()
 
3664
      sort_param                Sort parameters.
 
3665
 
 
3666
  NOTE
 
3667
    This is only called by a master thread if parallel repair is used.
 
3668
 
 
3669
  RETURN
 
3670
    0           OK
 
3671
    1           Error
 
3672
*/
 
3673
 
 
3674
int sort_write_record(MI_SORT_PARAM *sort_param)
 
3675
{
 
3676
  int flag;
 
3677
  uint length;
 
3678
  ulong block_length,reclength;
 
3679
  uchar *from;
 
3680
  uchar block_buff[8];
 
3681
  SORT_INFO *sort_info=sort_param->sort_info;
 
3682
  MI_CHECK *param=sort_info->param;
 
3683
  MI_INFO *info=sort_info->info;
 
3684
  MYISAM_SHARE *share=info->s;
 
3685
  DBUG_ENTER("sort_write_record");
 
3686
 
 
3687
  if (sort_param->fix_datafile)
 
3688
  {
 
3689
    switch (sort_info->new_data_file_type) {
 
3690
    case STATIC_RECORD:
 
3691
      if (my_b_write(&info->rec_cache,sort_param->record,
 
3692
                     share->base.pack_reclength))
 
3693
      {
 
3694
        mi_check_print_error(param,"%d when writing to datafile",my_errno);
 
3695
        DBUG_RETURN(1);
 
3696
      }
 
3697
      sort_param->filepos+=share->base.pack_reclength;
 
3698
      info->s->state.split++;
 
3699
      /* sort_info->param->glob_crc+=mi_static_checksum(info, sort_param->record); */
 
3700
      break;
 
3701
    case DYNAMIC_RECORD:
 
3702
      if (! info->blobs)
 
3703
        from=sort_param->rec_buff;
 
3704
      else
 
3705
      {
 
3706
        /* must be sure that local buffer is big enough */
 
3707
        reclength=info->s->base.pack_reclength+
 
3708
          _my_calc_total_blob_length(info,sort_param->record)+
 
3709
          ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
 
3710
          MI_DYN_DELETE_BLOCK_HEADER;
 
3711
        if (sort_info->buff_length < reclength)
 
3712
        {
 
3713
          if (!(sort_info->buff=my_realloc(sort_info->buff, (uint) reclength,
 
3714
                                           MYF(MY_FREE_ON_ERROR |
 
3715
                                               MY_ALLOW_ZERO_PTR))))
 
3716
            DBUG_RETURN(1);
 
3717
          sort_info->buff_length=reclength;
 
3718
        }
 
3719
        from= sort_info->buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER);
 
3720
      }
 
3721
      /* We can use info->checksum here as only one thread calls this. */
 
3722
      info->checksum=mi_checksum(info,sort_param->record);
 
3723
      reclength=_mi_rec_pack(info,from,sort_param->record);
 
3724
      flag=0;
 
3725
      /* sort_info->param->glob_crc+=info->checksum; */
 
3726
 
 
3727
      do
 
3728
      {
 
3729
        block_length=reclength+ 3 + test(reclength >= (65520-3));
 
3730
        if (block_length < share->base.min_block_length)
 
3731
          block_length=share->base.min_block_length;
 
3732
        info->update|=HA_STATE_WRITE_AT_END;
 
3733
        block_length=MY_ALIGN(block_length,MI_DYN_ALIGN_SIZE);
 
3734
        if (block_length > MI_MAX_BLOCK_LENGTH)
 
3735
          block_length=MI_MAX_BLOCK_LENGTH;
 
3736
        if (_mi_write_part_record(info,0L,block_length,
 
3737
                                  sort_param->filepos+block_length,
 
3738
                                  &from,&reclength,&flag))
 
3739
        {
 
3740
          mi_check_print_error(param,"%d when writing to datafile",my_errno);
 
3741
          DBUG_RETURN(1);
 
3742
        }
 
3743
        sort_param->filepos+=block_length;
 
3744
        info->s->state.split++;
 
3745
      } while (reclength);
 
3746
      /* sort_info->param->glob_crc+=info->checksum; */
 
3747
      break;
 
3748
    case COMPRESSED_RECORD:
 
3749
      reclength=info->packed_length;
 
3750
      length= save_pack_length((uint) share->pack.version, block_buff,
 
3751
                               reclength);
 
3752
      if (info->s->base.blobs)
 
3753
        length+= save_pack_length((uint) share->pack.version,
 
3754
                                  block_buff + length, info->blob_length);
 
3755
      if (my_b_write(&info->rec_cache,block_buff,length) ||
 
3756
          my_b_write(&info->rec_cache,(uchar*) sort_param->rec_buff,reclength))
 
3757
      {
 
3758
        mi_check_print_error(param,"%d when writing to datafile",my_errno);
 
3759
        DBUG_RETURN(1);
 
3760
      }
 
3761
      /* sort_info->param->glob_crc+=info->checksum; */
 
3762
      sort_param->filepos+=reclength+length;
 
3763
      info->s->state.split++;
 
3764
      break;
 
3765
    case BLOCK_RECORD:
 
3766
      assert(0);                                  /* Impossible */
 
3767
    }
 
3768
  }
 
3769
  if (sort_param->master)
 
3770
  {
 
3771
    info->state->records++;
 
3772
    if ((param->testflag & T_WRITE_LOOP) &&
 
3773
        (info->state->records % WRITE_COUNT) == 0)
 
3774
    {
 
3775
      char llbuff[22];
 
3776
      printf("%s\r", llstr(info->state->records,llbuff));
 
3777
      VOID(fflush(stdout));
 
3778
    }
 
3779
  }
 
3780
  DBUG_RETURN(0);
 
3781
} /* sort_write_record */
 
3782
 
 
3783
 
 
3784
        /* Compare two keys from _create_index_by_sort */
 
3785
 
 
3786
static int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,
 
3787
                        const void *b)
 
3788
{
 
3789
  uint not_used[2];
 
3790
  return (ha_key_cmp(sort_param->seg, *((uchar**) a), *((uchar**) b),
 
3791
                     USE_WHOLE_KEY, SEARCH_SAME, not_used));
 
3792
} /* sort_key_cmp */
 
3793
 
 
3794
 
 
3795
static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a)
 
3796
{
 
3797
  uint diff_pos[2];
 
3798
  char llbuff[22],llbuff2[22];
 
3799
  SORT_INFO *sort_info=sort_param->sort_info;
 
3800
  MI_CHECK *param= sort_info->param;
 
3801
  int cmp;
 
3802
 
 
3803
  if (sort_info->key_block->inited)
 
3804
  {
 
3805
    cmp=ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey,
 
3806
                   (uchar*) a, USE_WHOLE_KEY,SEARCH_FIND | SEARCH_UPDATE,
 
3807
                   diff_pos);
 
3808
    if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL)
 
3809
      ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey,
 
3810
                 (uchar*) a, USE_WHOLE_KEY, 
 
3811
                 SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diff_pos);
 
3812
    else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
 
3813
    {
 
3814
      diff_pos[0]= mi_collect_stats_nonulls_next(sort_param->seg,
 
3815
                                                 sort_param->notnull,
 
3816
                                                 sort_info->key_block->lastkey,
 
3817
                                                 (uchar*)a);
 
3818
    }
 
3819
    sort_param->unique[diff_pos[0]-1]++;
 
3820
  }
 
3821
  else
 
3822
  {
 
3823
    cmp= -1;
 
3824
    if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
 
3825
      mi_collect_stats_nonulls_first(sort_param->seg, sort_param->notnull,
 
3826
                                     (uchar*)a);
 
3827
  }
 
3828
  if ((sort_param->keyinfo->flag & HA_NOSAME) && cmp == 0)
 
3829
  {
 
3830
    sort_info->dupp++;
 
3831
    sort_info->info->lastpos=get_record_for_key(sort_info->info,
 
3832
                                                sort_param->keyinfo,
 
3833
                                                (uchar*) a);
 
3834
    mi_check_print_warning(param,
 
3835
                           "Duplicate key for record at %10s against record at %10s",
 
3836
                           llstr(sort_info->info->lastpos,llbuff),
 
3837
                           llstr(get_record_for_key(sort_info->info,
 
3838
                                                    sort_param->keyinfo,
 
3839
                                                    sort_info->key_block->
 
3840
                                                    lastkey),
 
3841
                                 llbuff2));
 
3842
    param->testflag|=T_RETRY_WITHOUT_QUICK;
 
3843
    if (sort_info->param->testflag & T_VERBOSE)
 
3844
      _mi_print_key(stdout,sort_param->seg,(uchar*) a, USE_WHOLE_KEY);
 
3845
    return (sort_delete_record(sort_param));
 
3846
  }
 
3847
#ifndef DBUG_OFF
 
3848
  if (cmp > 0)
 
3849
  {
 
3850
    mi_check_print_error(param,
 
3851
                         "Internal error: Keys are not in order from sort");
 
3852
    return(1);
 
3853
  }
 
3854
#endif
 
3855
  return (sort_insert_key(sort_param,sort_info->key_block,
 
3856
                          (uchar*) a, HA_OFFSET_ERROR));
 
3857
} /* sort_key_write */
 
3858
 
 
3859
int sort_ft_buf_flush(MI_SORT_PARAM *sort_param)
 
3860
{
 
3861
  SORT_INFO *sort_info=sort_param->sort_info;
 
3862
  SORT_KEY_BLOCKS *key_block=sort_info->key_block;
 
3863
  MYISAM_SHARE *share=sort_info->info->s;
 
3864
  uint val_off, val_len;
 
3865
  int error;
 
3866
  SORT_FT_BUF *ft_buf=sort_info->ft_buf;
 
3867
  uchar *from, *to;
 
3868
 
 
3869
  val_len=share->ft2_keyinfo.keylength;
 
3870
  get_key_full_length_rdonly(val_off, ft_buf->lastkey);
 
3871
  to=ft_buf->lastkey+val_off;
 
3872
 
 
3873
  if (ft_buf->buf)
 
3874
  {
 
3875
    /* flushing first-level tree */
 
3876
    error=sort_insert_key(sort_param,key_block,ft_buf->lastkey,
 
3877
                          HA_OFFSET_ERROR);
 
3878
    for (from=to+val_len;
 
3879
         !error && from < ft_buf->buf;
 
3880
         from+= val_len)
 
3881
    {
 
3882
      memcpy(to, from, val_len);
 
3883
      error=sort_insert_key(sort_param,key_block,ft_buf->lastkey,
 
3884
                            HA_OFFSET_ERROR);
 
3885
    }
 
3886
    return error;
 
3887
  }
 
3888
  /* flushing second-level tree keyblocks */
 
3889
  error=flush_pending_blocks(sort_param);
 
3890
  /* updating lastkey with second-level tree info */
 
3891
  ft_intXstore(ft_buf->lastkey+val_off, -ft_buf->count);
 
3892
  _mi_dpointer(sort_info->info, ft_buf->lastkey+val_off+HA_FT_WLEN,
 
3893
      share->state.key_root[sort_param->key]);
 
3894
  /* restoring first level tree data in sort_info/sort_param */
 
3895
  sort_info->key_block=sort_info->key_block_end- sort_info->param->sort_key_blocks;
 
3896
  sort_param->keyinfo=share->keyinfo+sort_param->key;
 
3897
  share->state.key_root[sort_param->key]=HA_OFFSET_ERROR;
 
3898
  /* writing lastkey in first-level tree */
 
3899
  return error ? error :
 
3900
                 sort_insert_key(sort_param,sort_info->key_block,
 
3901
                                 ft_buf->lastkey,HA_OFFSET_ERROR);
 
3902
}
 
3903
 
 
3904
static int sort_ft_key_write(MI_SORT_PARAM *sort_param, const void *a)
 
3905
{
 
3906
  uint a_len, val_off, val_len, error;
 
3907
  uchar *p;
 
3908
  SORT_INFO *sort_info=sort_param->sort_info;
 
3909
  SORT_FT_BUF *ft_buf=sort_info->ft_buf;
 
3910
  SORT_KEY_BLOCKS *key_block=sort_info->key_block;
 
3911
 
 
3912
  val_len=HA_FT_WLEN+sort_info->info->s->base.rec_reflength;
 
3913
  get_key_full_length_rdonly(a_len, (uchar *)a);
 
3914
 
 
3915
  if (!ft_buf)
 
3916
  {
 
3917
    /*
 
3918
      use two-level tree only if key_reflength fits in rec_reflength place
 
3919
      and row format is NOT static - for _mi_dpointer not to garble offsets
 
3920
     */
 
3921
    if ((sort_info->info->s->base.key_reflength <=
 
3922
         sort_info->info->s->base.rec_reflength) &&
 
3923
        (sort_info->info->s->options &
 
3924
          (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
 
3925
      ft_buf=(SORT_FT_BUF *)my_malloc(sort_param->keyinfo->block_length +
 
3926
                                      sizeof(SORT_FT_BUF), MYF(MY_WME));
 
3927
 
 
3928
    if (!ft_buf)
 
3929
    {
 
3930
      sort_param->key_write=sort_key_write;
 
3931
      return sort_key_write(sort_param, a);
 
3932
    }
 
3933
    sort_info->ft_buf=ft_buf;
 
3934
    goto word_init_ft_buf; /* no need to duplicate the code */
 
3935
  }
 
3936
  get_key_full_length_rdonly(val_off, ft_buf->lastkey);
 
3937
 
 
3938
  if (ha_compare_text(sort_param->seg->charset,
 
3939
                      ((uchar *)a)+1,a_len-1,
 
3940
                      ft_buf->lastkey+1,val_off-1, 0, 0)==0)
 
3941
  {
 
3942
    if (!ft_buf->buf) /* store in second-level tree */
 
3943
    {
 
3944
      ft_buf->count++;
 
3945
      return sort_insert_key(sort_param,key_block,
 
3946
                             ((uchar *)a)+a_len, HA_OFFSET_ERROR);
 
3947
    }
 
3948
 
 
3949
    /* storing the key in the buffer. */
 
3950
    memcpy (ft_buf->buf, (char *)a+a_len, val_len);
 
3951
    ft_buf->buf+=val_len;
 
3952
    if (ft_buf->buf < ft_buf->end)
 
3953
      return 0;
 
3954
 
 
3955
    /* converting to two-level tree */
 
3956
    p=ft_buf->lastkey+val_off;
 
3957
 
 
3958
    while (key_block->inited)
 
3959
      key_block++;
 
3960
    sort_info->key_block=key_block;
 
3961
    sort_param->keyinfo=& sort_info->info->s->ft2_keyinfo;
 
3962
    ft_buf->count=(uint) (ft_buf->buf - p)/val_len;
 
3963
 
 
3964
    /* flushing buffer to second-level tree */
 
3965
    for (error=0; !error && p < ft_buf->buf; p+= val_len)
 
3966
      error=sort_insert_key(sort_param,key_block,p,HA_OFFSET_ERROR);
 
3967
    ft_buf->buf=0;
 
3968
    return error;
 
3969
  }
 
3970
 
 
3971
  /* flushing buffer */
 
3972
  if ((error=sort_ft_buf_flush(sort_param)))
 
3973
    return error;
 
3974
 
 
3975
word_init_ft_buf:
 
3976
  a_len+=val_len;
 
3977
  memcpy(ft_buf->lastkey, a, a_len);
 
3978
  ft_buf->buf=ft_buf->lastkey+a_len;
 
3979
  /*
 
3980
    32 is just a safety margin here
 
3981
    (at least max(val_len, sizeof(nod_flag)) should be there).
 
3982
    May be better performance could be achieved if we'd put
 
3983
      (sort_info->keyinfo->block_length-32)/XXX
 
3984
      instead.
 
3985
        TODO: benchmark the best value for XXX.
 
3986
  */
 
3987
  ft_buf->end=ft_buf->lastkey+ (sort_param->keyinfo->block_length-32);
 
3988
  return 0;
 
3989
} /* sort_ft_key_write */
 
3990
 
 
3991
 
 
3992
        /* get pointer to record from a key */
 
3993
 
 
3994
static my_off_t get_record_for_key(MI_INFO *info, MI_KEYDEF *keyinfo,
 
3995
                                   uchar *key)
 
3996
{
 
3997
  return _mi_dpos(info,0,key+_mi_keylength(keyinfo,key));
 
3998
} /* get_record_for_key */
 
3999
 
 
4000
 
 
4001
        /* Insert a key in sort-key-blocks */
 
4002
 
 
4003
static int sort_insert_key(MI_SORT_PARAM *sort_param,
 
4004
                           register SORT_KEY_BLOCKS *key_block, uchar *key,
 
4005
                           my_off_t prev_block)
 
4006
{
 
4007
  uint a_length,t_length,nod_flag;
 
4008
  my_off_t filepos,key_file_length;
 
4009
  uchar *anc_buff,*lastkey;
 
4010
  MI_KEY_PARAM s_temp;
 
4011
  MI_INFO *info;
 
4012
  MI_KEYDEF *keyinfo=sort_param->keyinfo;
 
4013
  SORT_INFO *sort_info= sort_param->sort_info;
 
4014
  MI_CHECK *param=sort_info->param;
 
4015
  DBUG_ENTER("sort_insert_key");
 
4016
 
 
4017
  anc_buff=key_block->buff;
 
4018
  info=sort_info->info;
 
4019
  lastkey=key_block->lastkey;
 
4020
  nod_flag= (key_block == sort_info->key_block ? 0 :
 
4021
             info->s->base.key_reflength);
 
4022
 
 
4023
  if (!key_block->inited)
 
4024
  {
 
4025
    key_block->inited=1;
 
4026
    if (key_block == sort_info->key_block_end)
 
4027
    {
 
4028
      mi_check_print_error(param,"To many key-block-levels; Try increasing sort_key_blocks");
 
4029
      DBUG_RETURN(1);
 
4030
    }
 
4031
    a_length=2+nod_flag;
 
4032
    key_block->end_pos=anc_buff+2;
 
4033
    lastkey=0;                                  /* No previous key in block */
 
4034
  }
 
4035
  else
 
4036
    a_length=mi_getint(anc_buff);
 
4037
 
 
4038
        /* Save pointer to previous block */
 
4039
  if (nod_flag)
 
4040
    _mi_kpointer(info,key_block->end_pos,prev_block);
 
4041
 
 
4042
  t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,
 
4043
                                (uchar*) 0,lastkey,lastkey,key,
 
4044
                                 &s_temp);
 
4045
  (*keyinfo->store_key)(keyinfo, key_block->end_pos+nod_flag,&s_temp);
 
4046
  a_length+=t_length;
 
4047
  mi_putint(anc_buff,a_length,nod_flag);
 
4048
  key_block->end_pos+=t_length;
 
4049
  if (a_length <= keyinfo->block_length)
 
4050
  {
 
4051
    VOID(_mi_move_key(keyinfo,key_block->lastkey,key));
 
4052
    key_block->last_length=a_length-t_length;
 
4053
    DBUG_RETURN(0);
 
4054
  }
 
4055
 
 
4056
        /* Fill block with end-zero and write filled block */
 
4057
  mi_putint(anc_buff,key_block->last_length,nod_flag);
 
4058
  bzero((uchar*) anc_buff+key_block->last_length,
 
4059
        keyinfo->block_length- key_block->last_length);
 
4060
  key_file_length=info->state->key_file_length;
 
4061
  if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
 
4062
    DBUG_RETURN(1);
 
4063
 
 
4064
  /* If we read the page from the key cache, we have to write it back to it */
 
4065
  if (key_file_length == info->state->key_file_length)
 
4066
  {
 
4067
    if (_mi_write_keypage(info, keyinfo, filepos, DFLT_INIT_HITS, anc_buff))
 
4068
      DBUG_RETURN(1);
 
4069
  }
 
4070
  else if (my_pwrite(info->s->kfile,(uchar*) anc_buff,
 
4071
                     (uint) keyinfo->block_length,filepos, param->myf_rw))
 
4072
    DBUG_RETURN(1);
 
4073
  DBUG_DUMP("buff",(uchar*) anc_buff,mi_getint(anc_buff));
 
4074
 
 
4075
        /* Write separator-key to block in next level */
 
4076
  if (sort_insert_key(sort_param,key_block+1,key_block->lastkey,filepos))
 
4077
    DBUG_RETURN(1);
 
4078
 
 
4079
        /* clear old block and write new key in it */
 
4080
  key_block->inited=0;
 
4081
  DBUG_RETURN(sort_insert_key(sort_param, key_block,key,prev_block));
 
4082
} /* sort_insert_key */
 
4083
 
 
4084
 
 
4085
        /* Delete record when we found a duplicated key */
 
4086
 
 
4087
static int sort_delete_record(MI_SORT_PARAM *sort_param)
 
4088
{
 
4089
  uint i;
 
4090
  int old_file,error;
 
4091
  uchar *key;
 
4092
  SORT_INFO *sort_info=sort_param->sort_info;
 
4093
  MI_CHECK *param=sort_info->param;
 
4094
  MI_INFO *info=sort_info->info;
 
4095
  DBUG_ENTER("sort_delete_record");
 
4096
 
 
4097
  if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
 
4098
  {
 
4099
    mi_check_print_error(param,
 
4100
                         "Quick-recover aborted; Run recovery without switch -q or with switch -qq");
 
4101
    DBUG_RETURN(1);
 
4102
  }
 
4103
  if (info->s->options & HA_OPTION_COMPRESS_RECORD)
 
4104
  {
 
4105
    mi_check_print_error(param,
 
4106
                         "Recover aborted; Can't run standard recovery on compressed tables with errors in data-file. Use switch 'myisamchk --safe-recover' to fix it\n",stderr);;
 
4107
    DBUG_RETURN(1);
 
4108
  }
 
4109
 
 
4110
  old_file=info->dfile;
 
4111
  info->dfile=info->rec_cache.file;
 
4112
  if (sort_info->current_key)
 
4113
  {
 
4114
    key=info->lastkey+info->s->base.max_key_length;
 
4115
    if ((error=(*info->s->read_rnd)(info,sort_param->record,info->lastpos,0)) &&
 
4116
        error != HA_ERR_RECORD_DELETED)
 
4117
    {
 
4118
      mi_check_print_error(param,"Can't read record to be removed");
 
4119
      info->dfile=old_file;
 
4120
      DBUG_RETURN(1);
 
4121
    }
 
4122
 
 
4123
    for (i=0 ; i < sort_info->current_key ; i++)
 
4124
    {
 
4125
      uint key_length=_mi_make_key(info,i,key,sort_param->record,info->lastpos);
 
4126
      if (_mi_ck_delete(info,i,key,key_length))
 
4127
      {
 
4128
        mi_check_print_error(param,"Can't delete key %d from record to be removed",i+1);
 
4129
        info->dfile=old_file;
 
4130
        DBUG_RETURN(1);
 
4131
      }
 
4132
    }
 
4133
    if (sort_param->calc_checksum)
 
4134
      param->glob_crc-=(*info->s->calc_checksum)(info, sort_param->record);
 
4135
  }
 
4136
  error=flush_io_cache(&info->rec_cache) || (*info->s->delete_record)(info);
 
4137
  info->dfile=old_file;                         /* restore actual value */
 
4138
  info->state->records--;
 
4139
  DBUG_RETURN(error);
 
4140
} /* sort_delete_record */
 
4141
 
 
4142
        /* Fix all pending blocks and flush everything to disk */
 
4143
 
 
4144
int flush_pending_blocks(MI_SORT_PARAM *sort_param)
 
4145
{
 
4146
  uint nod_flag,length;
 
4147
  my_off_t filepos,key_file_length;
 
4148
  SORT_KEY_BLOCKS *key_block;
 
4149
  SORT_INFO *sort_info= sort_param->sort_info;
 
4150
  myf myf_rw=sort_info->param->myf_rw;
 
4151
  MI_INFO *info=sort_info->info;
 
4152
  MI_KEYDEF *keyinfo=sort_param->keyinfo;
 
4153
  DBUG_ENTER("flush_pending_blocks");
 
4154
 
 
4155
  filepos= HA_OFFSET_ERROR;                     /* if empty file */
 
4156
  nod_flag=0;
 
4157
  for (key_block=sort_info->key_block ; key_block->inited ; key_block++)
 
4158
  {
 
4159
    key_block->inited=0;
 
4160
    length=mi_getint(key_block->buff);
 
4161
    if (nod_flag)
 
4162
      _mi_kpointer(info,key_block->end_pos,filepos);
 
4163
    key_file_length=info->state->key_file_length;
 
4164
    bzero((uchar*) key_block->buff+length, keyinfo->block_length-length);
 
4165
    if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
 
4166
      DBUG_RETURN(1);
 
4167
 
 
4168
    /* If we read the page from the key cache, we have to write it back */
 
4169
    if (key_file_length == info->state->key_file_length)
 
4170
    {
 
4171
      if (_mi_write_keypage(info, keyinfo, filepos,
 
4172
                            DFLT_INIT_HITS, key_block->buff))
 
4173
        DBUG_RETURN(1);
 
4174
    }
 
4175
    else if (my_pwrite(info->s->kfile,(uchar*) key_block->buff,
 
4176
                       (uint) keyinfo->block_length,filepos, myf_rw))
 
4177
      DBUG_RETURN(1);
 
4178
    DBUG_DUMP("buff",(uchar*) key_block->buff,length);
 
4179
    nod_flag=1;
 
4180
  }
 
4181
  info->s->state.key_root[sort_param->key]=filepos; /* Last is root for tree */
 
4182
  DBUG_RETURN(0);
 
4183
} /* flush_pending_blocks */
 
4184
 
 
4185
        /* alloc space and pointers for key_blocks */
 
4186
 
 
4187
static SORT_KEY_BLOCKS *alloc_key_blocks(MI_CHECK *param, uint blocks,
 
4188
                                         uint buffer_length)
 
4189
{
 
4190
  reg1 uint i;
 
4191
  SORT_KEY_BLOCKS *block;
 
4192
  DBUG_ENTER("alloc_key_blocks");
 
4193
 
 
4194
  if (!(block=(SORT_KEY_BLOCKS*) my_malloc((sizeof(SORT_KEY_BLOCKS)+
 
4195
                                            buffer_length+IO_SIZE)*blocks,
 
4196
                                           MYF(0))))
 
4197
  {
 
4198
    mi_check_print_error(param,"Not enough memory for sort-key-blocks");
 
4199
    return(0);
 
4200
  }
 
4201
  for (i=0 ; i < blocks ; i++)
 
4202
  {
 
4203
    block[i].inited=0;
 
4204
    block[i].buff=(uchar*) (block+blocks)+(buffer_length+IO_SIZE)*i;
 
4205
  }
 
4206
  DBUG_RETURN(block);
 
4207
} /* alloc_key_blocks */
 
4208
 
 
4209
 
 
4210
        /* Check if file is almost full */
 
4211
 
 
4212
int test_if_almost_full(MI_INFO *info)
 
4213
{
 
4214
  if (info->s->options & HA_OPTION_COMPRESS_RECORD)
 
4215
    return 0;
 
4216
  return my_seek(info->s->kfile, 0L, MY_SEEK_END, MYF(MY_THREADSAFE)) / 10 * 9 >
 
4217
         (my_off_t) info->s->base.max_key_file_length ||
 
4218
         my_seek(info->dfile, 0L, MY_SEEK_END, MYF(0)) / 10 * 9 >
 
4219
         (my_off_t) info->s->base.max_data_file_length;
 
4220
}
 
4221
 
 
4222
        /* Recreate table with bigger more alloced record-data */
 
4223
 
 
4224
int recreate_table(MI_CHECK *param, MI_INFO **org_info, char *filename)
 
4225
{
 
4226
  int error;
 
4227
  MI_INFO info;
 
4228
  MYISAM_SHARE share;
 
4229
  MI_KEYDEF *keyinfo,*key,*key_end;
 
4230
  HA_KEYSEG *keysegs,*keyseg;
 
4231
  MI_COLUMNDEF *recdef,*rec,*end;
 
4232
  MI_UNIQUEDEF *uniquedef,*u_ptr,*u_end;
 
4233
  MI_STATUS_INFO status_info;
 
4234
  uint unpack,key_parts;
 
4235
  ha_rows max_records;
 
4236
  ulonglong file_length,tmp_length;
 
4237
  MI_CREATE_INFO create_info;
 
4238
  DBUG_ENTER("recreate_table");
 
4239
 
 
4240
  error=1;                                      /* Default error */
 
4241
  info= **org_info;
 
4242
  status_info= (*org_info)->state[0];
 
4243
  info.state= &status_info;
 
4244
  share= *(*org_info)->s;
 
4245
  unpack= (share.options & HA_OPTION_COMPRESS_RECORD) &&
 
4246
    (param->testflag & T_UNPACK);
 
4247
  if (!(keyinfo=(MI_KEYDEF*) my_alloca(sizeof(MI_KEYDEF)*share.base.keys)))
 
4248
    DBUG_RETURN(0);
 
4249
  memcpy((uchar*) keyinfo,(uchar*) share.keyinfo,
 
4250
         (size_t) (sizeof(MI_KEYDEF)*share.base.keys));
 
4251
 
 
4252
  key_parts= share.base.all_key_parts;
 
4253
  if (!(keysegs=(HA_KEYSEG*) my_alloca(sizeof(HA_KEYSEG)*
 
4254
                                       (key_parts+share.base.keys))))
 
4255
  {
 
4256
    my_afree((uchar*) keyinfo);
 
4257
    DBUG_RETURN(1);
 
4258
  }
 
4259
  if (!(recdef=(MI_COLUMNDEF*)
 
4260
        my_alloca(sizeof(MI_COLUMNDEF)*(share.base.fields+1))))
 
4261
  {
 
4262
    my_afree((uchar*) keyinfo);
 
4263
    my_afree((uchar*) keysegs);
 
4264
    DBUG_RETURN(1);
 
4265
  }
 
4266
  if (!(uniquedef=(MI_UNIQUEDEF*)
 
4267
        my_alloca(sizeof(MI_UNIQUEDEF)*(share.state.header.uniques+1))))
 
4268
  {
 
4269
    my_afree((uchar*) recdef);
 
4270
    my_afree((uchar*) keyinfo);
 
4271
    my_afree((uchar*) keysegs);
 
4272
    DBUG_RETURN(1);
 
4273
  }
 
4274
 
 
4275
  /* Copy the column definitions */
 
4276
  memcpy((uchar*) recdef,(uchar*) share.rec,
 
4277
         (size_t) (sizeof(MI_COLUMNDEF)*(share.base.fields+1)));
 
4278
  for (rec=recdef,end=recdef+share.base.fields; rec != end ; rec++)
 
4279
  {
 
4280
    if (unpack && !(share.options & HA_OPTION_PACK_RECORD) &&
 
4281
        rec->type != FIELD_BLOB &&
 
4282
        rec->type != FIELD_VARCHAR &&
 
4283
        rec->type != FIELD_CHECK)
 
4284
      rec->type=(int) FIELD_NORMAL;
 
4285
  }
 
4286
 
 
4287
  /* Change the new key to point at the saved key segments */
 
4288
  memcpy((uchar*) keysegs,(uchar*) share.keyparts,
 
4289
         (size_t) (sizeof(HA_KEYSEG)*(key_parts+share.base.keys+
 
4290
                                      share.state.header.uniques)));
 
4291
  keyseg=keysegs;
 
4292
  for (key=keyinfo,key_end=keyinfo+share.base.keys; key != key_end ; key++)
 
4293
  {
 
4294
    key->seg=keyseg;
 
4295
    for (; keyseg->type ; keyseg++)
 
4296
    {
 
4297
      if (param->language)
 
4298
        keyseg->language=param->language;       /* change language */
 
4299
    }
 
4300
    keyseg++;                                   /* Skip end pointer */
 
4301
  }
 
4302
 
 
4303
  /* Copy the unique definitions and change them to point at the new key
 
4304
     segments*/
 
4305
  memcpy((uchar*) uniquedef,(uchar*) share.uniqueinfo,
 
4306
         (size_t) (sizeof(MI_UNIQUEDEF)*(share.state.header.uniques)));
 
4307
  for (u_ptr=uniquedef,u_end=uniquedef+share.state.header.uniques;
 
4308
       u_ptr != u_end ; u_ptr++)
 
4309
  {
 
4310
    u_ptr->seg=keyseg;
 
4311
    keyseg+=u_ptr->keysegs+1;
 
4312
  }
 
4313
  if (share.options & HA_OPTION_COMPRESS_RECORD)
 
4314
    share.base.records=max_records=info.state->records;
 
4315
  else if (share.base.min_pack_length)
 
4316
    max_records=(ha_rows) (my_seek(info.dfile,0L,MY_SEEK_END,MYF(0)) /
 
4317
                           (ulong) share.base.min_pack_length);
 
4318
  else
 
4319
    max_records=0;
 
4320
  unpack= (share.options & HA_OPTION_COMPRESS_RECORD) &&
 
4321
    (param->testflag & T_UNPACK);
 
4322
  share.options&= ~HA_OPTION_TEMP_COMPRESS_RECORD;
 
4323
 
 
4324
  file_length=(ulonglong) my_seek(info.dfile,0L,MY_SEEK_END,MYF(0));
 
4325
  tmp_length= file_length+file_length/10;
 
4326
  set_if_bigger(file_length,param->max_data_file_length);
 
4327
  set_if_bigger(file_length,tmp_length);
 
4328
  set_if_bigger(file_length,(ulonglong) share.base.max_data_file_length);
 
4329
 
 
4330
  VOID(mi_close(*org_info));
 
4331
  bzero((char*) &create_info,sizeof(create_info));
 
4332
  create_info.max_rows=max(max_records,share.base.records);
 
4333
  create_info.reloc_rows=share.base.reloc;
 
4334
  create_info.old_options=(share.options |
 
4335
                           (unpack ? HA_OPTION_TEMP_COMPRESS_RECORD : 0));
 
4336
 
 
4337
  create_info.data_file_length=file_length;
 
4338
  create_info.auto_increment=share.state.auto_increment;
 
4339
  create_info.language = (param->language ? param->language :
 
4340
                          share.state.header.language);
 
4341
  create_info.key_file_length=  status_info.key_file_length;
 
4342
  /*
 
4343
    Allow for creating an auto_increment key. This has an effect only if
 
4344
    an auto_increment key exists in the original table.
 
4345
  */
 
4346
  create_info.with_auto_increment= TRUE;
 
4347
  /* We don't have to handle symlinks here because we are using
 
4348
     HA_DONT_TOUCH_DATA */
 
4349
  if (mi_create(filename,
 
4350
                share.base.keys - share.state.header.uniques,
 
4351
                keyinfo, share.base.fields, recdef,
 
4352
                share.state.header.uniques, uniquedef,
 
4353
                &create_info,
 
4354
                HA_DONT_TOUCH_DATA))
 
4355
  {
 
4356
    mi_check_print_error(param,"Got error %d when trying to recreate indexfile",my_errno);
 
4357
    goto end;
 
4358
  }
 
4359
  *org_info=mi_open(filename,O_RDWR,
 
4360
                    (param->testflag & T_WAIT_FOREVER) ? HA_OPEN_WAIT_IF_LOCKED :
 
4361
                    (param->testflag & T_DESCRIPT) ? HA_OPEN_IGNORE_IF_LOCKED :
 
4362
                    HA_OPEN_ABORT_IF_LOCKED);
 
4363
  if (!*org_info)
 
4364
  {
 
4365
    mi_check_print_error(param,"Got error %d when trying to open re-created indexfile",
 
4366
                my_errno);
 
4367
    goto end;
 
4368
  }
 
4369
  /* We are modifing */
 
4370
  (*org_info)->s->options&= ~HA_OPTION_READ_ONLY_DATA;
 
4371
  VOID(_mi_readinfo(*org_info,F_WRLCK,0));
 
4372
  (*org_info)->state->records=info.state->records;
 
4373
  if (share.state.create_time)
 
4374
    (*org_info)->s->state.create_time=share.state.create_time;
 
4375
  (*org_info)->s->state.unique=(*org_info)->this_unique=
 
4376
    share.state.unique;
 
4377
  (*org_info)->state->checksum=info.state->checksum;
 
4378
  (*org_info)->state->del=info.state->del;
 
4379
  (*org_info)->s->state.dellink=share.state.dellink;
 
4380
  (*org_info)->state->empty=info.state->empty;
 
4381
  (*org_info)->state->data_file_length=info.state->data_file_length;
 
4382
  if (update_state_info(param,*org_info,UPDATE_TIME | UPDATE_STAT |
 
4383
                        UPDATE_OPEN_COUNT))
 
4384
    goto end;
 
4385
  error=0;
 
4386
end:
 
4387
  my_afree((uchar*) uniquedef);
 
4388
  my_afree((uchar*) keyinfo);
 
4389
  my_afree((uchar*) recdef);
 
4390
  my_afree((uchar*) keysegs);
 
4391
  DBUG_RETURN(error);
 
4392
}
 
4393
 
 
4394
 
 
4395
        /* write suffix to data file if neaded */
 
4396
 
 
4397
int write_data_suffix(SORT_INFO *sort_info, my_bool fix_datafile)
 
4398
{
 
4399
  MI_INFO *info=sort_info->info;
 
4400
 
 
4401
  if (info->s->options & HA_OPTION_COMPRESS_RECORD && fix_datafile)
 
4402
  {
 
4403
    uchar buff[MEMMAP_EXTRA_MARGIN];
 
4404
    bzero(buff,sizeof(buff));
 
4405
    if (my_b_write(&info->rec_cache,buff,sizeof(buff)))
 
4406
    {
 
4407
      mi_check_print_error(sort_info->param,
 
4408
                           "%d when writing to datafile",my_errno);
 
4409
      return 1;
 
4410
    }
 
4411
    sort_info->param->read_cache.end_of_file+=sizeof(buff);
 
4412
  }
 
4413
  return 0;
 
4414
}
 
4415
 
 
4416
        /* Update state and myisamchk_time of indexfile */
 
4417
 
 
4418
int update_state_info(MI_CHECK *param, MI_INFO *info,uint update)
 
4419
{
 
4420
  MYISAM_SHARE *share=info->s;
 
4421
 
 
4422
  if (update & UPDATE_OPEN_COUNT)
 
4423
  {
 
4424
    share->state.open_count=0;
 
4425
    share->global_changed=0;
 
4426
  }
 
4427
  if (update & UPDATE_STAT)
 
4428
  {
 
4429
    uint i, key_parts= mi_uint2korr(share->state.header.key_parts);
 
4430
    share->state.rec_per_key_rows=info->state->records;
 
4431
    share->state.changed&= ~STATE_NOT_ANALYZED;
 
4432
    if (info->state->records)
 
4433
    {
 
4434
      for (i=0; i<key_parts; i++)
 
4435
      {
 
4436
        if (!(share->state.rec_per_key_part[i]=param->rec_per_key_part[i]))
 
4437
          share->state.changed|= STATE_NOT_ANALYZED;
 
4438
      }
 
4439
    }
 
4440
  }
 
4441
  if (update & (UPDATE_STAT | UPDATE_SORT | UPDATE_TIME | UPDATE_AUTO_INC))
 
4442
  {
 
4443
    if (update & UPDATE_TIME)
 
4444
    {
 
4445
      share->state.check_time= (long) time((time_t*) 0);
 
4446
      if (!share->state.create_time)
 
4447
        share->state.create_time=share->state.check_time;
 
4448
    }
 
4449
    /*
 
4450
      When tables are locked we haven't synched the share state and the
 
4451
      real state for a while so we better do it here before synching
 
4452
      the share state to disk. Only when table is write locked is it
 
4453
      necessary to perform this synch.
 
4454
    */
 
4455
    if (info->lock_type == F_WRLCK)
 
4456
      share->state.state= *info->state;
 
4457
    if (mi_state_info_write(share->kfile,&share->state,1+2))
 
4458
      goto err;
 
4459
    share->changed=0;
 
4460
  }
 
4461
  {                                             /* Force update of status */
 
4462
    int error;
 
4463
    uint r_locks=share->r_locks,w_locks=share->w_locks;
 
4464
    share->r_locks= share->w_locks= share->tot_locks= 0;
 
4465
    error=_mi_writeinfo(info,WRITEINFO_NO_UNLOCK);
 
4466
    share->r_locks=r_locks;
 
4467
    share->w_locks=w_locks;
 
4468
    share->tot_locks=r_locks+w_locks;
 
4469
    if (!error)
 
4470
      return 0;
 
4471
  }
 
4472
err:
 
4473
  mi_check_print_error(param,"%d when updating keyfile",my_errno);
 
4474
  return 1;
 
4475
}
 
4476
 
 
4477
        /*
 
4478
          Update auto increment value for a table
 
4479
          When setting the 'repair_only' flag we only want to change the
 
4480
          old auto_increment value if its wrong (smaller than some given key).
 
4481
          The reason is that we shouldn't change the auto_increment value
 
4482
          for a table without good reason when only doing a repair; If the
 
4483
          user have inserted and deleted rows, the auto_increment value
 
4484
          may be bigger than the biggest current row and this is ok.
 
4485
 
 
4486
          If repair_only is not set, we will update the flag to the value in
 
4487
          param->auto_increment is bigger than the biggest key.
 
4488
        */
 
4489
 
 
4490
void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
 
4491
                               my_bool repair_only)
 
4492
{
 
4493
  uchar *record= 0;
 
4494
  DBUG_ENTER("update_auto_increment_key");
 
4495
 
 
4496
  if (!info->s->base.auto_key ||
 
4497
      ! mi_is_key_active(info->s->state.key_map, info->s->base.auto_key - 1))
 
4498
  {
 
4499
    if (!(param->testflag & T_VERY_SILENT))
 
4500
      mi_check_print_info(param,
 
4501
                          "Table: %s doesn't have an auto increment key\n",
 
4502
                          param->isam_file_name);
 
4503
    DBUG_VOID_RETURN;
 
4504
  }
 
4505
  if (!(param->testflag & T_SILENT) &&
 
4506
      !(param->testflag & T_REP))
 
4507
    printf("Updating MyISAM file: %s\n", param->isam_file_name);
 
4508
  /*
 
4509
    We have to use an allocated buffer instead of info->rec_buff as 
 
4510
    _mi_put_key_in_record() may use info->rec_buff
 
4511
  */
 
4512
  if (!mi_alloc_rec_buff(info, -1, &record))
 
4513
  {
 
4514
    mi_check_print_error(param,"Not enough memory for extra record");
 
4515
    DBUG_VOID_RETURN;
 
4516
  }
 
4517
 
 
4518
  mi_extra(info,HA_EXTRA_KEYREAD,0);
 
4519
  if (mi_rlast(info, record, info->s->base.auto_key-1))
 
4520
  {
 
4521
    if (my_errno != HA_ERR_END_OF_FILE)
 
4522
    {
 
4523
      mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
 
4524
      my_free(mi_get_rec_buff_ptr(info, record), MYF(0));
 
4525
      mi_check_print_error(param,"%d when reading last record",my_errno);
 
4526
      DBUG_VOID_RETURN;
 
4527
    }
 
4528
    if (!repair_only)
 
4529
      info->s->state.auto_increment=param->auto_increment_value;
 
4530
  }
 
4531
  else
 
4532
  {
 
4533
    ulonglong auto_increment= retrieve_auto_increment(info, record);
 
4534
    set_if_bigger(info->s->state.auto_increment,auto_increment);
 
4535
    if (!repair_only)
 
4536
      set_if_bigger(info->s->state.auto_increment, param->auto_increment_value);
 
4537
  }
 
4538
  mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
 
4539
  my_free(mi_get_rec_buff_ptr(info, record), MYF(0));
 
4540
  update_state_info(param, info, UPDATE_AUTO_INC);
 
4541
  DBUG_VOID_RETURN;
 
4542
}
 
4543
 
 
4544
 
 
4545
/*
 
4546
  Update statistics for each part of an index
 
4547
 
 
4548
  SYNOPSIS
 
4549
    update_key_parts()
 
4550
      keyinfo           IN  Index information (only key->keysegs used)
 
4551
      rec_per_key_part  OUT Store statistics here
 
4552
      unique            IN  Array of (#distinct tuples)
 
4553
      notnull_tuples    IN  Array of (#tuples), or NULL
 
4554
      records               Number of records in the table
 
4555
 
 
4556
  DESCRIPTION
 
4557
    This function is called produce index statistics values from unique and 
 
4558
    notnull_tuples arrays after these arrays were produced with sequential
 
4559
    index scan (the scan is done in two places: chk_index() and
 
4560
    sort_key_write()).
 
4561
 
 
4562
    This function handles all 3 index statistics collection methods.
 
4563
 
 
4564
    Unique is an array:
 
4565
      unique[0]= (#different values of {keypart1}) - 1
 
4566
      unique[1]= (#different values of {keypart1,keypart2} tuple)-unique[0]-1
 
4567
      ...
 
4568
 
 
4569
    For MI_STATS_METHOD_IGNORE_NULLS method, notnull_tuples is an array too:
 
4570
      notnull_tuples[0]= (#of {keypart1} tuples such that keypart1 is not NULL)
 
4571
      notnull_tuples[1]= (#of {keypart1,keypart2} tuples such that all 
 
4572
                          keypart{i} are not NULL)
 
4573
      ...
 
4574
    For all other statistics collection methods notnull_tuples==NULL.
 
4575
 
 
4576
    Output is an array:
 
4577
    rec_per_key_part[k] = 
 
4578
     = E(#records in the table such that keypart_1=c_1 AND ... AND 
 
4579
         keypart_k=c_k for arbitrary constants c_1 ... c_k) 
 
4580
     
 
4581
     = {assuming that values have uniform distribution and index contains all
 
4582
        tuples from the domain (or that {c_1, ..., c_k} tuple is choosen from
 
4583
        index tuples}
 
4584
     
 
4585
     = #tuples-in-the-index / #distinct-tuples-in-the-index.
 
4586
    
 
4587
    The #tuples-in-the-index and #distinct-tuples-in-the-index have different 
 
4588
    meaning depending on which statistics collection method is used:
 
4589
    
 
4590
    MI_STATS_METHOD_*  how are nulls compared?  which tuples are counted?
 
4591
     NULLS_EQUAL            NULL == NULL           all tuples in table
 
4592
     NULLS_NOT_EQUAL        NULL != NULL           all tuples in table
 
4593
     IGNORE_NULLS               n/a             tuples that don't have NULLs
 
4594
*/
 
4595
 
 
4596
void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
 
4597
                      ulonglong *unique, ulonglong *notnull,
 
4598
                      ulonglong records)
 
4599
{
 
4600
  ulonglong count=0,tmp, unique_tuples;
 
4601
  ulonglong tuples= records;
 
4602
  uint parts;
 
4603
  for (parts=0 ; parts < keyinfo->keysegs  ; parts++)
 
4604
  {
 
4605
    count+=unique[parts];
 
4606
    unique_tuples= count + 1;    
 
4607
    if (notnull)
 
4608
    {
 
4609
      tuples= notnull[parts];
 
4610
      /* 
 
4611
        #(unique_tuples not counting tuples with NULLs) = 
 
4612
          #(unique_tuples counting tuples with NULLs as different) - 
 
4613
          #(tuples with NULLs)
 
4614
      */
 
4615
      unique_tuples -= (records - notnull[parts]);
 
4616
    }
 
4617
    
 
4618
    if (unique_tuples == 0)
 
4619
      tmp= 1;
 
4620
    else if (count == 0)
 
4621
      tmp= tuples; /* 1 unique tuple */
 
4622
    else
 
4623
      tmp= (tuples + unique_tuples/2) / unique_tuples;
 
4624
 
 
4625
    /* 
 
4626
      for some weird keys (e.g. FULLTEXT) tmp can be <1 here. 
 
4627
      let's ensure it is not
 
4628
    */
 
4629
    set_if_bigger(tmp,1);
 
4630
    if (tmp >= (ulonglong) ~(ulong) 0)
 
4631
      tmp=(ulonglong) ~(ulong) 0;
 
4632
 
 
4633
    *rec_per_key_part=(ulong) tmp;
 
4634
    rec_per_key_part++;
 
4635
  }
 
4636
}
 
4637
 
 
4638
 
 
4639
static ha_checksum mi_byte_checksum(const uchar *buf, uint length)
 
4640
{
 
4641
  ha_checksum crc;
 
4642
  const uchar *end=buf+length;
 
4643
  for (crc=0; buf != end; buf++)
 
4644
    crc=((crc << 1) + *((uchar*) buf)) +
 
4645
      test(crc & (((ha_checksum) 1) << (8*sizeof(ha_checksum)-1)));
 
4646
  return crc;
 
4647
}
 
4648
 
 
4649
static my_bool mi_too_big_key_for_sort(MI_KEYDEF *key, ha_rows rows)
 
4650
{
 
4651
  uint key_maxlength=key->maxlength;
 
4652
  if (key->flag & HA_FULLTEXT)
 
4653
  {
 
4654
    uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT*
 
4655
                                  key->seg->charset->mbmaxlen;
 
4656
    key_maxlength+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
 
4657
  }
 
4658
  return (key->flag & HA_SPATIAL) ||
 
4659
          (key->flag & (HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY | HA_FULLTEXT) &&
 
4660
          ((ulonglong) rows * key_maxlength >
 
4661
           (ulonglong) myisam_max_temp_length));
 
4662
}
 
4663
 
 
4664
/*
 
4665
  Deactivate all not unique index that can be recreated fast
 
4666
  These include packed keys on which sorting will use more temporary
 
4667
  space than the max allowed file length or for which the unpacked keys
 
4668
  will take much more space than packed keys.
 
4669
  Note that 'rows' may be zero for the case when we don't know how many
 
4670
  rows we will put into the file.
 
4671
 */
 
4672
 
 
4673
void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows)
 
4674
{
 
4675
  MYISAM_SHARE *share=info->s;
 
4676
  MI_KEYDEF    *key=share->keyinfo;
 
4677
  uint          i;
 
4678
 
 
4679
  DBUG_ASSERT(info->state->records == 0 &&
 
4680
              (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES));
 
4681
  for (i=0 ; i < share->base.keys ; i++,key++)
 
4682
  {
 
4683
    if (!(key->flag & (HA_NOSAME | HA_SPATIAL | HA_AUTO_KEY)) &&
 
4684
        ! mi_too_big_key_for_sort(key,rows) && info->s->base.auto_key != i+1)
 
4685
    {
 
4686
      mi_clear_key_active(share->state.key_map, i);
 
4687
      info->update|= HA_STATE_CHANGED;
 
4688
    }
 
4689
  }
 
4690
}
 
4691
 
 
4692
 
 
4693
/*
 
4694
  Return TRUE if we can use repair by sorting
 
4695
  One can set the force argument to force to use sorting
 
4696
  even if the temporary file would be quite big!
 
4697
*/
 
4698
 
 
4699
my_bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows,
 
4700
                            ulonglong key_map, my_bool force)
 
4701
{
 
4702
  MYISAM_SHARE *share=info->s;
 
4703
  MI_KEYDEF *key=share->keyinfo;
 
4704
  uint i;
 
4705
 
 
4706
  /*
 
4707
    mi_repair_by_sort only works if we have at least one key. If we don't
 
4708
    have any keys, we should use the normal repair.
 
4709
  */
 
4710
  if (! mi_is_any_key_active(key_map))
 
4711
    return FALSE;                               /* Can't use sort */
 
4712
  for (i=0 ; i < share->base.keys ; i++,key++)
 
4713
  {
 
4714
    if (!force && mi_too_big_key_for_sort(key,rows))
 
4715
      return FALSE;
 
4716
  }
 
4717
  return TRUE;
 
4718
}
 
4719
 
 
4720
 
 
4721
static void
 
4722
set_data_file_type(SORT_INFO *sort_info, MYISAM_SHARE *share)
 
4723
{
 
4724
  if ((sort_info->new_data_file_type=share->data_file_type) ==
 
4725
      COMPRESSED_RECORD && sort_info->param->testflag & T_UNPACK)
 
4726
  {
 
4727
    MYISAM_SHARE tmp;
 
4728
 
 
4729
    if (share->options & HA_OPTION_PACK_RECORD)
 
4730
      sort_info->new_data_file_type = DYNAMIC_RECORD;
 
4731
    else
 
4732
      sort_info->new_data_file_type = STATIC_RECORD;
 
4733
 
 
4734
    /* Set delete_function for sort_delete_record() */
 
4735
    memcpy((char*) &tmp, share, sizeof(*share));
 
4736
    tmp.options= ~HA_OPTION_COMPRESS_RECORD;
 
4737
    mi_setup_functions(&tmp);
 
4738
    share->delete_record=tmp.delete_record;
 
4739
  }
 
4740
}