~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/myisam/mi_check.c

  • Committer: Lee
  • Date: 2008-10-30 22:02:01 UTC
  • mto: (572.1.2 devel)
  • mto: This revision was merged to the branch mainline in revision 573.
  • Revision ID: lbieber@lbieber-desktop-20081030220201-elb6qprbzpn7c5a4
add my name to the AUTHORS file

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
 
12
12
   You should have received a copy of the GNU General Public License
13
13
   along with this program; if not, write to the Free Software
14
 
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
15
 
16
16
/* Describe, check and repair of MyISAM tables */
17
17
 
40
40
  only. And it is sufficient to calculate the checksum once only.
41
41
*/
42
42
 
43
 
#include "myisam_priv.h"
44
 
#include "drizzled/internal/m_string.h"
 
43
#include "myisamdef.h"
45
44
#include <stdarg.h>
 
45
#include <mysys/my_getopt.h>
46
46
#ifdef HAVE_SYS_VADVISE_H
47
47
#include <sys/vadvise.h>
48
48
#endif
53
53
#include <sys/mman.h>
54
54
#endif
55
55
#include <drizzled/util/test.h>
56
 
#include "drizzled/error.h"
57
 
 
58
 
#include <algorithm>
59
 
 
60
 
using namespace std;
61
 
using namespace drizzled;
62
 
using namespace drizzled::internal;
63
 
 
64
 
 
65
 
#define my_off_t2double(A)  ((double) (my_off_t) (A))
 
56
 
66
57
 
67
58
/* Functions defined in this file */
68
59
 
74
65
static ha_checksum calc_checksum(ha_rows count);
75
66
static int writekeys(MI_SORT_PARAM *sort_param);
76
67
static int sort_one_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo,
77
 
                          my_off_t pagepos, int new_file);
78
 
int sort_key_read(MI_SORT_PARAM *sort_param,void *key);
79
 
int sort_get_next_record(MI_SORT_PARAM *sort_param);
80
 
int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,const void *b);
81
 
int sort_key_write(MI_SORT_PARAM *sort_param, const void *a);
82
 
my_off_t get_record_for_key(MI_INFO *info,MI_KEYDEF *keyinfo,
83
 
                            unsigned char *key);
84
 
int sort_insert_key(MI_SORT_PARAM  *sort_param,
85
 
                    register SORT_KEY_BLOCKS *key_block,
86
 
                    unsigned char *key, my_off_t prev_block);
87
 
int sort_delete_record(MI_SORT_PARAM *sort_param);
88
 
 
 
68
                          my_off_t pagepos, File new_file);
 
69
static int sort_key_read(MI_SORT_PARAM *sort_param,void *key);
 
70
static int sort_get_next_record(MI_SORT_PARAM *sort_param);
 
71
static int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,const void *b);
 
72
static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a);
 
73
static my_off_t get_record_for_key(MI_INFO *info,MI_KEYDEF *keyinfo,
 
74
                                unsigned char *key);
 
75
static int sort_insert_key(MI_SORT_PARAM  *sort_param,
 
76
                           register SORT_KEY_BLOCKS *key_block,
 
77
                           unsigned char *key, my_off_t prev_block);
 
78
static int sort_delete_record(MI_SORT_PARAM *sort_param);
89
79
/*static int flush_pending_blocks(MI_CHECK *param);*/
90
80
static SORT_KEY_BLOCKS  *alloc_key_blocks(MI_CHECK *param, uint32_t blocks,
91
81
                                          uint32_t buffer_length);
129
119
    /* Don't count this as a real warning, as check can correct this ! */
130
120
    uint32_t save=param->warning_printed;
131
121
    mi_check_print_warning(param,
132
 
                           share->state.open_count==1 ?
133
 
                           "%d client is using or hasn't closed the table properly" :
 
122
                           share->state.open_count==1 ? 
 
123
                           "%d client is using or hasn't closed the table properly" : 
134
124
                           "%d clients are using or haven't closed the table properly",
135
125
                           share->state.open_count);
136
126
    /* If this will be fixed by the check, forget the warning */
258
248
  unsigned char *buff;
259
249
 
260
250
  if (param->testflag & T_VERBOSE)
261
 
    printf("block_size %4u:", block_size);
 
251
    printf("block_size %4u:", block_size); /* purecov: tested */
262
252
 
263
253
  next_link=info->s->state.key_del[nr];
264
254
  records= (ha_rows) (info->state->key_file_length / block_size);
272
262
    /* Key blocks must lay within the key file length entirely. */
273
263
    if (next_link + block_size > info->state->key_file_length)
274
264
    {
 
265
      /* purecov: begin tested */
275
266
      mi_check_print_error(param, "Invalid key block position: %s  "
276
267
                           "key block size: %u  file_length: %s",
277
268
                           llstr(next_link, llbuff), block_size,
278
269
                           llstr(info->state->key_file_length, llbuff2));
279
270
      return(1);
 
271
      /* purecov: end */
280
272
    }
281
273
 
282
274
    /* Key blocks must be aligned at MI_MIN_KEY_BLOCK_LENGTH. */
283
275
    if (next_link & (MI_MIN_KEY_BLOCK_LENGTH - 1))
284
276
    {
 
277
      /* purecov: begin tested */
285
278
      mi_check_print_error(param, "Mis-aligned key block: %s  "
286
279
                           "minimum key block length: %u",
287
280
                           llstr(next_link, llbuff), MI_MIN_KEY_BLOCK_LENGTH);
288
281
      return(1);
 
282
      /* purecov: end */
289
283
    }
290
284
 
291
285
    /*
293
287
      If the key cache block size is smaller than block_size, we can so
294
288
      avoid unecessary eviction of cache block.
295
289
    */
296
 
    if (!(buff=key_cache_read(info->s->getKeyCache(),
 
290
    if (!(buff=key_cache_read(info->s->key_cache,
297
291
                              info->s->kfile, next_link, DFLT_INIT_HITS,
298
292
                              (unsigned char*) info->buff, MI_MIN_KEY_BLOCK_LENGTH,
299
293
                              MI_MIN_KEY_BLOCK_LENGTH, 1)))
300
294
    {
 
295
      /* purecov: begin tested */
301
296
      mi_check_print_error(param, "key cache read error for block: %s",
302
297
                           llstr(next_link,llbuff));
303
298
      return(1);
 
299
      /* purecov: end */
304
300
    }
305
301
    next_link=mi_sizekorr(buff);
306
302
    records--;
328
324
  if (!(param->testflag & T_SILENT)) puts("- check file-size");
329
325
 
330
326
  /* The following is needed if called externally (not from myisamchk) */
331
 
  flush_key_blocks(info->s->getKeyCache(),
 
327
  flush_key_blocks(info->s->key_cache,
332
328
                   info->s->kfile, FLUSH_FORCE_WRITE);
333
329
 
334
 
  size= lseek(info->s->kfile, 0, SEEK_END);
 
330
  size= my_seek(info->s->kfile, 0L, MY_SEEK_END, MYF(MY_THREADSAFE));
335
331
  if ((skr=(my_off_t) info->state->key_file_length) != size)
336
332
  {
337
333
    /* Don't give error if file generated by myisampack */
355
351
                           llstr(info->state->key_file_length,buff),
356
352
                           llstr(info->s->base.max_key_file_length-1,buff));
357
353
 
358
 
  size=lseek(info->dfile,0L,SEEK_END);
 
354
  size=my_seek(info->dfile,0L,MY_SEEK_END,MYF(0));
359
355
  skr=(my_off_t) info->state->data_file_length;
360
356
  if (info->s->options & HA_OPTION_COMPRESS_RECORD)
361
357
    skr+= MEMMAP_EXTRA_MARGIN;
444
440
    found_keys++;
445
441
 
446
442
    param->record_checksum=init_checksum;
447
 
 
 
443
    
448
444
    memset(&param->unique_count, 0, sizeof(param->unique_count));
449
445
    memset(&param->notnull_count, 0, sizeof(param->notnull_count));
450
446
 
459
455
                  llstr(share->state.key_root[key],buff));
460
456
      if (!(param->testflag & T_INFO))
461
457
        return(-1);
462
 
      result= UINT32_MAX;
 
458
      result= -1;
463
459
      continue;
464
460
    }
465
461
    param->key_file_blocks+=keyinfo->block_length;
478
474
                    llstr(info->state->records,buff2));
479
475
        if (!(param->testflag & T_INFO))
480
476
        return(-1);
481
 
        result= UINT32_MAX;
 
477
        result= -1;
482
478
        continue;
483
479
      }
484
480
      if (found_keys - full_text_keys == 1 &&
495
491
          mi_check_print_error(param,"Key 1 doesn't point at all records");
496
492
        if (!(param->testflag & T_INFO))
497
493
          return(-1);
498
 
        result= UINT32_MAX;
 
494
        result= -1;
499
495
        continue;
500
496
      }
501
497
    }
550
546
    if (param->testflag & T_STATISTICS)
551
547
      update_key_parts(keyinfo, rec_per_key_part, param->unique_count,
552
548
                       param->stats_method == MI_STATS_METHOD_IGNORE_NULLS?
553
 
                       param->notnull_count: NULL,
 
549
                       param->notnull_count: NULL, 
554
550
                       (uint64_t)info->state->records);
555
551
  }
556
552
  if (param->testflag & T_INFO)
585
581
  /* Key blocks must lay within the key file length entirely. */
586
582
  if (page + keyinfo->block_length > info->state->key_file_length)
587
583
  {
 
584
    /* purecov: begin tested */
588
585
    /* Give it a chance to fit in the real file size. */
589
 
    my_off_t max_length= lseek(info->s->kfile, 0, SEEK_END);
 
586
    my_off_t max_length= my_seek(info->s->kfile, 0L, MY_SEEK_END,
 
587
                                 MYF(MY_THREADSAFE));
590
588
    mi_check_print_error(param, "Invalid key block position: %s  "
591
589
                         "key block size: %u  file_length: %s",
592
590
                         llstr(page, llbuff), keyinfo->block_length,
596
594
    /* Fix the remebered key file length. */
597
595
    info->state->key_file_length= (max_length &
598
596
                                   ~ (my_off_t) (keyinfo->block_length - 1));
 
597
    /* purecov: end */
599
598
  }
600
599
 
601
600
  /* Key blocks must be aligned at MI_MIN_KEY_BLOCK_LENGTH. */
602
601
  if (page & (MI_MIN_KEY_BLOCK_LENGTH - 1))
603
602
  {
 
603
    /* purecov: begin tested */
604
604
    mi_check_print_error(param, "Mis-aligned key block: %s  "
605
605
                         "minimum key block length: %u",
606
606
                         llstr(page, llbuff), MI_MIN_KEY_BLOCK_LENGTH);
607
607
    goto err;
 
608
    /* purecov: end */
608
609
  }
609
610
 
610
611
  if (!_mi_fetch_keypage(info,keyinfo,page, DFLT_INIT_HITS,buff,0))
619
620
 
620
621
  return(0);
621
622
 
 
623
  /* purecov: begin tested */
622
624
err:
623
625
  return(1);
 
626
  /* purecov: end */
624
627
}
625
628
 
626
629
 
670
673
    1. Find out which prefix tuples of last_key don't contain NULLs, and
671
674
       update the array of notnull counters accordingly.
672
675
    2. Find the first keypart number where the prev_key and last_key tuples
673
 
       are different(A), or last_key has NULL value(B), and return it, so the
674
 
       caller can count number of unique tuples for each key prefix. We don't
675
 
       need (B) to be counted, and that is compensated back in
 
676
       are different(A), or last_key has NULL value(B), and return it, so the 
 
677
       caller can count number of unique tuples for each key prefix. We don't 
 
678
       need (B) to be counted, and that is compensated back in 
676
679
       update_key_parts().
677
680
 
678
681
  RETURN
687
690
  uint32_t first_null_seg, kp;
688
691
  HA_KEYSEG *seg;
689
692
 
690
 
  /*
 
693
  /* 
691
694
     Find the first keypart where values are different or either of them is
692
695
     NULL. We get results in diffs array:
693
696
     diffs[0]= 1 + number of first different keypart
695
698
                      last_key that is NULL or different from corresponding
696
699
                      value in prev_key.
697
700
  */
698
 
  ha_key_cmp(keyseg, prev_key, last_key, USE_WHOLE_KEY,
 
701
  ha_key_cmp(keyseg, prev_key, last_key, USE_WHOLE_KEY, 
699
702
             SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diffs);
700
703
  seg= keyseg + diffs[0] - 1;
701
704
 
704
707
  for (kp= 0; kp < first_null_seg; kp++)
705
708
    notnull[kp]++;
706
709
 
707
 
  /*
 
710
  /* 
708
711
    Return 1+ number of first key part where values differ. Don't care if
709
712
    these were NULLs and not .... We compensate for that in
710
713
    update_key_parts.
726
729
  char llbuff[22];
727
730
  uint32_t diff_pos[2];
728
731
 
729
 
  if (!(temp_buff=(unsigned char*) malloc(keyinfo->block_length)))
 
732
  if (!(temp_buff=(unsigned char*) my_alloca((uint) keyinfo->block_length)))
730
733
  {
731
734
    mi_check_print_error(param,"Not enough memory for keyblock");
732
735
    return(-1);
795
798
                     diff_pos);
796
799
        else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
797
800
        {
798
 
          diff_pos[0]= mi_collect_stats_nonulls_next(keyinfo->seg,
 
801
          diff_pos[0]= mi_collect_stats_nonulls_next(keyinfo->seg, 
799
802
                                                  param->notnull_count,
800
803
                                                  info->lastkey, key);
801
804
        }
802
805
        param->unique_count[diff_pos[0]-1]++;
803
806
      }
804
807
      else
805
 
      {
 
808
      {  
806
809
        if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
807
810
          mi_collect_stats_nonulls_first(keyinfo->seg, param->notnull_count,
808
811
                                         key);
824
827
                llstr(page,llbuff), used_length, (keypos - buff));
825
828
    goto err;
826
829
  }
827
 
  free(temp_buff);
 
830
  my_afree((unsigned char*) temp_buff);
828
831
  return(0);
829
832
 err:
830
 
  free(temp_buff);
 
833
  my_afree((unsigned char*) temp_buff);
831
834
  return(1);
832
835
} /* chk_index */
833
836
 
894
897
      puts("- check record links");
895
898
  }
896
899
 
897
 
  if (!mi_alloc_rec_buff(info, SIZE_MAX, &record))
 
900
  if (!mi_alloc_rec_buff(info, -1, &record))
898
901
  {
899
902
    mi_check_print_error(param,"Not enough memory for record");
900
903
    return(-1);
1186
1189
  }
1187
1190
  else if (param->glob_crc != info->state->checksum &&
1188
1191
           (info->s->options &
1189
 
            (HA_OPTION_COMPRESS_RECORD)))
 
1192
            (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)))
1190
1193
  {
1191
1194
    mi_check_print_warning(param,
1192
1195
                           "Record checksum is not the same as checksum stored in the index file\n");
1265
1268
  free(mi_get_rec_buff_ptr(info, record));
1266
1269
  return (error);
1267
1270
 err:
1268
 
  mi_check_print_error(param,"got error: %d when reading datafile at record: %s",errno, llstr(records,llbuff));
 
1271
  mi_check_print_error(param,"got error: %d when reading datafile at record: %s",my_errno, llstr(records,llbuff));
1269
1272
 err2:
1270
1273
  free(mi_get_rec_buff_ptr(info, record));
1271
1274
  param->testflag|=T_RETRY_WITHOUT_QUICK;
1313
1316
 
1314
1317
    However, there is an exception. Sometimes MySQL disables non-unique
1315
1318
    indexes when the table is empty (e.g. when copying a table in
1316
 
    drizzled::alter_table()). When enabling the non-unique indexes, they
 
1319
    mysql_alter_table()). When enabling the non-unique indexes, they
1317
1320
    are still empty. So there is no index block that can be lost. This
1318
1321
    optimization is implemented in this function.
1319
1322
 
1362
1365
        Flush dirty blocks of this index file from key cache and remove
1363
1366
        all blocks of this index file from key cache.
1364
1367
      */
1365
 
      error= flush_key_blocks(share->getKeyCache(), share->kfile,
 
1368
      error= flush_key_blocks(share->key_cache, share->kfile,
1366
1369
                              FLUSH_FORCE_WRITE);
1367
1370
      goto end;
1368
1371
    }
1375
1378
  }
1376
1379
 
1377
1380
  /* Remove all key blocks of this index file from key cache. */
1378
 
  if ((error= flush_key_blocks(share->getKeyCache(), share->kfile,
 
1381
  if ((error= flush_key_blocks(share->key_cache, share->kfile,
1379
1382
                               FLUSH_IGNORE_CHANGED)))
1380
 
    goto end;
 
1383
    goto end; /* purecov: inspected */
1381
1384
 
1382
1385
  /* Clear index root block pointers. */
1383
1386
  for (i= 0; i < share->base.keys; i++)
1406
1409
  int error,got_error;
1407
1410
  ha_rows start_records,new_header_length;
1408
1411
  my_off_t del;
1409
 
  int new_file;
 
1412
  File new_file;
1410
1413
  MYISAM_SHARE *share=info->s;
1411
1414
  char llbuff[22],llbuff2[22];
1412
1415
  SORT_INFO sort_info;
1428
1431
  }
1429
1432
  param->testflag|=T_REP; /* for easy checking */
1430
1433
 
1431
 
  if (info->s->options & (HA_OPTION_COMPRESS_RECORD))
 
1434
  if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
1432
1435
    param->testflag|=T_CALC_CHECKSUM;
1433
1436
 
1434
1437
  if (!param->using_global_keycache)
1435
 
    assert(0);
 
1438
    init_key_cache(dflt_key_cache, param->key_cache_block_size,
 
1439
                   param->use_buffers, 0, 0);
1436
1440
 
1437
 
  if (param->read_cache.init_io_cache(info->dfile, (uint) param->read_buffer_length, READ_CACHE,share->pack.header_length,1,MYF(MY_WME)))
 
1441
  if (init_io_cache(&param->read_cache,info->dfile,
 
1442
                    (uint) param->read_buffer_length,
 
1443
                    READ_CACHE,share->pack.header_length,1,MYF(MY_WME)))
1438
1444
  {
1439
1445
    memset(&info->rec_cache, 0, sizeof(info->rec_cache));
1440
1446
    goto err;
1441
1447
  }
1442
 
  if (not rep_quick)
1443
 
  {
1444
 
    if (info->rec_cache.init_io_cache(-1, (uint) param->write_buffer_length, WRITE_CACHE, new_header_length, 1, MYF(MY_WME | MY_WAIT_IF_FULL)))
1445
 
    {
 
1448
  if (!rep_quick)
 
1449
    if (init_io_cache(&info->rec_cache,-1,(uint) param->write_buffer_length,
 
1450
                      WRITE_CACHE, new_header_length, 1,
 
1451
                      MYF(MY_WME | MY_WAIT_IF_FULL)))
1446
1452
      goto err;
1447
 
    }
1448
 
  }
1449
1453
  info->opt_flag|=WRITE_CACHE_USED;
1450
 
  if (!mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.record) ||
1451
 
      !mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.rec_buff))
 
1454
  if (!mi_alloc_rec_buff(info, -1, &sort_param.record) ||
 
1455
      !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff))
1452
1456
  {
1453
1457
    mi_check_print_error(param, "Not enough memory for extra record");
1454
1458
    goto err;
1457
1461
  if (!rep_quick)
1458
1462
  {
1459
1463
    /* Get real path for data file */
1460
 
    if ((new_file=my_create(internal::fn_format(param->temp_filename,
 
1464
    if ((new_file=my_create(fn_format(param->temp_filename,
1461
1465
                                      share->data_file_name, "",
1462
1466
                                      DATA_TMP_EXT, 2+4),
1463
1467
                            0,param->tmpfile_createflag,
1485
1489
  sort_param.pos=sort_param.max_pos=share->pack.header_length;
1486
1490
  sort_param.filepos=new_header_length;
1487
1491
  param->read_cache.end_of_file=sort_info.filelength=
1488
 
    lseek(info->dfile,0L,SEEK_END);
 
1492
    my_seek(info->dfile,0L,MY_SEEK_END,MYF(0));
1489
1493
  sort_info.dupp=0;
1490
1494
  sort_param.fix_datafile= (bool) (! rep_quick);
1491
1495
  sort_param.master=1;
1513
1517
  {
1514
1518
    if (writekeys(&sort_param))
1515
1519
    {
1516
 
      if (errno != HA_ERR_FOUND_DUPP_KEY)
 
1520
      if (my_errno != HA_ERR_FOUND_DUPP_KEY)
1517
1521
        goto err;
1518
1522
      mi_check_print_info(param,"Duplicate key %2d for record at %10s against new record at %10s",
1519
1523
                          info->errkey+1,
1548
1552
  {
1549
1553
    mi_check_print_warning(param,
1550
1554
                           "Can't change size of indexfile, error: %d",
1551
 
                           errno);
 
1555
                           my_errno);
1552
1556
    goto err;
1553
1557
  }
1554
1558
 
1574
1578
 
1575
1579
  if (!rep_quick)
1576
1580
  {
1577
 
    internal::my_close(info->dfile,MYF(0));
 
1581
    my_close(info->dfile,MYF(0));
1578
1582
    info->dfile=new_file;
1579
1583
    info->state->data_file_length=sort_param.filepos;
1580
1584
    share->state.version=(ulong) time((time_t*) 0);     /* Force reopen */
1607
1611
    /* Replace the actual file with the temporary file */
1608
1612
    if (new_file >= 0)
1609
1613
    {
1610
 
      internal::my_close(new_file,MYF(0));
 
1614
      my_close(new_file,MYF(0));
1611
1615
      info->dfile=new_file= -1;
1612
1616
      if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
1613
1617
                            DATA_TMP_EXT, share->base.raid_chunks,
1620
1624
  if (got_error)
1621
1625
  {
1622
1626
    if (! param->error_printed)
1623
 
      mi_check_print_error(param,"%d for record at pos %s",errno,
 
1627
      mi_check_print_error(param,"%d for record at pos %s",my_errno,
1624
1628
                  llstr(sort_param.start_recpos,llbuff));
1625
1629
    if (new_file >= 0)
1626
1630
    {
1627
 
      internal::my_close(new_file,MYF(0));
 
1631
      my_close(new_file,MYF(0));
1628
1632
      my_delete(param->temp_filename, MYF(MY_WME));
1629
1633
      info->rec_cache.file=-1; /* don't flush data to new_file, it's closed */
1630
1634
    }
1641
1645
  rec_buff_ptr= NULL;
1642
1646
 
1643
1647
  free(sort_info.buff);
1644
 
  param->read_cache.end_io_cache();
 
1648
  end_io_cache(&param->read_cache);
1645
1649
  info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
1646
 
  info->rec_cache.end_io_cache();
1647
 
  got_error|= flush_blocks(param, share->getKeyCache(), share->kfile);
1648
 
  if (not got_error && param->testflag & T_UNPACK)
 
1650
  end_io_cache(&info->rec_cache);
 
1651
  got_error|=flush_blocks(param, share->key_cache, share->kfile);
 
1652
  if (!got_error && param->testflag & T_UNPACK)
1649
1653
  {
1650
1654
    share->state.header.options[0]&= (unsigned char) ~HA_OPTION_COMPRESS_RECORD;
1651
1655
    share->pack.header_length=0;
1682
1686
  return(0);
1683
1687
 
1684
1688
 err:
1685
 
  if (errno == HA_ERR_FOUND_DUPP_KEY)
 
1689
  if (my_errno == HA_ERR_FOUND_DUPP_KEY)
1686
1690
  {
1687
1691
    info->errkey=(int) i;                       /* This key was found */
1688
1692
    while ( i-- > 0 )
1751
1755
 
1752
1756
        /* Tell system that we want all memory for our cache */
1753
1757
 
1754
 
void lock_memory(MI_CHECK *)
 
1758
void lock_memory(MI_CHECK *param __attribute__((unused)))
1755
1759
{
 
1760
#ifdef SUN_OS                           /* Key-cacheing thrases on sun 4.1 */
 
1761
  if (param->opt_lock_memory)
 
1762
  {
 
1763
    int success = mlockall(MCL_CURRENT);        /* or plock(DATLOCK); */
 
1764
    if (geteuid() == 0 && success != 0)
 
1765
      mi_check_print_warning(param,
 
1766
                             "Failed to lock memory. errno %d",my_errno);
 
1767
  }
 
1768
#endif
1756
1769
} /* lock_memory */
1757
1770
 
1758
1771
 
1759
1772
        /* Flush all changed blocks to disk */
1760
1773
 
1761
 
int flush_blocks(MI_CHECK *param, KEY_CACHE *key_cache, int file)
 
1774
int flush_blocks(MI_CHECK *param, KEY_CACHE *key_cache, File file)
1762
1775
{
1763
1776
  if (flush_key_blocks(key_cache, file, FLUSH_RELEASE))
1764
1777
  {
1765
 
    mi_check_print_error(param,"%d when trying to write bufferts",errno);
 
1778
    mi_check_print_error(param,"%d when trying to write bufferts",my_errno);
1766
1779
    return(1);
1767
1780
  }
1768
1781
  if (!param->using_global_keycache)
1777
1790
{
1778
1791
  register uint32_t key;
1779
1792
  register MI_KEYDEF *keyinfo;
1780
 
  int new_file;
 
1793
  File new_file;
1781
1794
  my_off_t index_pos[HA_MAX_POSSIBLE_KEY];
1782
1795
  uint32_t r_locks,w_locks;
1783
1796
  int old_lock;
1792
1805
    printf("- Sorting index for MyISAM-table '%s'\n",name);
1793
1806
 
1794
1807
  /* Get real path for index file */
1795
 
  internal::fn_format(param->temp_filename,name,"", MI_NAME_IEXT,2+4+32);
1796
 
  if ((new_file=my_create(internal::fn_format(param->temp_filename,param->temp_filename,
 
1808
  fn_format(param->temp_filename,name,"", MI_NAME_IEXT,2+4+32);
 
1809
  if ((new_file=my_create(fn_format(param->temp_filename,param->temp_filename,
1797
1810
                                    "", INDEX_TMP_EXT,2+4),
1798
1811
                          0,param->tmpfile_createflag,MYF(0))) <= 0)
1799
1812
  {
1824
1837
  }
1825
1838
 
1826
1839
  /* Flush key cache for this file if we are calling this outside myisamchk */
1827
 
  flush_key_blocks(share->getKeyCache(), share->kfile, FLUSH_IGNORE_CHANGED);
 
1840
  flush_key_blocks(share->key_cache,share->kfile, FLUSH_IGNORE_CHANGED);
1828
1841
 
1829
1842
  share->state.version=(ulong) time((time_t*) 0);
1830
1843
  old_state= share->state;                      /* save state if not stored */
1835
1848
        /* Put same locks as old file */
1836
1849
  share->r_locks= share->w_locks= share->tot_locks= 0;
1837
1850
  (void) _mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE);
1838
 
  internal::my_close(share->kfile,MYF(MY_WME));
 
1851
  my_close(share->kfile,MYF(MY_WME));
1839
1852
  share->kfile = -1;
1840
 
  internal::my_close(new_file,MYF(MY_WME));
 
1853
  my_close(new_file,MYF(MY_WME));
1841
1854
  if (change_to_newfile(share->index_file_name,MI_NAME_IEXT,INDEX_TMP_EXT,0,
1842
1855
                        MYF(0)) ||
1843
1856
      mi_open_keyfile(share))
1861
1874
  return(0);
1862
1875
 
1863
1876
err:
1864
 
  internal::my_close(new_file,MYF(MY_WME));
 
1877
  my_close(new_file,MYF(MY_WME));
1865
1878
err2:
1866
1879
  my_delete(param->temp_filename,MYF(MY_WME));
1867
1880
  return(-1);
1871
1884
         /* Sort records recursive using one index */
1872
1885
 
1873
1886
static int sort_one_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
1874
 
                          my_off_t pagepos, int new_file)
 
1887
                          my_off_t pagepos, File new_file)
1875
1888
{
1876
1889
  uint32_t length,nod_flag,used_length, key_length;
1877
1890
  unsigned char *buff,*keypos,*endpos;
1882
1895
  new_page_pos=param->new_file_pos;
1883
1896
  param->new_file_pos+=keyinfo->block_length;
1884
1897
 
1885
 
  if (!(buff=(unsigned char*) malloc(keyinfo->block_length)))
 
1898
  if (!(buff=(unsigned char*) my_alloca((uint) keyinfo->block_length)))
1886
1899
  {
1887
1900
    mi_check_print_error(param,"Not enough memory for key block");
1888
1901
    return(-1);
1922
1935
  if (my_pwrite(new_file,(unsigned char*) buff,(uint) keyinfo->block_length,
1923
1936
                new_page_pos,MYF(MY_NABP | MY_WAIT_IF_FULL)))
1924
1937
  {
1925
 
    mi_check_print_error(param,"Can't write indexblock, error: %d",errno);
 
1938
    mi_check_print_error(param,"Can't write indexblock, error: %d",my_errno);
1926
1939
    goto err;
1927
1940
  }
1928
 
  free(buff);
 
1941
  my_afree((unsigned char*) buff);
1929
1942
  return(0);
1930
1943
err:
1931
 
  free(buff);
 
1944
  my_afree((unsigned char*) buff);
1932
1945
  return(1);
1933
1946
} /* sort_one_index */
1934
1947
 
1944
1957
 
1945
1958
int change_to_newfile(const char * filename, const char * old_ext,
1946
1959
                      const char * new_ext,
1947
 
                      uint32_t raid_chunks,
 
1960
                      uint32_t raid_chunks __attribute__((unused)),
1948
1961
                      myf MyFlags)
1949
1962
{
1950
 
  (void)raid_chunks;
1951
1963
  char old_filename[FN_REFLEN],new_filename[FN_REFLEN];
1952
1964
  /* Get real path to filename */
1953
 
  (void) internal::fn_format(old_filename,filename,"",old_ext,2+4+32);
 
1965
  (void) fn_format(old_filename,filename,"",old_ext,2+4+32);
1954
1966
  return my_redel(old_filename,
1955
 
                  internal::fn_format(new_filename,old_filename,"",new_ext,2+4),
 
1967
                  fn_format(new_filename,old_filename,"",new_ext,2+4),
1956
1968
                  MYF(MY_WME | MY_LINK_WARNING | MyFlags));
1957
1969
} /* change_to_newfile */
1958
1970
 
1960
1972
 
1961
1973
        /* Copy a block between two files */
1962
1974
 
1963
 
int filecopy(MI_CHECK *param, int to,int from,my_off_t start,
 
1975
int filecopy(MI_CHECK *param, File to,File from,my_off_t start,
1964
1976
             my_off_t length, const char *type)
1965
1977
{
1966
1978
  char tmp_buff[IO_SIZE],*buff;
1967
1979
  ulong buff_length;
1968
1980
 
1969
 
  buff_length=(ulong) min(param->write_buffer_length, (size_t)length);
1970
 
  if (!(buff=(char *)malloc(buff_length)))
 
1981
  buff_length=(ulong) cmin(param->write_buffer_length,length);
 
1982
  if (!(buff=my_malloc(buff_length,MYF(0))))
1971
1983
  {
1972
1984
    buff=tmp_buff; buff_length=IO_SIZE;
1973
1985
  }
1974
1986
 
1975
 
  lseek(from,start,SEEK_SET);
 
1987
  my_seek(from,start,MY_SEEK_SET,MYF(0));
1976
1988
  while (length > buff_length)
1977
1989
  {
1978
1990
    if (my_read(from,(unsigned char*) buff,buff_length,MYF(MY_NABP)) ||
1990
2002
  if (buff != tmp_buff)
1991
2003
    free(buff);
1992
2004
  mi_check_print_error(param,"Can't copy %s to tempfile, error %d",
1993
 
                       type,errno);
 
2005
                       type,my_errno);
1994
2006
  return(1);
1995
2007
}
1996
2008
 
2018
2030
  ulong length;
2019
2031
  ha_rows start_records;
2020
2032
  my_off_t new_header_length,del;
2021
 
  int new_file;
 
2033
  File new_file;
2022
2034
  MI_SORT_PARAM sort_param;
2023
2035
  MYISAM_SHARE *share=info->s;
2024
2036
  HA_KEYSEG *keyseg;
2039
2051
  }
2040
2052
  param->testflag|=T_REP; /* for easy checking */
2041
2053
 
2042
 
  if (info->s->options & (HA_OPTION_COMPRESS_RECORD))
 
2054
  if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
2043
2055
    param->testflag|=T_CALC_CHECKSUM;
2044
2056
 
2045
2057
  memset(&sort_info, 0, sizeof(sort_info));
2046
2058
  memset(&sort_param, 0, sizeof(sort_param));
2047
2059
  if (!(sort_info.key_block=
2048
 
        alloc_key_blocks(param, (uint) param->sort_key_blocks, share->base.max_key_block_length))
2049
 
      || param->read_cache.init_io_cache(info->dfile, (uint) param->read_buffer_length, READ_CACHE,share->pack.header_length,1,MYF(MY_WME))
2050
 
      || (! rep_quick && info->rec_cache.init_io_cache(info->dfile, (uint) param->write_buffer_length, WRITE_CACHE,new_header_length,1, MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw)))
2051
 
  {
 
2060
        alloc_key_blocks(param,
 
2061
                         (uint) param->sort_key_blocks,
 
2062
                         share->base.max_key_block_length))
 
2063
      || init_io_cache(&param->read_cache,info->dfile,
 
2064
                       (uint) param->read_buffer_length,
 
2065
                       READ_CACHE,share->pack.header_length,1,MYF(MY_WME)) ||
 
2066
      (! rep_quick &&
 
2067
       init_io_cache(&info->rec_cache,info->dfile,
 
2068
                     (uint) param->write_buffer_length,
 
2069
                     WRITE_CACHE,new_header_length,1,
 
2070
                     MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw)))
2052
2071
    goto err;
2053
 
  }
2054
2072
  sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
2055
2073
  info->opt_flag|=WRITE_CACHE_USED;
2056
2074
  info->rec_cache.file=info->dfile;             /* for sort_delete_record */
2057
2075
 
2058
 
  if (!mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.record) ||
2059
 
      !mi_alloc_rec_buff(info, SIZE_MAX, &sort_param.rec_buff))
 
2076
  if (!mi_alloc_rec_buff(info, -1, &sort_param.record) ||
 
2077
      !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff))
2060
2078
  {
2061
2079
    mi_check_print_error(param, "Not enough memory for extra record");
2062
2080
    goto err;
2064
2082
  if (!rep_quick)
2065
2083
  {
2066
2084
    /* Get real path for data file */
2067
 
    if ((new_file=my_create(internal::fn_format(param->temp_filename,
 
2085
    if ((new_file=my_create(fn_format(param->temp_filename,
2068
2086
                                      share->data_file_name, "",
2069
2087
                                      DATA_TMP_EXT, 2+4),
2070
2088
                            0,param->tmpfile_createflag,
2106
2124
  sort_info.dupp=0;
2107
2125
  sort_info.buff=0;
2108
2126
  param->read_cache.end_of_file=sort_info.filelength=
2109
 
    lseek(param->read_cache.file,0L,SEEK_END);
 
2127
    my_seek(param->read_cache.file,0L,MY_SEEK_END,MYF(0));
2110
2128
 
2111
2129
  sort_param.wordlist=NULL;
2112
2130
 
2113
2131
  if (share->data_file_type == DYNAMIC_RECORD)
2114
 
    length=max(share->base.min_pack_length+1,share->base.min_block_length);
 
2132
    length=cmax(share->base.min_pack_length+1,share->base.min_block_length);
2115
2133
  else if (share->data_file_type == COMPRESSED_RECORD)
2116
2134
    length=share->base.min_block_length;
2117
2135
  else
2121
2139
     (ha_rows) (sort_info.filelength/length+1));
2122
2140
  sort_param.key_cmp=sort_key_cmp;
2123
2141
  sort_param.lock_in_memory=lock_memory;
 
2142
  sort_param.tmpdir=param->tmpdir;
2124
2143
  sort_param.sort_info=&sort_info;
2125
2144
  sort_param.fix_datafile= (bool) (! rep_quick);
2126
2145
  sort_param.master =1;
2127
 
 
 
2146
  
2128
2147
  del=info->state->del;
2129
2148
  param->glob_crc=0;
2130
2149
  if (param->testflag & T_CALC_CHECKSUM)
2185
2204
    }
2186
2205
    /* No need to calculate checksum again. */
2187
2206
    sort_param.calc_checksum= 0;
2188
 
    sort_param.wordroot.free_root(MYF(0));
 
2207
    free_root(&sort_param.wordroot, MYF(0));
2189
2208
 
2190
2209
    /* Set for next loop */
2191
2210
    sort_info.max_records= (ha_rows) info->state->records;
2201
2220
    if (sort_param.fix_datafile)
2202
2221
    {
2203
2222
      param->read_cache.end_of_file=sort_param.filepos;
2204
 
      if (write_data_suffix(&sort_info, 1) || info->rec_cache.end_io_cache())
2205
 
      {
 
2223
      if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache))
2206
2224
        goto err;
2207
 
      }
2208
2225
      if (param->testflag & T_SAFE_REPAIR)
2209
2226
      {
2210
2227
        /* Don't repair if we loosed more than one row */
2218
2235
        sort_param.filepos;
2219
2236
      /* Only whole records */
2220
2237
      share->state.version=(ulong) time((time_t*) 0);
2221
 
      internal::my_close(info->dfile,MYF(0));
 
2238
      my_close(info->dfile,MYF(0));
2222
2239
      info->dfile=new_file;
2223
2240
      share->data_file_type=sort_info.new_data_file_type;
2224
2241
      share->pack.header_length=(ulong) new_header_length;
2228
2245
      info->state->data_file_length=sort_param.max_pos;
2229
2246
 
2230
2247
    param->read_cache.file=info->dfile;         /* re-init read cache */
2231
 
    param->read_cache.reinit_io_cache(READ_CACHE,share->pack.header_length, 1,1);
 
2248
    reinit_io_cache(&param->read_cache,READ_CACHE,share->pack.header_length,
 
2249
                    1,1);
2232
2250
  }
2233
2251
 
2234
2252
  if (param->testflag & T_WRITE_LOOP)
2260
2278
      if (ftruncate(info->dfile, skr))
2261
2279
        mi_check_print_warning(param,
2262
2280
                               "Can't change size of datafile,  error: %d",
2263
 
                               errno);
 
2281
                               my_errno);
2264
2282
  }
2265
2283
  if (param->testflag & T_CALC_CHECKSUM)
2266
2284
    info->state->checksum=param->glob_crc;
2268
2286
  if (ftruncate(share->kfile, info->state->key_file_length))
2269
2287
    mi_check_print_warning(param,
2270
2288
                           "Can't change size of indexfile, error: %d",
2271
 
                           errno);
 
2289
                           my_errno);
2272
2290
 
2273
2291
  if (!(param->testflag & T_SILENT))
2274
2292
  {
2285
2303
    memcpy( &share->state.state, info->state, sizeof(*info->state));
2286
2304
 
2287
2305
err:
2288
 
  got_error|= flush_blocks(param, share->getKeyCache(), share->kfile);
2289
 
  info->rec_cache.end_io_cache();
 
2306
  got_error|= flush_blocks(param, share->key_cache, share->kfile);
 
2307
  end_io_cache(&info->rec_cache);
2290
2308
  if (!got_error)
2291
2309
  {
2292
2310
    /* Replace the actual file with the temporary file */
2293
2311
    if (new_file >= 0)
2294
2312
    {
2295
 
      internal::my_close(new_file,MYF(0));
 
2313
      my_close(new_file,MYF(0));
2296
2314
      info->dfile=new_file= -1;
2297
2315
      if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
2298
2316
                            DATA_TMP_EXT, share->base.raid_chunks,
2305
2323
  if (got_error)
2306
2324
  {
2307
2325
    if (! param->error_printed)
2308
 
      mi_check_print_error(param,"%d when fixing table",errno);
 
2326
      mi_check_print_error(param,"%d when fixing table",my_errno);
2309
2327
    if (new_file >= 0)
2310
2328
    {
2311
 
      internal::my_close(new_file,MYF(0));
 
2329
      my_close(new_file,MYF(0));
2312
2330
      my_delete(param->temp_filename, MYF(MY_WME));
2313
2331
      if (info->dfile == new_file)
2314
2332
        info->dfile= -1;
2330
2348
 
2331
2349
  free((unsigned char*) sort_info.key_block);
2332
2350
  free(sort_info.buff);
2333
 
  param->read_cache.end_io_cache();
 
2351
  end_io_cache(&param->read_cache);
 
2352
  info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
 
2353
  if (!got_error && (param->testflag & T_UNPACK))
 
2354
  {
 
2355
    share->state.header.options[0]&= (unsigned char) ~HA_OPTION_COMPRESS_RECORD;
 
2356
    share->pack.header_length=0;
 
2357
  }
 
2358
  return(got_error);
 
2359
}
 
2360
 
 
2361
/*
 
2362
  Threaded repair of table using sorting
 
2363
 
 
2364
  SYNOPSIS
 
2365
    mi_repair_parallel()
 
2366
    param               Repair parameters
 
2367
    info                MyISAM handler to repair
 
2368
    name                Name of table (for warnings)
 
2369
    rep_quick           set to <> 0 if we should not change data file
 
2370
 
 
2371
  DESCRIPTION
 
2372
    Same as mi_repair_by_sort but do it multithreaded
 
2373
    Each key is handled by a separate thread.
 
2374
    TODO: make a number of threads a parameter
 
2375
 
 
2376
    In parallel repair we use one thread per index. There are two modes:
 
2377
 
 
2378
    Quick
 
2379
 
 
2380
      Only the indexes are rebuilt. All threads share a read buffer.
 
2381
      Every thread that needs fresh data in the buffer enters the shared
 
2382
      cache lock. The last thread joining the lock reads the buffer from
 
2383
      the data file and wakes all other threads.
 
2384
 
 
2385
    Non-quick
 
2386
 
 
2387
      The data file is rebuilt and all indexes are rebuilt to point to
 
2388
      the new record positions. One thread is the master thread. It
 
2389
      reads from the old data file and writes to the new data file. It
 
2390
      also creates one of the indexes. The other threads read from a
 
2391
      buffer which is filled by the master. If they need fresh data,
 
2392
      they enter the shared cache lock. If the masters write buffer is
 
2393
      full, it flushes it to the new data file and enters the shared
 
2394
      cache lock too. When all threads joined in the lock, the master
 
2395
      copies its write buffer to the read buffer for the other threads
 
2396
      and wakes them.
 
2397
 
 
2398
  RESULT
 
2399
    0   ok
 
2400
    <>0 Error
 
2401
*/
 
2402
 
 
2403
int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
 
2404
                        const char * name, int rep_quick)
 
2405
{
 
2406
  int got_error;
 
2407
  uint32_t i,key, total_key_length, istep;
 
2408
  ulong rec_length;
 
2409
  ha_rows start_records;
 
2410
  my_off_t new_header_length,del;
 
2411
  File new_file;
 
2412
  MI_SORT_PARAM *sort_param=0;
 
2413
  MYISAM_SHARE *share=info->s;
 
2414
  ulong   *rec_per_key_part;
 
2415
  HA_KEYSEG *keyseg;
 
2416
  char llbuff[22];
 
2417
  IO_CACHE new_data_cache; /* For non-quick repair. */
 
2418
  IO_CACHE_SHARE io_share;
 
2419
  SORT_INFO sort_info;
 
2420
  uint64_t key_map= 0;
 
2421
  pthread_attr_t thr_attr;
 
2422
  ulong max_pack_reclength;
 
2423
 
 
2424
  start_records=info->state->records;
 
2425
  got_error=1;
 
2426
  new_file= -1;
 
2427
  new_header_length=(param->testflag & T_UNPACK) ? 0 :
 
2428
    share->pack.header_length;
 
2429
  if (!(param->testflag & T_SILENT))
 
2430
  {
 
2431
    printf("- parallel recovering (with sort) MyISAM-table '%s'\n",name);
 
2432
    printf("Data records: %s\n", llstr(start_records,llbuff));
 
2433
  }
 
2434
  param->testflag|=T_REP; /* for easy checking */
 
2435
 
 
2436
  if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
 
2437
    param->testflag|=T_CALC_CHECKSUM;
 
2438
 
 
2439
  /*
 
2440
    Quick repair (not touching data file, rebuilding indexes):
 
2441
    {
 
2442
      Read  cache is (MI_CHECK *param)->read_cache using info->dfile.
 
2443
    }
 
2444
 
 
2445
    Non-quick repair (rebuilding data file and indexes):
 
2446
    {
 
2447
      Master thread:
 
2448
 
 
2449
        Read  cache is (MI_CHECK *param)->read_cache using info->dfile.
 
2450
        Write cache is (MI_INFO   *info)->rec_cache  using new_file.
 
2451
 
 
2452
      Slave threads:
 
2453
 
 
2454
        Read  cache is new_data_cache synced to master rec_cache.
 
2455
 
 
2456
      The final assignment of the filedescriptor for rec_cache is done
 
2457
      after the cache creation.
 
2458
 
 
2459
      Don't check file size on new_data_cache, as the resulting file size
 
2460
      is not known yet.
 
2461
 
 
2462
      As rec_cache and new_data_cache are synced, write_buffer_length is
 
2463
      used for the read cache 'new_data_cache'. Both start at the same
 
2464
      position 'new_header_length'.
 
2465
    }
 
2466
  */
 
2467
  memset(&sort_info, 0, sizeof(sort_info));
 
2468
  /* Initialize pthread structures before goto err. */
 
2469
  pthread_mutex_init(&sort_info.mutex, MY_MUTEX_INIT_FAST);
 
2470
  pthread_cond_init(&sort_info.cond, 0);
 
2471
 
 
2472
  if (!(sort_info.key_block=
 
2473
        alloc_key_blocks(param, (uint) param->sort_key_blocks,
 
2474
                         share->base.max_key_block_length)) ||
 
2475
      init_io_cache(&param->read_cache, info->dfile,
 
2476
                    (uint) param->read_buffer_length,
 
2477
                    READ_CACHE, share->pack.header_length, 1, MYF(MY_WME)) ||
 
2478
      (!rep_quick &&
 
2479
       (init_io_cache(&info->rec_cache, info->dfile,
 
2480
                      (uint) param->write_buffer_length,
 
2481
                      WRITE_CACHE, new_header_length, 1,
 
2482
                      MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw) ||
 
2483
        init_io_cache(&new_data_cache, -1,
 
2484
                      (uint) param->write_buffer_length,
 
2485
                      READ_CACHE, new_header_length, 1,
 
2486
                      MYF(MY_WME | MY_DONT_CHECK_FILESIZE)))))
 
2487
    goto err;
 
2488
  sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
 
2489
  info->opt_flag|=WRITE_CACHE_USED;
 
2490
  info->rec_cache.file=info->dfile;         /* for sort_delete_record */
 
2491
 
 
2492
  if (!rep_quick)
 
2493
  {
 
2494
    /* Get real path for data file */
 
2495
    if ((new_file=my_create(fn_format(param->temp_filename,
 
2496
                                      share->data_file_name, "",
 
2497
                                      DATA_TMP_EXT,
 
2498
                                      2+4),
 
2499
                            0,param->tmpfile_createflag,
 
2500
                            MYF(0))) < 0)
 
2501
    {
 
2502
      mi_check_print_error(param,"Can't create new tempfile: '%s'",
 
2503
                           param->temp_filename);
 
2504
      goto err;
 
2505
    }
 
2506
    if (new_header_length &&
 
2507
        filecopy(param, new_file,info->dfile,0L,new_header_length,
 
2508
                 "datafile-header"))
 
2509
      goto err;
 
2510
    if (param->testflag & T_UNPACK)
 
2511
    {
 
2512
      share->options&= ~HA_OPTION_COMPRESS_RECORD;
 
2513
      mi_int2store(share->state.header.options,share->options);
 
2514
    }
 
2515
    share->state.dellink= HA_OFFSET_ERROR;
 
2516
    info->rec_cache.file=new_file;
 
2517
  }
 
2518
 
 
2519
  info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
 
2520
 
 
2521
  /* Optionally drop indexes and optionally modify the key_map. */
 
2522
  mi_drop_all_indexes(param, info, false);
 
2523
  key_map= share->state.key_map;
 
2524
  if (param->testflag & T_CREATE_MISSING_KEYS)
 
2525
  {
 
2526
    /* Invert the copied key_map to recreate all disabled indexes. */
 
2527
    key_map= ~key_map;
 
2528
  }
 
2529
 
 
2530
  sort_info.info=info;
 
2531
  sort_info.param = param;
 
2532
 
 
2533
  set_data_file_type(&sort_info, share);
 
2534
  sort_info.dupp=0;
 
2535
  sort_info.buff=0;
 
2536
  param->read_cache.end_of_file=sort_info.filelength=
 
2537
    my_seek(param->read_cache.file,0L,MY_SEEK_END,MYF(0));
 
2538
 
 
2539
  if (share->data_file_type == DYNAMIC_RECORD)
 
2540
    rec_length=cmax(share->base.min_pack_length+1,share->base.min_block_length);
 
2541
  else if (share->data_file_type == COMPRESSED_RECORD)
 
2542
    rec_length=share->base.min_block_length;
 
2543
  else
 
2544
    rec_length=share->base.pack_reclength;
 
2545
  /*
 
2546
    +1 below is required hack for parallel repair mode.
 
2547
    The info->state->records value, that is compared later
 
2548
    to sort_info.max_records and cannot exceed it, is
 
2549
    increased in sort_key_write. In mi_repair_by_sort, sort_key_write
 
2550
    is called after sort_key_read, where the comparison is performed,
 
2551
    but in parallel mode master thread can call sort_key_write
 
2552
    before some other repair thread calls sort_key_read.
 
2553
    Furthermore I'm not even sure +1 would be enough.
 
2554
    May be sort_info.max_records shold be always set to max value in
 
2555
    parallel mode.
 
2556
  */
 
2557
  sort_info.max_records=
 
2558
    ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records + 1:
 
2559
     (ha_rows) (sort_info.filelength/rec_length+1));
 
2560
 
 
2561
  del=info->state->del;
 
2562
  param->glob_crc=0;
 
2563
  /* for compressed tables */
 
2564
  max_pack_reclength= share->base.pack_reclength;
 
2565
  if (share->options & HA_OPTION_COMPRESS_RECORD)
 
2566
    set_if_bigger(max_pack_reclength, share->max_pack_length);
 
2567
  if (!(sort_param=(MI_SORT_PARAM *)
 
2568
        my_malloc((uint) share->base.keys *
 
2569
                  (sizeof(MI_SORT_PARAM) + max_pack_reclength),
 
2570
                  MYF(MY_ZEROFILL))))
 
2571
  {
 
2572
    mi_check_print_error(param,"Not enough memory for key!");
 
2573
    goto err;
 
2574
  }
 
2575
  total_key_length=0;
 
2576
  rec_per_key_part= param->rec_per_key_part;
 
2577
  info->state->records=info->state->del=share->state.split=0;
 
2578
  info->state->empty=0;
 
2579
 
 
2580
  for (i=key=0, istep=1 ; key < share->base.keys ;
 
2581
       rec_per_key_part+=sort_param[i].keyinfo->keysegs, i+=istep, key++)
 
2582
  {
 
2583
    sort_param[i].key=key;
 
2584
    sort_param[i].keyinfo=share->keyinfo+key;
 
2585
    sort_param[i].seg=sort_param[i].keyinfo->seg;
 
2586
    /*
 
2587
      Skip this index if it is marked disabled in the copied
 
2588
      (and possibly inverted) key_map.
 
2589
    */
 
2590
    if (! mi_is_key_active(key_map, key))
 
2591
    {
 
2592
      /* Remember old statistics for key */
 
2593
      assert(rec_per_key_part >= param->rec_per_key_part);
 
2594
      memcpy(rec_per_key_part,
 
2595
             (share->state.rec_per_key_part +
 
2596
              (rec_per_key_part - param->rec_per_key_part)),
 
2597
             sort_param[i].keyinfo->keysegs*sizeof(*rec_per_key_part));
 
2598
      istep=0;
 
2599
      continue;
 
2600
    }
 
2601
    istep=1;
 
2602
    if ((!(param->testflag & T_SILENT)))
 
2603
      printf ("- Fixing index %d\n",key+1);
 
2604
    {
 
2605
      sort_param[i].key_read=sort_key_read;
 
2606
      sort_param[i].key_write=sort_key_write;
 
2607
    }
 
2608
    sort_param[i].key_cmp=sort_key_cmp;
 
2609
    sort_param[i].lock_in_memory=lock_memory;
 
2610
    sort_param[i].tmpdir=param->tmpdir;
 
2611
    sort_param[i].sort_info=&sort_info;
 
2612
    sort_param[i].master=0;
 
2613
    sort_param[i].fix_datafile=0;
 
2614
    sort_param[i].calc_checksum= 0;
 
2615
 
 
2616
    sort_param[i].filepos=new_header_length;
 
2617
    sort_param[i].max_pos=sort_param[i].pos=share->pack.header_length;
 
2618
 
 
2619
    sort_param[i].record= (((unsigned char *)(sort_param+share->base.keys))+
 
2620
                           (max_pack_reclength * i));
 
2621
    if (!mi_alloc_rec_buff(info, -1, &sort_param[i].rec_buff))
 
2622
    {
 
2623
      mi_check_print_error(param,"Not enough memory!");
 
2624
      goto err;
 
2625
    }
 
2626
 
 
2627
    sort_param[i].key_length=share->rec_reflength;
 
2628
    for (keyseg=sort_param[i].seg; keyseg->type != HA_KEYTYPE_END;
 
2629
         keyseg++)
 
2630
    {
 
2631
      sort_param[i].key_length+=keyseg->length;
 
2632
      if (keyseg->flag & HA_SPACE_PACK)
 
2633
        sort_param[i].key_length+=get_pack_length(keyseg->length);
 
2634
      if (keyseg->flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
 
2635
        sort_param[i].key_length+=2 + test(keyseg->length >= 127);
 
2636
      if (keyseg->flag & HA_NULL_PART)
 
2637
        sort_param[i].key_length++;
 
2638
    }
 
2639
    total_key_length+=sort_param[i].key_length;
 
2640
  }
 
2641
  sort_info.total_keys=i;
 
2642
  sort_param[0].master= 1;
 
2643
  sort_param[0].fix_datafile= (bool)(! rep_quick);
 
2644
  sort_param[0].calc_checksum= test(param->testflag & T_CALC_CHECKSUM);
 
2645
 
 
2646
  sort_info.got_error=0;
 
2647
  pthread_mutex_lock(&sort_info.mutex);
 
2648
 
 
2649
  /*
 
2650
    Initialize the I/O cache share for use with the read caches and, in
 
2651
    case of non-quick repair, the write cache. When all threads join on
 
2652
    the cache lock, the writer copies the write cache contents to the
 
2653
    read caches.
 
2654
  */
 
2655
  if (i > 1)
 
2656
  {
 
2657
    if (rep_quick)
 
2658
      init_io_cache_share(&param->read_cache, &io_share, NULL, i);
 
2659
    else
 
2660
      init_io_cache_share(&new_data_cache, &io_share, &info->rec_cache, i);
 
2661
  }
 
2662
  else
 
2663
    io_share.total_threads= 0; /* share not used */
 
2664
 
 
2665
  (void) pthread_attr_init(&thr_attr);
 
2666
  (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
 
2667
 
 
2668
  for (i=0 ; i < sort_info.total_keys ; i++)
 
2669
  {
 
2670
    /*
 
2671
      Copy the properly initialized IO_CACHE structure so that every
 
2672
      thread has its own copy. In quick mode param->read_cache is shared
 
2673
      for use by all threads. In non-quick mode all threads but the
 
2674
      first copy the shared new_data_cache, which is synchronized to the
 
2675
      write cache of the first thread. The first thread copies
 
2676
      param->read_cache, which is not shared.
 
2677
    */
 
2678
    sort_param[i].read_cache= ((rep_quick || !i) ? param->read_cache :
 
2679
                               new_data_cache);
 
2680
 
 
2681
    /*
 
2682
      two approaches: the same amount of memory for each thread
 
2683
      or the memory for the same number of keys for each thread...
 
2684
      In the second one all the threads will fill their sort_buffers
 
2685
      (and call write_keys) at the same time, putting more stress on i/o.
 
2686
    */
 
2687
    sort_param[i].sortbuff_size=
 
2688
#ifndef USING_SECOND_APPROACH
 
2689
      param->sort_buffer_length/sort_info.total_keys;
 
2690
#else
 
2691
      param->sort_buffer_length*sort_param[i].key_length/total_key_length;
 
2692
#endif
 
2693
    if (pthread_create(&sort_param[i].thr, &thr_attr,
 
2694
                       thr_find_all_keys,
 
2695
                       (void *) (sort_param+i)))
 
2696
    {
 
2697
      mi_check_print_error(param,"Cannot start a repair thread");
 
2698
      /* Cleanup: Detach from the share. Avoid others to be blocked. */
 
2699
      if (io_share.total_threads)
 
2700
        remove_io_thread(&sort_param[i].read_cache);
 
2701
      sort_info.got_error=1;
 
2702
    }
 
2703
    else
 
2704
      sort_info.threads_running++;
 
2705
  }
 
2706
  (void) pthread_attr_destroy(&thr_attr);
 
2707
 
 
2708
  /* waiting for all threads to finish */
 
2709
  while (sort_info.threads_running)
 
2710
    pthread_cond_wait(&sort_info.cond, &sort_info.mutex);
 
2711
  pthread_mutex_unlock(&sort_info.mutex);
 
2712
 
 
2713
  if ((got_error= thr_write_keys(sort_param)))
 
2714
  {
 
2715
    param->retry_repair=1;
 
2716
    goto err;
 
2717
  }
 
2718
  got_error=1;                          /* Assume the following may go wrong */
 
2719
 
 
2720
  if (sort_param[0].fix_datafile)
 
2721
  {
 
2722
    /*
 
2723
      Append some nuls to the end of a memory mapped file. Destroy the
 
2724
      write cache. The master thread did already detach from the share
 
2725
      by remove_io_thread() in sort.c:thr_find_all_keys().
 
2726
    */
 
2727
    if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache))
 
2728
      goto err;
 
2729
    if (param->testflag & T_SAFE_REPAIR)
 
2730
    {
 
2731
      /* Don't repair if we loosed more than one row */
 
2732
      if (info->state->records+1 < start_records)
 
2733
      {
 
2734
        info->state->records=start_records;
 
2735
        goto err;
 
2736
      }
 
2737
    }
 
2738
    share->state.state.data_file_length= info->state->data_file_length=
 
2739
      sort_param->filepos;
 
2740
    /* Only whole records */
 
2741
    share->state.version=(ulong) time((time_t*) 0);
 
2742
 
 
2743
    /*
 
2744
      Exchange the data file descriptor of the table, so that we use the
 
2745
      new file from now on.
 
2746
     */
 
2747
    my_close(info->dfile,MYF(0));
 
2748
    info->dfile=new_file;
 
2749
 
 
2750
    share->data_file_type=sort_info.new_data_file_type;
 
2751
    share->pack.header_length=(ulong) new_header_length;
 
2752
  }
 
2753
  else
 
2754
    info->state->data_file_length=sort_param->max_pos;
 
2755
 
 
2756
  if (rep_quick && del+sort_info.dupp != info->state->del)
 
2757
  {
 
2758
    mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
 
2759
    mi_check_print_error(param,"Run recovery again without -q");
 
2760
    param->retry_repair=1;
 
2761
    param->testflag|=T_RETRY_WITHOUT_QUICK;
 
2762
    goto err;
 
2763
  }
 
2764
 
 
2765
  if (rep_quick & T_FORCE_UNIQUENESS)
 
2766
  {
 
2767
    my_off_t skr=info->state->data_file_length+
 
2768
      (share->options & HA_OPTION_COMPRESS_RECORD ?
 
2769
       MEMMAP_EXTRA_MARGIN : 0);
 
2770
#ifdef USE_RELOC
 
2771
    if (share->data_file_type == STATIC_RECORD &&
 
2772
        skr < share->base.reloc*share->base.min_pack_length)
 
2773
      skr=share->base.reloc*share->base.min_pack_length;
 
2774
#endif
 
2775
    if (skr != sort_info.filelength && !info->s->base.raid_type)
 
2776
      if (ftruncate(info->dfile, skr))
 
2777
        mi_check_print_warning(param,
 
2778
                               "Can't change size of datafile,  error: %d",
 
2779
                               my_errno);
 
2780
  }
 
2781
  if (param->testflag & T_CALC_CHECKSUM)
 
2782
    info->state->checksum=param->glob_crc;
 
2783
 
 
2784
  if (ftruncate(share->kfile, info->state->key_file_length))
 
2785
    mi_check_print_warning(param,
 
2786
                           "Can't change size of indexfile, error: %d", my_errno);
 
2787
 
 
2788
  if (!(param->testflag & T_SILENT))
 
2789
  {
 
2790
    if (start_records != info->state->records)
 
2791
      printf("Data records: %s\n", llstr(info->state->records,llbuff));
 
2792
    if (sort_info.dupp)
 
2793
      mi_check_print_warning(param,
 
2794
                             "%s records have been removed",
 
2795
                             llstr(sort_info.dupp,llbuff));
 
2796
  }
 
2797
  got_error=0;
 
2798
 
 
2799
  if (&share->state.state != info->state)
 
2800
    memcpy(&share->state.state, info->state, sizeof(*info->state));
 
2801
 
 
2802
err:
 
2803
  got_error|= flush_blocks(param, share->key_cache, share->kfile);
 
2804
  /*
 
2805
    Destroy the write cache. The master thread did already detach from
 
2806
    the share by remove_io_thread() or it was not yet started (if the
 
2807
    error happend before creating the thread).
 
2808
  */
 
2809
  end_io_cache(&info->rec_cache);
 
2810
  /*
 
2811
    Destroy the new data cache in case of non-quick repair. All slave
 
2812
    threads did either detach from the share by remove_io_thread()
 
2813
    already or they were not yet started (if the error happend before
 
2814
    creating the threads).
 
2815
  */
 
2816
  if (!rep_quick)
 
2817
    end_io_cache(&new_data_cache);
 
2818
  if (!got_error)
 
2819
  {
 
2820
    /* Replace the actual file with the temporary file */
 
2821
    if (new_file >= 0)
 
2822
    {
 
2823
      my_close(new_file,MYF(0));
 
2824
      info->dfile=new_file= -1;
 
2825
      if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
 
2826
                            DATA_TMP_EXT, share->base.raid_chunks,
 
2827
                            (param->testflag & T_BACKUP_DATA ?
 
2828
                             MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
 
2829
          mi_open_datafile(info,share,-1))
 
2830
        got_error=1;
 
2831
    }
 
2832
  }
 
2833
  if (got_error)
 
2834
  {
 
2835
    if (! param->error_printed)
 
2836
      mi_check_print_error(param,"%d when fixing table",my_errno);
 
2837
    if (new_file >= 0)
 
2838
    {
 
2839
      my_close(new_file,MYF(0));
 
2840
      my_delete(param->temp_filename, MYF(MY_WME));
 
2841
      if (info->dfile == new_file)
 
2842
        info->dfile= -1;
 
2843
    }
 
2844
    mi_mark_crashed_on_repair(info);
 
2845
  }
 
2846
  else if (key_map == share->state.key_map)
 
2847
    share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
 
2848
  share->state.changed|=STATE_NOT_SORTED_PAGES;
 
2849
 
 
2850
  pthread_cond_destroy (&sort_info.cond);
 
2851
  pthread_mutex_destroy(&sort_info.mutex);
 
2852
 
 
2853
  free((unsigned char*) sort_info.key_block);
 
2854
  free((unsigned char*) sort_param);
 
2855
  free(sort_info.buff);
 
2856
  end_io_cache(&param->read_cache);
2334
2857
  info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
2335
2858
  if (!got_error && (param->testflag & T_UNPACK))
2336
2859
  {
2342
2865
 
2343
2866
        /* Read next record and return next key */
2344
2867
 
2345
 
int sort_key_read(MI_SORT_PARAM *sort_param, void *key)
 
2868
static int sort_key_read(MI_SORT_PARAM *sort_param, void *key)
2346
2869
{
2347
2870
  int error;
2348
2871
  SORT_INFO *sort_info=sort_param->sort_info;
2361
2884
    (info->s->rec_reflength+
2362
2885
     _mi_make_key(info, sort_param->key, (unsigned char*) key,
2363
2886
                  sort_param->record, sort_param->filepos));
2364
 
#ifdef HAVE_VALGRIND
2365
 
  memset((unsigned char *)key+sort_param->real_key_length, 0,
 
2887
#ifdef HAVE_purify
 
2888
  memset(key+sort_param->real_key_length, 0,
2366
2889
         (sort_param->key_length-sort_param->real_key_length));
2367
2890
#endif
2368
2891
  return(sort_write_record(sort_param));
2400
2923
    > 0         error
2401
2924
*/
2402
2925
 
2403
 
int sort_get_next_record(MI_SORT_PARAM *sort_param)
 
2926
static int sort_get_next_record(MI_SORT_PARAM *sort_param)
2404
2927
{
2405
2928
  int searching;
2406
2929
  int parallel_flag;
2690
3213
        {
2691
3214
          mi_check_print_info(param,
2692
3215
                              "Read error for block at: %s (error: %d); Skipped",
2693
 
                              llstr(block_info.filepos,llbuff),errno);
 
3216
                              llstr(block_info.filepos,llbuff),my_errno);
2694
3217
          goto try_next;
2695
3218
        }
2696
3219
        left_length-=block_info.data_len;
2783
3306
      if (my_b_write(&info->rec_cache,sort_param->record,
2784
3307
                     share->base.pack_reclength))
2785
3308
      {
2786
 
        mi_check_print_error(param,"%d when writing to datafile",errno);
 
3309
        mi_check_print_error(param,"%d when writing to datafile",my_errno);
2787
3310
        return(1);
2788
3311
      }
2789
3312
      sort_param->filepos+=share->base.pack_reclength;
2802
3325
          MI_DYN_DELETE_BLOCK_HEADER;
2803
3326
        if (sort_info->buff_length < reclength)
2804
3327
        {
2805
 
          void *tmpptr= NULL;
2806
 
          tmpptr= realloc(sort_info->buff, reclength);
2807
 
          if(tmpptr)
2808
 
          {
2809
 
            sort_info->buff_length=reclength;
2810
 
            sort_info->buff= (unsigned char *)tmpptr;
2811
 
          }
2812
 
          else
2813
 
          {
2814
 
            mi_check_print_error(param,"Could not realloc() sort_info->buff "
2815
 
                                 " to %ul bytes", reclength);
2816
 
            return(1);
2817
 
          }
 
3328
          if (!(sort_info->buff=my_realloc(sort_info->buff, (uint) reclength,
 
3329
                                           MYF(MY_FREE_ON_ERROR |
 
3330
                                               MY_ALLOW_ZERO_PTR))))
 
3331
            return(1);
 
3332
          sort_info->buff_length=reclength;
2818
3333
        }
2819
3334
        from= sort_info->buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER);
2820
3335
      }
2837
3352
                                  sort_param->filepos+block_length,
2838
3353
                                  &from,&reclength,&flag))
2839
3354
        {
2840
 
          mi_check_print_error(param,"%d when writing to datafile",errno);
 
3355
          mi_check_print_error(param,"%d when writing to datafile",my_errno);
2841
3356
          return(1);
2842
3357
        }
2843
3358
        sort_param->filepos+=block_length;
2867
3382
 
2868
3383
        /* Compare two keys from _create_index_by_sort */
2869
3384
 
2870
 
int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a, const void *b)
 
3385
static int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,
 
3386
                        const void *b)
2871
3387
{
2872
3388
  uint32_t not_used[2];
2873
3389
  return (ha_key_cmp(sort_param->seg, *((unsigned char* const *) a), *((unsigned char* const *) b),
2875
3391
} /* sort_key_cmp */
2876
3392
 
2877
3393
 
2878
 
int sort_key_write(MI_SORT_PARAM *sort_param, const void *a)
 
3394
static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a)
2879
3395
{
2880
3396
  uint32_t diff_pos[2];
2881
3397
  char llbuff[22],llbuff2[22];
2890
3406
                   diff_pos);
2891
3407
    if (param->stats_method == MI_STATS_METHOD_NULLS_NOT_EQUAL)
2892
3408
      ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey,
2893
 
                 (unsigned char*) a, USE_WHOLE_KEY,
 
3409
                 (unsigned char*) a, USE_WHOLE_KEY, 
2894
3410
                 SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL, diff_pos);
2895
3411
    else if (param->stats_method == MI_STATS_METHOD_IGNORE_NULLS)
2896
3412
    {
2932
3448
 
2933
3449
        /* get pointer to record from a key */
2934
3450
 
2935
 
my_off_t get_record_for_key(MI_INFO *info, MI_KEYDEF *keyinfo,
2936
 
                            unsigned char *key) {
 
3451
static my_off_t get_record_for_key(MI_INFO *info, MI_KEYDEF *keyinfo,
 
3452
                                   unsigned char *key)
 
3453
{
2937
3454
  return _mi_dpos(info,0,key+_mi_keylength(keyinfo,key));
2938
3455
} /* get_record_for_key */
2939
3456
 
2940
3457
 
2941
3458
        /* Insert a key in sort-key-blocks */
2942
3459
 
2943
 
int sort_insert_key(MI_SORT_PARAM *sort_param,
2944
 
                    register SORT_KEY_BLOCKS *key_block, unsigned char *key,
2945
 
                    my_off_t prev_block)
 
3460
static int sort_insert_key(MI_SORT_PARAM *sort_param,
 
3461
                           register SORT_KEY_BLOCKS *key_block, unsigned char *key,
 
3462
                           my_off_t prev_block)
2946
3463
{
2947
3464
  uint32_t a_length,t_length,nod_flag;
2948
3465
  my_off_t filepos,key_file_length;
3022
3539
 
3023
3540
        /* Delete record when we found a duplicated key */
3024
3541
 
3025
 
int sort_delete_record(MI_SORT_PARAM *sort_param)
 
3542
static int sort_delete_record(MI_SORT_PARAM *sort_param)
3026
3543
{
3027
3544
  uint32_t i;
3028
3545
  int old_file,error;
3125
3642
  register uint32_t i;
3126
3643
  SORT_KEY_BLOCKS *block;
3127
3644
 
3128
 
  if (!(block=(SORT_KEY_BLOCKS*) malloc((sizeof(SORT_KEY_BLOCKS)+
3129
 
                                        buffer_length+IO_SIZE)*blocks)))
 
3645
  if (!(block=(SORT_KEY_BLOCKS*) my_malloc((sizeof(SORT_KEY_BLOCKS)+
 
3646
                                            buffer_length+IO_SIZE)*blocks,
 
3647
                                           MYF(0))))
3130
3648
  {
3131
3649
    mi_check_print_error(param,"Not enough memory for sort-key-blocks");
3132
3650
    return(0);
3146
3664
{
3147
3665
  if (info->s->options & HA_OPTION_COMPRESS_RECORD)
3148
3666
    return 0;
3149
 
  return (my_off_t)(lseek(info->s->kfile, 0L, SEEK_END) / 10 * 9) >
 
3667
  return my_seek(info->s->kfile, 0L, MY_SEEK_END, MYF(MY_THREADSAFE)) / 10 * 9 >
3150
3668
         (my_off_t) info->s->base.max_key_file_length ||
3151
 
         (my_off_t)(lseek(info->dfile, 0L, SEEK_END) / 10 * 9) >
 
3669
         my_seek(info->dfile, 0L, MY_SEEK_END, MYF(0)) / 10 * 9 >
3152
3670
         (my_off_t) info->s->base.max_data_file_length;
3153
3671
}
3154
3672
 
 
3673
        /* Recreate table with bigger more alloced record-data */
 
3674
 
 
3675
int recreate_table(MI_CHECK *param, MI_INFO **org_info, char *filename)
 
3676
{
 
3677
  int error;
 
3678
  MI_INFO info;
 
3679
  MYISAM_SHARE share;
 
3680
  MI_KEYDEF *keyinfo,*key,*key_end;
 
3681
  HA_KEYSEG *keysegs,*keyseg;
 
3682
  MI_COLUMNDEF *recdef,*rec,*end;
 
3683
  MI_UNIQUEDEF *uniquedef,*u_ptr,*u_end;
 
3684
  MI_STATUS_INFO status_info;
 
3685
  uint32_t unpack,key_parts;
 
3686
  ha_rows max_records;
 
3687
  uint64_t file_length,tmp_length;
 
3688
  MI_CREATE_INFO create_info;
 
3689
 
 
3690
  error=1;                                      /* Default error */
 
3691
  info= **org_info;
 
3692
  status_info= (*org_info)->state[0];
 
3693
  info.state= &status_info;
 
3694
  share= *(*org_info)->s;
 
3695
  unpack= (share.options & HA_OPTION_COMPRESS_RECORD) &&
 
3696
    (param->testflag & T_UNPACK);
 
3697
  if (!(keyinfo=(MI_KEYDEF*) my_alloca(sizeof(MI_KEYDEF)*share.base.keys)))
 
3698
    return(0);
 
3699
  memcpy(keyinfo,share.keyinfo,sizeof(MI_KEYDEF)*share.base.keys);
 
3700
 
 
3701
  key_parts= share.base.all_key_parts;
 
3702
  if (!(keysegs=(HA_KEYSEG*) my_alloca(sizeof(HA_KEYSEG)*
 
3703
                                       (key_parts+share.base.keys))))
 
3704
  {
 
3705
    my_afree((unsigned char*) keyinfo);
 
3706
    return(1);
 
3707
  }
 
3708
  if (!(recdef=(MI_COLUMNDEF*)
 
3709
        my_alloca(sizeof(MI_COLUMNDEF)*(share.base.fields+1))))
 
3710
  {
 
3711
    my_afree((unsigned char*) keyinfo);
 
3712
    my_afree((unsigned char*) keysegs);
 
3713
    return(1);
 
3714
  }
 
3715
  if (!(uniquedef=(MI_UNIQUEDEF*)
 
3716
        my_alloca(sizeof(MI_UNIQUEDEF)*(share.state.header.uniques+1))))
 
3717
  {
 
3718
    my_afree((unsigned char*) recdef);
 
3719
    my_afree((unsigned char*) keyinfo);
 
3720
    my_afree((unsigned char*) keysegs);
 
3721
    return(1);
 
3722
  }
 
3723
 
 
3724
  /* Copy the column definitions */
 
3725
  memcpy(recdef, share.rec, sizeof(MI_COLUMNDEF)*(share.base.fields+1));
 
3726
  for (rec=recdef,end=recdef+share.base.fields; rec != end ; rec++)
 
3727
  {
 
3728
    if (unpack && !(share.options & HA_OPTION_PACK_RECORD) &&
 
3729
        rec->type != FIELD_BLOB &&
 
3730
        rec->type != FIELD_VARCHAR &&
 
3731
        rec->type != FIELD_CHECK)
 
3732
      rec->type=(int) FIELD_NORMAL;
 
3733
  }
 
3734
 
 
3735
  /* Change the new key to point at the saved key segments */
 
3736
  memcpy(keysegs,share.keyparts,
 
3737
         sizeof(HA_KEYSEG)*(key_parts+share.base.keys+
 
3738
                            share.state.header.uniques));
 
3739
  keyseg=keysegs;
 
3740
  for (key=keyinfo,key_end=keyinfo+share.base.keys; key != key_end ; key++)
 
3741
  {
 
3742
    key->seg=keyseg;
 
3743
    for (; keyseg->type ; keyseg++)
 
3744
    {
 
3745
      if (param->language)
 
3746
        keyseg->language=param->language;       /* change language */
 
3747
    }
 
3748
    keyseg++;                                   /* Skip end pointer */
 
3749
  }
 
3750
 
 
3751
  /* Copy the unique definitions and change them to point at the new key
 
3752
     segments*/
 
3753
  memcpy(uniquedef,share.uniqueinfo,
 
3754
         sizeof(MI_UNIQUEDEF)*(share.state.header.uniques));
 
3755
  for (u_ptr=uniquedef,u_end=uniquedef+share.state.header.uniques;
 
3756
       u_ptr != u_end ; u_ptr++)
 
3757
  {
 
3758
    u_ptr->seg=keyseg;
 
3759
    keyseg+=u_ptr->keysegs+1;
 
3760
  }
 
3761
  if (share.options & HA_OPTION_COMPRESS_RECORD)
 
3762
    share.base.records=max_records=info.state->records;
 
3763
  else if (share.base.min_pack_length)
 
3764
    max_records=(ha_rows) (my_seek(info.dfile,0L,MY_SEEK_END,MYF(0)) /
 
3765
                           (ulong) share.base.min_pack_length);
 
3766
  else
 
3767
    max_records=0;
 
3768
  unpack= (share.options & HA_OPTION_COMPRESS_RECORD) &&
 
3769
    (param->testflag & T_UNPACK);
 
3770
  share.options&= ~HA_OPTION_TEMP_COMPRESS_RECORD;
 
3771
 
 
3772
  file_length=(uint64_t) my_seek(info.dfile,0L,MY_SEEK_END,MYF(0));
 
3773
  tmp_length= file_length+file_length/10;
 
3774
  set_if_bigger(file_length,param->max_data_file_length);
 
3775
  set_if_bigger(file_length,tmp_length);
 
3776
  set_if_bigger(file_length,(uint64_t) share.base.max_data_file_length);
 
3777
 
 
3778
  mi_close(*org_info);
 
3779
  memset(&create_info, 0, sizeof(create_info));
 
3780
  create_info.max_rows=cmax(max_records,share.base.records);
 
3781
  create_info.reloc_rows=share.base.reloc;
 
3782
  create_info.old_options=(share.options |
 
3783
                           (unpack ? HA_OPTION_TEMP_COMPRESS_RECORD : 0));
 
3784
 
 
3785
  create_info.data_file_length=file_length;
 
3786
  create_info.auto_increment=share.state.auto_increment;
 
3787
  create_info.language = (param->language ? param->language :
 
3788
                          share.state.header.language);
 
3789
  create_info.key_file_length=  status_info.key_file_length;
 
3790
  /*
 
3791
    Allow for creating an auto_increment key. This has an effect only if
 
3792
    an auto_increment key exists in the original table.
 
3793
  */
 
3794
  create_info.with_auto_increment= true;
 
3795
  /* We don't have to handle symlinks here because we are using
 
3796
     HA_DONT_TOUCH_DATA */
 
3797
  if (mi_create(filename,
 
3798
                share.base.keys - share.state.header.uniques,
 
3799
                keyinfo, share.base.fields, recdef,
 
3800
                share.state.header.uniques, uniquedef,
 
3801
                &create_info,
 
3802
                HA_DONT_TOUCH_DATA))
 
3803
  {
 
3804
    mi_check_print_error(param,"Got error %d when trying to recreate indexfile",my_errno);
 
3805
    goto end;
 
3806
  }
 
3807
  *org_info=mi_open(filename,O_RDWR,
 
3808
                    (param->testflag & T_WAIT_FOREVER) ? HA_OPEN_WAIT_IF_LOCKED :
 
3809
                    (param->testflag & T_DESCRIPT) ? HA_OPEN_IGNORE_IF_LOCKED :
 
3810
                    HA_OPEN_ABORT_IF_LOCKED);
 
3811
  if (!*org_info)
 
3812
  {
 
3813
    mi_check_print_error(param,"Got error %d when trying to open re-created indexfile",
 
3814
                my_errno);
 
3815
    goto end;
 
3816
  }
 
3817
  /* We are modifing */
 
3818
  (*org_info)->s->options&= ~HA_OPTION_READ_ONLY_DATA;
 
3819
  _mi_readinfo(*org_info,F_WRLCK,0);
 
3820
  (*org_info)->state->records=info.state->records;
 
3821
  if (share.state.create_time)
 
3822
    (*org_info)->s->state.create_time=share.state.create_time;
 
3823
  (*org_info)->s->state.unique=(*org_info)->this_unique=
 
3824
    share.state.unique;
 
3825
  (*org_info)->state->checksum=info.state->checksum;
 
3826
  (*org_info)->state->del=info.state->del;
 
3827
  (*org_info)->s->state.dellink=share.state.dellink;
 
3828
  (*org_info)->state->empty=info.state->empty;
 
3829
  (*org_info)->state->data_file_length=info.state->data_file_length;
 
3830
  if (update_state_info(param,*org_info,UPDATE_TIME | UPDATE_STAT |
 
3831
                        UPDATE_OPEN_COUNT))
 
3832
    goto end;
 
3833
  error=0;
 
3834
end:
 
3835
  my_afree((unsigned char*) uniquedef);
 
3836
  my_afree((unsigned char*) keyinfo);
 
3837
  my_afree((unsigned char*) recdef);
 
3838
  my_afree((unsigned char*) keysegs);
 
3839
  return(error);
 
3840
}
 
3841
 
3155
3842
 
3156
3843
        /* write suffix to data file if neaded */
3157
3844
 
3166
3853
    if (my_b_write(&info->rec_cache,buff,sizeof(buff)))
3167
3854
    {
3168
3855
      mi_check_print_error(sort_info->param,
3169
 
                           "%d when writing to datafile",errno);
 
3856
                           "%d when writing to datafile",my_errno);
3170
3857
      return 1;
3171
3858
    }
3172
3859
    sort_info->param->read_cache.end_of_file+=sizeof(buff);
3231
3918
      return 0;
3232
3919
  }
3233
3920
err:
3234
 
  mi_check_print_error(param,"%d when updating keyfile",errno);
 
3921
  mi_check_print_error(param,"%d when updating keyfile",my_errno);
3235
3922
  return 1;
3236
3923
}
3237
3924
 
3266
3953
      !(param->testflag & T_REP))
3267
3954
    printf("Updating MyISAM file: %s\n", param->isam_file_name);
3268
3955
  /*
3269
 
    We have to use an allocated buffer instead of info->rec_buff as
 
3956
    We have to use an allocated buffer instead of info->rec_buff as 
3270
3957
    _mi_put_key_in_record() may use info->rec_buff
3271
3958
  */
3272
 
  if (!mi_alloc_rec_buff(info, SIZE_MAX, &record))
 
3959
  if (!mi_alloc_rec_buff(info, -1, &record))
3273
3960
  {
3274
3961
    mi_check_print_error(param,"Not enough memory for extra record");
3275
3962
    return;
3278
3965
  mi_extra(info,HA_EXTRA_KEYREAD,0);
3279
3966
  if (mi_rlast(info, record, info->s->base.auto_key-1))
3280
3967
  {
3281
 
    if (errno != HA_ERR_END_OF_FILE)
 
3968
    if (my_errno != HA_ERR_END_OF_FILE)
3282
3969
    {
3283
3970
      mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
3284
3971
      free(mi_get_rec_buff_ptr(info, record));
3285
 
      mi_check_print_error(param,"%d when reading last record",errno);
 
3972
      mi_check_print_error(param,"%d when reading last record",my_errno);
3286
3973
      return;
3287
3974
    }
3288
3975
    if (!repair_only)
3314
4001
      records               Number of records in the table
3315
4002
 
3316
4003
  DESCRIPTION
3317
 
    This function is called produce index statistics values from unique and
 
4004
    This function is called produce index statistics values from unique and 
3318
4005
    notnull_tuples arrays after these arrays were produced with sequential
3319
4006
    index scan (the scan is done in two places: chk_index() and
3320
4007
    sort_key_write()).
3328
4015
 
3329
4016
    For MI_STATS_METHOD_IGNORE_NULLS method, notnull_tuples is an array too:
3330
4017
      notnull_tuples[0]= (#of {keypart1} tuples such that keypart1 is not NULL)
3331
 
      notnull_tuples[1]= (#of {keypart1,keypart2} tuples such that all
 
4018
      notnull_tuples[1]= (#of {keypart1,keypart2} tuples such that all 
3332
4019
                          keypart{i} are not NULL)
3333
4020
      ...
3334
4021
    For all other statistics collection methods notnull_tuples==NULL.
3335
4022
 
3336
4023
    Output is an array:
3337
 
    rec_per_key_part[k] =
3338
 
     = E(#records in the table such that keypart_1=c_1 AND ... AND
3339
 
         keypart_k=c_k for arbitrary constants c_1 ... c_k)
3340
 
 
 
4024
    rec_per_key_part[k] = 
 
4025
     = E(#records in the table such that keypart_1=c_1 AND ... AND 
 
4026
         keypart_k=c_k for arbitrary constants c_1 ... c_k) 
 
4027
     
3341
4028
     = {assuming that values have uniform distribution and index contains all
3342
4029
        tuples from the domain (or that {c_1, ..., c_k} tuple is choosen from
3343
4030
        index tuples}
3344
 
 
 
4031
     
3345
4032
     = #tuples-in-the-index / #distinct-tuples-in-the-index.
3346
 
 
3347
 
    The #tuples-in-the-index and #distinct-tuples-in-the-index have different
 
4033
    
 
4034
    The #tuples-in-the-index and #distinct-tuples-in-the-index have different 
3348
4035
    meaning depending on which statistics collection method is used:
3349
 
 
 
4036
    
3350
4037
    MI_STATS_METHOD_*  how are nulls compared?  which tuples are counted?
3351
4038
     NULLS_EQUAL            NULL == NULL           all tuples in table
3352
4039
     NULLS_NOT_EQUAL        NULL != NULL           all tuples in table
3363
4050
  for (parts=0 ; parts < keyinfo->keysegs  ; parts++)
3364
4051
  {
3365
4052
    count+=unique[parts];
3366
 
    unique_tuples= count + 1;
 
4053
    unique_tuples= count + 1;    
3367
4054
    if (notnull)
3368
4055
    {
3369
4056
      tuples= notnull[parts];
3370
 
      /*
3371
 
        #(unique_tuples not counting tuples with NULLs) =
3372
 
          #(unique_tuples counting tuples with NULLs as different) -
 
4057
      /* 
 
4058
        #(unique_tuples not counting tuples with NULLs) = 
 
4059
          #(unique_tuples counting tuples with NULLs as different) - 
3373
4060
          #(tuples with NULLs)
3374
4061
      */
3375
4062
      unique_tuples -= (records - notnull[parts]);
3376
4063
    }
3377
 
 
 
4064
    
3378
4065
    if (unique_tuples == 0)
3379
4066
      tmp= 1;
3380
4067
    else if (count == 0)
3382
4069
    else
3383
4070
      tmp= (tuples + unique_tuples/2) / unique_tuples;
3384
4071
 
3385
 
    /*
3386
 
      for some weird keys (e.g. FULLTEXT) tmp can be <1 here.
 
4072
    /* 
 
4073
      for some weird keys (e.g. FULLTEXT) tmp can be <1 here. 
3387
4074
      let's ensure it is not
3388
4075
    */
3389
 
    if (tmp < 1)
3390
 
      tmp= 1;
 
4076
    set_if_bigger(tmp,1);
3391
4077
    if (tmp >= (uint64_t) ~(ulong) 0)
3392
4078
      tmp=(uint64_t) ~(ulong) 0;
3393
4079
 
3412
4098
  uint32_t key_maxlength=key->maxlength;
3413
4099
  return (key->flag & (HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY) &&
3414
4100
          ((uint64_t) rows * key_maxlength >
3415
 
           (uint64_t) MAX_FILE_SIZE));
 
4101
           (uint64_t) myisam_max_temp_length));
3416
4102
}
3417
4103
 
3418
4104
/*