~drizzle-trunk/drizzle/development

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