~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
1802.10.2 by Monty Taylor
Update all of the copyright headers to include the correct address.
14
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
1 by brian
clean slate
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
1130.3.28 by Monty Taylor
Moved heapdef.h and myisamdef.h to *_priv.h for easier filtering for include guard check.
43
#include "myisam_priv.h"
2173.2.1 by Monty Taylor
Fixes incorrect usage of include
44
#include <drizzled/internal/m_string.h>
1 by brian
clean slate
45
#include <stdarg.h>
46
#ifdef HAVE_SYS_VADVISE_H
47
#include <sys/vadvise.h>
48
#endif
470 by Monty Taylor
Remove madvise decl.
49
#ifdef HAVE_SYS_TYPES
50
#include <sys/types.h>
51
#endif
1 by brian
clean slate
52
#ifdef HAVE_SYS_MMAN_H
53
#include <sys/mman.h>
54
#endif
492.1.7 by Monty Taylor
Moved test() to its own file.
55
#include <drizzled/util/test.h>
2173.2.1 by Monty Taylor
Fixes incorrect usage of include
56
#include <drizzled/error.h>
492.1.7 by Monty Taylor
Moved test() to its own file.
57
1067.4.8 by Nathan Williams
Converted all usages of cmin/cmax in plugin directory to std::min/max.
58
#include <algorithm>
59
60
using namespace std;
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
61
using namespace drizzled;
62
using namespace drizzled::internal;
1067.4.8 by Nathan Williams
Converted all usages of cmin/cmax in plugin directory to std::min/max.
63
1 by brian
clean slate
64
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
65
#define my_off_t2double(A)  ((double) (my_off_t) (A))
66
489.1.6 by Monty Taylor
Removed RAID garbage.
67
/* Functions defined in this file */
1 by brian
clean slate
68
482 by Brian Aker
Remove uint.
69
static int check_k_link(MI_CHECK *param, MI_INFO *info,uint32_t nr);
1 by brian
clean slate
70
static int chk_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo,
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
71
		     my_off_t page, unsigned char *buff, ha_rows *keys,
482 by Brian Aker
Remove uint.
72
		     ha_checksum *key_checksum, uint32_t level);
73
static uint32_t isam_key_length(MI_INFO *info,MI_KEYDEF *keyinfo);
1 by brian
clean slate
74
static ha_checksum calc_checksum(ha_rows count);
75
static int writekeys(MI_SORT_PARAM *sort_param);
76
static int sort_one_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo,
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
77
			  my_off_t pagepos, int new_file);
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
78
int sort_key_read(MI_SORT_PARAM *sort_param,void *key);
79
int sort_get_next_record(MI_SORT_PARAM *sort_param);
80
int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,const void *b);
81
int sort_key_write(MI_SORT_PARAM *sort_param, const void *a);
82
my_off_t get_record_for_key(MI_INFO *info,MI_KEYDEF *keyinfo,
83
                            unsigned char *key);
84
int sort_insert_key(MI_SORT_PARAM  *sort_param,
85
                    register SORT_KEY_BLOCKS *key_block,
86
                    unsigned char *key, my_off_t prev_block);
87
int sort_delete_record(MI_SORT_PARAM *sort_param);
88
1 by brian
clean slate
89
/*static int flush_pending_blocks(MI_CHECK *param);*/
482 by Brian Aker
Remove uint.
90
static SORT_KEY_BLOCKS	*alloc_key_blocks(MI_CHECK *param, uint32_t blocks,
91
					  uint32_t buffer_length);
92
static ha_checksum mi_byte_checksum(const unsigned char *buf, uint32_t length);
1 by brian
clean slate
93
static void set_data_file_type(SORT_INFO *sort_info, MYISAM_SHARE *share);
94
95
void myisamchk_init(MI_CHECK *param)
96
{
212.6.12 by Mats Kindahl
Removing redundant use of casts in MyISAM storage for memcmp(), memcpy(), memset(), and memmove().
97
  memset(param, 0, sizeof(*param));
1 by brian
clean slate
98
  param->opt_follow_links=1;
151 by Brian Aker
Ulonglong to uint64_t
99
  param->keys_in_use= ~(uint64_t) 0;
1 by brian
clean slate
100
  param->search_after_block=HA_OFFSET_ERROR;
101
  param->auto_increment_value= 0;
102
  param->use_buffers=USE_BUFFER_INIT;
103
  param->read_buffer_length=READ_BUFFER_INIT;
104
  param->write_buffer_length=READ_BUFFER_INIT;
105
  param->sort_buffer_length=SORT_BUFFER_INIT;
106
  param->sort_key_blocks=BUFFERS_WHEN_SORTING;
107
  param->tmpfile_createflag=O_RDWR | O_TRUNC | O_EXCL;
108
  param->myf_rw=MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL);
109
  param->start_check_pos=0;
163 by Brian Aker
Merge Monty's code.
110
  param->max_record_length= INT64_MAX;
1 by brian
clean slate
111
  param->key_cache_block_size= KEY_CACHE_BLOCK_SIZE;
112
  param->stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
113
}
114
115
	/* Check the status flags for the table */
116
117
int chk_status(MI_CHECK *param, register MI_INFO *info)
118
{
119
  MYISAM_SHARE *share=info->s;
120
121
  if (mi_is_crashed_on_repair(info))
122
    mi_check_print_warning(param,
123
			   "Table is marked as crashed and last repair failed");
124
  else if (mi_is_crashed(info))
125
    mi_check_print_warning(param,
126
			   "Table is marked as crashed");
127
  if (share->state.open_count != (uint) (info->s->global_changed ? 1 : 0))
128
  {
129
    /* Don't count this as a real warning, as check can correct this ! */
482 by Brian Aker
Remove uint.
130
    uint32_t save=param->warning_printed;
1 by brian
clean slate
131
    mi_check_print_warning(param,
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
132
			   share->state.open_count==1 ?
133
			   "%d client is using or hasn't closed the table properly" :
1 by brian
clean slate
134
			   "%d clients are using or haven't closed the table properly",
135
			   share->state.open_count);
136
    /* If this will be fixed by the check, forget the warning */
137
    if (param->testflag & T_UPDATE_STATE)
138
      param->warning_printed=save;
139
  }
140
  return 0;
141
}
142
143
	/* Check delete links */
144
482 by Brian Aker
Remove uint.
145
int chk_del(MI_CHECK *param, register MI_INFO *info, uint32_t test_flag)
1 by brian
clean slate
146
{
147
  register ha_rows i;
482 by Brian Aker
Remove uint.
148
  uint32_t delete_link_length;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
149
  my_off_t empty, next_link, old_link= 0;
1 by brian
clean slate
150
  char buff[22],buff2[22];
151
152
  param->record_checksum=0;
153
  delete_link_length=((info->s->options & HA_OPTION_PACK_RECORD) ? 20 :
154
		      info->s->rec_reflength+1);
155
156
  if (!(test_flag & T_SILENT))
157
    puts("- check record delete-chain");
158
159
  next_link=info->s->state.dellink;
160
  if (info->state->del == 0)
161
  {
162
    if (test_flag & T_VERBOSE)
163
    {
164
      puts("No recordlinks");
165
    }
166
  }
167
  else
168
  {
169
    if (test_flag & T_VERBOSE)
170
      printf("Recordlinks:    ");
171
    empty=0;
172
    for (i= info->state->del ; i > 0L && next_link != HA_OFFSET_ERROR ; i--)
173
    {
174
      if (*killed_ptr(param))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
175
        return(1);
1 by brian
clean slate
176
      if (test_flag & T_VERBOSE)
177
	printf(" %9s",llstr(next_link,buff));
178
      if (next_link >= info->state->data_file_length)
179
	goto wrong;
481 by Brian Aker
Remove all of uchar.
180
      if (my_pread(info->dfile, (unsigned char*) buff,delete_link_length,
1 by brian
clean slate
181
		   next_link,MYF(MY_NABP)))
182
      {
183
	if (test_flag & T_VERBOSE) puts("");
184
	mi_check_print_error(param,"Can't read delete-link at filepos: %s",
185
		    llstr(next_link,buff));
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
186
	return(1);
1 by brian
clean slate
187
      }
188
      if (*buff != '\0')
189
      {
190
	if (test_flag & T_VERBOSE) puts("");
191
	mi_check_print_error(param,"Record at pos: %s is not remove-marked",
192
		    llstr(next_link,buff));
193
	goto wrong;
194
      }
195
      if (info->s->options & HA_OPTION_PACK_RECORD)
196
      {
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
197
	my_off_t prev_link=mi_sizekorr(buff+12);
1 by brian
clean slate
198
	if (empty && prev_link != old_link)
199
	{
200
	  if (test_flag & T_VERBOSE) puts("");
201
	  mi_check_print_error(param,"Deleted block at %s doesn't point back at previous delete link",llstr(next_link,buff2));
202
	  goto wrong;
203
	}
204
	old_link=next_link;
205
	next_link=mi_sizekorr(buff+4);
206
	empty+=mi_uint3korr(buff+1);
207
      }
208
      else
209
      {
210
	param->record_checksum+=(ha_checksum) next_link;
481 by Brian Aker
Remove all of uchar.
211
	next_link=_mi_rec_pos(info->s,(unsigned char*) buff+1);
1 by brian
clean slate
212
	empty+=info->s->base.pack_reclength;
213
      }
214
    }
215
    if (test_flag & T_VERBOSE)
216
      puts("\n");
217
    if (empty != info->state->empty)
218
    {
219
      mi_check_print_warning(param,
220
			     "Found %s deleted space in delete link chain. Should be %s",
221
			     llstr(empty,buff2),
222
			     llstr(info->state->empty,buff));
223
    }
224
    if (next_link != HA_OFFSET_ERROR)
225
    {
226
      mi_check_print_error(param,
227
			   "Found more than the expected %s deleted rows in delete link chain",
228
			   llstr(info->state->del, buff));
229
      goto wrong;
230
    }
231
    if (i != 0)
232
    {
233
      mi_check_print_error(param,
234
			   "Found %s deleted rows in delete link chain. Should be %s",
235
			   llstr(info->state->del - i, buff2),
236
			   llstr(info->state->del, buff));
237
      goto wrong;
238
    }
239
  }
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
240
  return(0);
1 by brian
clean slate
241
242
wrong:
243
  param->testflag|=T_RETRY_WITHOUT_QUICK;
244
  if (test_flag & T_VERBOSE) puts("");
245
  mi_check_print_error(param,"record delete-link-chain corrupted");
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
246
  return(1);
1 by brian
clean slate
247
} /* chk_del */
248
249
250
	/* Check delete links in index file */
251
482 by Brian Aker
Remove uint.
252
static int check_k_link(MI_CHECK *param, register MI_INFO *info, uint32_t nr)
1 by brian
clean slate
253
{
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
254
  my_off_t next_link;
482 by Brian Aker
Remove uint.
255
  uint32_t block_size=(nr+1)*MI_MIN_KEY_BLOCK_LENGTH;
1 by brian
clean slate
256
  ha_rows records;
257
  char llbuff[21], llbuff2[21];
481 by Brian Aker
Remove all of uchar.
258
  unsigned char *buff;
1 by brian
clean slate
259
260
  if (param->testflag & T_VERBOSE)
971.6.11 by Eric Day
Removed purecov messages.
261
    printf("block_size %4u:", block_size);
1 by brian
clean slate
262
263
  next_link=info->s->state.key_del[nr];
264
  records= (ha_rows) (info->state->key_file_length / block_size);
265
  while (next_link != HA_OFFSET_ERROR && records > 0)
266
  {
267
    if (*killed_ptr(param))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
268
      return(1);
1 by brian
clean slate
269
    if (param->testflag & T_VERBOSE)
270
      printf("%16s",llstr(next_link,llbuff));
271
272
    /* Key blocks must lay within the key file length entirely. */
273
    if (next_link + block_size > info->state->key_file_length)
274
    {
275
      mi_check_print_error(param, "Invalid key block position: %s  "
276
                           "key block size: %u  file_length: %s",
277
                           llstr(next_link, llbuff), block_size,
278
                           llstr(info->state->key_file_length, llbuff2));
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
279
      return(1);
1 by brian
clean slate
280
    }
281
282
    /* Key blocks must be aligned at MI_MIN_KEY_BLOCK_LENGTH. */
283
    if (next_link & (MI_MIN_KEY_BLOCK_LENGTH - 1))
284
    {
285
      mi_check_print_error(param, "Mis-aligned key block: %s  "
286
                           "minimum key block length: %u",
287
                           llstr(next_link, llbuff), MI_MIN_KEY_BLOCK_LENGTH);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
288
      return(1);
1 by brian
clean slate
289
    }
290
291
    /*
292
      Read the key block with MI_MIN_KEY_BLOCK_LENGTH to find next link.
293
      If the key cache block size is smaller than block_size, we can so
294
      avoid unecessary eviction of cache block.
295
    */
1689.2.15 by Brian Aker
Encapsulate the key cache object in myisam.
296
    if (!(buff=key_cache_read(info->s->getKeyCache(),
1 by brian
clean slate
297
                              info->s->kfile, next_link, DFLT_INIT_HITS,
481 by Brian Aker
Remove all of uchar.
298
                              (unsigned char*) info->buff, MI_MIN_KEY_BLOCK_LENGTH,
1 by brian
clean slate
299
                              MI_MIN_KEY_BLOCK_LENGTH, 1)))
300
    {
301
      mi_check_print_error(param, "key cache read error for block: %s",
302
			   llstr(next_link,llbuff));
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
303
      return(1);
1 by brian
clean slate
304
    }
305
    next_link=mi_sizekorr(buff);
306
    records--;
307
    param->key_file_blocks+=block_size;
308
  }
309
  if (param->testflag & T_VERBOSE)
310
  {
311
    if (next_link != HA_OFFSET_ERROR)
312
      printf("%16s\n",llstr(next_link,llbuff));
313
    else
314
      puts("");
315
  }
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
316
  return (next_link != HA_OFFSET_ERROR);
1 by brian
clean slate
317
} /* check_k_link */
318
319
320
	/* Check sizes of files */
321
322
int chk_size(MI_CHECK *param, register MI_INFO *info)
323
{
324
  int error=0;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
325
  register my_off_t skr,size;
1 by brian
clean slate
326
  char buff[22],buff2[22];
327
328
  if (!(param->testflag & T_SILENT)) puts("- check file-size");
329
330
  /* The following is needed if called externally (not from myisamchk) */
1689.2.15 by Brian Aker
Encapsulate the key cache object in myisam.
331
  flush_key_blocks(info->s->getKeyCache(),
1 by brian
clean slate
332
		   info->s->kfile, FLUSH_FORCE_WRITE);
333
656.1.39 by Monty Taylor
Removed my_seek, my_tell, my_fwrite, my_fseek.
334
  size= lseek(info->s->kfile, 0, SEEK_END);
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
335
  if ((skr=(my_off_t) info->state->key_file_length) != size)
1 by brian
clean slate
336
  {
337
    /* Don't give error if file generated by myisampack */
338
    if (skr > size && mi_is_any_key_active(info->s->state.key_map))
339
    {
340
      error=1;
341
      mi_check_print_error(param,
342
			   "Size of indexfile is: %-8s        Should be: %s",
343
			   llstr(size,buff), llstr(skr,buff2));
344
    }
345
    else
346
      mi_check_print_warning(param,
347
			     "Size of indexfile is: %-8s      Should be: %s",
348
			     llstr(size,buff), llstr(skr,buff2));
349
  }
350
  if (!(param->testflag & T_VERY_SILENT) &&
351
      ! (info->s->options & HA_OPTION_COMPRESS_RECORD) &&
151 by Brian Aker
Ulonglong to uint64_t
352
      uint64_t2double(info->state->key_file_length) >
353
      uint64_t2double(info->s->base.margin_key_file_length)*0.9)
1 by brian
clean slate
354
    mi_check_print_warning(param,"Keyfile is almost full, %10s of %10s used",
355
			   llstr(info->state->key_file_length,buff),
356
			   llstr(info->s->base.max_key_file_length-1,buff));
357
656.1.39 by Monty Taylor
Removed my_seek, my_tell, my_fwrite, my_fseek.
358
  size=lseek(info->dfile,0L,SEEK_END);
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
359
  skr=(my_off_t) info->state->data_file_length;
1 by brian
clean slate
360
  if (info->s->options & HA_OPTION_COMPRESS_RECORD)
361
    skr+= MEMMAP_EXTRA_MARGIN;
362
#ifdef USE_RELOC
363
  if (info->data_file_type == STATIC_RECORD &&
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
364
      skr < (my_off_t) info->s->base.reloc*info->s->base.min_pack_length)
365
    skr=(my_off_t) info->s->base.reloc*info->s->base.min_pack_length;
1 by brian
clean slate
366
#endif
367
  if (skr != size)
368
  {
369
    info->state->data_file_length=size;	/* Skip other errors */
370
    if (skr > size && skr != size + MEMMAP_EXTRA_MARGIN)
371
    {
372
      error=1;
373
      mi_check_print_error(param,"Size of datafile is: %-9s         Should be: %s",
374
		    llstr(size,buff), llstr(skr,buff2));
375
      param->testflag|=T_RETRY_WITHOUT_QUICK;
376
    }
377
    else
378
    {
379
      mi_check_print_warning(param,
380
			     "Size of datafile is: %-9s       Should be: %s",
381
			     llstr(size,buff), llstr(skr,buff2));
382
    }
383
  }
384
  if (!(param->testflag & T_VERY_SILENT) &&
385
      !(info->s->options & HA_OPTION_COMPRESS_RECORD) &&
151 by Brian Aker
Ulonglong to uint64_t
386
      uint64_t2double(info->state->data_file_length) >
387
      (uint64_t2double(info->s->base.max_data_file_length)*0.9))
1 by brian
clean slate
388
    mi_check_print_warning(param, "Datafile is almost full, %10s of %10s used",
389
			   llstr(info->state->data_file_length,buff),
390
			   llstr(info->s->base.max_data_file_length-1,buff2));
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
391
  return(error);
1 by brian
clean slate
392
} /* chk_size */
393
394
395
	/* Check keys */
396
397
int chk_key(MI_CHECK *param, register MI_INFO *info)
398
{
482 by Brian Aker
Remove uint.
399
  uint32_t key,found_keys=0,full_text_keys=0,result=0;
1 by brian
clean slate
400
  ha_rows keys;
401
  ha_checksum old_record_checksum,init_checksum;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
402
  my_off_t all_keydata,all_totaldata,key_totlength,length;
1 by brian
clean slate
403
  ulong   *rec_per_key_part;
404
  MYISAM_SHARE *share=info->s;
405
  MI_KEYDEF *keyinfo;
406
  char buff[22],buff2[22];
407
408
  if (!(param->testflag & T_SILENT))
409
    puts("- check key delete-chain");
410
411
  param->key_file_blocks=info->s->base.keystart;
412
  for (key=0 ; key < info->s->state.header.max_block_size_index ; key++)
413
    if (check_k_link(param,info,key))
414
    {
415
      if (param->testflag & T_VERBOSE) puts("");
416
      mi_check_print_error(param,"key delete-link-chain corrupted");
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
417
      return(-1);
1 by brian
clean slate
418
    }
419
420
  if (!(param->testflag & T_SILENT)) puts("- check index reference");
421
422
  all_keydata=all_totaldata=key_totlength=0;
423
  old_record_checksum=0;
424
  init_checksum=param->record_checksum;
425
  if (!(share->options &
426
	(HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
427
    old_record_checksum=calc_checksum(info->state->records+info->state->del-1)*
428
      share->base.pack_reclength;
429
  rec_per_key_part= param->rec_per_key_part;
430
  for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
431
       rec_per_key_part+=keyinfo->keysegs, key++, keyinfo++)
432
  {
433
    param->key_crc[key]=0;
434
    if (! mi_is_key_active(share->state.key_map, key))
435
    {
436
      /* 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().
437
      assert(rec_per_key_part >= param->rec_per_key_part);
438
      memcpy(rec_per_key_part,
439
	     (share->state.rec_per_key_part +
440
              (rec_per_key_part - param->rec_per_key_part)),
1 by brian
clean slate
441
	     keyinfo->keysegs*sizeof(*rec_per_key_part));
442
      continue;
443
    }
444
    found_keys++;
445
446
    param->record_checksum=init_checksum;
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
447
212.6.12 by Mats Kindahl
Removing redundant use of casts in MyISAM storage for memcmp(), memcpy(), memset(), and memmove().
448
    memset(&param->unique_count, 0, sizeof(param->unique_count));
449
    memset(&param->notnull_count, 0, sizeof(param->notnull_count));
1 by brian
clean slate
450
451
    if ((!(param->testflag & T_SILENT)))
452
      printf ("- check data record references index: %d\n",key+1);
249 by Brian Aker
Random key cleanup (it is a friday...)
453
    if (share->state.key_root[key] == HA_OFFSET_ERROR && (info->state->records == 0))
1 by brian
clean slate
454
      goto do_stat;
455
    if (!_mi_fetch_keypage(info,keyinfo,share->state.key_root[key],
456
                           DFLT_INIT_HITS,info->buff,0))
457
    {
458
      mi_check_print_error(param,"Can't read indexpage from filepos: %s",
459
		  llstr(share->state.key_root[key],buff));
460
      if (!(param->testflag & T_INFO))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
461
	return(-1);
1816.2.4 by Monty Taylor
Cleaned up a bunch more warnings.
462
      result= UINT32_MAX;
1 by brian
clean slate
463
      continue;
464
    }
465
    param->key_file_blocks+=keyinfo->block_length;
466
    keys=0;
467
    param->keydata=param->totaldata=0;
468
    param->key_blocks=0;
469
    param->max_level=0;
470
    if (chk_index(param,info,keyinfo,share->state.key_root[key],info->buff,
471
		  &keys, param->key_crc+key,1))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
472
      return(-1);
249 by Brian Aker
Random key cleanup (it is a friday...)
473
    if(!(0))
1 by brian
clean slate
474
    {
475
      if (keys != info->state->records)
476
      {
477
	mi_check_print_error(param,"Found %s keys of %s",llstr(keys,buff),
478
		    llstr(info->state->records,buff2));
479
	if (!(param->testflag & T_INFO))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
480
	return(-1);
1816.2.4 by Monty Taylor
Cleaned up a bunch more warnings.
481
	result= UINT32_MAX;
1 by brian
clean slate
482
	continue;
483
      }
484
      if (found_keys - full_text_keys == 1 &&
485
	  ((share->options &
486
	    (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
487
	   (param->testflag & T_DONT_CHECK_CHECKSUM)))
488
	old_record_checksum=param->record_checksum;
489
      else if (old_record_checksum != param->record_checksum)
490
      {
491
	if (key)
492
	  mi_check_print_error(param,"Key %u doesn't point at same records that key 1",
493
		      key+1);
494
	else
495
	  mi_check_print_error(param,"Key 1 doesn't point at all records");
496
	if (!(param->testflag & T_INFO))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
497
	  return(-1);
1816.2.4 by Monty Taylor
Cleaned up a bunch more warnings.
498
	result= UINT32_MAX;
1 by brian
clean slate
499
	continue;
500
      }
501
    }
502
    if ((uint) share->base.auto_key -1 == key)
503
    {
504
      /* Check that auto_increment key is bigger than max key value */
151 by Brian Aker
Ulonglong to uint64_t
505
      uint64_t auto_increment;
1 by brian
clean slate
506
      info->lastinx=key;
507
      _mi_read_key_record(info, 0L, info->rec_buff);
508
      auto_increment= retrieve_auto_increment(info, info->rec_buff);
509
      if (auto_increment > info->s->state.auto_increment)
510
      {
511
        mi_check_print_warning(param, "Auto-increment value: %s is smaller "
512
                               "than max used value: %s",
513
                               llstr(info->s->state.auto_increment,buff2),
514
                               llstr(auto_increment, buff));
515
      }
516
      if (param->testflag & T_AUTO_INC)
517
      {
518
        set_if_bigger(info->s->state.auto_increment,
519
                      auto_increment);
520
        set_if_bigger(info->s->state.auto_increment,
521
                      param->auto_increment_value);
522
      }
523
524
      /* Check that there isn't a row with auto_increment = 0 in the table */
525
      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.
526
      memset(info->lastkey, 0, keyinfo->seg->length);
481 by Brian Aker
Remove all of uchar.
527
      if (!mi_rkey(info, info->rec_buff, key, (const unsigned char*) info->lastkey,
1 by brian
clean slate
528
		   (key_part_map)1, HA_READ_KEY_EXACT))
529
      {
530
	/* Don't count this as a real warning, as myisamchk can't correct it */
482 by Brian Aker
Remove uint.
531
	uint32_t save=param->warning_printed;
1 by brian
clean slate
532
        mi_check_print_warning(param, "Found row where the auto_increment "
533
                               "column has the value 0");
534
	param->warning_printed=save;
535
      }
536
      mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
537
    }
538
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
539
    length=(my_off_t) isam_key_length(info,keyinfo)*keys + param->key_blocks*2;
1 by brian
clean slate
540
    if (param->testflag & T_INFO && param->totaldata != 0L && keys != 0L)
541
      printf("Key: %2d:  Keyblocks used: %3d%%  Packed: %4d%%  Max levels: %2d\n",
542
	     key+1,
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
543
	     (int) (my_off_t2double(param->keydata)*100.0/my_off_t2double(param->totaldata)),
544
	     (int) ((my_off_t2double(length) - my_off_t2double(param->keydata))*100.0/
545
		    my_off_t2double(length)),
1 by brian
clean slate
546
	     param->max_level);
547
    all_keydata+=param->keydata; all_totaldata+=param->totaldata; key_totlength+=length;
548
549
do_stat:
550
    if (param->testflag & T_STATISTICS)
551
      update_key_parts(keyinfo, rec_per_key_part, param->unique_count,
552
                       param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
553
                       param->notnull_count: NULL,
151 by Brian Aker
Ulonglong to uint64_t
554
                       (uint64_t)info->state->records);
1 by brian
clean slate
555
  }
556
  if (param->testflag & T_INFO)
557
  {
558
    if (all_totaldata != 0L && found_keys > 0)
559
      printf("Total:    Keyblocks used: %3d%%  Packed: %4d%%\n\n",
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
560
	     (int) (my_off_t2double(all_keydata)*100.0/
561
		    my_off_t2double(all_totaldata)),
562
	     (int) ((my_off_t2double(key_totlength) -
563
		     my_off_t2double(all_keydata))*100.0/
564
		     my_off_t2double(key_totlength)));
1 by brian
clean slate
565
    else if (all_totaldata != 0L && mi_is_any_key_active(share->state.key_map))
566
      puts("");
567
  }
568
  if (param->key_file_blocks != info->state->key_file_length &&
151 by Brian Aker
Ulonglong to uint64_t
569
      param->keys_in_use != ~(uint64_t) 0)
1 by brian
clean slate
570
    mi_check_print_warning(param, "Some data are unreferenced in keyfile");
571
  if (found_keys != full_text_keys)
572
    param->record_checksum=old_record_checksum-init_checksum;	/* Remove delete links */
573
  else
574
    param->record_checksum=0;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
575
  return(result);
1 by brian
clean slate
576
} /* chk_key */
577
578
579
static int chk_index_down(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
580
                     my_off_t page, unsigned char *buff, ha_rows *keys,
482 by Brian Aker
Remove uint.
581
                     ha_checksum *key_checksum, uint32_t level)
1 by brian
clean slate
582
{
583
  char llbuff[22],llbuff2[22];
584
585
  /* Key blocks must lay within the key file length entirely. */
586
  if (page + keyinfo->block_length > info->state->key_file_length)
587
  {
588
    /* Give it a chance to fit in the real file size. */
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
589
    my_off_t max_length= lseek(info->s->kfile, 0, SEEK_END);
1 by brian
clean slate
590
    mi_check_print_error(param, "Invalid key block position: %s  "
591
                         "key block size: %u  file_length: %s",
592
                         llstr(page, llbuff), keyinfo->block_length,
593
                         llstr(info->state->key_file_length, llbuff2));
594
    if (page + keyinfo->block_length > max_length)
595
      goto err;
596
    /* Fix the remebered key file length. */
597
    info->state->key_file_length= (max_length &
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
598
                                   ~ (my_off_t) (keyinfo->block_length - 1));
1 by brian
clean slate
599
  }
600
601
  /* Key blocks must be aligned at MI_MIN_KEY_BLOCK_LENGTH. */
602
  if (page & (MI_MIN_KEY_BLOCK_LENGTH - 1))
603
  {
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
  }
609
610
  if (!_mi_fetch_keypage(info,keyinfo,page, DFLT_INIT_HITS,buff,0))
611
  {
612
    mi_check_print_error(param,"Can't read key from filepos: %s",
613
        llstr(page,llbuff));
614
    goto err;
615
  }
616
  param->key_file_blocks+=keyinfo->block_length;
617
  if (chk_index(param,info,keyinfo,page,buff,keys,key_checksum,level))
618
    goto err;
619
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
620
  return(0);
1 by brian
clean slate
621
622
err:
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
623
  return(1);
1 by brian
clean slate
624
}
625
626
627
/*
628
  "Ignore NULLs" statistics collection method: process first index tuple.
629
630
  SYNOPSIS
631
    mi_collect_stats_nonulls_first()
632
      keyseg   IN     Array of key part descriptions
633
      notnull  INOUT  Array, notnull[i] = (number of {keypart1...keypart_i}
634
                                           tuples that don't contain NULLs)
635
      key      IN     Key values tuple
636
637
  DESCRIPTION
638
    Process the first index tuple - find out which prefix tuples don't
639
    contain NULLs, and update the array of notnull counters accordingly.
640
*/
641
642
static
151 by Brian Aker
Ulonglong to uint64_t
643
void mi_collect_stats_nonulls_first(HA_KEYSEG *keyseg, uint64_t *notnull,
481 by Brian Aker
Remove all of uchar.
644
                                    unsigned char *key)
1 by brian
clean slate
645
{
482 by Brian Aker
Remove uint.
646
  uint32_t first_null, kp;
1 by brian
clean slate
647
  first_null= ha_find_null(keyseg, key) - keyseg;
648
  /*
649
    All prefix tuples that don't include keypart_{first_null} are not-null
650
    tuples (and all others aren't), increment counters for them.
651
  */
652
  for (kp= 0; kp < first_null; kp++)
653
    notnull[kp]++;
654
}
655
656
657
/*
658
  "Ignore NULLs" statistics collection method: process next index tuple.
659
660
  SYNOPSIS
661
    mi_collect_stats_nonulls_next()
662
      keyseg   IN     Array of key part descriptions
663
      notnull  INOUT  Array, notnull[i] = (number of {keypart1...keypart_i}
664
                                           tuples that don't contain NULLs)
665
      prev_key IN     Previous key values tuple
666
      last_key IN     Next key values tuple
667
668
  DESCRIPTION
669
    Process the next index tuple:
670
    1. Find out which prefix tuples of last_key don't contain NULLs, and
671
       update the array of notnull counters accordingly.
672
    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:
673
       are different(A), or last_key has NULL value(B), and return it, so the
674
       caller can count number of unique tuples for each key prefix. We don't
675
       need (B) to be counted, and that is compensated back in
1 by brian
clean slate
676
       update_key_parts().
677
678
  RETURN
679
    1 + number of first keypart where values differ or last_key tuple has NULL
680
*/
681
682
static
151 by Brian Aker
Ulonglong to uint64_t
683
int mi_collect_stats_nonulls_next(HA_KEYSEG *keyseg, uint64_t *notnull,
481 by Brian Aker
Remove all of uchar.
684
                                  unsigned char *prev_key, unsigned char *last_key)
1 by brian
clean slate
685
{
482 by Brian Aker
Remove uint.
686
  uint32_t diffs[2];
687
  uint32_t first_null_seg, kp;
1 by brian
clean slate
688
  HA_KEYSEG *seg;
689
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
690
  /*
1 by brian
clean slate
691
     Find the first keypart where values are different or either of them is
692
     NULL. We get results in diffs array:
693
     diffs[0]= 1 + number of first different keypart
694
     diffs[1]=offset: (last_key + diffs[1]) points to first value in
695
                      last_key that is NULL or different from corresponding
696
                      value in prev_key.
697
  */
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
698
  ha_key_cmp(keyseg, prev_key, last_key, USE_WHOLE_KEY,
1 by brian
clean slate
699
             SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diffs);
700
  seg= keyseg + diffs[0] - 1;
701
702
  /* Find first NULL in last_key */
703
  first_null_seg= ha_find_null(seg, last_key + diffs[1]) - keyseg;
704
  for (kp= 0; kp < first_null_seg; kp++)
705
    notnull[kp]++;
706
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
707
  /*
1 by brian
clean slate
708
    Return 1+ number of first key part where values differ. Don't care if
709
    these were NULLs and not .... We compensate for that in
710
    update_key_parts.
711
  */
712
  return diffs[0];
713
}
714
715
716
	/* Check if index is ok */
717
718
static int chk_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
719
		     my_off_t page, unsigned char *buff, ha_rows *keys,
482 by Brian Aker
Remove uint.
720
		     ha_checksum *key_checksum, uint32_t level)
1 by brian
clean slate
721
{
722
  int flag;
482 by Brian Aker
Remove uint.
723
  uint32_t used_length,comp_flag,nod_flag,key_length=0;
2253.1.1 by Andrew Hutchings
Fix Drizzle to compile in GCC 4.6 (which fires warnings and therefore errors if a variable is set and not read)
724
  unsigned char key[HA_MAX_POSSIBLE_KEY_BUFF],*temp_buff,*keypos,*endpos;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
725
  my_off_t next_page,record;
1 by brian
clean slate
726
  char llbuff[22];
482 by Brian Aker
Remove uint.
727
  uint32_t diff_pos[2];
1 by brian
clean slate
728
656.1.1 by Monty Taylor
OOOh doggie. Got rid of my_alloca.
729
  if (!(temp_buff=(unsigned char*) malloc(keyinfo->block_length)))
1 by brian
clean slate
730
  {
731
    mi_check_print_error(param,"Not enough memory for keyblock");
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
732
    return(-1);
1 by brian
clean slate
733
  }
734
735
  if (keyinfo->flag & HA_NOSAME)
736
    comp_flag=SEARCH_FIND | SEARCH_UPDATE;	/* Not real duplicates */
737
  else
738
    comp_flag=SEARCH_SAME;			/* Keys in positionorder */
739
  nod_flag=mi_test_if_nod(buff);
740
  used_length=mi_getint(buff);
741
  keypos=buff+2+nod_flag;
742
  endpos=buff+used_length;
743
744
  param->keydata+=used_length; param->totaldata+=keyinfo->block_length;	/* INFO */
745
  param->key_blocks++;
746
  if (level > param->max_level)
747
    param->max_level=level;
748
749
  if (used_length > keyinfo->block_length)
750
  {
751
    mi_check_print_error(param,"Wrong pageinfo at page: %s",
752
			 llstr(page,llbuff));
753
    goto err;
754
  }
755
  for ( ;; )
756
  {
757
    if (*killed_ptr(param))
758
      goto err;
212.6.12 by Mats Kindahl
Removing redundant use of casts in MyISAM storage for memcmp(), memcpy(), memset(), and memmove().
759
    memcpy(info->lastkey,key,key_length);
1 by brian
clean slate
760
    info->lastkey_length=key_length;
761
    if (nod_flag)
762
    {
763
      next_page=_mi_kpos(nod_flag,keypos);
764
      if (chk_index_down(param,info,keyinfo,next_page,
765
                         temp_buff,keys,key_checksum,level+1))
766
	goto err;
767
    }
768
    if (keypos >= endpos ||
769
	(key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0)
770
      break;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
771
    assert(key_length <= sizeof(key));
1 by brian
clean slate
772
    if (keypos > endpos)
773
    {
774
      mi_check_print_error(param,"Wrong key block length at page: %s",llstr(page,llbuff));
775
      goto err;
776
    }
777
    if ((*keys)++ &&
778
	(flag=ha_key_cmp(keyinfo->seg,info->lastkey,key,key_length,
779
			 comp_flag, diff_pos)) >=0)
780
    {
781
      if (comp_flag & SEARCH_FIND && flag == 0)
782
	mi_check_print_error(param,"Found duplicated key at page %s",llstr(page,llbuff));
783
      else
784
	mi_check_print_error(param,"Key in wrong position at page %s",llstr(page,llbuff));
785
      goto err;
786
    }
787
    if (param->testflag & T_STATISTICS)
788
    {
789
      if (*keys != 1L)				/* not first_key */
790
      {
791
        if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL)
792
          ha_key_cmp(keyinfo->seg,info->lastkey,key,USE_WHOLE_KEY,
793
                     SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL,
794
                     diff_pos);
795
        else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
796
        {
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
797
          diff_pos[0]= mi_collect_stats_nonulls_next(keyinfo->seg,
1 by brian
clean slate
798
                                                  param->notnull_count,
799
                                                  info->lastkey, key);
800
        }
801
	param->unique_count[diff_pos[0]-1]++;
802
      }
803
      else
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
804
      {
1 by brian
clean slate
805
        if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
806
          mi_collect_stats_nonulls_first(keyinfo->seg, param->notnull_count,
807
                                         key);
808
      }
809
    }
481 by Brian Aker
Remove all of uchar.
810
    (*key_checksum)+= mi_byte_checksum((unsigned char*) key,
1 by brian
clean slate
811
				       key_length- info->s->rec_reflength);
812
    record= _mi_dpos(info,0,key+key_length);
813
    if (record >= info->state->data_file_length)
814
    {
815
      mi_check_print_error(param,"Found key at page %s that points to record outside datafile",llstr(page,llbuff));
816
      goto err;
817
    }
818
    param->record_checksum+=(ha_checksum) record;
819
  }
820
  if (keypos != endpos)
821
  {
822
    mi_check_print_error(param,"Keyblock size at page %s is not correct.  Block length: %d  key length: %d",
823
                llstr(page,llbuff), used_length, (keypos - buff));
824
    goto err;
825
  }
656.1.1 by Monty Taylor
OOOh doggie. Got rid of my_alloca.
826
  free(temp_buff);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
827
  return(0);
1 by brian
clean slate
828
 err:
656.1.1 by Monty Taylor
OOOh doggie. Got rid of my_alloca.
829
  free(temp_buff);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
830
  return(1);
1 by brian
clean slate
831
} /* chk_index */
832
833
834
	/* Calculate a checksum of 1+2+3+4...N = N*(N+1)/2 without overflow */
835
836
static ha_checksum calc_checksum(ha_rows count)
837
{
151 by Brian Aker
Ulonglong to uint64_t
838
  uint64_t sum,a,b;
1 by brian
clean slate
839
840
  sum=0;
841
  a=count; b=count+1;
842
  if (a & 1)
843
    b>>=1;
844
  else
845
    a>>=1;
846
  while (b)
847
  {
848
    if (b & 1)
849
      sum+=a;
850
    a<<=1; b>>=1;
851
  }
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
852
  return((ha_checksum) sum);
1 by brian
clean slate
853
} /* calc_checksum */
854
855
856
	/* Calc length of key in normal isam */
857
482 by Brian Aker
Remove uint.
858
static uint32_t isam_key_length(MI_INFO *info, register MI_KEYDEF *keyinfo)
1 by brian
clean slate
859
{
482 by Brian Aker
Remove uint.
860
  uint32_t length;
1 by brian
clean slate
861
  HA_KEYSEG *keyseg;
862
863
  length= info->s->rec_reflength;
864
  for (keyseg=keyinfo->seg ; keyseg->type ; keyseg++)
865
    length+= keyseg->length;
866
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
867
  return(length);
1 by brian
clean slate
868
} /* key_length */
869
870
871
	/* Check that record-link is ok */
872
873
int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend)
874
{
875
  int	error,got_error,flag;
2253.1.1 by Andrew Hutchings
Fix Drizzle to compile in GCC 4.6 (which fires warnings and therefore errors if a variable is set and not read)
876
  uint	key, left_length= 0, b_type;
1 by brian
clean slate
877
  ha_rows records, del_blocks;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
878
  my_off_t used, empty, pos, splits, start_recpos= 0,
1 by brian
clean slate
879
           del_length, link_used, start_block;
481 by Brian Aker
Remove all of uchar.
880
  unsigned char	*record= NULL, *to= NULL;
1 by brian
clean slate
881
  char llbuff[22],llbuff2[22],llbuff3[22];
882
  ha_checksum intern_record_checksum;
883
  ha_checksum key_checksum[HA_MAX_POSSIBLE_KEY];
884
  MI_KEYDEF *keyinfo;
885
  MI_BLOCK_INFO block_info;
886
887
  if (!(param->testflag & T_SILENT))
888
  {
889
    if (extend)
890
      puts("- check records and index references");
891
    else
892
      puts("- check record links");
893
  }
894
1816.2.4 by Monty Taylor
Cleaned up a bunch more warnings.
895
  if (!mi_alloc_rec_buff(info, SIZE_MAX, &record))
1 by brian
clean slate
896
  {
897
    mi_check_print_error(param,"Not enough memory for record");
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
898
    return(-1);
1 by brian
clean slate
899
  }
900
  records=del_blocks=0;
901
  used=link_used=splits=del_length=0;
902
  intern_record_checksum=param->glob_crc=0;
903
  got_error=error=0;
904
  empty=info->s->pack.header_length;
905
906
  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().
907
  memset(key_checksum, 0, info->s->base.keys * sizeof(key_checksum[0]));
1 by brian
clean slate
908
  while (pos < info->state->data_file_length)
909
  {
910
    if (*killed_ptr(param))
911
      goto err2;
912
    switch (info->s->data_file_type) {
913
    case STATIC_RECORD:
481 by Brian Aker
Remove all of uchar.
914
      if (my_b_read(&param->read_cache,(unsigned char*) record,
1 by brian
clean slate
915
		    info->s->base.pack_reclength))
916
	goto err;
917
      start_recpos=pos;
918
      pos+=info->s->base.pack_reclength;
919
      splits++;
920
      if (*record == '\0')
921
      {
922
	del_blocks++;
923
	del_length+=info->s->base.pack_reclength;
924
	continue;					/* Record removed */
925
      }
926
      param->glob_crc+= mi_static_checksum(info,record);
927
      used+=info->s->base.pack_reclength;
928
      break;
929
    case DYNAMIC_RECORD:
930
      flag=block_info.second_read=0;
931
      block_info.next_filepos=pos;
932
      do
933
      {
481 by Brian Aker
Remove all of uchar.
934
	if (_mi_read_cache(&param->read_cache,(unsigned char*) block_info.header,
1 by brian
clean slate
935
			   (start_block=block_info.next_filepos),
936
			   sizeof(block_info.header),
937
			   (flag ? 0 : READING_NEXT) | READING_HEADER))
938
	  goto err;
939
	if (start_block & (MI_DYN_ALIGN_SIZE-1))
940
	{
941
	  mi_check_print_error(param,"Wrong aligned block at %s",
942
			       llstr(start_block,llbuff));
943
	  goto err2;
944
	}
945
	b_type=_mi_get_block_info(&block_info,-1,start_block);
946
	if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
947
		      BLOCK_FATAL_ERROR))
948
	{
949
	  if (b_type & BLOCK_SYNC_ERROR)
950
	  {
951
	    if (flag)
952
	    {
953
	      mi_check_print_error(param,"Unexpected byte: %d at link: %s",
954
			  (int) block_info.header[0],
955
			  llstr(start_block,llbuff));
956
	      goto err2;
957
	    }
958
	    pos=block_info.filepos+block_info.block_len;
959
	    goto next;
960
	  }
961
	  if (b_type & BLOCK_DELETED)
962
	  {
963
	    if (block_info.block_len < info->s->base.min_block_length)
964
	    {
965
	      mi_check_print_error(param,
966
				   "Deleted block with impossible length %lu at %s",
967
				   block_info.block_len,llstr(pos,llbuff));
968
	      goto err2;
969
	    }
970
	    if ((block_info.next_filepos != HA_OFFSET_ERROR &&
971
		 block_info.next_filepos >= info->state->data_file_length) ||
972
		(block_info.prev_filepos != HA_OFFSET_ERROR &&
973
		 block_info.prev_filepos >= info->state->data_file_length))
974
	    {
975
	      mi_check_print_error(param,"Delete link points outside datafile at %s",
976
			  llstr(pos,llbuff));
977
	      goto err2;
978
	    }
979
	    del_blocks++;
980
	    del_length+=block_info.block_len;
981
	    pos=block_info.filepos+block_info.block_len;
982
	    splits++;
983
	    goto next;
984
	  }
985
	  mi_check_print_error(param,"Wrong bytesec: %d-%d-%d at linkstart: %s",
986
			       block_info.header[0],block_info.header[1],
987
			       block_info.header[2],
988
			       llstr(start_block,llbuff));
989
	  goto err2;
990
	}
991
	if (info->state->data_file_length < block_info.filepos+
992
	    block_info.block_len)
993
	{
994
	  mi_check_print_error(param,
995
			       "Recordlink that points outside datafile at %s",
996
			       llstr(pos,llbuff));
997
	  got_error=1;
998
	  break;
999
	}
1000
	splits++;
1001
	if (!flag++)				/* First block */
1002
	{
1003
	  start_recpos=pos;
1004
	  pos=block_info.filepos+block_info.block_len;
1005
	  if (block_info.rec_len > (uint) info->s->base.max_pack_length)
1006
	  {
1007
	    mi_check_print_error(param,"Found too long record (%lu) at %s",
1008
				 (ulong) block_info.rec_len,
1009
				 llstr(start_recpos,llbuff));
1010
	    got_error=1;
1011
	    break;
1012
	  }
1013
	  if (info->s->base.blobs)
1014
	  {
1015
	    if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
1016
					&info->rec_buff)))
1017
	    {
1018
	      mi_check_print_error(param,
1019
				   "Not enough memory (%lu) for blob at %s",
1020
				   (ulong) block_info.rec_len,
1021
				   llstr(start_recpos,llbuff));
1022
	      got_error=1;
1023
	      break;
1024
	    }
1025
	  }
1026
	  else
1027
	    to= info->rec_buff;
1028
	  left_length=block_info.rec_len;
1029
	}
1030
	if (left_length < block_info.data_len)
1031
	{
1032
	  mi_check_print_error(param,"Found too long record (%lu) at %s",
1033
			       (ulong) block_info.data_len,
1034
			       llstr(start_recpos,llbuff));
1035
	  got_error=1;
1036
	  break;
1037
	}
481 by Brian Aker
Remove all of uchar.
1038
	if (_mi_read_cache(&param->read_cache,(unsigned char*) to,block_info.filepos,
1 by brian
clean slate
1039
			   (uint) block_info.data_len,
1040
			   flag == 1 ? READING_NEXT : 0))
1041
	  goto err;
1042
	to+=block_info.data_len;
1043
	link_used+= block_info.filepos-start_block;
1044
	used+= block_info.filepos - start_block + block_info.data_len;
1045
	empty+=block_info.block_len-block_info.data_len;
1046
	left_length-=block_info.data_len;
1047
	if (left_length)
1048
	{
1049
	  if (b_type & BLOCK_LAST)
1050
	  {
1051
	    mi_check_print_error(param,
1052
				 "Wrong record length %s of %s at %s",
1053
				 llstr(block_info.rec_len-left_length,llbuff),
1054
				 llstr(block_info.rec_len, llbuff2),
1055
				 llstr(start_recpos,llbuff3));
1056
	    got_error=1;
1057
	    break;
1058
	  }
1059
	  if (info->state->data_file_length < block_info.next_filepos)
1060
	  {
1061
	    mi_check_print_error(param,
1062
				 "Found next-recordlink that points outside datafile at %s",
1063
				 llstr(block_info.filepos,llbuff));
1064
	    got_error=1;
1065
	    break;
1066
	  }
1067
	}
1068
      } while (left_length);
1069
      if (! got_error)
1070
      {
1071
	if (_mi_rec_unpack(info,record,info->rec_buff,block_info.rec_len) ==
1072
	    MY_FILE_ERROR)
1073
	{
1074
	  mi_check_print_error(param,"Found wrong record at %s",
1075
			       llstr(start_recpos,llbuff));
1076
	  got_error=1;
1077
	}
1078
	else
1079
	{
1080
	  info->checksum=mi_checksum(info,record);
1081
	  if (param->testflag & (T_EXTEND | T_MEDIUM | T_VERBOSE))
1082
	  {
1083
	    if (_mi_rec_check(info,record, info->rec_buff,block_info.rec_len,
1084
                              test(info->s->calc_checksum)))
1085
	    {
1086
	      mi_check_print_error(param,"Found wrong packed record at %s",
1087
			  llstr(start_recpos,llbuff));
1088
	      got_error=1;
1089
	    }
1090
	  }
1091
	  if (!got_error)
1092
	    param->glob_crc+= info->checksum;
1093
	}
1094
      }
1095
      else if (!flag)
1096
	pos=block_info.filepos+block_info.block_len;
1097
      break;
1098
    case COMPRESSED_RECORD:
1099
    case BLOCK_RECORD:
1100
      assert(0);                                /* Impossible */
1101
    } /* switch */
1102
    if (! got_error)
1103
    {
1104
      intern_record_checksum+=(ha_checksum) start_recpos;
1105
      records++;
1106
      if (param->testflag & T_WRITE_LOOP && records % WRITE_COUNT == 0)
1107
      {
398.1.10 by Monty Taylor
Actually removed VOID() this time.
1108
	printf("%s\r", llstr(records,llbuff)); fflush(stdout);
1 by brian
clean slate
1109
      }
1110
1111
      /* Check if keys match the record */
1112
1113
      for (key=0,keyinfo= info->s->keyinfo; key < info->s->base.keys;
1114
	   key++,keyinfo++)
1115
      {
1116
        if (mi_is_key_active(info->s->state.key_map, key))
1117
	{
1118
	  {
482 by Brian Aker
Remove uint.
1119
	    uint32_t key_length=_mi_make_key(info,key,info->lastkey,record,
1 by brian
clean slate
1120
					 start_recpos);
1121
	    if (extend)
1122
	    {
1123
	      /* We don't need to lock the key tree here as we don't allow
1124
		 concurrent threads when running myisamchk
1125
	      */
1126
              int search_result=
1127
                _mi_search(info,keyinfo,info->lastkey,key_length,
1128
                           SEARCH_SAME, info->s->state.key_root[key]);
1129
              if (search_result)
1130
              {
1131
                mi_check_print_error(param,"Record at: %10s  "
1132
                                     "Can't find key for index: %2d",
1133
                                     llstr(start_recpos,llbuff),key+1);
1134
                if (error++ > MAXERR || !(param->testflag & T_VERBOSE))
1135
                  goto err2;
1136
              }
1137
	    }
1138
	    else
481 by Brian Aker
Remove all of uchar.
1139
	      key_checksum[key]+=mi_byte_checksum((unsigned char*) info->lastkey,
1 by brian
clean slate
1140
						  key_length);
1141
	  }
1142
	}
1143
      }
1144
    }
1145
    else
1146
    {
1147
      got_error=0;
1148
      if (error++ > MAXERR || !(param->testflag & T_VERBOSE))
1149
	goto err2;
1150
    }
1151
  next:;				/* Next record */
1152
  }
1153
  if (param->testflag & T_WRITE_LOOP)
1154
  {
398.1.10 by Monty Taylor
Actually removed VOID() this time.
1155
    fputs("          \r",stdout); fflush(stdout);
1 by brian
clean slate
1156
  }
1157
  if (records != info->state->records)
1158
  {
1159
    mi_check_print_error(param,"Record-count is not ok; is %-10s   Should be: %s",
1160
		llstr(records,llbuff), llstr(info->state->records,llbuff2));
1161
    error=1;
1162
  }
1163
  else if (param->record_checksum &&
1164
	   param->record_checksum != intern_record_checksum)
1165
  {
1166
    mi_check_print_error(param,
1167
			 "Keypointers and record positions doesn't match");
1168
    error=1;
1169
  }
1170
  else if (param->glob_crc != info->state->checksum &&
1171
	   (info->s->options &
1117.1.2 by Brian Aker
Remove CHECKSUM option in create table.
1172
	    (HA_OPTION_COMPRESS_RECORD)))
1 by brian
clean slate
1173
  {
1174
    mi_check_print_warning(param,
1175
			   "Record checksum is not the same as checksum stored in the index file\n");
1176
    error=1;
1177
  }
1178
  else if (!extend)
1179
  {
1180
    for (key=0 ; key < info->s->base.keys;  key++)
1181
    {
249 by Brian Aker
Random key cleanup (it is a friday...)
1182
      if (key_checksum[key] != param->key_crc[key])
1 by brian
clean slate
1183
      {
1184
	mi_check_print_error(param,"Checksum for key: %2d doesn't match checksum for records",
1185
		    key+1);
1186
	error=1;
1187
      }
1188
    }
1189
  }
1190
1191
  if (del_length != info->state->empty)
1192
  {
1193
    mi_check_print_warning(param,
1194
			   "Found %s deleted space.   Should be %s",
1195
			   llstr(del_length,llbuff2),
1196
			   llstr(info->state->empty,llbuff));
1197
  }
1198
  if (used+empty+del_length != info->state->data_file_length)
1199
  {
1200
    mi_check_print_warning(param,
1201
			   "Found %s record-data and %s unused data and %s deleted-data",
1202
			   llstr(used,llbuff),llstr(empty,llbuff2),
1203
			   llstr(del_length,llbuff3));
1204
    mi_check_print_warning(param,
1205
			   "Total %s, Should be: %s",
1206
			   llstr((used+empty+del_length),llbuff),
1207
			   llstr(info->state->data_file_length,llbuff2));
1208
  }
1209
  if (del_blocks != info->state->del)
1210
  {
1211
    mi_check_print_warning(param,
1212
			   "Found %10s deleted blocks       Should be: %s",
1213
			   llstr(del_blocks,llbuff),
1214
			   llstr(info->state->del,llbuff2));
1215
  }
1216
  if (splits != info->s->state.split)
1217
  {
1218
    mi_check_print_warning(param,
1219
			   "Found %10s parts                Should be: %s parts",
1220
			   llstr(splits,llbuff),
1221
			   llstr(info->s->state.split,llbuff2));
1222
  }
1223
  if (param->testflag & T_INFO)
1224
  {
1225
    if (param->warning_printed || param->error_printed)
1226
      puts("");
1227
    if (used != 0 && ! param->error_printed)
1228
    {
1229
      printf("Records:%18s    M.recordlength:%9lu   Packed:%14.0f%%\n",
1230
	     llstr(records,llbuff), (long)((used-link_used)/records),
1231
	     (info->s->base.blobs ? 0.0 :
151 by Brian Aker
Ulonglong to uint64_t
1232
	      (uint64_t2double((uint64_t) info->s->base.reclength*records)-
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
1233
	       my_off_t2double(used))/
151 by Brian Aker
Ulonglong to uint64_t
1234
	      uint64_t2double((uint64_t) info->s->base.reclength*records)*100.0));
1 by brian
clean slate
1235
      printf("Recordspace used:%9.0f%%   Empty space:%12d%%  Blocks/Record: %6.2f\n",
151 by Brian Aker
Ulonglong to uint64_t
1236
	     (uint64_t2double(used-link_used)/uint64_t2double(used-link_used+empty)*100.0),
1237
	     (!records ? 100 : (int) (uint64_t2double(del_length+empty)/
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
1238
				      my_off_t2double(used)*100.0)),
151 by Brian Aker
Ulonglong to uint64_t
1239
	     uint64_t2double(splits - del_blocks) / records);
1 by brian
clean slate
1240
    }
1241
    printf("Record blocks:%12s    Delete blocks:%10s\n",
1242
	   llstr(splits-del_blocks,llbuff),llstr(del_blocks,llbuff2));
1243
    printf("Record data:  %12s    Deleted data: %10s\n",
1244
	   llstr(used-link_used,llbuff),llstr(del_length,llbuff2));
1245
    printf("Lost space:   %12s    Linkdata:     %10s\n",
1246
	   llstr(empty,llbuff),llstr(link_used,llbuff2));
1247
  }
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.
1248
  free(mi_get_rec_buff_ptr(info, record));
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1249
  return (error);
1 by brian
clean slate
1250
 err:
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
1251
  mi_check_print_error(param,"got error: %d when reading datafile at record: %s",errno, llstr(records,llbuff));
1 by brian
clean slate
1252
 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.
1253
  free(mi_get_rec_buff_ptr(info, record));
1 by brian
clean slate
1254
  param->testflag|=T_RETRY_WITHOUT_QUICK;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1255
  return(1);
1 by brian
clean slate
1256
} /* chk_data_link */
1257
1258
1259
/**
1260
  @brief Drop all indexes
1261
1262
  @param[in]    param           check parameters
1263
  @param[in]    info            MI_INFO handle
1264
  @param[in]    force           if to force drop all indexes
1265
1266
  @return       status
1267
    @retval     0               OK
1268
    @retval     != 0            Error
1269
1270
  @note
1271
    Once allocated, index blocks remain part of the key file forever.
1272
    When indexes are disabled, no block is freed. When enabling indexes,
1273
    no block is freed either. The new indexes are create from new
1274
    blocks. (Bug #4692)
1275
1276
    Before recreating formerly disabled indexes, the unused blocks
1277
    must be freed. There are two options to do this:
1278
    - Follow the tree of disabled indexes, add all blocks to the
1279
      deleted blocks chain. Would require a lot of random I/O.
1280
    - Drop all blocks by clearing all index root pointers and all
1281
      delete chain pointers and resetting key_file_length to the end
1282
      of the index file header. This requires to recreate all indexes,
1283
      even those that may still be intact.
1284
    The second method is probably faster in most cases.
1285
1286
    When disabling indexes, MySQL disables either all indexes or all
1287
    non-unique indexes. When MySQL [re-]enables disabled indexes
1288
    (T_CREATE_MISSING_KEYS), then we either have "lost" blocks in the
1289
    index file, or there are no non-unique indexes. In the latter case,
1290
    mi_repair*() would not be called as there would be no disabled
1291
    indexes.
1292
1293
    If there would be more unique indexes than disabled (non-unique)
1294
    indexes, we could do the first method. But this is not implemented
1295
    yet. By now we drop and recreate all indexes when repair is called.
1296
1297
    However, there is an exception. Sometimes MySQL disables non-unique
1298
    indexes when the table is empty (e.g. when copying a table in
1130.3.13 by Monty Taylor
Finished cleaning namespaces in drizzled/statement
1299
    drizzled::alter_table()). When enabling the non-unique indexes, they
1 by brian
clean slate
1300
    are still empty. So there is no index block that can be lost. This
1301
    optimization is implemented in this function.
1302
1303
    Note that in normal repair (T_CREATE_MISSING_KEYS not set) we
1304
    recreate all enabled indexes unconditonally. We do not change the
1305
    key_map. Otherwise we invert the key map temporarily (outside of
1306
    this function) and recreate the then "seemingly" enabled indexes.
1307
    When we cannot use the optimization, and drop all indexes, we
1308
    pretend that all indexes were disabled. By the inversion, we will
1309
    then recrate all indexes.
1310
*/
1311
281 by Brian Aker
Converted myisam away from my_bool
1312
static int mi_drop_all_indexes(MI_CHECK *param, MI_INFO *info, bool force)
1 by brian
clean slate
1313
{
1314
  MYISAM_SHARE *share= info->s;
1315
  MI_STATE_INFO *state= &share->state;
482 by Brian Aker
Remove uint.
1316
  uint32_t i;
1 by brian
clean slate
1317
  int error;
1318
1319
  /*
1320
    If any of the disabled indexes has a key block assigned, we must
1321
    drop and recreate all indexes to avoid losing index blocks.
1322
1323
    If we want to recreate disabled indexes only _and_ all of these
1324
    indexes are empty, we don't need to recreate the existing indexes.
1325
  */
1326
  if (!force && (param->testflag & T_CREATE_MISSING_KEYS))
1327
  {
1328
    for (i= 0; i < share->base.keys; i++)
1329
    {
1330
      if ((state->key_root[i] != HA_OFFSET_ERROR) &&
1331
          !mi_is_key_active(state->key_map, i))
1332
      {
1333
        /*
1334
          This index has at least one key block and it is disabled.
1335
          We would lose its block(s) if would just recreate it.
1336
          So we need to drop and recreate all indexes.
1337
        */
1338
        break;
1339
      }
1340
    }
1341
    if (i >= share->base.keys)
1342
    {
1343
      /*
1344
        All of the disabled indexes are empty. We can just recreate them.
1345
        Flush dirty blocks of this index file from key cache and remove
1346
        all blocks of this index file from key cache.
1347
      */
1689.2.15 by Brian Aker
Encapsulate the key cache object in myisam.
1348
      error= flush_key_blocks(share->getKeyCache(), share->kfile,
1 by brian
clean slate
1349
                              FLUSH_FORCE_WRITE);
1350
      goto end;
1351
    }
1352
    /*
1353
      We do now drop all indexes and declare them disabled. With the
1354
      T_CREATE_MISSING_KEYS flag, mi_repair*() will recreate all
1355
      disabled indexes and enable them.
1356
    */
1357
    mi_clear_all_keys_active(state->key_map);
1358
  }
1359
1360
  /* Remove all key blocks of this index file from key cache. */
1689.2.15 by Brian Aker
Encapsulate the key cache object in myisam.
1361
  if ((error= flush_key_blocks(share->getKeyCache(), share->kfile,
1 by brian
clean slate
1362
                               FLUSH_IGNORE_CHANGED)))
971.6.11 by Eric Day
Removed purecov messages.
1363
    goto end;
1 by brian
clean slate
1364
1365
  /* Clear index root block pointers. */
1366
  for (i= 0; i < share->base.keys; i++)
1367
    state->key_root[i]= HA_OFFSET_ERROR;
1368
1369
  /* Clear the delete chains. */
1370
  for (i= 0; i < state->header.max_block_size_index; i++)
1371
    state->key_del[i]= HA_OFFSET_ERROR;
1372
1373
  /* Reset index file length to end of index file header. */
1374
  info->state->key_file_length= share->base.keystart;
1375
1376
  /* error= 0; set by last (error= flush_key_bocks()). */
1377
1378
 end:
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1379
  return(error);
1 by brian
clean slate
1380
}
1381
1382
1383
	/* Recover old table by reading each record and writing all keys */
1384
	/* Save new datafile-name in temp_filename */
1385
1386
int mi_repair(MI_CHECK *param, register MI_INFO *info,
1387
	      char * name, int rep_quick)
1388
{
1389
  int error,got_error;
1390
  ha_rows start_records,new_header_length;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
1391
  my_off_t del;
1241.9.1 by Monty Taylor
Removed global.h. Fixed all the headers.
1392
  int new_file;
1 by brian
clean slate
1393
  MYISAM_SHARE *share=info->s;
1394
  char llbuff[22],llbuff2[22];
1395
  SORT_INFO sort_info;
1396
  MI_SORT_PARAM sort_param;
1397
212.6.12 by Mats Kindahl
Removing redundant use of casts in MyISAM storage for memcmp(), memcpy(), memset(), and memmove().
1398
  memset(&sort_info, 0, sizeof(sort_info));
1399
  memset(&sort_param, 0, sizeof(sort_param));
1 by brian
clean slate
1400
  start_records=info->state->records;
1401
  new_header_length=(param->testflag & T_UNPACK) ? 0L :
1402
    share->pack.header_length;
1403
  got_error=1;
1404
  new_file= -1;
1405
  sort_param.sort_info=&sort_info;
1406
1407
  if (!(param->testflag & T_SILENT))
1408
  {
1409
    printf("- recovering (with keycache) MyISAM-table '%s'\n",name);
1410
    printf("Data records: %s\n", llstr(info->state->records,llbuff));
1411
  }
1412
  param->testflag|=T_REP; /* for easy checking */
1413
1117.1.2 by Brian Aker
Remove CHECKSUM option in create table.
1414
  if (info->s->options & (HA_OPTION_COMPRESS_RECORD))
1 by brian
clean slate
1415
    param->testflag|=T_CALC_CHECKSUM;
1416
1417
  if (!param->using_global_keycache)
1689.2.17 by Brian Aker
Use local key_Cache, global now goes away.
1418
    assert(0);
1 by brian
clean slate
1419
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
1420
  if (param->read_cache.init_io_cache(info->dfile, (uint) param->read_buffer_length, READ_CACHE,share->pack.header_length,1,MYF(MY_WME)))
1 by brian
clean slate
1421
  {
212.6.1 by Mats Kindahl
Replacing all bzero() calls with memset() calls and removing the bzero.c file.
1422
    memset(&info->rec_cache, 0, sizeof(info->rec_cache));
1 by brian
clean slate
1423
    goto err;
1424
  }
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
1425
  if (not rep_quick)
1426
  {
1427
    if (info->rec_cache.init_io_cache(-1, (uint) param->write_buffer_length, WRITE_CACHE, new_header_length, 1, MYF(MY_WME | MY_WAIT_IF_FULL)))
1428
    {
1 by brian
clean slate
1429
      goto err;
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
1430
    }
1431
  }
1 by brian
clean slate
1432
  info->opt_flag|=WRITE_CACHE_USED;
1816.2.4 by Monty Taylor
Cleaned up a bunch more warnings.
1433
  if (!mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.record) ||
1434
      !mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.rec_buff))
1 by brian
clean slate
1435
  {
1436
    mi_check_print_error(param, "Not enough memory for extra record");
1437
    goto err;
1438
  }
1439
1440
  if (!rep_quick)
1441
  {
1442
    /* Get real path for data file */
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
1443
    if ((new_file=my_create(internal::fn_format(param->temp_filename,
489.1.6 by Monty Taylor
Removed RAID garbage.
1444
                                      share->data_file_name, "",
1445
                                      DATA_TMP_EXT, 2+4),
1446
                            0,param->tmpfile_createflag,
1447
                            MYF(0))) < 0)
1 by brian
clean slate
1448
    {
1449
      mi_check_print_error(param,"Can't create new tempfile: '%s'",
489.1.6 by Monty Taylor
Removed RAID garbage.
1450
                           param->temp_filename);
1 by brian
clean slate
1451
      goto err;
1452
    }
1453
    if (new_header_length &&
1454
        filecopy(param,new_file,info->dfile,0L,new_header_length,
489.1.6 by Monty Taylor
Removed RAID garbage.
1455
                 "datafile-header"))
1 by brian
clean slate
1456
      goto err;
1457
    info->s->state.dellink= HA_OFFSET_ERROR;
1458
    info->rec_cache.file=new_file;
1459
    if (param->testflag & T_UNPACK)
1460
    {
1461
      share->options&= ~HA_OPTION_COMPRESS_RECORD;
1462
      mi_int2store(share->state.header.options,share->options);
1463
    }
1464
  }
1465
  sort_info.info=info;
1466
  sort_info.param = param;
1467
  sort_param.read_cache=param->read_cache;
1468
  sort_param.pos=sort_param.max_pos=share->pack.header_length;
1469
  sort_param.filepos=new_header_length;
1470
  param->read_cache.end_of_file=sort_info.filelength=
656.1.39 by Monty Taylor
Removed my_seek, my_tell, my_fwrite, my_fseek.
1471
    lseek(info->dfile,0L,SEEK_END);
1 by brian
clean slate
1472
  sort_info.dupp=0;
281 by Brian Aker
Converted myisam away from my_bool
1473
  sort_param.fix_datafile= (bool) (! rep_quick);
1 by brian
clean slate
1474
  sort_param.master=1;
1475
  sort_info.max_records= ~(ha_rows) 0;
1476
1477
  set_data_file_type(&sort_info, share);
1478
  del=info->state->del;
1479
  info->state->records=info->state->del=share->state.split=0;
1480
  info->state->empty=0;
1481
  param->glob_crc=0;
1482
  if (param->testflag & T_CALC_CHECKSUM)
1483
    sort_param.calc_checksum= 1;
1484
1485
  info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
1486
1487
  /* This function always recreates all enabled indexes. */
1488
  if (param->testflag & T_CREATE_MISSING_KEYS)
1489
    mi_set_all_keys_active(share->state.key_map, share->base.keys);
163 by Brian Aker
Merge Monty's code.
1490
  mi_drop_all_indexes(param, info, true);
1 by brian
clean slate
1491
1492
  lock_memory(param);			/* Everything is alloced */
1493
1494
  /* Re-create all keys, which are set in key_map. */
1495
  while (!(error=sort_get_next_record(&sort_param)))
1496
  {
1497
    if (writekeys(&sort_param))
1498
    {
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
1499
      if (errno != HA_ERR_FOUND_DUPP_KEY)
1 by brian
clean slate
1500
	goto err;
1501
      mi_check_print_info(param,"Duplicate key %2d for record at %10s against new record at %10s",
1502
			  info->errkey+1,
1503
			  llstr(sort_param.start_recpos,llbuff),
1504
			  llstr(info->dupp_key_pos,llbuff2));
1505
      if (param->testflag & T_VERBOSE)
1506
      {
398.1.10 by Monty Taylor
Actually removed VOID() this time.
1507
	_mi_make_key(info,(uint) info->errkey,info->lastkey,
1508
                     sort_param.record,0L);
1 by brian
clean slate
1509
      }
1510
      sort_info.dupp++;
1511
      if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
1512
      {
1513
        param->testflag|=T_RETRY_WITHOUT_QUICK;
1514
	param->error_printed=1;
1515
	goto err;
1516
      }
1517
      continue;
1518
    }
1519
    if (sort_write_record(&sort_param))
1520
      goto err;
1521
  }
281 by Brian Aker
Converted myisam away from my_bool
1522
  if (error > 0 || write_data_suffix(&sort_info, (bool)!rep_quick) ||
1 by brian
clean slate
1523
      flush_io_cache(&info->rec_cache) || param->read_cache.error < 0)
1524
    goto err;
1525
1526
  if (param->testflag & T_WRITE_LOOP)
1527
  {
398.1.10 by Monty Taylor
Actually removed VOID() this time.
1528
    fputs("          \r",stdout); fflush(stdout);
1 by brian
clean slate
1529
  }
30 by Brian Aker
Large file and ftruncate() support
1530
  if (ftruncate(share->kfile, info->state->key_file_length))
1 by brian
clean slate
1531
  {
1532
    mi_check_print_warning(param,
1533
			   "Can't change size of indexfile, error: %d",
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
1534
			   errno);
1 by brian
clean slate
1535
    goto err;
1536
  }
1537
1538
  if (rep_quick && del+sort_info.dupp != info->state->del)
1539
  {
1540
    mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
1541
    mi_check_print_error(param,"Run recovery again without -q");
1542
    got_error=1;
1543
    param->retry_repair=1;
1544
    param->testflag|=T_RETRY_WITHOUT_QUICK;
1545
    goto err;
1546
  }
1547
  if (param->testflag & T_SAFE_REPAIR)
1548
  {
1549
    /* Don't repair if we loosed more than one row */
1550
    if (info->state->records+1 < start_records)
1551
    {
1552
      info->state->records=start_records;
1553
      got_error=1;
1554
      goto err;
1555
    }
1556
  }
1557
1558
  if (!rep_quick)
1559
  {
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
1560
    internal::my_close(info->dfile,MYF(0));
1 by brian
clean slate
1561
    info->dfile=new_file;
1562
    info->state->data_file_length=sort_param.filepos;
1563
    share->state.version=(ulong) time((time_t*) 0);	/* Force reopen */
1564
  }
1565
  else
1566
  {
1567
    info->state->data_file_length=sort_param.max_pos;
1568
  }
1569
  if (param->testflag & T_CALC_CHECKSUM)
1570
    info->state->checksum=param->glob_crc;
1571
1572
  if (!(param->testflag & T_SILENT))
1573
  {
1574
    if (start_records != info->state->records)
1575
      printf("Data records: %s\n", llstr(info->state->records,llbuff));
1576
    if (sort_info.dupp)
1577
      mi_check_print_warning(param,
1578
			     "%s records have been removed",
1579
			     llstr(sort_info.dupp,llbuff));
1580
  }
1581
1582
  got_error=0;
1583
  /* If invoked by external program that uses thr_lock */
1584
  if (&share->state.state != info->state)
1585
    memcpy( &share->state.state, info->state, sizeof(*info->state));
1586
1587
err:
1588
  if (!got_error)
1589
  {
1590
    /* Replace the actual file with the temporary file */
1591
    if (new_file >= 0)
1592
    {
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
1593
      internal::my_close(new_file,MYF(0));
1 by brian
clean slate
1594
      info->dfile=new_file= -1;
1595
      if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
1596
			    DATA_TMP_EXT, share->base.raid_chunks,
2241.4.19 by Stewart Smith
A few unused bits of mysys. Turns out internal::get_date() was only used by one codepath of my_redel() which was never called due to a flag never being set in MyISAM. Just rip it out. One whole less file to build
1597
			    MYF(0)) ||
1 by brian
clean slate
1598
	  mi_open_datafile(info,share,-1))
1599
	got_error=1;
1600
    }
1601
  }
1602
  if (got_error)
1603
  {
1604
    if (! param->error_printed)
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
1605
      mi_check_print_error(param,"%d for record at pos %s",errno,
1 by brian
clean slate
1606
		  llstr(sort_param.start_recpos,llbuff));
1607
    if (new_file >= 0)
1608
    {
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
1609
      internal::my_close(new_file,MYF(0));
489.1.6 by Monty Taylor
Removed RAID garbage.
1610
      my_delete(param->temp_filename, MYF(MY_WME));
1 by brian
clean slate
1611
      info->rec_cache.file=-1; /* don't flush data to new_file, it's closed */
1612
    }
1613
    mi_mark_crashed_on_repair(info);
1614
  }
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.
1615
1616
  void * rec_buff_ptr= NULL;
1617
  rec_buff_ptr= mi_get_rec_buff_ptr(info, sort_param.rec_buff);
2353.3.1 by Mark Atwood
fix cppcheck redundantIfDelete0 warnings. It is safe to deallocate a NULL pointer
1618
  free(rec_buff_ptr);
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.
1619
  rec_buff_ptr= mi_get_rec_buff_ptr(info, sort_param.record);
2353.3.1 by Mark Atwood
fix cppcheck redundantIfDelete0 warnings. It is safe to deallocate a NULL pointer
1620
  free(rec_buff_ptr);
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.
1621
  rec_buff_ptr= NULL;
1622
1623
  free(sort_info.buff);
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
1624
  param->read_cache.end_io_cache();
1 by brian
clean slate
1625
  info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
1626
  info->rec_cache.end_io_cache();
1689.2.15 by Brian Aker
Encapsulate the key cache object in myisam.
1627
  got_error|= flush_blocks(param, share->getKeyCache(), share->kfile);
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
1628
  if (not got_error && param->testflag & T_UNPACK)
1 by brian
clean slate
1629
  {
481 by Brian Aker
Remove all of uchar.
1630
    share->state.header.options[0]&= (unsigned char) ~HA_OPTION_COMPRESS_RECORD;
1 by brian
clean slate
1631
    share->pack.header_length=0;
1632
    share->data_file_type=sort_info.new_data_file_type;
1633
  }
1634
  share->state.changed|= (STATE_NOT_OPTIMIZED_KEYS | STATE_NOT_SORTED_PAGES |
1635
			  STATE_NOT_ANALYZED);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1636
  return(got_error);
1 by brian
clean slate
1637
}
1638
1639
1640
/* Uppate keyfile when doing repair */
1641
1642
static int writekeys(MI_SORT_PARAM *sort_param)
1643
{
482 by Brian Aker
Remove uint.
1644
  register uint32_t i;
481 by Brian Aker
Remove all of uchar.
1645
  unsigned char    *key;
1 by brian
clean slate
1646
  MI_INFO  *info=   sort_param->sort_info->info;
481 by Brian Aker
Remove all of uchar.
1647
  unsigned char    *buff=   sort_param->record;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
1648
  my_off_t filepos= sort_param->filepos;
1 by brian
clean slate
1649
1650
  key=info->lastkey+info->s->base.max_key_length;
1651
  for (i=0 ; i < info->s->base.keys ; i++)
1652
  {
1653
    if (mi_is_key_active(info->s->state.key_map, i))
1654
    {
1655
      {
482 by Brian Aker
Remove uint.
1656
	uint32_t key_length=_mi_make_key(info,i,key,buff,filepos);
1 by brian
clean slate
1657
	if (_mi_ck_write(info,i,key,key_length))
1658
	  goto err;
1659
      }
1660
    }
1661
  }
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1662
  return(0);
1 by brian
clean slate
1663
1664
 err:
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
1665
  if (errno == HA_ERR_FOUND_DUPP_KEY)
1 by brian
clean slate
1666
  {
1667
    info->errkey=(int) i;			/* This key was found */
1668
    while ( i-- > 0 )
1669
    {
1670
      if (mi_is_key_active(info->s->state.key_map, i))
1671
      {
1672
	{
482 by Brian Aker
Remove uint.
1673
	  uint32_t key_length=_mi_make_key(info,i,key,buff,filepos);
1 by brian
clean slate
1674
	  if (_mi_ck_delete(info,i,key,key_length))
1675
	    break;
1676
	}
1677
      }
1678
    }
1679
  }
1680
  /* Remove checksum that was added to glob_crc in sort_get_next_record */
1681
  if (sort_param->calc_checksum)
1682
    sort_param->sort_info->param->glob_crc-= info->checksum;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1683
  return(-1);
1 by brian
clean slate
1684
} /* writekeys */
1685
1686
1687
	/* Change all key-pointers that points to a records */
1688
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
1689
int movepoint(register MI_INFO *info, unsigned char *record, my_off_t oldpos,
1690
	      my_off_t newpos, uint32_t prot_key)
1 by brian
clean slate
1691
{
482 by Brian Aker
Remove uint.
1692
  register uint32_t i;
481 by Brian Aker
Remove all of uchar.
1693
  unsigned char *key;
482 by Brian Aker
Remove uint.
1694
  uint32_t key_length;
1 by brian
clean slate
1695
1696
  key=info->lastkey+info->s->base.max_key_length;
1697
  for (i=0 ; i < info->s->base.keys; i++)
1698
  {
1699
    if (i != prot_key && mi_is_key_active(info->s->state.key_map, i))
1700
    {
1701
      key_length=_mi_make_key(info,i,key,record,oldpos);
1702
      if (info->s->keyinfo[i].flag & HA_NOSAME)
1703
      {					/* Change pointer direct */
482 by Brian Aker
Remove uint.
1704
	uint32_t nod_flag;
1 by brian
clean slate
1705
	MI_KEYDEF *keyinfo;
1706
	keyinfo=info->s->keyinfo+i;
1707
	if (_mi_search(info,keyinfo,key,USE_WHOLE_KEY,
1708
		       (uint) (SEARCH_SAME | SEARCH_SAVE_BUFF),
1709
		       info->s->state.key_root[i]))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1710
	  return(-1);
1 by brian
clean slate
1711
	nod_flag=mi_test_if_nod(info->buff);
1712
	_mi_dpointer(info,info->int_keypos-nod_flag-
1713
		     info->s->rec_reflength,newpos);
1714
	if (_mi_write_keypage(info,keyinfo,info->last_keypage,
1715
                              DFLT_INIT_HITS,info->buff))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1716
	  return(-1);
1 by brian
clean slate
1717
      }
1718
      else
1719
      {					/* Change old key to new */
1720
	if (_mi_ck_delete(info,i,key,key_length))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1721
	  return(-1);
1 by brian
clean slate
1722
	key_length=_mi_make_key(info,i,key,record,newpos);
1723
	if (_mi_ck_write(info,i,key,key_length))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1724
	  return(-1);
1 by brian
clean slate
1725
      }
1726
    }
1727
  }
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1728
  return(0);
1 by brian
clean slate
1729
} /* movepoint */
1730
1731
1732
	/* Tell system that we want all memory for our cache */
1733
1280.1.4 by Brian Aker
Remove dead option.
1734
void lock_memory(MI_CHECK *)
1 by brian
clean slate
1735
{
1736
} /* lock_memory */
1737
1738
1739
	/* Flush all changed blocks to disk */
1740
1241.9.1 by Monty Taylor
Removed global.h. Fixed all the headers.
1741
int flush_blocks(MI_CHECK *param, KEY_CACHE *key_cache, int file)
1 by brian
clean slate
1742
{
1743
  if (flush_key_blocks(key_cache, file, FLUSH_RELEASE))
1744
  {
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
1745
    mi_check_print_error(param,"%d when trying to write bufferts",errno);
1 by brian
clean slate
1746
    return(1);
1747
  }
1748
  if (!param->using_global_keycache)
1749
    end_key_cache(key_cache,1);
1750
  return 0;
1751
} /* flush_blocks */
1752
1753
1754
	/* Sort index for more efficent reads */
1755
1756
int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name)
1757
{
482 by Brian Aker
Remove uint.
1758
  register uint32_t key;
1 by brian
clean slate
1759
  register MI_KEYDEF *keyinfo;
1241.9.1 by Monty Taylor
Removed global.h. Fixed all the headers.
1760
  int new_file;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
1761
  my_off_t index_pos[HA_MAX_POSSIBLE_KEY];
482 by Brian Aker
Remove uint.
1762
  uint32_t r_locks,w_locks;
1 by brian
clean slate
1763
  int old_lock;
1764
  MYISAM_SHARE *share=info->s;
1765
  MI_STATE_INFO old_state;
1766
1767
  /* cannot sort index files with R-tree indexes */
1768
  for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
1769
       key++,keyinfo++)
1770
1771
  if (!(param->testflag & T_SILENT))
1772
    printf("- Sorting index for MyISAM-table '%s'\n",name);
1773
1774
  /* Get real path for index file */
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
1775
  internal::fn_format(param->temp_filename,name,"", MI_NAME_IEXT,2+4+32);
1776
  if ((new_file=my_create(internal::fn_format(param->temp_filename,param->temp_filename,
1 by brian
clean slate
1777
				    "", INDEX_TMP_EXT,2+4),
1778
			  0,param->tmpfile_createflag,MYF(0))) <= 0)
1779
  {
1780
    mi_check_print_error(param,"Can't create new tempfile: '%s'",
1781
			 param->temp_filename);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1782
    return(-1);
1 by brian
clean slate
1783
  }
1784
  if (filecopy(param, new_file,share->kfile,0L,
1785
	       (ulong) share->base.keystart, "headerblock"))
1786
    goto err;
1787
1788
  param->new_file_pos=share->base.keystart;
1789
  for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
1790
       key++,keyinfo++)
1791
  {
1792
    if (! mi_is_key_active(info->s->state.key_map, key))
1793
      continue;
1794
1795
    if (share->state.key_root[key] != HA_OFFSET_ERROR)
1796
    {
1797
      index_pos[key]=param->new_file_pos;	/* Write first block here */
1798
      if (sort_one_index(param,info,keyinfo,share->state.key_root[key],
1799
			 new_file))
1800
	goto err;
1801
    }
1802
    else
1803
      index_pos[key]= HA_OFFSET_ERROR;		/* No blocks */
1804
  }
1805
1806
  /* Flush key cache for this file if we are calling this outside myisamchk */
1689.2.15 by Brian Aker
Encapsulate the key cache object in myisam.
1807
  flush_key_blocks(share->getKeyCache(), share->kfile, FLUSH_IGNORE_CHANGED);
1 by brian
clean slate
1808
1809
  share->state.version=(ulong) time((time_t*) 0);
1810
  old_state= share->state;			/* save state if not stored */
1811
  r_locks=   share->r_locks;
1812
  w_locks=   share->w_locks;
1813
  old_lock=  info->lock_type;
1814
1815
	/* Put same locks as old file */
1816
  share->r_locks= share->w_locks= share->tot_locks= 0;
1817
  (void) _mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE);
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
1818
  internal::my_close(share->kfile,MYF(MY_WME));
1 by brian
clean slate
1819
  share->kfile = -1;
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
1820
  internal::my_close(new_file,MYF(MY_WME));
1 by brian
clean slate
1821
  if (change_to_newfile(share->index_file_name,MI_NAME_IEXT,INDEX_TMP_EXT,0,
1822
			MYF(0)) ||
1823
      mi_open_keyfile(share))
1824
    goto err2;
1825
  info->lock_type= F_UNLCK;			/* Force mi_readinfo to lock */
1826
  _mi_readinfo(info,F_WRLCK,0);			/* Will lock the table */
1827
  info->lock_type=  old_lock;
1828
  share->r_locks=   r_locks;
1829
  share->w_locks=   w_locks;
1830
  share->tot_locks= r_locks+w_locks;
1831
  share->state=     old_state;			/* Restore old state */
1832
1833
  info->state->key_file_length=param->new_file_pos;
1834
  info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
1835
  for (key=0 ; key < info->s->base.keys ; key++)
1836
    info->s->state.key_root[key]=index_pos[key];
1837
  for (key=0 ; key < info->s->state.header.max_block_size_index ; key++)
1838
    info->s->state.key_del[key]=  HA_OFFSET_ERROR;
1839
1840
  info->s->state.changed&= ~STATE_NOT_SORTED_PAGES;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1841
  return(0);
1 by brian
clean slate
1842
1843
err:
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
1844
  internal::my_close(new_file,MYF(MY_WME));
1 by brian
clean slate
1845
err2:
398.1.10 by Monty Taylor
Actually removed VOID() this time.
1846
  my_delete(param->temp_filename,MYF(MY_WME));
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1847
  return(-1);
1 by brian
clean slate
1848
} /* mi_sort_index */
1849
1850
1851
	 /* Sort records recursive using one index */
1852
1853
static int sort_one_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
1854
			  my_off_t pagepos, int new_file)
1 by brian
clean slate
1855
{
482 by Brian Aker
Remove uint.
1856
  uint32_t length,nod_flag,used_length, key_length;
481 by Brian Aker
Remove all of uchar.
1857
  unsigned char *buff,*keypos,*endpos;
1858
  unsigned char key[HA_MAX_POSSIBLE_KEY_BUFF];
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
1859
  my_off_t new_page_pos,next_page;
1 by brian
clean slate
1860
  char llbuff[22];
1861
1862
  new_page_pos=param->new_file_pos;
1863
  param->new_file_pos+=keyinfo->block_length;
1864
656.1.1 by Monty Taylor
OOOh doggie. Got rid of my_alloca.
1865
  if (!(buff=(unsigned char*) malloc(keyinfo->block_length)))
1 by brian
clean slate
1866
  {
1867
    mi_check_print_error(param,"Not enough memory for key block");
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1868
    return(-1);
1 by brian
clean slate
1869
  }
1870
  if (!_mi_fetch_keypage(info,keyinfo,pagepos,DFLT_INIT_HITS,buff,0))
1871
  {
1872
    mi_check_print_error(param,"Can't read key block from filepos: %s",
1873
		llstr(pagepos,llbuff));
1874
    goto err;
1875
  }
249 by Brian Aker
Random key cleanup (it is a friday...)
1876
  if ((nod_flag=mi_test_if_nod(buff)))
1 by brian
clean slate
1877
  {
1878
    used_length=mi_getint(buff);
1879
    keypos=buff+2+nod_flag;
1880
    endpos=buff+used_length;
1881
    for ( ;; )
1882
    {
1883
      if (nod_flag)
1884
      {
1885
	next_page=_mi_kpos(nod_flag,keypos);
1886
	_mi_kpointer(info,keypos-nod_flag,param->new_file_pos); /* Save new pos */
1887
	if (sort_one_index(param,info,keyinfo,next_page,new_file))
1888
	{
1889
	  goto err;
1890
	}
1891
      }
1892
      if (keypos >= endpos ||
1893
	  (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0)
1894
	break;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1895
      assert(keypos <= endpos);
1 by brian
clean slate
1896
    }
1897
  }
1898
1899
  /* Fill block with zero and write it to the new index file */
1900
  length=mi_getint(buff);
212.6.12 by Mats Kindahl
Removing redundant use of casts in MyISAM storage for memcmp(), memcpy(), memset(), and memmove().
1901
  memset(buff+length, 0, keyinfo->block_length-length);
481 by Brian Aker
Remove all of uchar.
1902
  if (my_pwrite(new_file,(unsigned char*) buff,(uint) keyinfo->block_length,
1 by brian
clean slate
1903
		new_page_pos,MYF(MY_NABP | MY_WAIT_IF_FULL)))
1904
  {
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
1905
    mi_check_print_error(param,"Can't write indexblock, error: %d",errno);
1 by brian
clean slate
1906
    goto err;
1907
  }
656.1.1 by Monty Taylor
OOOh doggie. Got rid of my_alloca.
1908
  free(buff);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1909
  return(0);
1 by brian
clean slate
1910
err:
656.1.1 by Monty Taylor
OOOh doggie. Got rid of my_alloca.
1911
  free(buff);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1912
  return(1);
1 by brian
clean slate
1913
} /* sort_one_index */
1914
1915
1916
	/*
1917
	  Let temporary file replace old file.
1918
	  This assumes that the new file was created in the same
1919
	  directory as given by realpath(filename).
1920
	  This will ensure that any symlinks that are used will still work.
1921
	  Copy stats from old file to new file, deletes orignal and
1922
	  changes new file name to old file name
1923
	*/
1924
1925
int change_to_newfile(const char * filename, const char * old_ext,
1926
		      const char * new_ext,
779.3.1 by Monty Taylor
More cleanup.
1927
		      uint32_t raid_chunks,
1 by brian
clean slate
1928
		      myf MyFlags)
1929
{
779.3.1 by Monty Taylor
More cleanup.
1930
  (void)raid_chunks;
1 by brian
clean slate
1931
  char old_filename[FN_REFLEN],new_filename[FN_REFLEN];
1932
  /* Get real path to filename */
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
1933
  (void) internal::fn_format(old_filename,filename,"",old_ext,2+4+32);
1 by brian
clean slate
1934
  return my_redel(old_filename,
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
1935
		  internal::fn_format(new_filename,old_filename,"",new_ext,2+4),
1 by brian
clean slate
1936
		  MYF(MY_WME | MY_LINK_WARNING | MyFlags));
1937
} /* change_to_newfile */
1938
1939
1940
1941
	/* Copy a block between two files */
1942
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
1943
int filecopy(MI_CHECK *param, int to,int from,my_off_t start,
1944
	     my_off_t length, const char *type)
1 by brian
clean slate
1945
{
1946
  char tmp_buff[IO_SIZE],*buff;
1947
  ulong buff_length;
1948
1067.4.14 by Nathan Williams
Fixed type differences for min/max calls on OSX.
1949
  buff_length=(ulong) min(param->write_buffer_length, (size_t)length);
960.2.2 by Monty Taylor
Moved MyISAM files to C++ so we can continue to consolidate code.
1950
  if (!(buff=(char *)malloc(buff_length)))
1 by brian
clean slate
1951
  {
1952
    buff=tmp_buff; buff_length=IO_SIZE;
1953
  }
1954
656.1.39 by Monty Taylor
Removed my_seek, my_tell, my_fwrite, my_fseek.
1955
  lseek(from,start,SEEK_SET);
1 by brian
clean slate
1956
  while (length > buff_length)
1957
  {
481 by Brian Aker
Remove all of uchar.
1958
    if (my_read(from,(unsigned char*) buff,buff_length,MYF(MY_NABP)) ||
1959
	my_write(to,(unsigned char*) buff,buff_length,param->myf_rw))
1 by brian
clean slate
1960
      goto err;
1961
    length-= buff_length;
1962
  }
481 by Brian Aker
Remove all of uchar.
1963
  if (my_read(from,(unsigned char*) buff,(uint) length,MYF(MY_NABP)) ||
1964
      my_write(to,(unsigned char*) buff,(uint) length,param->myf_rw))
1 by brian
clean slate
1965
    goto err;
1966
  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.
1967
    free(buff);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1968
  return(0);
1 by brian
clean slate
1969
err:
1970
  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.
1971
    free(buff);
1 by brian
clean slate
1972
  mi_check_print_error(param,"Can't copy %s to tempfile, error %d",
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
1973
		       type,errno);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
1974
  return(1);
1 by brian
clean slate
1975
}
1976
1977
1978
/*
1979
  Repair table or given index using sorting
1980
1981
  SYNOPSIS
1982
    mi_repair_by_sort()
1983
    param		Repair parameters
1984
    info		MyISAM handler to repair
1985
    name		Name of table (for warnings)
1986
    rep_quick		set to <> 0 if we should not change data file
1987
1988
  RESULT
1989
    0	ok
1990
    <>0	Error
1991
*/
1992
1993
int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
1994
		      const char * name, int rep_quick)
1995
{
1996
  int got_error;
482 by Brian Aker
Remove uint.
1997
  uint32_t i;
1 by brian
clean slate
1998
  ulong length;
1999
  ha_rows start_records;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
2000
  my_off_t new_header_length,del;
1241.9.1 by Monty Taylor
Removed global.h. Fixed all the headers.
2001
  int new_file;
1 by brian
clean slate
2002
  MI_SORT_PARAM sort_param;
2003
  MYISAM_SHARE *share=info->s;
2004
  HA_KEYSEG *keyseg;
2005
  ulong   *rec_per_key_part;
2006
  char llbuff[22];
2007
  SORT_INFO sort_info;
151 by Brian Aker
Ulonglong to uint64_t
2008
  uint64_t key_map= 0;
1 by brian
clean slate
2009
2010
  start_records=info->state->records;
2011
  got_error=1;
2012
  new_file= -1;
2013
  new_header_length=(param->testflag & T_UNPACK) ? 0 :
2014
    share->pack.header_length;
2015
  if (!(param->testflag & T_SILENT))
2016
  {
2017
    printf("- recovering (with sort) MyISAM-table '%s'\n",name);
2018
    printf("Data records: %s\n", llstr(start_records,llbuff));
2019
  }
2020
  param->testflag|=T_REP; /* for easy checking */
2021
1117.1.2 by Brian Aker
Remove CHECKSUM option in create table.
2022
  if (info->s->options & (HA_OPTION_COMPRESS_RECORD))
1 by brian
clean slate
2023
    param->testflag|=T_CALC_CHECKSUM;
2024
212.6.12 by Mats Kindahl
Removing redundant use of casts in MyISAM storage for memcmp(), memcpy(), memset(), and memmove().
2025
  memset(&sort_info, 0, sizeof(sort_info));
2026
  memset(&sort_param, 0, sizeof(sort_param));
1 by brian
clean slate
2027
  if (!(sort_info.key_block=
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
2028
        alloc_key_blocks(param, (uint) param->sort_key_blocks, share->base.max_key_block_length))
2029
      || param->read_cache.init_io_cache(info->dfile, (uint) param->read_buffer_length, READ_CACHE,share->pack.header_length,1,MYF(MY_WME))
2030
      || (! rep_quick && info->rec_cache.init_io_cache(info->dfile, (uint) param->write_buffer_length, WRITE_CACHE,new_header_length,1, MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw)))
2031
  {
1 by brian
clean slate
2032
    goto err;
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
2033
  }
1 by brian
clean slate
2034
  sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
2035
  info->opt_flag|=WRITE_CACHE_USED;
2036
  info->rec_cache.file=info->dfile;		/* for sort_delete_record */
2037
1816.2.4 by Monty Taylor
Cleaned up a bunch more warnings.
2038
  if (!mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.record) ||
2039
      !mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.rec_buff))
1 by brian
clean slate
2040
  {
2041
    mi_check_print_error(param, "Not enough memory for extra record");
2042
    goto err;
2043
  }
2044
  if (!rep_quick)
2045
  {
2046
    /* Get real path for data file */
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
2047
    if ((new_file=my_create(internal::fn_format(param->temp_filename,
489.1.6 by Monty Taylor
Removed RAID garbage.
2048
                                      share->data_file_name, "",
2049
                                      DATA_TMP_EXT, 2+4),
2050
                            0,param->tmpfile_createflag,
2051
                            MYF(0))) < 0)
1 by brian
clean slate
2052
    {
2053
      mi_check_print_error(param,"Can't create new tempfile: '%s'",
489.1.6 by Monty Taylor
Removed RAID garbage.
2054
                           param->temp_filename);
1 by brian
clean slate
2055
      goto err;
2056
    }
2057
    if (new_header_length &&
2058
        filecopy(param, new_file,info->dfile,0L,new_header_length,
2059
		 "datafile-header"))
2060
      goto err;
2061
    if (param->testflag & T_UNPACK)
2062
    {
2063
      share->options&= ~HA_OPTION_COMPRESS_RECORD;
2064
      mi_int2store(share->state.header.options,share->options);
2065
    }
2066
    share->state.dellink= HA_OFFSET_ERROR;
2067
    info->rec_cache.file=new_file;
2068
  }
2069
2070
  info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
2071
2072
  /* Optionally drop indexes and optionally modify the key_map. */
163 by Brian Aker
Merge Monty's code.
2073
  mi_drop_all_indexes(param, info, false);
1 by brian
clean slate
2074
  key_map= share->state.key_map;
2075
  if (param->testflag & T_CREATE_MISSING_KEYS)
2076
  {
2077
    /* Invert the copied key_map to recreate all disabled indexes. */
2078
    key_map= ~key_map;
2079
  }
2080
2081
  sort_info.info=info;
2082
  sort_info.param = param;
2083
2084
  set_data_file_type(&sort_info, share);
2085
  sort_param.filepos=new_header_length;
2086
  sort_info.dupp=0;
2087
  sort_info.buff=0;
2088
  param->read_cache.end_of_file=sort_info.filelength=
656.1.39 by Monty Taylor
Removed my_seek, my_tell, my_fwrite, my_fseek.
2089
    lseek(param->read_cache.file,0L,SEEK_END);
1 by brian
clean slate
2090
2091
  sort_param.wordlist=NULL;
2092
2093
  if (share->data_file_type == DYNAMIC_RECORD)
1067.4.8 by Nathan Williams
Converted all usages of cmin/cmax in plugin directory to std::min/max.
2094
    length=max(share->base.min_pack_length+1,share->base.min_block_length);
1 by brian
clean slate
2095
  else if (share->data_file_type == COMPRESSED_RECORD)
2096
    length=share->base.min_block_length;
2097
  else
2098
    length=share->base.pack_reclength;
2099
  sort_info.max_records=
2100
    ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records :
2101
     (ha_rows) (sort_info.filelength/length+1));
2102
  sort_param.key_cmp=sort_key_cmp;
2103
  sort_param.lock_in_memory=lock_memory;
2104
  sort_param.sort_info=&sort_info;
281 by Brian Aker
Converted myisam away from my_bool
2105
  sort_param.fix_datafile= (bool) (! rep_quick);
1 by brian
clean slate
2106
  sort_param.master =1;
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
2107
1 by brian
clean slate
2108
  del=info->state->del;
2109
  param->glob_crc=0;
2110
  if (param->testflag & T_CALC_CHECKSUM)
2111
    sort_param.calc_checksum= 1;
2112
2113
  rec_per_key_part= param->rec_per_key_part;
2114
  for (sort_param.key=0 ; sort_param.key < share->base.keys ;
2115
       rec_per_key_part+=sort_param.keyinfo->keysegs, sort_param.key++)
2116
  {
2117
    sort_param.read_cache=param->read_cache;
2118
    sort_param.keyinfo=share->keyinfo+sort_param.key;
2119
    sort_param.seg=sort_param.keyinfo->seg;
2120
    /*
2121
      Skip this index if it is marked disabled in the copied
2122
      (and possibly inverted) key_map.
2123
    */
2124
    if (! mi_is_key_active(key_map, sort_param.key))
2125
    {
2126
      /* 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().
2127
      assert(rec_per_key_part >= param->rec_per_key_part);
2128
      memcpy(rec_per_key_part,
2129
	     (share->state.rec_per_key_part +
2130
              (rec_per_key_part - param->rec_per_key_part)),
1 by brian
clean slate
2131
	     sort_param.keyinfo->keysegs*sizeof(*rec_per_key_part));
2132
      continue;
2133
    }
2134
2135
    if ((!(param->testflag & T_SILENT)))
2136
      printf ("- Fixing index %d\n",sort_param.key+1);
2137
    sort_param.max_pos=sort_param.pos=share->pack.header_length;
2138
    keyseg=sort_param.seg;
212.6.12 by Mats Kindahl
Removing redundant use of casts in MyISAM storage for memcmp(), memcpy(), memset(), and memmove().
2139
    memset(sort_param.unique, 0, sizeof(sort_param.unique));
1 by brian
clean slate
2140
    sort_param.key_length=share->rec_reflength;
2141
    for (i=0 ; keyseg[i].type != HA_KEYTYPE_END; i++)
2142
    {
2143
      sort_param.key_length+=keyseg[i].length;
2144
      if (keyseg[i].flag & HA_SPACE_PACK)
2145
	sort_param.key_length+=get_pack_length(keyseg[i].length);
2146
      if (keyseg[i].flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
2147
	sort_param.key_length+=2 + test(keyseg[i].length >= 127);
2148
      if (keyseg[i].flag & HA_NULL_PART)
2149
	sort_param.key_length++;
2150
    }
2151
    info->state->records=info->state->del=share->state.split=0;
2152
    info->state->empty=0;
2153
2154
    {
2155
      sort_param.key_read=sort_key_read;
2156
      sort_param.key_write=sort_key_write;
2157
    }
2158
2159
    if (_create_index_by_sort(&sort_param,
281 by Brian Aker
Converted myisam away from my_bool
2160
			      (bool) (!(param->testflag & T_VERBOSE)),
1 by brian
clean slate
2161
			      (uint) param->sort_buffer_length))
2162
    {
2163
      param->retry_repair=1;
2164
      goto err;
2165
    }
2166
    /* No need to calculate checksum again. */
2167
    sort_param.calc_checksum= 0;
1487 by Brian Aker
More updates for memory::Root
2168
    sort_param.wordroot.free_root(MYF(0));
1 by brian
clean slate
2169
2170
    /* Set for next loop */
2171
    sort_info.max_records= (ha_rows) info->state->records;
2172
2173
    if (param->testflag & T_STATISTICS)
2174
      update_key_parts(sort_param.keyinfo, rec_per_key_part, sort_param.unique,
2175
                       param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
2176
                       sort_param.notnull: NULL,
151 by Brian Aker
Ulonglong to uint64_t
2177
                       (uint64_t) info->state->records);
1 by brian
clean slate
2178
    /* Enable this index in the permanent (not the copied) key_map. */
2179
    mi_set_key_active(share->state.key_map, sort_param.key);
2180
2181
    if (sort_param.fix_datafile)
2182
    {
2183
      param->read_cache.end_of_file=sort_param.filepos;
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
2184
      if (write_data_suffix(&sort_info, 1) || info->rec_cache.end_io_cache())
1578.4.4 by Brian Aker
Remove pthread mutex that is no longer used (since IO_CACHE is single
2185
      {
1 by brian
clean slate
2186
	goto err;
1578.4.4 by Brian Aker
Remove pthread mutex that is no longer used (since IO_CACHE is single
2187
      }
1 by brian
clean slate
2188
      if (param->testflag & T_SAFE_REPAIR)
2189
      {
2190
	/* Don't repair if we loosed more than one row */
2191
	if (info->state->records+1 < start_records)
2192
	{
2193
	  info->state->records=start_records;
2194
	  goto err;
2195
	}
2196
      }
2197
      share->state.state.data_file_length = info->state->data_file_length=
2198
	sort_param.filepos;
2199
      /* Only whole records */
2200
      share->state.version=(ulong) time((time_t*) 0);
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
2201
      internal::my_close(info->dfile,MYF(0));
1 by brian
clean slate
2202
      info->dfile=new_file;
2203
      share->data_file_type=sort_info.new_data_file_type;
2204
      share->pack.header_length=(ulong) new_header_length;
2205
      sort_param.fix_datafile=0;
2206
    }
2207
    else
2208
      info->state->data_file_length=sort_param.max_pos;
2209
2210
    param->read_cache.file=info->dfile;		/* re-init read cache */
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
2211
    param->read_cache.reinit_io_cache(READ_CACHE,share->pack.header_length, 1,1);
1 by brian
clean slate
2212
  }
2213
2214
  if (param->testflag & T_WRITE_LOOP)
2215
  {
398.1.10 by Monty Taylor
Actually removed VOID() this time.
2216
    fputs("          \r",stdout); fflush(stdout);
1 by brian
clean slate
2217
  }
2218
2219
  if (rep_quick && del+sort_info.dupp != info->state->del)
2220
  {
2221
    mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
2222
    mi_check_print_error(param,"Run recovery again without -q");
2223
    got_error=1;
2224
    param->retry_repair=1;
2225
    param->testflag|=T_RETRY_WITHOUT_QUICK;
2226
    goto err;
2227
  }
2228
2229
  if (rep_quick & T_FORCE_UNIQUENESS)
2230
  {
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
2231
    my_off_t skr=info->state->data_file_length+
1 by brian
clean slate
2232
      (share->options & HA_OPTION_COMPRESS_RECORD ?
2233
       MEMMAP_EXTRA_MARGIN : 0);
2234
#ifdef USE_RELOC
2235
    if (share->data_file_type == STATIC_RECORD &&
2236
	skr < share->base.reloc*share->base.min_pack_length)
2237
      skr=share->base.reloc*share->base.min_pack_length;
2238
#endif
2239
    if (skr != sort_info.filelength && !info->s->base.raid_type)
30 by Brian Aker
Large file and ftruncate() support
2240
      if (ftruncate(info->dfile, skr))
1 by brian
clean slate
2241
	mi_check_print_warning(param,
2242
			       "Can't change size of datafile,  error: %d",
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
2243
			       errno);
1 by brian
clean slate
2244
  }
2245
  if (param->testflag & T_CALC_CHECKSUM)
2246
    info->state->checksum=param->glob_crc;
2247
30 by Brian Aker
Large file and ftruncate() support
2248
  if (ftruncate(share->kfile, info->state->key_file_length))
1 by brian
clean slate
2249
    mi_check_print_warning(param,
2250
			   "Can't change size of indexfile, error: %d",
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
2251
			   errno);
1 by brian
clean slate
2252
2253
  if (!(param->testflag & T_SILENT))
2254
  {
2255
    if (start_records != info->state->records)
2256
      printf("Data records: %s\n", llstr(info->state->records,llbuff));
2257
    if (sort_info.dupp)
2258
      mi_check_print_warning(param,
2259
			     "%s records have been removed",
2260
			     llstr(sort_info.dupp,llbuff));
2261
  }
2262
  got_error=0;
2263
2264
  if (&share->state.state != info->state)
2265
    memcpy( &share->state.state, info->state, sizeof(*info->state));
2266
2267
err:
1689.2.15 by Brian Aker
Encapsulate the key cache object in myisam.
2268
  got_error|= flush_blocks(param, share->getKeyCache(), share->kfile);
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
2269
  info->rec_cache.end_io_cache();
1 by brian
clean slate
2270
  if (!got_error)
2271
  {
2272
    /* Replace the actual file with the temporary file */
2273
    if (new_file >= 0)
2274
    {
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
2275
      internal::my_close(new_file,MYF(0));
1 by brian
clean slate
2276
      info->dfile=new_file= -1;
2277
      if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
2278
			    DATA_TMP_EXT, share->base.raid_chunks,
2241.4.19 by Stewart Smith
A few unused bits of mysys. Turns out internal::get_date() was only used by one codepath of my_redel() which was never called due to a flag never being set in MyISAM. Just rip it out. One whole less file to build
2279
			    MYF(0)) ||
1 by brian
clean slate
2280
	  mi_open_datafile(info,share,-1))
2281
	got_error=1;
2282
    }
2283
  }
2284
  if (got_error)
2285
  {
2286
    if (! param->error_printed)
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
2287
      mi_check_print_error(param,"%d when fixing table",errno);
1 by brian
clean slate
2288
    if (new_file >= 0)
2289
    {
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
2290
      internal::my_close(new_file,MYF(0));
489.1.6 by Monty Taylor
Removed RAID garbage.
2291
      my_delete(param->temp_filename, MYF(MY_WME));
1 by brian
clean slate
2292
      if (info->dfile == new_file)
489.1.6 by Monty Taylor
Removed RAID garbage.
2293
        info->dfile= -1;
1 by brian
clean slate
2294
    }
2295
    mi_mark_crashed_on_repair(info);
2296
  }
2297
  else if (key_map == share->state.key_map)
2298
    share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
2299
  share->state.changed|=STATE_NOT_SORTED_PAGES;
2300
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.
2301
  void * rec_buff_ptr= NULL;
2302
  rec_buff_ptr= mi_get_rec_buff_ptr(info, sort_param.rec_buff);
2353.3.1 by Mark Atwood
fix cppcheck redundantIfDelete0 warnings. It is safe to deallocate a NULL pointer
2303
  free(rec_buff_ptr);
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.
2304
  rec_buff_ptr= mi_get_rec_buff_ptr(info, sort_param.record);
2353.3.1 by Mark Atwood
fix cppcheck redundantIfDelete0 warnings. It is safe to deallocate a NULL pointer
2305
  free(rec_buff_ptr);
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.
2306
  rec_buff_ptr= NULL;
2307
481 by Brian Aker
Remove all of uchar.
2308
  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.
2309
  free(sort_info.buff);
1909.1.1 by Brian Aker
Encapsulation of IO_CACHE.
2310
  param->read_cache.end_io_cache();
1 by brian
clean slate
2311
  info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
2312
  if (!got_error && (param->testflag & T_UNPACK))
2313
  {
481 by Brian Aker
Remove all of uchar.
2314
    share->state.header.options[0]&= (unsigned char) ~HA_OPTION_COMPRESS_RECORD;
1 by brian
clean slate
2315
    share->pack.header_length=0;
2316
  }
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2317
  return(got_error);
1 by brian
clean slate
2318
}
2319
2320
	/* Read next record and return next key */
2321
960.3.1 by Monty Taylor
Fixed linkage issues on solaris.
2322
int sort_key_read(MI_SORT_PARAM *sort_param, void *key)
1 by brian
clean slate
2323
{
2324
  int error;
2325
  SORT_INFO *sort_info=sort_param->sort_info;
2326
  MI_INFO *info=sort_info->info;
2327
2328
  if ((error=sort_get_next_record(sort_param)))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2329
    return(error);
1 by brian
clean slate
2330
  if (info->state->records == sort_info->max_records)
2331
  {
2332
    mi_check_print_error(sort_info->param,
2333
			 "Key %d - Found too many records; Can't continue",
2334
                         sort_param->key+1);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2335
    return(1);
1 by brian
clean slate
2336
  }
2337
  sort_param->real_key_length=
2338
    (info->s->rec_reflength+
481 by Brian Aker
Remove all of uchar.
2339
     _mi_make_key(info, sort_param->key, (unsigned char*) key,
1 by brian
clean slate
2340
		  sort_param->record, sort_param->filepos));
1859.2.14 by Brian Aker
Add support for --with-valgrind
2341
#ifdef HAVE_VALGRIND
760.1.5 by Kristian Nielsen
Fix a few warnings when building with -DHAVE_purify
2342
  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.
2343
         (sort_param->key_length-sort_param->real_key_length));
1 by brian
clean slate
2344
#endif
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2345
  return(sort_write_record(sort_param));
1 by brian
clean slate
2346
} /* sort_key_read */
2347
2348
2349
/*
2350
  Read next record from file using parameters in sort_info.
2351
2352
  SYNOPSIS
2353
    sort_get_next_record()
2354
      sort_param                Information about and for the sort process
2355
2356
  NOTE
2357
2358
    Dynamic Records With Non-Quick Parallel Repair
2359
2360
      For non-quick parallel repair we use a synchronized read/write
2361
      cache. This means that one thread is the master who fixes the data
2362
      file by reading each record from the old data file and writing it
2363
      to the new data file. By doing this the records in the new data
2364
      file are written contiguously. Whenever the write buffer is full,
2365
      it is copied to the read buffer. The slaves read from the read
2366
      buffer, which is not associated with a file. Thus read_cache.file
2367
      is -1. When using _mi_read_cache(), the slaves must always set
2368
      flag to READING_NEXT so that the function never tries to read from
2369
      file. This is safe because the records are contiguous. There is no
2370
      need to read outside the cache. This condition is evaluated in the
2371
      variable 'parallel_flag' for quick reference. read_cache.file must
2372
      be >= 0 in every other case.
2373
2374
  RETURN
2375
    -1          end of file
2376
    0           ok
2377
    > 0         error
2378
*/
2379
960.3.1 by Monty Taylor
Fixed linkage issues on solaris.
2380
int sort_get_next_record(MI_SORT_PARAM *sort_param)
1 by brian
clean slate
2381
{
2382
  int searching;
2383
  int parallel_flag;
482 by Brian Aker
Remove uint.
2384
  uint32_t found_record,b_type,left_length;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
2385
  my_off_t pos;
481 by Brian Aker
Remove all of uchar.
2386
  unsigned char *to= NULL;
1 by brian
clean slate
2387
  MI_BLOCK_INFO block_info;
2388
  SORT_INFO *sort_info=sort_param->sort_info;
2389
  MI_CHECK *param=sort_info->param;
2390
  MI_INFO *info=sort_info->info;
2391
  MYISAM_SHARE *share=info->s;
2392
  char llbuff[22],llbuff2[22];
2393
2394
  if (*killed_ptr(param))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2395
    return(1);
1 by brian
clean slate
2396
2397
  switch (share->data_file_type) {
2398
  case STATIC_RECORD:
2399
    for (;;)
2400
    {
2401
      if (my_b_read(&sort_param->read_cache,sort_param->record,
2402
		    share->base.pack_reclength))
2403
      {
2404
	if (sort_param->read_cache.error)
2405
	  param->out_flag |= O_DATA_LOST;
2406
        param->retry_repair=1;
2407
        param->testflag|=T_RETRY_WITHOUT_QUICK;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2408
	return(-1);
1 by brian
clean slate
2409
      }
2410
      sort_param->start_recpos=sort_param->pos;
2411
      if (!sort_param->fix_datafile)
2412
      {
2413
	sort_param->filepos=sort_param->pos;
2414
        if (sort_param->master)
2415
	  share->state.split++;
2416
      }
2417
      sort_param->max_pos=(sort_param->pos+=share->base.pack_reclength);
2418
      if (*sort_param->record)
2419
      {
2420
	if (sort_param->calc_checksum)
2421
	  param->glob_crc+= (info->checksum=
2422
			     mi_static_checksum(info,sort_param->record));
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2423
	return(0);
1 by brian
clean slate
2424
      }
2425
      if (!sort_param->fix_datafile && sort_param->master)
2426
      {
2427
	info->state->del++;
2428
	info->state->empty+=share->base.pack_reclength;
2429
      }
2430
    }
2431
  case DYNAMIC_RECORD:
2432
    pos= sort_param->pos;
2433
    searching= (sort_param->fix_datafile && (param->testflag & T_EXTEND));
2434
    parallel_flag= (sort_param->read_cache.file < 0) ? READING_NEXT : 0;
2435
    for (;;)
2436
    {
2437
      found_record=block_info.second_read= 0;
2438
      left_length=1;
2439
      if (searching)
2440
      {
2441
	pos=MY_ALIGN(pos,MI_DYN_ALIGN_SIZE);
2442
        param->testflag|=T_RETRY_WITHOUT_QUICK;
2443
	sort_param->start_recpos=pos;
2444
      }
2445
      do
2446
      {
2447
	if (pos > sort_param->max_pos)
2448
	  sort_param->max_pos=pos;
2449
	if (pos & (MI_DYN_ALIGN_SIZE-1))
2450
	{
2451
	  if ((param->testflag & T_VERBOSE) || searching == 0)
2452
	    mi_check_print_info(param,"Wrong aligned block at %s",
2453
				llstr(pos,llbuff));
2454
	  if (searching)
2455
	    goto try_next;
2456
	}
2457
	if (found_record && pos == param->search_after_block)
2458
	  mi_check_print_info(param,"Block: %s used by record at %s",
2459
		     llstr(param->search_after_block,llbuff),
2460
		     llstr(sort_param->start_recpos,llbuff2));
2461
	if (_mi_read_cache(&sort_param->read_cache,
481 by Brian Aker
Remove all of uchar.
2462
                           (unsigned char*) block_info.header,pos,
1 by brian
clean slate
2463
			   MI_BLOCK_INFO_HEADER_LENGTH,
2464
			   (! found_record ? READING_NEXT : 0) |
2465
                           parallel_flag | READING_HEADER))
2466
	{
2467
	  if (found_record)
2468
	  {
2469
	    mi_check_print_info(param,
2470
				"Can't read whole record at %s (errno: %d)",
2471
				llstr(sort_param->start_recpos,llbuff),errno);
2472
	    goto try_next;
2473
	  }
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2474
	  return(-1);
1 by brian
clean slate
2475
	}
2476
	if (searching && ! sort_param->fix_datafile)
2477
	{
2478
	  param->error_printed=1;
2479
          param->retry_repair=1;
2480
          param->testflag|=T_RETRY_WITHOUT_QUICK;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2481
	  return(1);	/* Something wrong with data */
1 by brian
clean slate
2482
	}
2483
	b_type=_mi_get_block_info(&block_info,-1,pos);
2484
	if ((b_type & (BLOCK_ERROR | BLOCK_FATAL_ERROR)) ||
2485
	   ((b_type & BLOCK_FIRST) &&
2486
	     (block_info.rec_len < (uint) share->base.min_pack_length ||
2487
	      block_info.rec_len > (uint) share->base.max_pack_length)))
2488
	{
482 by Brian Aker
Remove uint.
2489
	  uint32_t i;
1 by brian
clean slate
2490
	  if (param->testflag & T_VERBOSE || searching == 0)
2491
	    mi_check_print_info(param,
2492
				"Wrong bytesec: %3d-%3d-%3d at %10s; Skipped",
2493
		       block_info.header[0],block_info.header[1],
2494
		       block_info.header[2],llstr(pos,llbuff));
2495
	  if (found_record)
2496
	    goto try_next;
2497
	  block_info.second_read=0;
2498
	  searching=1;
2499
	  /* Search after block in read header string */
2500
	  for (i=MI_DYN_ALIGN_SIZE ;
2501
	       i < MI_BLOCK_INFO_HEADER_LENGTH ;
2502
	       i+= MI_DYN_ALIGN_SIZE)
2503
	    if (block_info.header[i] >= 1 &&
2504
		block_info.header[i] <= MI_MAX_DYN_HEADER_BYTE)
2505
	      break;
2506
	  pos+=(ulong) i;
2507
	  sort_param->start_recpos=pos;
2508
	  continue;
2509
	}
2510
	if (b_type & BLOCK_DELETED)
2511
	{
281 by Brian Aker
Converted myisam away from my_bool
2512
	  bool error=0;
1 by brian
clean slate
2513
	  if (block_info.block_len+ (uint) (block_info.filepos-pos) <
2514
	      share->base.min_block_length)
2515
	  {
2516
	    if (!searching)
2517
	      mi_check_print_info(param,
2518
				  "Deleted block with impossible length %u at %s",
2519
				  block_info.block_len,llstr(pos,llbuff));
2520
	    error=1;
2521
	  }
2522
	  else
2523
	  {
2524
	    if ((block_info.next_filepos != HA_OFFSET_ERROR &&
2525
		 block_info.next_filepos >=
2526
		 info->state->data_file_length) ||
2527
		(block_info.prev_filepos != HA_OFFSET_ERROR &&
2528
		 block_info.prev_filepos >= info->state->data_file_length))
2529
	    {
2530
	      if (!searching)
2531
		mi_check_print_info(param,
2532
				    "Delete link points outside datafile at %s",
2533
				    llstr(pos,llbuff));
2534
	      error=1;
2535
	    }
2536
	  }
2537
	  if (error)
2538
	  {
2539
	    if (found_record)
2540
	      goto try_next;
2541
	    searching=1;
2542
	    pos+= MI_DYN_ALIGN_SIZE;
2543
	    sort_param->start_recpos=pos;
2544
	    block_info.second_read=0;
2545
	    continue;
2546
	  }
2547
	}
2548
	else
2549
	{
2550
	  if (block_info.block_len+ (uint) (block_info.filepos-pos) <
2551
	      share->base.min_block_length ||
2552
	      block_info.block_len > (uint) share->base.max_pack_length+
2553
	      MI_SPLIT_LENGTH)
2554
	  {
2555
	    if (!searching)
2556
	      mi_check_print_info(param,
2557
				  "Found block with impossible length %u at %s; Skipped",
2558
				  block_info.block_len+ (uint) (block_info.filepos-pos),
2559
				  llstr(pos,llbuff));
2560
	    if (found_record)
2561
	      goto try_next;
2562
	    searching=1;
2563
	    pos+= MI_DYN_ALIGN_SIZE;
2564
	    sort_param->start_recpos=pos;
2565
	    block_info.second_read=0;
2566
	    continue;
2567
	  }
2568
	}
2569
	if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
2570
	{
2571
          if (!sort_param->fix_datafile && sort_param->master &&
2572
              (b_type & BLOCK_DELETED))
2573
	  {
2574
	    info->state->empty+=block_info.block_len;
2575
	    info->state->del++;
2576
	    share->state.split++;
2577
	  }
2578
	  if (found_record)
2579
	    goto try_next;
2580
	  if (searching)
2581
	  {
2582
	    pos+=MI_DYN_ALIGN_SIZE;
2583
	    sort_param->start_recpos=pos;
2584
	  }
2585
	  else
2586
	    pos=block_info.filepos+block_info.block_len;
2587
	  block_info.second_read=0;
2588
	  continue;
2589
	}
2590
2591
	if (!sort_param->fix_datafile && sort_param->master)
2592
	  share->state.split++;
2593
	if (! found_record++)
2594
	{
2595
	  sort_param->find_length=left_length=block_info.rec_len;
2596
	  sort_param->start_recpos=pos;
2597
	  if (!sort_param->fix_datafile)
2598
	    sort_param->filepos=sort_param->start_recpos;
2599
	  if (sort_param->fix_datafile && (param->testflag & T_EXTEND))
2600
	    sort_param->pos=block_info.filepos+1;
2601
	  else
2602
	    sort_param->pos=block_info.filepos+block_info.block_len;
2603
	  if (share->base.blobs)
2604
	  {
2605
	    if (!(to=mi_alloc_rec_buff(info,block_info.rec_len,
2606
				       &(sort_param->rec_buff))))
2607
	    {
2608
	      if (param->max_record_length >= block_info.rec_len)
2609
	      {
2610
		mi_check_print_error(param,"Not enough memory for blob at %s (need %lu)",
2611
				     llstr(sort_param->start_recpos,llbuff),
2612
				     (ulong) block_info.rec_len);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2613
		return(1);
1 by brian
clean slate
2614
	      }
2615
	      else
2616
	      {
2617
		mi_check_print_info(param,"Not enough memory for blob at %s (need %lu); Row skipped",
2618
				    llstr(sort_param->start_recpos,llbuff),
2619
				    (ulong) block_info.rec_len);
2620
		goto try_next;
2621
	      }
2622
	    }
2623
	  }
2624
	  else
2625
	    to= sort_param->rec_buff;
2626
	}
2627
	if (left_length < block_info.data_len || ! block_info.data_len)
2628
	{
2629
	  mi_check_print_info(param,
2630
			      "Found block with too small length at %s; Skipped",
2631
			      llstr(sort_param->start_recpos,llbuff));
2632
	  goto try_next;
2633
	}
2634
	if (block_info.filepos + block_info.data_len >
2635
	    sort_param->read_cache.end_of_file)
2636
	{
2637
	  mi_check_print_info(param,
2638
			      "Found block that points outside data file at %s",
2639
			      llstr(sort_param->start_recpos,llbuff));
2640
	  goto try_next;
2641
	}
2642
        /*
2643
          Copy information that is already read. Avoid accessing data
2644
          below the cache start. This could happen if the header
2645
          streched over the end of the previous buffer contents.
2646
        */
2647
        {
482 by Brian Aker
Remove uint.
2648
          uint32_t header_len= (uint) (block_info.filepos - pos);
2649
          uint32_t prefetch_len= (MI_BLOCK_INFO_HEADER_LENGTH - header_len);
1 by brian
clean slate
2650
2651
          if (prefetch_len > block_info.data_len)
2652
            prefetch_len= block_info.data_len;
2653
          if (prefetch_len)
2654
          {
2655
            memcpy(to, block_info.header + header_len, prefetch_len);
2656
            block_info.filepos+= prefetch_len;
2657
            block_info.data_len-= prefetch_len;
2658
            left_length-= prefetch_len;
2659
            to+= prefetch_len;
2660
          }
2661
        }
2662
        if (block_info.data_len &&
2663
            _mi_read_cache(&sort_param->read_cache,to,block_info.filepos,
2664
                           block_info.data_len,
2665
                           (found_record == 1 ? READING_NEXT : 0) |
2666
                           parallel_flag))
2667
	{
2668
	  mi_check_print_info(param,
2669
			      "Read error for block at: %s (error: %d); Skipped",
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
2670
			      llstr(block_info.filepos,llbuff),errno);
1 by brian
clean slate
2671
	  goto try_next;
2672
	}
2673
	left_length-=block_info.data_len;
2674
	to+=block_info.data_len;
2675
	pos=block_info.next_filepos;
2676
	if (pos == HA_OFFSET_ERROR && left_length)
2677
	{
2678
	  mi_check_print_info(param,"Wrong block with wrong total length starting at %s",
2679
			      llstr(sort_param->start_recpos,llbuff));
2680
	  goto try_next;
2681
	}
2682
	if (pos + MI_BLOCK_INFO_HEADER_LENGTH > sort_param->read_cache.end_of_file)
2683
	{
2684
	  mi_check_print_info(param,"Found link that points at %s (outside data file) at %s",
2685
			      llstr(pos,llbuff2),
2686
			      llstr(sort_param->start_recpos,llbuff));
2687
	  goto try_next;
2688
	}
2689
      } while (left_length);
2690
2691
      if (_mi_rec_unpack(info,sort_param->record,sort_param->rec_buff,
2692
			 sort_param->find_length) != MY_FILE_ERROR)
2693
      {
2694
	if (sort_param->read_cache.error < 0)
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2695
	  return(1);
1 by brian
clean slate
2696
	if (sort_param->calc_checksum)
2697
	  info->checksum= mi_checksum(info, sort_param->record);
2698
	if ((param->testflag & (T_EXTEND | T_REP)) || searching)
2699
	{
2700
	  if (_mi_rec_check(info, sort_param->record, sort_param->rec_buff,
2701
                            sort_param->find_length,
2702
                            (param->testflag & T_QUICK) &&
2703
                            sort_param->calc_checksum &&
2704
                            test(info->s->calc_checksum)))
2705
	  {
2706
	    mi_check_print_info(param,"Found wrong packed record at %s",
2707
				llstr(sort_param->start_recpos,llbuff));
2708
	    goto try_next;
2709
	  }
2710
	}
2711
	if (sort_param->calc_checksum)
2712
	  param->glob_crc+= info->checksum;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2713
	return(0);
1 by brian
clean slate
2714
      }
2715
      if (!searching)
2716
        mi_check_print_info(param,"Key %d - Found wrong stored record at %s",
2717
                            sort_param->key+1,
2718
                            llstr(sort_param->start_recpos,llbuff));
2719
    try_next:
2720
      pos=(sort_param->start_recpos+=MI_DYN_ALIGN_SIZE);
2721
      searching=1;
2722
    }
2723
  case COMPRESSED_RECORD:
2724
  case BLOCK_RECORD:
2725
    assert(0);                                  /* Impossible */
2726
  }
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2727
  return(1);                               /* Impossible */
1 by brian
clean slate
2728
}
2729
2730
2731
/*
2732
  Write record to new file.
2733
2734
  SYNOPSIS
2735
    sort_write_record()
2736
      sort_param                Sort parameters.
2737
2738
  NOTE
2739
    This is only called by a master thread if parallel repair is used.
2740
2741
  RETURN
2742
    0           OK
2743
    1           Error
2744
*/
2745
2746
int sort_write_record(MI_SORT_PARAM *sort_param)
2747
{
2748
  int flag;
2749
  ulong block_length,reclength;
481 by Brian Aker
Remove all of uchar.
2750
  unsigned char *from;
1 by brian
clean slate
2751
  SORT_INFO *sort_info=sort_param->sort_info;
2752
  MI_CHECK *param=sort_info->param;
2753
  MI_INFO *info=sort_info->info;
2754
  MYISAM_SHARE *share=info->s;
2755
2756
  if (sort_param->fix_datafile)
2757
  {
2758
    switch (sort_info->new_data_file_type) {
2759
    case STATIC_RECORD:
2760
      if (my_b_write(&info->rec_cache,sort_param->record,
2761
		     share->base.pack_reclength))
2762
      {
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
2763
	mi_check_print_error(param,"%d when writing to datafile",errno);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2764
	return(1);
1 by brian
clean slate
2765
      }
2766
      sort_param->filepos+=share->base.pack_reclength;
2767
      info->s->state.split++;
2768
      /* sort_info->param->glob_crc+=mi_static_checksum(info, sort_param->record); */
2769
      break;
2770
    case DYNAMIC_RECORD:
2771
      if (! info->blobs)
2772
	from=sort_param->rec_buff;
2773
      else
2774
      {
2775
	/* must be sure that local buffer is big enough */
2776
	reclength=info->s->base.pack_reclength+
2777
	  _my_calc_total_blob_length(info,sort_param->record)+
2778
	  ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
2779
	  MI_DYN_DELETE_BLOCK_HEADER;
2780
	if (sort_info->buff_length < reclength)
2781
	{
656.1.47 by Monty Taylor
More malloc return check fixes.
2782
          void *tmpptr= NULL;
673.3.16 by Stewart Smith
fix realloc in mi_check causing crash in repair use_frm
2783
	  tmpptr= realloc(sort_info->buff, reclength);
2784
          if(tmpptr)
2785
          {
656.1.47 by Monty Taylor
More malloc return check fixes.
2786
	    sort_info->buff_length=reclength;
960.2.2 by Monty Taylor
Moved MyISAM files to C++ so we can continue to consolidate code.
2787
            sort_info->buff= (unsigned char *)tmpptr;
673.3.16 by Stewart Smith
fix realloc in mi_check causing crash in repair use_frm
2788
          }
2789
          else
2790
          {
2791
            mi_check_print_error(param,"Could not realloc() sort_info->buff "
2792
                                 " to %ul bytes", reclength);
2793
            return(1);
2794
          }
1 by brian
clean slate
2795
	}
2796
	from= sort_info->buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER);
2797
      }
2798
      /* We can use info->checksum here as only one thread calls this. */
2799
      info->checksum=mi_checksum(info,sort_param->record);
2800
      reclength=_mi_rec_pack(info,from,sort_param->record);
2801
      flag=0;
2802
      /* sort_info->param->glob_crc+=info->checksum; */
2803
2804
      do
2805
      {
2806
	block_length=reclength+ 3 + test(reclength >= (65520-3));
2807
	if (block_length < share->base.min_block_length)
2808
	  block_length=share->base.min_block_length;
2809
	info->update|=HA_STATE_WRITE_AT_END;
2810
	block_length=MY_ALIGN(block_length,MI_DYN_ALIGN_SIZE);
2811
	if (block_length > MI_MAX_BLOCK_LENGTH)
2812
	  block_length=MI_MAX_BLOCK_LENGTH;
2813
	if (_mi_write_part_record(info,0L,block_length,
2814
				  sort_param->filepos+block_length,
2815
				  &from,&reclength,&flag))
2816
	{
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
2817
	  mi_check_print_error(param,"%d when writing to datafile",errno);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2818
	  return(1);
1 by brian
clean slate
2819
	}
2820
	sort_param->filepos+=block_length;
2821
	info->s->state.split++;
2822
      } while (reclength);
2823
      /* sort_info->param->glob_crc+=info->checksum; */
2824
      break;
2825
    case COMPRESSED_RECORD:
2826
    case BLOCK_RECORD:
2827
      assert(0);                                  /* Impossible */
2828
    }
2829
  }
2830
  if (sort_param->master)
2831
  {
2832
    info->state->records++;
2833
    if ((param->testflag & T_WRITE_LOOP) &&
2834
        (info->state->records % WRITE_COUNT) == 0)
2835
    {
2836
      char llbuff[22];
2837
      printf("%s\r", llstr(info->state->records,llbuff));
398.1.10 by Monty Taylor
Actually removed VOID() this time.
2838
      fflush(stdout);
1 by brian
clean slate
2839
    }
2840
  }
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2841
  return(0);
1 by brian
clean slate
2842
} /* sort_write_record */
2843
2844
2845
	/* Compare two keys from _create_index_by_sort */
2846
960.3.1 by Monty Taylor
Fixed linkage issues on solaris.
2847
int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a, const void *b)
1 by brian
clean slate
2848
{
482 by Brian Aker
Remove uint.
2849
  uint32_t not_used[2];
481 by Brian Aker
Remove all of uchar.
2850
  return (ha_key_cmp(sort_param->seg, *((unsigned char* const *) a), *((unsigned char* const *) b),
1 by brian
clean slate
2851
		     USE_WHOLE_KEY, SEARCH_SAME, not_used));
2852
} /* sort_key_cmp */
2853
2854
960.3.1 by Monty Taylor
Fixed linkage issues on solaris.
2855
int sort_key_write(MI_SORT_PARAM *sort_param, const void *a)
1 by brian
clean slate
2856
{
482 by Brian Aker
Remove uint.
2857
  uint32_t diff_pos[2];
1 by brian
clean slate
2858
  char llbuff[22],llbuff2[22];
2859
  SORT_INFO *sort_info=sort_param->sort_info;
2860
  MI_CHECK *param= sort_info->param;
2861
  int cmp;
2862
2863
  if (sort_info->key_block->inited)
2864
  {
2865
    cmp=ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey,
481 by Brian Aker
Remove all of uchar.
2866
		   (unsigned char*) a, USE_WHOLE_KEY,SEARCH_FIND | SEARCH_UPDATE,
1 by brian
clean slate
2867
		   diff_pos);
2868
    if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL)
2869
      ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey,
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
2870
                 (unsigned char*) a, USE_WHOLE_KEY,
1 by brian
clean slate
2871
                 SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diff_pos);
2872
    else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
2873
    {
2874
      diff_pos[0]= mi_collect_stats_nonulls_next(sort_param->seg,
2875
                                                 sort_param->notnull,
2876
                                                 sort_info->key_block->lastkey,
481 by Brian Aker
Remove all of uchar.
2877
                                                 (unsigned char*)a);
1 by brian
clean slate
2878
    }
2879
    sort_param->unique[diff_pos[0]-1]++;
2880
  }
2881
  else
2882
  {
2883
    cmp= -1;
2884
    if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
2885
      mi_collect_stats_nonulls_first(sort_param->seg, sort_param->notnull,
481 by Brian Aker
Remove all of uchar.
2886
                                     (unsigned char*)a);
1 by brian
clean slate
2887
  }
2888
  if ((sort_param->keyinfo->flag & HA_NOSAME) && cmp == 0)
2889
  {
2890
    sort_info->dupp++;
2891
    sort_info->info->lastpos=get_record_for_key(sort_info->info,
2892
						sort_param->keyinfo,
481 by Brian Aker
Remove all of uchar.
2893
						(unsigned char*) a);
1 by brian
clean slate
2894
    mi_check_print_warning(param,
2895
			   "Duplicate key for record at %10s against record at %10s",
2896
			   llstr(sort_info->info->lastpos,llbuff),
2897
			   llstr(get_record_for_key(sort_info->info,
2898
						    sort_param->keyinfo,
2899
						    sort_info->key_block->
2900
						    lastkey),
2901
				 llbuff2));
2902
    param->testflag|=T_RETRY_WITHOUT_QUICK;
2903
    return (sort_delete_record(sort_param));
2904
  }
2905
  return (sort_insert_key(sort_param,sort_info->key_block,
481 by Brian Aker
Remove all of uchar.
2906
			  (unsigned char*) a, HA_OFFSET_ERROR));
1 by brian
clean slate
2907
} /* sort_key_write */
2908
2909
2910
	/* get pointer to record from a key */
2911
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
2912
my_off_t get_record_for_key(MI_INFO *info, MI_KEYDEF *keyinfo,
960.3.1 by Monty Taylor
Fixed linkage issues on solaris.
2913
                            unsigned char *key) {
1 by brian
clean slate
2914
  return _mi_dpos(info,0,key+_mi_keylength(keyinfo,key));
2915
} /* get_record_for_key */
2916
2917
2918
	/* Insert a key in sort-key-blocks */
2919
960.3.1 by Monty Taylor
Fixed linkage issues on solaris.
2920
int sort_insert_key(MI_SORT_PARAM *sort_param,
2921
                    register SORT_KEY_BLOCKS *key_block, unsigned char *key,
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
2922
                    my_off_t prev_block)
1 by brian
clean slate
2923
{
482 by Brian Aker
Remove uint.
2924
  uint32_t a_length,t_length,nod_flag;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
2925
  my_off_t filepos,key_file_length;
481 by Brian Aker
Remove all of uchar.
2926
  unsigned char *anc_buff,*lastkey;
1 by brian
clean slate
2927
  MI_KEY_PARAM s_temp;
2928
  MI_INFO *info;
2929
  MI_KEYDEF *keyinfo=sort_param->keyinfo;
2930
  SORT_INFO *sort_info= sort_param->sort_info;
2931
  MI_CHECK *param=sort_info->param;
2932
2933
  anc_buff=key_block->buff;
2934
  info=sort_info->info;
2935
  lastkey=key_block->lastkey;
2936
  nod_flag= (key_block == sort_info->key_block ? 0 :
2937
	     info->s->base.key_reflength);
2938
2939
  if (!key_block->inited)
2940
  {
2941
    key_block->inited=1;
2942
    if (key_block == sort_info->key_block_end)
2943
    {
2944
      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
2945
      return(1);
1 by brian
clean slate
2946
    }
2947
    a_length=2+nod_flag;
2948
    key_block->end_pos=anc_buff+2;
2949
    lastkey=0;					/* No previous key in block */
2950
  }
2951
  else
2952
    a_length=mi_getint(anc_buff);
2953
2954
	/* Save pointer to previous block */
2955
  if (nod_flag)
2956
    _mi_kpointer(info,key_block->end_pos,prev_block);
2957
2958
  t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,
481 by Brian Aker
Remove all of uchar.
2959
				(unsigned char*) 0,lastkey,lastkey,key,
1 by brian
clean slate
2960
				 &s_temp);
2961
  (*keyinfo->store_key)(keyinfo, key_block->end_pos+nod_flag,&s_temp);
2962
  a_length+=t_length;
2963
  mi_putint(anc_buff,a_length,nod_flag);
2964
  key_block->end_pos+=t_length;
2965
  if (a_length <= keyinfo->block_length)
2966
  {
398.1.10 by Monty Taylor
Actually removed VOID() this time.
2967
    _mi_move_key(keyinfo,key_block->lastkey,key);
1 by brian
clean slate
2968
    key_block->last_length=a_length-t_length;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2969
    return(0);
1 by brian
clean slate
2970
  }
2971
2972
	/* Fill block with end-zero and write filled block */
2973
  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().
2974
  memset(anc_buff+key_block->last_length, 0,
2975
         keyinfo->block_length - key_block->last_length);
1 by brian
clean slate
2976
  key_file_length=info->state->key_file_length;
2977
  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
2978
    return(1);
1 by brian
clean slate
2979
2980
  /* If we read the page from the key cache, we have to write it back to it */
2981
  if (key_file_length == info->state->key_file_length)
2982
  {
2983
    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
2984
      return(1);
1 by brian
clean slate
2985
  }
481 by Brian Aker
Remove all of uchar.
2986
  else if (my_pwrite(info->s->kfile,(unsigned char*) anc_buff,
1 by brian
clean slate
2987
		     (uint) keyinfo->block_length,filepos, param->myf_rw))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2988
    return(1);
1 by brian
clean slate
2989
2990
	/* Write separator-key to block in next level */
2991
  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
2992
    return(1);
1 by brian
clean slate
2993
2994
	/* clear old block and write new key in it */
2995
  key_block->inited=0;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
2996
  return(sort_insert_key(sort_param, key_block,key,prev_block));
1 by brian
clean slate
2997
} /* sort_insert_key */
2998
2999
3000
	/* Delete record when we found a duplicated key */
3001
960.3.1 by Monty Taylor
Fixed linkage issues on solaris.
3002
int sort_delete_record(MI_SORT_PARAM *sort_param)
1 by brian
clean slate
3003
{
482 by Brian Aker
Remove uint.
3004
  uint32_t i;
1 by brian
clean slate
3005
  int old_file,error;
481 by Brian Aker
Remove all of uchar.
3006
  unsigned char *key;
1 by brian
clean slate
3007
  SORT_INFO *sort_info=sort_param->sort_info;
3008
  MI_CHECK *param=sort_info->param;
3009
  MI_INFO *info=sort_info->info;
3010
3011
  if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
3012
  {
3013
    mi_check_print_error(param,
3014
			 "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
3015
    return(1);
1 by brian
clean slate
3016
  }
3017
  if (info->s->options & HA_OPTION_COMPRESS_RECORD)
3018
  {
3019
    mi_check_print_error(param,
3020
			 "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
3021
    return(1);
1 by brian
clean slate
3022
  }
3023
3024
  old_file=info->dfile;
3025
  info->dfile=info->rec_cache.file;
3026
  if (sort_info->current_key)
3027
  {
3028
    key=info->lastkey+info->s->base.max_key_length;
3029
    if ((error=(*info->s->read_rnd)(info,sort_param->record,info->lastpos,0)) &&
3030
	error != HA_ERR_RECORD_DELETED)
3031
    {
3032
      mi_check_print_error(param,"Can't read record to be removed");
3033
      info->dfile=old_file;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
3034
      return(1);
1 by brian
clean slate
3035
    }
3036
3037
    for (i=0 ; i < sort_info->current_key ; i++)
3038
    {
482 by Brian Aker
Remove uint.
3039
      uint32_t key_length=_mi_make_key(info,i,key,sort_param->record,info->lastpos);
1 by brian
clean slate
3040
      if (_mi_ck_delete(info,i,key,key_length))
3041
      {
3042
	mi_check_print_error(param,"Can't delete key %d from record to be removed",i+1);
3043
	info->dfile=old_file;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
3044
	return(1);
1 by brian
clean slate
3045
      }
3046
    }
3047
    if (sort_param->calc_checksum)
3048
      param->glob_crc-=(*info->s->calc_checksum)(info, sort_param->record);
3049
  }
3050
  error=flush_io_cache(&info->rec_cache) || (*info->s->delete_record)(info);
3051
  info->dfile=old_file;				/* restore actual value */
3052
  info->state->records--;
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
3053
  return(error);
1 by brian
clean slate
3054
} /* sort_delete_record */
3055
3056
	/* Fix all pending blocks and flush everything to disk */
3057
3058
int flush_pending_blocks(MI_SORT_PARAM *sort_param)
3059
{
482 by Brian Aker
Remove uint.
3060
  uint32_t nod_flag,length;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
3061
  my_off_t filepos,key_file_length;
1 by brian
clean slate
3062
  SORT_KEY_BLOCKS *key_block;
3063
  SORT_INFO *sort_info= sort_param->sort_info;
3064
  myf myf_rw=sort_info->param->myf_rw;
3065
  MI_INFO *info=sort_info->info;
3066
  MI_KEYDEF *keyinfo=sort_param->keyinfo;
3067
3068
  filepos= HA_OFFSET_ERROR;			/* if empty file */
3069
  nod_flag=0;
3070
  for (key_block=sort_info->key_block ; key_block->inited ; key_block++)
3071
  {
3072
    key_block->inited=0;
3073
    length=mi_getint(key_block->buff);
3074
    if (nod_flag)
3075
      _mi_kpointer(info,key_block->end_pos,filepos);
3076
    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().
3077
    memset(key_block->buff+length, 0, keyinfo->block_length-length);
1 by brian
clean slate
3078
    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
3079
      return(1);
1 by brian
clean slate
3080
3081
    /* If we read the page from the key cache, we have to write it back */
3082
    if (key_file_length == info->state->key_file_length)
3083
    {
3084
      if (_mi_write_keypage(info, keyinfo, filepos,
3085
                            DFLT_INIT_HITS, key_block->buff))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
3086
	return(1);
1 by brian
clean slate
3087
    }
481 by Brian Aker
Remove all of uchar.
3088
    else if (my_pwrite(info->s->kfile,(unsigned char*) key_block->buff,
1 by brian
clean slate
3089
		       (uint) keyinfo->block_length,filepos, myf_rw))
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
3090
      return(1);
1 by brian
clean slate
3091
    nod_flag=1;
3092
  }
3093
  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
3094
  return(0);
1 by brian
clean slate
3095
} /* flush_pending_blocks */
3096
3097
	/* alloc space and pointers for key_blocks */
3098
482 by Brian Aker
Remove uint.
3099
static SORT_KEY_BLOCKS *alloc_key_blocks(MI_CHECK *param, uint32_t blocks,
3100
                                         uint32_t buffer_length)
1 by brian
clean slate
3101
{
482 by Brian Aker
Remove uint.
3102
  register uint32_t i;
1 by brian
clean slate
3103
  SORT_KEY_BLOCKS *block;
3104
656.1.25 by Monty Taylor
Removed my_malloc stuff from storage/
3105
  if (!(block=(SORT_KEY_BLOCKS*) malloc((sizeof(SORT_KEY_BLOCKS)+
3106
                                        buffer_length+IO_SIZE)*blocks)))
1 by brian
clean slate
3107
  {
3108
    mi_check_print_error(param,"Not enough memory for sort-key-blocks");
3109
    return(0);
3110
  }
3111
  for (i=0 ; i < blocks ; i++)
3112
  {
3113
    block[i].inited=0;
481 by Brian Aker
Remove all of uchar.
3114
    block[i].buff=(unsigned char*) (block+blocks)+(buffer_length+IO_SIZE)*i;
1 by brian
clean slate
3115
  }
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
3116
  return(block);
1 by brian
clean slate
3117
} /* alloc_key_blocks */
3118
3119
3120
	/* Check if file is almost full */
3121
3122
int test_if_almost_full(MI_INFO *info)
3123
{
3124
  if (info->s->options & HA_OPTION_COMPRESS_RECORD)
3125
    return 0;
1241.13.1 by Monty Taylor
Put my_off_t back... but this time localized only to myisam and mysys.
3126
  return (my_off_t)(lseek(info->s->kfile, 0L, SEEK_END) / 10 * 9) >
3127
         (my_off_t) info->s->base.max_key_file_length ||
3128
         (my_off_t)(lseek(info->dfile, 0L, SEEK_END) / 10 * 9) >
3129
         (my_off_t) info->s->base.max_data_file_length;
1 by brian
clean slate
3130
}
3131
3132
3133
	/* write suffix to data file if neaded */
3134
281 by Brian Aker
Converted myisam away from my_bool
3135
int write_data_suffix(SORT_INFO *sort_info, bool fix_datafile)
1 by brian
clean slate
3136
{
3137
  MI_INFO *info=sort_info->info;
3138
3139
  if (info->s->options & HA_OPTION_COMPRESS_RECORD && fix_datafile)
3140
  {
481 by Brian Aker
Remove all of uchar.
3141
    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.
3142
    memset(buff, 0, sizeof(buff));
1 by brian
clean slate
3143
    if (my_b_write(&info->rec_cache,buff,sizeof(buff)))
3144
    {
3145
      mi_check_print_error(sort_info->param,
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
3146
			   "%d when writing to datafile",errno);
1 by brian
clean slate
3147
      return 1;
3148
    }
3149
    sort_info->param->read_cache.end_of_file+=sizeof(buff);
3150
  }
3151
  return 0;
3152
}
3153
3154
	/* Update state and myisamchk_time of indexfile */
3155
482 by Brian Aker
Remove uint.
3156
int update_state_info(MI_CHECK *param, MI_INFO *info,uint32_t update)
1 by brian
clean slate
3157
{
3158
  MYISAM_SHARE *share=info->s;
3159
3160
  if (update & UPDATE_OPEN_COUNT)
3161
  {
3162
    share->state.open_count=0;
3163
    share->global_changed=0;
3164
  }
3165
  if (update & UPDATE_STAT)
3166
  {
482 by Brian Aker
Remove uint.
3167
    uint32_t i, key_parts= mi_uint2korr(share->state.header.key_parts);
1 by brian
clean slate
3168
    share->state.rec_per_key_rows=info->state->records;
3169
    share->state.changed&= ~STATE_NOT_ANALYZED;
3170
    if (info->state->records)
3171
    {
3172
      for (i=0; i<key_parts; i++)
3173
      {
3174
        if (!(share->state.rec_per_key_part[i]=param->rec_per_key_part[i]))
3175
          share->state.changed|= STATE_NOT_ANALYZED;
3176
      }
3177
    }
3178
  }
3179
  if (update & (UPDATE_STAT | UPDATE_SORT | UPDATE_TIME | UPDATE_AUTO_INC))
3180
  {
3181
    if (update & UPDATE_TIME)
3182
    {
3183
      share->state.check_time= (long) time((time_t*) 0);
3184
      if (!share->state.create_time)
3185
	share->state.create_time=share->state.check_time;
3186
    }
3187
    /*
3188
      When tables are locked we haven't synched the share state and the
3189
      real state for a while so we better do it here before synching
3190
      the share state to disk. Only when table is write locked is it
3191
      necessary to perform this synch.
3192
    */
3193
    if (info->lock_type == F_WRLCK)
3194
      share->state.state= *info->state;
3195
    if (mi_state_info_write(share->kfile,&share->state,1+2))
3196
      goto err;
3197
    share->changed=0;
3198
  }
3199
  {						/* Force update of status */
3200
    int error;
482 by Brian Aker
Remove uint.
3201
    uint32_t r_locks=share->r_locks,w_locks=share->w_locks;
1 by brian
clean slate
3202
    share->r_locks= share->w_locks= share->tot_locks= 0;
3203
    error=_mi_writeinfo(info,WRITEINFO_NO_UNLOCK);
3204
    share->r_locks=r_locks;
3205
    share->w_locks=w_locks;
3206
    share->tot_locks=r_locks+w_locks;
3207
    if (!error)
3208
      return 0;
3209
  }
3210
err:
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
3211
  mi_check_print_error(param,"%d when updating keyfile",errno);
1 by brian
clean slate
3212
  return 1;
3213
}
3214
3215
	/*
3216
	  Update auto increment value for a table
3217
	  When setting the 'repair_only' flag we only want to change the
3218
	  old auto_increment value if its wrong (smaller than some given key).
3219
	  The reason is that we shouldn't change the auto_increment value
3220
	  for a table without good reason when only doing a repair; If the
3221
	  user have inserted and deleted rows, the auto_increment value
3222
	  may be bigger than the biggest current row and this is ok.
3223
3224
	  If repair_only is not set, we will update the flag to the value in
3225
	  param->auto_increment is bigger than the biggest key.
3226
	*/
3227
3228
void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
281 by Brian Aker
Converted myisam away from my_bool
3229
			       bool repair_only)
1 by brian
clean slate
3230
{
481 by Brian Aker
Remove all of uchar.
3231
  unsigned char *record= 0;
1 by brian
clean slate
3232
3233
  if (!info->s->base.auto_key ||
3234
      ! mi_is_key_active(info->s->state.key_map, info->s->base.auto_key - 1))
3235
  {
3236
    if (!(param->testflag & T_VERY_SILENT))
3237
      mi_check_print_info(param,
3238
			  "Table: %s doesn't have an auto increment key\n",
3239
			  param->isam_file_name);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
3240
    return;
1 by brian
clean slate
3241
  }
3242
  if (!(param->testflag & T_SILENT) &&
3243
      !(param->testflag & T_REP))
3244
    printf("Updating MyISAM file: %s\n", param->isam_file_name);
3245
  /*
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
3246
    We have to use an allocated buffer instead of info->rec_buff as
1 by brian
clean slate
3247
    _mi_put_key_in_record() may use info->rec_buff
3248
  */
1816.2.4 by Monty Taylor
Cleaned up a bunch more warnings.
3249
  if (!mi_alloc_rec_buff(info, SIZE_MAX, &record))
1 by brian
clean slate
3250
  {
3251
    mi_check_print_error(param,"Not enough memory for extra record");
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
3252
    return;
1 by brian
clean slate
3253
  }
3254
3255
  mi_extra(info,HA_EXTRA_KEYREAD,0);
3256
  if (mi_rlast(info, record, info->s->base.auto_key-1))
3257
  {
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
3258
    if (errno != HA_ERR_END_OF_FILE)
1 by brian
clean slate
3259
    {
3260
      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.
3261
      free(mi_get_rec_buff_ptr(info, record));
1241.9.57 by Monty Taylor
Oy. Bigger change than I normally like - but this stuff is all intertwined.
3262
      mi_check_print_error(param,"%d when reading last record",errno);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
3263
      return;
1 by brian
clean slate
3264
    }
3265
    if (!repair_only)
3266
      info->s->state.auto_increment=param->auto_increment_value;
3267
  }
3268
  else
3269
  {
151 by Brian Aker
Ulonglong to uint64_t
3270
    uint64_t auto_increment= retrieve_auto_increment(info, record);
1 by brian
clean slate
3271
    set_if_bigger(info->s->state.auto_increment,auto_increment);
3272
    if (!repair_only)
3273
      set_if_bigger(info->s->state.auto_increment, param->auto_increment_value);
3274
  }
3275
  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.
3276
  free(mi_get_rec_buff_ptr(info, record));
1 by brian
clean slate
3277
  update_state_info(param, info, UPDATE_AUTO_INC);
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
3278
  return;
1 by brian
clean slate
3279
}
3280
3281
3282
/*
3283
  Update statistics for each part of an index
3284
3285
  SYNOPSIS
3286
    update_key_parts()
3287
      keyinfo           IN  Index information (only key->keysegs used)
3288
      rec_per_key_part  OUT Store statistics here
3289
      unique            IN  Array of (#distinct tuples)
3290
      notnull_tuples    IN  Array of (#tuples), or NULL
3291
      records               Number of records in the table
3292
3293
  DESCRIPTION
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
3294
    This function is called produce index statistics values from unique and
1 by brian
clean slate
3295
    notnull_tuples arrays after these arrays were produced with sequential
3296
    index scan (the scan is done in two places: chk_index() and
3297
    sort_key_write()).
3298
3299
    This function handles all 3 index statistics collection methods.
3300
3301
    Unique is an array:
3302
      unique[0]= (#different values of {keypart1}) - 1
3303
      unique[1]= (#different values of {keypart1,keypart2} tuple)-unique[0]-1
3304
      ...
3305
3306
    For MI_STATS_METHOD_IGNORE_NULLS method, notnull_tuples is an array too:
3307
      notnull_tuples[0]= (#of {keypart1} tuples such that keypart1 is not NULL)
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
3308
      notnull_tuples[1]= (#of {keypart1,keypart2} tuples such that all
1 by brian
clean slate
3309
                          keypart{i} are not NULL)
3310
      ...
3311
    For all other statistics collection methods notnull_tuples==NULL.
3312
3313
    Output is an array:
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
3314
    rec_per_key_part[k] =
3315
     = E(#records in the table such that keypart_1=c_1 AND ... AND
3316
         keypart_k=c_k for arbitrary constants c_1 ... c_k)
3317
1 by brian
clean slate
3318
     = {assuming that values have uniform distribution and index contains all
3319
        tuples from the domain (or that {c_1, ..., c_k} tuple is choosen from
3320
        index tuples}
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
3321
1 by brian
clean slate
3322
     = #tuples-in-the-index / #distinct-tuples-in-the-index.
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
3323
3324
    The #tuples-in-the-index and #distinct-tuples-in-the-index have different
1 by brian
clean slate
3325
    meaning depending on which statistics collection method is used:
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
3326
1 by brian
clean slate
3327
    MI_STATS_METHOD_*  how are nulls compared?  which tuples are counted?
3328
     NULLS_EQUAL            NULL == NULL           all tuples in table
3329
     NULLS_NOT_EQUAL        NULL != NULL           all tuples in table
3330
     IGNORE_NULLS               n/a             tuples that don't have NULLs
3331
*/
3332
3333
void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
151 by Brian Aker
Ulonglong to uint64_t
3334
                      uint64_t *unique, uint64_t *notnull,
3335
                      uint64_t records)
1 by brian
clean slate
3336
{
151 by Brian Aker
Ulonglong to uint64_t
3337
  uint64_t count=0,tmp, unique_tuples;
3338
  uint64_t tuples= records;
482 by Brian Aker
Remove uint.
3339
  uint32_t parts;
1 by brian
clean slate
3340
  for (parts=0 ; parts < keyinfo->keysegs  ; parts++)
3341
  {
3342
    count+=unique[parts];
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
3343
    unique_tuples= count + 1;
1 by brian
clean slate
3344
    if (notnull)
3345
    {
3346
      tuples= notnull[parts];
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
3347
      /*
3348
        #(unique_tuples not counting tuples with NULLs) =
3349
          #(unique_tuples counting tuples with NULLs as different) -
1 by brian
clean slate
3350
          #(tuples with NULLs)
3351
      */
3352
      unique_tuples -= (records - notnull[parts]);
3353
    }
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
3354
1 by brian
clean slate
3355
    if (unique_tuples == 0)
3356
      tmp= 1;
3357
    else if (count == 0)
3358
      tmp= tuples; /* 1 unique tuple */
3359
    else
3360
      tmp= (tuples + unique_tuples/2) / unique_tuples;
3361
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
3362
    /*
3363
      for some weird keys (e.g. FULLTEXT) tmp can be <1 here.
1 by brian
clean slate
3364
      let's ensure it is not
3365
    */
942.1.11 by Monty Taylor
Fixed a coupla build things.
3366
    if (tmp < 1)
3367
      tmp= 1;
151 by Brian Aker
Ulonglong to uint64_t
3368
    if (tmp >= (uint64_t) ~(ulong) 0)
3369
      tmp=(uint64_t) ~(ulong) 0;
1 by brian
clean slate
3370
3371
    *rec_per_key_part=(ulong) tmp;
3372
    rec_per_key_part++;
3373
  }
3374
}
3375
3376
482 by Brian Aker
Remove uint.
3377
static ha_checksum mi_byte_checksum(const unsigned char *buf, uint32_t length)
1 by brian
clean slate
3378
{
3379
  ha_checksum crc;
481 by Brian Aker
Remove all of uchar.
3380
  const unsigned char *end=buf+length;
1 by brian
clean slate
3381
  for (crc=0; buf != end; buf++)
481 by Brian Aker
Remove all of uchar.
3382
    crc=((crc << 1) + *((unsigned char*) buf)) +
1 by brian
clean slate
3383
      test(crc & (((ha_checksum) 1) << (8*sizeof(ha_checksum)-1)));
3384
  return crc;
3385
}
3386
281 by Brian Aker
Converted myisam away from my_bool
3387
static bool mi_too_big_key_for_sort(MI_KEYDEF *key, ha_rows rows)
1 by brian
clean slate
3388
{
482 by Brian Aker
Remove uint.
3389
  uint32_t key_maxlength=key->maxlength;
249 by Brian Aker
Random key cleanup (it is a friday...)
3390
  return (key->flag & (HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY) &&
151 by Brian Aker
Ulonglong to uint64_t
3391
	  ((uint64_t) rows * key_maxlength >
1115.1.3 by Brian Aker
Remove final bits of "myisam" specific from drizzled.cc
3392
	   (uint64_t) MAX_FILE_SIZE));
1 by brian
clean slate
3393
}
3394
3395
/*
3396
  Deactivate all not unique index that can be recreated fast
3397
  These include packed keys on which sorting will use more temporary
3398
  space than the max allowed file length or for which the unpacked keys
3399
  will take much more space than packed keys.
3400
  Note that 'rows' may be zero for the case when we don't know how many
3401
  rows we will put into the file.
3402
 */
3403
3404
void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows)
3405
{
3406
  MYISAM_SHARE *share=info->s;
3407
  MI_KEYDEF    *key=share->keyinfo;
482 by Brian Aker
Remove uint.
3408
  uint32_t          i;
1 by brian
clean slate
3409
51.1.92 by Jay Pipes
Removed/replaced DBUG symbols and TRUE/FALSE
3410
  assert(info->state->records == 0 &&
1 by brian
clean slate
3411
              (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES));
3412
  for (i=0 ; i < share->base.keys ; i++,key++)
3413
  {
249 by Brian Aker
Random key cleanup (it is a friday...)
3414
    if (!(key->flag & (HA_NOSAME | HA_AUTO_KEY)) &&
1 by brian
clean slate
3415
        ! mi_too_big_key_for_sort(key,rows) && info->s->base.auto_key != i+1)
3416
    {
3417
      mi_clear_key_active(share->state.key_map, i);
3418
      info->update|= HA_STATE_CHANGED;
3419
    }
3420
  }
3421
}
3422
3423
3424
/*
163 by Brian Aker
Merge Monty's code.
3425
  Return true if we can use repair by sorting
1 by brian
clean slate
3426
  One can set the force argument to force to use sorting
3427
  even if the temporary file would be quite big!
3428
*/
3429
281 by Brian Aker
Converted myisam away from my_bool
3430
bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows,
3431
			    uint64_t key_map, bool force)
1 by brian
clean slate
3432
{
3433
  MYISAM_SHARE *share=info->s;
3434
  MI_KEYDEF *key=share->keyinfo;
482 by Brian Aker
Remove uint.
3435
  uint32_t i;
1 by brian
clean slate
3436
3437
  /*
3438
    mi_repair_by_sort only works if we have at least one key. If we don't
3439
    have any keys, we should use the normal repair.
3440
  */
3441
  if (! mi_is_any_key_active(key_map))
163 by Brian Aker
Merge Monty's code.
3442
    return false;				/* Can't use sort */
1 by brian
clean slate
3443
  for (i=0 ; i < share->base.keys ; i++,key++)
3444
  {
3445
    if (!force && mi_too_big_key_for_sort(key,rows))
163 by Brian Aker
Merge Monty's code.
3446
      return false;
1 by brian
clean slate
3447
  }
163 by Brian Aker
Merge Monty's code.
3448
  return true;
1 by brian
clean slate
3449
}
3450
3451
3452
static void
3453
set_data_file_type(SORT_INFO *sort_info, MYISAM_SHARE *share)
3454
{
3455
  if ((sort_info->new_data_file_type=share->data_file_type) ==
3456
      COMPRESSED_RECORD && sort_info->param->testflag & T_UNPACK)
3457
  {
3458
    MYISAM_SHARE tmp;
3459
3460
    if (share->options & HA_OPTION_PACK_RECORD)
3461
      sort_info->new_data_file_type = DYNAMIC_RECORD;
3462
    else
3463
      sort_info->new_data_file_type = STATIC_RECORD;
3464
3465
    /* 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().
3466
    memcpy(&tmp, share, sizeof(*share));
1 by brian
clean slate
3467
    tmp.options= ~HA_OPTION_COMPRESS_RECORD;
3468
    mi_setup_functions(&tmp);
3469
    share->delete_record=tmp.delete_record;
3470
  }
3471
}