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