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