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