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