ha_myisam.cc 44.7 KB
Newer Older
1
/* Copyright (C) 2000,2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
2

unknown's avatar
unknown committed
3 4 5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
7

unknown's avatar
unknown committed
8 9 10 11
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
12

unknown's avatar
unknown committed
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */


#ifdef __GNUC__
#pragma implementation				// gcc: Class implementation
#endif

#include "mysql_priv.h"
#include <m_ctype.h>
#include <myisampack.h>
#include "ha_myisam.h"
#include <stdarg.h>
#ifndef MASTER
#include "../srclib/myisam/myisamdef.h"
#else
#include "../myisam/myisamdef.h"
unknown's avatar
unknown committed
31
#include "../myisam/rt_index.h"
unknown's avatar
unknown committed
32 33
#endif

34
ulong myisam_recover_options= HA_RECOVER_NONE;
35

36
/* bits in myisam_recover_options */
37
const char *myisam_recover_names[] =
38
{ "DEFAULT", "BACKUP", "FORCE", "QUICK", NullS};
39
TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"",
40
				 myisam_recover_names, NULL};
41

42

unknown's avatar
unknown committed
43 44 45 46 47
/*****************************************************************************
** MyISAM tables
*****************************************************************************/

// collect errors printed by mi_check routines
48

unknown's avatar
unknown committed
49 50 51 52
static void mi_check_print_msg(MI_CHECK *param,	const char* msg_type,
			       const char *fmt, va_list args)
{
  THD* thd = (THD*)param->thd;
53 54
  Protocol *protocol= thd->protocol;
  uint length, msg_length;
unknown's avatar
unknown committed
55
  char msgbuf[MI_MAX_MSG_BUF];
56
  char name[NAME_LEN*2+2];
unknown's avatar
unknown committed
57

58
  msg_length= my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
unknown's avatar
unknown committed
59 60
  msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia

unknown's avatar
unknown committed
61 62
  DBUG_PRINT(msg_type,("message: %s",msgbuf));

63
  if (!thd->vio_ok())
unknown's avatar
unknown committed
64 65 66 67
  {
    sql_print_error(msgbuf);
    return;
  }
68

69 70
  if (param->testflag & (T_CREATE_MISSING_KEYS | T_SAFE_REPAIR |
			 T_AUTO_REPAIR))
71 72 73 74
  {
    my_message(ER_NOT_KEYFILE,msgbuf,MYF(MY_WME));
    return;
  }
75 76
  length=(uint) (strxmov(name, param->db_name,".",param->table_name,NullS) -
		 name);
77
  protocol->prepare_for_resend();
78 79 80 81
  protocol->store(name, length, system_charset_info);
  protocol->store(param->op_name, system_charset_info);
  protocol->store(msg_type, system_charset_info);
  protocol->store(msgbuf, msg_length, system_charset_info);
82
  if (protocol->write())
83 84
    sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n",
		    msgbuf);
85
  return;
unknown's avatar
unknown committed
86 87 88 89
}

extern "C" {

90
volatile my_bool *killed_ptr(MI_CHECK *param)
unknown's avatar
unknown committed
91 92 93 94
{
  return &(((THD *)(param->thd))->killed);
}

unknown's avatar
unknown committed
95 96 97
void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
{
  param->error_printed|=1;
98
  param->out_flag|= O_DATA_LOST;
unknown's avatar
unknown committed
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
  va_list args;
  va_start(args, fmt);
  mi_check_print_msg(param, "error", fmt, args);
  va_end(args);
}

void mi_check_print_info(MI_CHECK *param, const char *fmt,...)
{
  va_list args;
  va_start(args, fmt);
  mi_check_print_msg(param, "info", fmt, args);
  va_end(args);
}

void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
{
  param->warning_printed=1;
116
  param->out_flag|= O_DATA_LOST;
unknown's avatar
unknown committed
117 118 119 120 121 122 123 124 125
  va_list args;
  va_start(args, fmt);
  mi_check_print_msg(param, "warning", fmt, args);
  va_end(args);
}

}

const char **ha_myisam::bas_ext() const
unknown's avatar
unknown committed
126
{ static const char *ext[]= { ".MYI",".MYD", NullS }; return ext; }
unknown's avatar
unknown committed
127 128


129 130
const char *ha_myisam::index_type(uint key_number)
{
131
  return ((table->key_info[key_number].flags & HA_FULLTEXT) ? 
132
	  "FULLTEXT" :
133 134 135 136
	  (table->key_info[key_number].flags & HA_SPATIAL) ?
	  "SPATIAL" :
	  (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ?
	  "RTREE" :
137 138 139
	  "BTREE");
}

unknown's avatar
SCRUM  
unknown committed
140
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
141 142 143 144 145 146
int ha_myisam::net_read_dump(NET* net)
{
  int data_fd = file->dfile;
  int error = 0;

  my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME));
147
  for (;;)
unknown's avatar
unknown committed
148
  {
unknown's avatar
unknown committed
149
    ulong packet_len = my_net_read(net);
unknown's avatar
unknown committed
150 151 152 153 154 155 156 157
    if (!packet_len)
      break ; // end of file
    if (packet_len == packet_error)
    {
      sql_print_error("ha_myisam::net_read_dump - read error ");
      error= -1;
      goto err;
    }
unknown's avatar
unknown committed
158
    if (my_write(data_fd, (byte*)net->read_pos, (uint) packet_len,
unknown's avatar
unknown committed
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
		 MYF(MY_WME|MY_FNABP)))
    {
      error = errno;
      goto err;
    }
  }
err:
  return error;
}


int ha_myisam::dump(THD* thd, int fd)
{
  MYISAM_SHARE* share = file->s;
  NET* net = &thd->net;
  uint blocksize = share->blocksize;
  my_off_t bytes_to_read = share->state.state.data_file_length;
  int data_fd = file->dfile;
  byte * buf = (byte*) my_malloc(blocksize, MYF(MY_WME));
178
  if (!buf)
unknown's avatar
unknown committed
179 180 181 182
    return ENOMEM;

  int error = 0;
  my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME));
183
  for (; bytes_to_read > 0;)
unknown's avatar
unknown committed
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
  {
    uint bytes = my_read(data_fd, buf, blocksize, MYF(MY_WME));
    if (bytes == MY_FILE_ERROR)
    {
      error = errno;
      goto err;
    }

    if (fd >= 0)
    {
      if (my_write(fd, buf, bytes, MYF(MY_WME | MY_FNABP)))
      {
	error = errno ? errno : EPIPE;
	goto err;
      }
    }
    else
    {
      if (my_net_write(net, (char*) buf, bytes))
      {
	error = errno ? errno : EPIPE;
	goto err;
      }
    }
    bytes_to_read -= bytes;
  }

  if (fd < 0)
  {
    my_net_write(net, "", 0);
    net_flush(net);
  }
216

unknown's avatar
unknown committed
217 218 219 220
err:
  my_free((gptr) buf, MYF(0));
  return error;
}
unknown's avatar
SCRUM  
unknown committed
221
#endif /* HAVE_REPLICATION */
unknown's avatar
unknown committed
222

223 224
	/* Name is here without an extension */

225
int ha_myisam::open(const char *name, int mode, uint test_if_locked)
unknown's avatar
unknown committed
226
{
227
  if (!(file=mi_open(name, mode, test_if_locked)))
unknown's avatar
unknown committed
228
    return (my_errno ? my_errno : -1);
unknown's avatar
unknown committed
229
  
230
  if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE))
unknown's avatar
unknown committed
231
    VOID(mi_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0));
unknown's avatar
unknown committed
232 233
  info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
  if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
unknown's avatar
unknown committed
234
    VOID(mi_extra(file, HA_EXTRA_WAIT_LOCK, 0));
unknown's avatar
unknown committed
235
  if (!table->db_record_offset)
236
    int_table_flags|=HA_REC_NOT_IN_SEQ;
unknown's avatar
unknown committed
237 238
  if (file->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
    int_table_flags|=HA_HAS_CHECKSUM;
unknown's avatar
unknown committed
239 240 241 242 243 244 245 246 247 248 249 250 251
  return (0);
}

int ha_myisam::close(void)
{
  MI_INFO *tmp=file;
  file=0;
  return mi_close(tmp);
}

int ha_myisam::write_row(byte * buf)
{
  statistic_increment(ha_write_count,&LOCK_status);
unknown's avatar
unknown committed
252 253

  /* If we have a timestamp column, update it to the current time */
254 255
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
    table->timestamp_field->set_time();
unknown's avatar
unknown committed
256 257 258 259 260

  /*
    If we have an auto_increment column and we are writing a changed row
    or a new row, then update the auto_increment value in the record.
  */
unknown's avatar
unknown committed
261 262 263 264 265 266 267
  if (table->next_number_field && buf == table->record[0])
    update_auto_increment();
  return mi_write(file,buf);
}

int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
{
268
  if (!file) return HA_ADMIN_INTERNAL_ERROR;
unknown's avatar
unknown committed
269
  int error;
unknown's avatar
unknown committed
270 271
  MI_CHECK param;
  MYISAM_SHARE* share = file->s;
unknown's avatar
unknown committed
272
  const char *old_proc_info=thd->proc_info;
273

unknown's avatar
unknown committed
274
  thd->proc_info="Checking table";
unknown's avatar
unknown committed
275 276 277
  myisamchk_init(&param);
  param.thd = thd;
  param.op_name = (char*)"check";
278
  param.db_name    = table->table_cache_key;
unknown's avatar
unknown committed
279
  param.table_name = table->table_name;
280
  param.testflag = check_opt->flags | T_CHECK | T_SILENT;
281

unknown's avatar
unknown committed
282 283 284 285
  if (!(table->db_stat & HA_READ_ONLY))
    param.testflag|= T_STATISTICS;
  param.using_global_keycache = 1;

286 287
  if (!mi_is_crashed(file) &&
      (((param.testflag & T_CHECK_ONLY_CHANGED) &&
288 289
	!(share->state.changed & (STATE_CHANGED | STATE_CRASHED |
				  STATE_CRASHED_ON_REPAIR)) &&
290
	share->state.open_count == 0) ||
291
       ((param.testflag & T_FAST) && (share->state.open_count ==
292
				      (uint) (share->global_changed ? 1 : 0)))))
293
    return HA_ADMIN_ALREADY_DONE;
294

295
  error = chk_status(&param, file);		// Not fatal
unknown's avatar
unknown committed
296
  error = chk_size(&param, file);
297 298 299 300 301
  if (!error)
    error |= chk_del(&param, file, param.testflag);
  if (!error)
    error = chk_key(&param, file);
  if (!error)
unknown's avatar
unknown committed
302
  {
unknown's avatar
unknown committed
303
    if ((!(param.testflag & T_QUICK) &&
304 305 306
	 ((share->options &
	   (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
	  (param.testflag & (T_EXTEND | T_MEDIUM)))) ||
307
	mi_is_crashed(file))
unknown's avatar
unknown committed
308
    {
309 310
      uint old_testflag=param.testflag;
      param.testflag|=T_MEDIUM;
311 312 313 314 315
      init_io_cache(&param.read_cache, file->dfile,
		    my_default_record_cache_size, READ_CACHE,
		    share->pack.header_length, 1, MYF(MY_WME));
      error |= chk_data_link(&param, file, param.testflag & T_EXTEND);
      end_io_cache(&(param.read_cache));
316
      param.testflag=old_testflag;
unknown's avatar
unknown committed
317 318 319
    }
  }
  if (!error)
320
  {
unknown's avatar
unknown committed
321
    if ((share->state.changed & (STATE_CHANGED |
322 323
				 STATE_CRASHED_ON_REPAIR |
				 STATE_CRASHED | STATE_NOT_ANALYZED)) ||
324 325
	(param.testflag & T_STATISTICS) ||
	mi_is_crashed(file))
unknown's avatar
unknown committed
326 327 328
    {
      file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
      pthread_mutex_lock(&share->intern_lock);
329 330
      share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
			       STATE_CRASHED_ON_REPAIR);
unknown's avatar
unknown committed
331
      if (!(table->db_stat & HA_READ_ONLY))
332 333
	error=update_state_info(&param,file,UPDATE_TIME | UPDATE_OPEN_COUNT |
				UPDATE_STAT);
unknown's avatar
unknown committed
334
      pthread_mutex_unlock(&share->intern_lock);
335 336
      info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
	   HA_STATUS_CONST);
unknown's avatar
unknown committed
337 338
    }
  }
339
  else if (!mi_is_crashed(file) && !thd->killed)
unknown's avatar
unknown committed
340 341 342 343
  {
    mi_mark_crashed(file);
    file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
  }
344

unknown's avatar
unknown committed
345
  thd->proc_info=old_proc_info;
346
  return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
unknown's avatar
unknown committed
347 348 349 350 351 352 353 354 355
}


/*
  analyze the key distribution in the table
  As the table may be only locked for read, we have to take into account that
  two threads may do an analyze at the same time!
*/

356
int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt)
unknown's avatar
unknown committed
357
{
358
  int error=0;
unknown's avatar
unknown committed
359 360
  MI_CHECK param;
  MYISAM_SHARE* share = file->s;
361

unknown's avatar
unknown committed
362 363
  myisamchk_init(&param);
  param.thd = thd;
364
  param.op_name = (char*) "analyze";
365
  param.db_name    = table->table_cache_key;
unknown's avatar
unknown committed
366 367 368 369 370
  param.table_name = table->table_name;
  param.testflag=(T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
		  T_DONT_CHECK_CHECKSUM);
  param.using_global_keycache = 1;

371 372 373 374 375
  if (!(share->state.changed & STATE_NOT_ANALYZED))
    return HA_ADMIN_ALREADY_DONE;

  error = chk_key(&param, file);
  if (!error)
376
  {
377 378 379
    pthread_mutex_lock(&share->intern_lock);
    error=update_state_info(&param,file,UPDATE_STAT);
    pthread_mutex_unlock(&share->intern_lock);
unknown's avatar
unknown committed
380
  }
381
  else if (!mi_is_crashed(file) && !thd->killed)
382 383
    mi_mark_crashed(file);
  return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
unknown's avatar
unknown committed
384 385
}

386

unknown's avatar
unknown committed
387 388 389
int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt)
{
  HA_CHECK_OPT tmp_check_opt;
390
  char* backup_dir= thd->lex->backup_dir;
unknown's avatar
unknown committed
391 392
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
  char* table_name = table->real_name;
393 394
  int error;
  const char* errmsg;
395 396
  DBUG_ENTER("restore");

397 398
  if (fn_format_relative_to_data_home(src_path, table_name, backup_dir,
				      MI_NAME_DEXT))
399
    DBUG_RETURN(HA_ADMIN_INVALID);
unknown's avatar
unknown committed
400

401 402 403 404
  if (my_copy(src_path, fn_format(dst_path, table->path, "",
				  MI_NAME_DEXT, 4), MYF(MY_WME)))
  {
    error = HA_ADMIN_FAILED;
unknown's avatar
unknown committed
405
    errmsg = "Failed in my_copy (Error %d)";
406 407
    goto err;
  }
408

unknown's avatar
unknown committed
409
  tmp_check_opt.init();
unknown's avatar
unknown committed
410
  tmp_check_opt.flags |= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK;
411
  DBUG_RETURN(repair(thd, &tmp_check_opt));
412

unknown's avatar
unknown committed
413 414
 err:
  {
unknown's avatar
unknown committed
415 416 417 418
    MI_CHECK param;
    myisamchk_init(&param);
    param.thd = thd;
    param.op_name = (char*)"restore";
419
    param.db_name    = table->table_cache_key;
unknown's avatar
unknown committed
420 421
    param.table_name = table->table_name;
    param.testflag = 0;
422
    mi_check_print_error(&param, errmsg, my_errno);
423
    DBUG_RETURN(error);
unknown's avatar
unknown committed
424 425 426
  }
}

427

unknown's avatar
unknown committed
428 429
int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt)
{
430
  char* backup_dir= thd->lex->backup_dir;
unknown's avatar
unknown committed
431 432
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
  char* table_name = table->real_name;
433 434 435 436 437 438 439
  int error;
  const char *errmsg;
  DBUG_ENTER("ha_myisam::backup");

  if (fn_format_relative_to_data_home(dst_path, table_name, backup_dir,
				      reg_ext))
  {
440
    errmsg = "Failed in fn_format() for .frm file (errno: %d)";
441 442 443
    error = HA_ADMIN_INVALID;
    goto err;
  }
unknown's avatar
unknown committed
444

445 446
  if (my_copy(fn_format(src_path, table->path,"", reg_ext, MY_UNPACK_FILENAME),
	      dst_path,
447
	      MYF(MY_WME | MY_HOLD_ORIGINAL_MODES | MY_DONT_OVERWRITE_FILE)))
448
  {
449
    error = HA_ADMIN_FAILED;
450
    errmsg = "Failed copying .frm file (errno: %d)";
451
    goto err;
452
  }
unknown's avatar
unknown committed
453

454 455 456 457
  /* Change extension */
  if (!fn_format(dst_path, dst_path, "", MI_NAME_DEXT,
		 MY_REPLACE_EXT | MY_UNPACK_FILENAME | MY_SAFE_PATH))
  {
458
    errmsg = "Failed in fn_format() for .MYD file (errno: %d)";
459 460 461
    error = HA_ADMIN_INVALID;
    goto err;
  }
462

463 464
  if (my_copy(fn_format(src_path, table->path,"", MI_NAME_DEXT,
			MY_UNPACK_FILENAME),
465
	      dst_path,
466
	      MYF(MY_WME | MY_HOLD_ORIGINAL_MODES | MY_DONT_OVERWRITE_FILE)))
467
  {
468
    errmsg = "Failed copying .MYD file (errno: %d)";
469 470 471 472 473
    error= HA_ADMIN_FAILED;
    goto err;
  }
  DBUG_RETURN(HA_ADMIN_OK);

474 475 476 477 478 479
 err:
  {
    MI_CHECK param;
    myisamchk_init(&param);
    param.thd = thd;
    param.op_name = (char*)"backup";
480
    param.db_name    = table->table_cache_key;
481 482
    param.table_name = table->table_name;
    param.testflag = 0;
483 484
    mi_check_print_error(&param,errmsg, my_errno);
    DBUG_RETURN(error);
485
  }
unknown's avatar
unknown committed
486 487
}

unknown's avatar
unknown committed
488

489
int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt)
unknown's avatar
unknown committed
490
{
491
  int error;
unknown's avatar
unknown committed
492
  MI_CHECK param;
493
  ha_rows start_records;
494

unknown's avatar
unknown committed
495 496
  if (!file) return HA_ADMIN_INTERNAL_ERROR;

unknown's avatar
unknown committed
497 498 499
  myisamchk_init(&param);
  param.thd = thd;
  param.op_name = (char*) "repair";
unknown's avatar
unknown committed
500
  param.testflag = ((check_opt->flags & ~(T_EXTEND)) |
unknown's avatar
unknown committed
501
		    T_SILENT | T_FORCE_CREATE | T_CALC_CHECKSUM |
unknown's avatar
unknown committed
502
		    (check_opt->flags & T_EXTEND ? T_REP : T_REP_BY_SORT));
503
  param.sort_buffer_length=  check_opt->sort_buffer_size;
504 505
  start_records=file->state->records;
  while ((error=repair(thd,param,0)) && param.retry_repair)
506
  {
507
    param.retry_repair=0;
508 509
    if (test_all_bits(param.testflag,
		      (uint) (T_RETRY_WITHOUT_QUICK | T_QUICK)))
510
    {
unknown's avatar
unknown committed
511
      param.testflag&= ~T_RETRY_WITHOUT_QUICK;
unknown's avatar
unknown committed
512 513
      sql_print_information("Retrying repair of: '%s' without quick",
                            table->path);
514 515
      continue;
    }
unknown's avatar
unknown committed
516
    param.testflag&= ~T_QUICK;
517 518
    if ((param.testflag & T_REP_BY_SORT))
    {
unknown's avatar
unknown committed
519
      param.testflag= (param.testflag & ~T_REP_BY_SORT) | T_REP;
unknown's avatar
unknown committed
520 521
      sql_print_information("Retrying repair of: '%s' with keycache",
                            table->path);
522 523 524 525
      continue;
    }
    break;
  }
unknown's avatar
unknown committed
526 527
  if (!error && start_records != file->state->records &&
      !(check_opt->flags & T_VERY_SILENT))
528 529
  {
    char llbuff[22],llbuff2[22];
unknown's avatar
unknown committed
530 531 532 533
    sql_print_information("Found %s of %s rows when repairing '%s'",
                          llstr(file->state->records, llbuff),
                          llstr(start_records, llbuff2),
                          table->path);
534
  }
535
  return error;
536 537 538 539
}

int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt)
{
540
  int error;
541 542 543 544 545 546 547 548 549
  if (!file) return HA_ADMIN_INTERNAL_ERROR;
  MI_CHECK param;

  myisamchk_init(&param);
  param.thd = thd;
  param.op_name = (char*) "optimize";
  param.testflag = (check_opt->flags | T_SILENT | T_FORCE_CREATE |
		    T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX);
  param.sort_buffer_length=  check_opt->sort_buffer_size;
550 551 552 553 554 555 556 557
  if ((error= repair(thd,param,1)) && param.retry_repair)
  {
    sql_print_warning("Warning: Optimize table got errno %d, retrying",
                      my_errno);
    param.testflag&= ~T_REP_BY_SORT;
    error= repair(thd,param,1);
  }
  return error;
558 559 560
}


561
int ha_myisam::repair(THD *thd, MI_CHECK &param, bool optimize)
562
{
563
  int error=0;
564
  uint local_testflag=param.testflag;
565
  bool optimize_done= !optimize, statistics_done=0;
566
  const char *old_proc_info=thd->proc_info;
567
  char fixed_name[FN_REFLEN];
568
  MYISAM_SHARE* share = file->s;
unknown's avatar
unknown committed
569
  ha_rows rows= file->state->records;
unknown's avatar
unknown committed
570
  DBUG_ENTER("ha_myisam::repair");
571

572
  param.db_name    = table->table_cache_key;
573
  param.table_name = table->table_name;
unknown's avatar
unknown committed
574 575
  param.tmpfile_createflag = O_RDWR | O_TRUNC;
  param.using_global_keycache = 1;
576
  param.thd=thd;
577
  param.tmpdir=&mysql_tmpdir_list;
unknown's avatar
unknown committed
578
  param.out_flag=0;
579
  strmov(fixed_name,file->filename);
580

unknown's avatar
unknown committed
581
  // Don't lock tables if we have used LOCK TABLE
unknown's avatar
unknown committed
582
  if (!thd->locked_tables && 
583
      mi_lock_database(file, table->tmp_table ? F_EXTRA_LCK : F_WRLCK))
unknown's avatar
unknown committed
584 585 586 587 588
  {
    mi_check_print_error(&param,ER(ER_CANT_LOCK),my_errno);
    DBUG_RETURN(HA_ADMIN_FAILED);
  }

589
  if (!optimize ||
590
      ((file->state->del || share->state.split != file->state->records) &&
unknown's avatar
unknown committed
591
       (!(param.testflag & T_QUICK) ||
592
	!(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))))
593
  {
unknown's avatar
unknown committed
594
    ulonglong key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ?
595 596
			((ulonglong) 1L << share->base.keys)-1 :
			share->state.key_map);
597
    uint testflag=param.testflag;
598
    if (mi_test_if_sort_rep(file,file->state->records,key_map,0) &&
599
	(local_testflag & T_REP_BY_SORT))
600
    {
601
      local_testflag|= T_STATISTICS;
602
      param.testflag|= T_STATISTICS;		// We get this for free
603
      statistics_done=1;
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
      if (current_thd->variables.myisam_repair_threads>1)
      {
        char buf[40];
        /* TODO: respect myisam_repair_threads variable */
        my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map));
        thd->proc_info=buf;
        error = mi_repair_parallel(&param, file, fixed_name,
            param.testflag & T_QUICK);
        thd->proc_info="Repair done"; // to reset proc_info, as
                                      // it was pointing to local buffer
      }
      else
      {
        thd->proc_info="Repair by sorting";
        error = mi_repair_by_sort(&param, file, fixed_name,
            param.testflag & T_QUICK);
      }
621 622 623
    }
    else
    {
unknown's avatar
unknown committed
624
      thd->proc_info="Repair with keycache";
625
      param.testflag &= ~T_REP_BY_SORT;
unknown's avatar
unknown committed
626
      error=  mi_repair(&param, file, fixed_name,
627
			param.testflag & T_QUICK);
628
    }
629 630
    param.testflag=testflag;
    optimize_done=1;
631 632 633
  }
  if (!error)
  {
634
    if ((local_testflag & T_SORT_INDEX) &&
635 636 637 638 639 640
	(share->state.changed & STATE_NOT_SORTED_PAGES))
    {
      optimize_done=1;
      thd->proc_info="Sorting index";
      error=mi_sort_index(&param,file,fixed_name);
    }
641
    if (!statistics_done && (local_testflag & T_STATISTICS))
642
    {
643 644 645 646 647 648 649 650
      if (share->state.changed & STATE_NOT_ANALYZED)
      {
	optimize_done=1;
	thd->proc_info="Analyzing";
	error = chk_key(&param, file);
      }
      else
	local_testflag&= ~T_STATISTICS;		// Don't update statistics
651 652
    }
  }
unknown's avatar
unknown committed
653
  thd->proc_info="Saving state";
unknown's avatar
unknown committed
654
  if (!error)
655
  {
656
    if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file))
unknown's avatar
unknown committed
657
    {
658 659
      share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
			       STATE_CRASHED_ON_REPAIR);
unknown's avatar
unknown committed
660 661
      file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
    }
unknown's avatar
unknown committed
662 663 664 665
    /*
      the following 'if', thought conceptually wrong,
      is a useful optimization nevertheless.
    */
unknown's avatar
unknown committed
666
    if (file->state != &file->s->state.state)
unknown's avatar
unknown committed
667
      file->s->state.state = *file->state;
unknown's avatar
unknown committed
668 669
    if (file->s->base.auto_key)
      update_auto_increment_key(&param, file, 1);
670 671 672 673 674
    if (optimize_done)
      error = update_state_info(&param, file,
				UPDATE_TIME | UPDATE_OPEN_COUNT |
				(local_testflag &
				 T_STATISTICS ? UPDATE_STAT : 0));
675 676
    info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
	 HA_STATUS_CONST);
677
    if (rows != file->state->records && ! (param.testflag & T_VERY_SILENT))
unknown's avatar
unknown committed
678 679 680 681 682 683
    {
      char llbuff[22],llbuff2[22];
      mi_check_print_warning(&param,"Number of rows changed from %s to %s",
			     llstr(rows,llbuff),
			     llstr(file->state->records,llbuff2));
    }
unknown's avatar
unknown committed
684
  }
unknown's avatar
unknown committed
685
  else
unknown's avatar
unknown committed
686
  {
unknown's avatar
unknown committed
687
    mi_mark_crashed_on_repair(file);
unknown's avatar
unknown committed
688
    file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
unknown's avatar
unknown committed
689
    update_state_info(&param, file, 0);
unknown's avatar
unknown committed
690
  }
691
  thd->proc_info=old_proc_info;
unknown's avatar
unknown committed
692 693
  if (!thd->locked_tables)
    mi_lock_database(file,F_UNLCK);
unknown's avatar
unknown committed
694 695
  DBUG_RETURN(error ? HA_ADMIN_FAILED :
	      !optimize_done ? HA_ADMIN_ALREADY_DONE : HA_ADMIN_OK);
unknown's avatar
unknown committed
696 697 698
}


unknown's avatar
unknown committed
699
/*
700
  Assign table indexes to a specific key cache.
unknown's avatar
unknown committed
701 702 703 704
*/

int ha_myisam::assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt)
{
unknown's avatar
unknown committed
705
  KEY_CACHE *new_key_cache= check_opt->key_cache;
706
  const char *errmsg= 0;
unknown's avatar
unknown committed
707
  int error= HA_ADMIN_OK;
unknown's avatar
unknown committed
708 709 710 711
  ulonglong map= ~(ulonglong) 0;
  TABLE_LIST *table_list= table->pos_in_table_list;
  DBUG_ENTER("ha_myisam::assign_to_keycache");

unknown's avatar
unknown committed
712
  /* Check validity of the index references */
713
  if (table_list->use_index)
unknown's avatar
unknown committed
714
  {
715
    /* We only come here when the user did specify an index map */
unknown's avatar
unknown committed
716
    key_map kmap;
717
    if (get_key_map_from_key_list(&kmap, table, table_list->use_index))
unknown's avatar
unknown committed
718 719 720 721 722
    {
      errmsg= thd->net.last_error;
      error= HA_ADMIN_FAILED;
      goto err;
    }
723
    map= kmap.to_ulonglong();
unknown's avatar
unknown committed
724 725
  }

726
  if ((error= mi_assign_to_key_cache(file, map, new_key_cache)))
unknown's avatar
unknown committed
727
  { 
unknown's avatar
unknown committed
728 729 730 731
    char buf[80];
    my_snprintf(buf, sizeof(buf),
		"Failed to flush to index file (errno: %d)", error);
    errmsg= buf;
unknown's avatar
unknown committed
732
    error= HA_ADMIN_CORRUPT;
unknown's avatar
unknown committed
733
  }
unknown's avatar
unknown committed
734

unknown's avatar
unknown committed
735
 err:
736
  if (error != HA_ADMIN_OK)
unknown's avatar
unknown committed
737
  {
738
    /* Send error to user */
unknown's avatar
unknown committed
739 740 741 742 743 744 745 746 747
    MI_CHECK param;
    myisamchk_init(&param);
    param.thd= thd;
    param.op_name= (char*)"assign_to_keycache";
    param.db_name= table->table_cache_key;
    param.table_name= table->table_name;
    param.testflag= 0;
    mi_check_print_error(&param, errmsg);
  }
unknown's avatar
unknown committed
748
  DBUG_RETURN(error);
unknown's avatar
unknown committed
749 750 751
}


unknown's avatar
unknown committed
752 753 754 755 756 757 758 759 760 761 762 763 764 765
/*
  Preload pages of the index file for a table into the key cache.
*/

int ha_myisam::preload_keys(THD* thd, HA_CHECK_OPT *check_opt)
{
  int error;
  const char *errmsg;
  ulonglong map= ~(ulonglong) 0;
  TABLE_LIST *table_list= table->pos_in_table_list;
  my_bool ignore_leaves= table_list->ignore_leaves;

  DBUG_ENTER("ha_myisam::preload_keys");

766
  /* Check validity of the index references */
unknown's avatar
unknown committed
767 768
  if (table_list->use_index)
  {
769 770 771
    key_map kmap;
    get_key_map_from_key_list(&kmap, table, table_list->use_index);
    if (kmap.is_set_all())
unknown's avatar
unknown committed
772 773 774 775 776
    {
      errmsg= thd->net.last_error;
      error= HA_ADMIN_FAILED;
      goto err;
    }
777 778
    if (!kmap.is_clear_all())
      map= kmap.to_ulonglong();
unknown's avatar
unknown committed
779
  }
780

unknown's avatar
unknown committed
781 782 783 784 785 786 787 788 789 790 791 792
  mi_extra(file, HA_EXTRA_PRELOAD_BUFFER_SIZE,
           (void *) &thd->variables.preload_buff_size);

  if ((error= mi_preload(file, map, ignore_leaves)))
  {
    switch (error) {
    case HA_ERR_NON_UNIQUE_BLOCK_SIZE:
      errmsg= "Indexes use different block sizes";
      break;
    case HA_ERR_OUT_OF_MEM:
      errmsg= "Failed to allocate buffer";
      break;
793
    default:
unknown's avatar
unknown committed
794
      char buf[ERRMSGSIZE+20];
795
      my_snprintf(buf, ERRMSGSIZE,
unknown's avatar
unknown committed
796 797 798 799 800 801
                  "Failed to read from index file (errno: %d)", my_errno);
      errmsg= buf;
    }
    error= HA_ADMIN_FAILED;
    goto err;
  }
802

unknown's avatar
unknown committed
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
  DBUG_RETURN(HA_ADMIN_OK);

 err:
  {
    MI_CHECK param;
    myisamchk_init(&param);
    param.thd= thd;
    param.op_name= (char*)"preload_keys";
    param.db_name= table->table_cache_key;
    param.table_name= table->table_name;
    param.testflag= 0;
    mi_check_print_error(&param, errmsg);
    DBUG_RETURN(error);
  }
}

819

unknown's avatar
unknown committed
820
/*
821 822
  Disable indexes, making it persistent if requested.

823
  SYNOPSIS
824 825 826 827 828 829 830 831 832 833 834 835 836 837
    disable_indexes()
    mode        mode of operation:
                HA_KEY_SWITCH_NONUNIQ      disable all non-unique keys
                HA_KEY_SWITCH_ALL          disable all keys
                HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent
                HA_KEY_SWITCH_ALL_SAVE     dis. all keys and make persistent

  IMPLEMENTATION
    HA_KEY_SWITCH_NONUNIQ       is not implemented.
    HA_KEY_SWITCH_ALL_SAVE      is not implemented.

  RETURN
    0  ok
    HA_ERR_WRONG_COMMAND  mode not implemented.
838
*/
839 840

int ha_myisam::disable_indexes(uint mode)
841
{
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
  int error;

  if (mode == HA_KEY_SWITCH_ALL)
  {
    /* call a storage engine function to switch the key map */
    error= mi_disable_indexes(file);
  }
  else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
  {
    mi_extra(file, HA_EXTRA_NO_KEYS, 0);
    info(HA_STATUS_CONST);                        // Read new key info
    error= 0;
  }
  else
  {
    /* mode not implemented */
    error= HA_ERR_WRONG_COMMAND;
  }
  return error;
861 862
}

863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892

/*
  Enable indexes, making it persistent if requested.

  SYNOPSIS
    enable_indexes()
    mode        mode of operation:
                HA_KEY_SWITCH_NONUNIQ      enable all non-unique keys
                HA_KEY_SWITCH_ALL          enable all keys
                HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent
                HA_KEY_SWITCH_ALL_SAVE     en. all keys and make persistent

  DESCRIPTION
    Enable indexes, which might have been disabled by disable_index() before.
    The modes without _SAVE work only if both data and indexes are empty,
    since the MyISAM repair would enable them persistently.
    To be sure in these cases, call handler::delete_all_rows() before.

  IMPLEMENTATION
    HA_KEY_SWITCH_NONUNIQ       is not implemented.
    HA_KEY_SWITCH_ALL_SAVE      is not implemented.

  RETURN
    0  ok
    !=0  Error, among others:
    HA_ERR_CRASHED  data or index is non-empty. Delete all rows and retry.
    HA_ERR_WRONG_COMMAND  mode not implemented.
*/

int ha_myisam::enable_indexes(uint mode)
893
{
894 895
  int error;

896
  if (file->s->state.key_map == set_bits(ulonglong, file->s->base.keys))
897 898
  {
    /* All indexes are enabled already. */
899
    return 0;
900
  }
901

902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
  if (mode == HA_KEY_SWITCH_ALL)
  {
    error= mi_enable_indexes(file);
    /*
       Do not try to repair on error,
       as this could make the enabled state persistent,
       but mode==HA_KEY_SWITCH_ALL forbids it.
    */
  }
  else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
  {
    THD *thd=current_thd;
    MI_CHECK param;
    const char *save_proc_info=thd->proc_info;
    thd->proc_info="Creating index";
    myisamchk_init(&param);
    param.op_name = (char*) "recreating_index";
    param.testflag = (T_SILENT | T_REP_BY_SORT | T_QUICK |
                      T_CREATE_MISSING_KEYS);
    param.myf_rw&= ~MY_WAIT_IF_FULL;
    param.sort_buffer_length=  thd->variables.myisam_sort_buff_size;
    param.tmpdir=&mysql_tmpdir_list;
924 925 926 927 928 929 930
    if ((error= (repair(thd,param,0) != HA_ADMIN_OK)) && param.retry_repair)
    {
      sql_print_warning("Warning: Enabling keys got errno %d, retrying",
                        my_errno);
      param.testflag&= ~(T_REP_BY_SORT | T_QUICK);
      error= (repair(thd,param,0) != HA_ADMIN_OK);
    }
931 932 933 934 935 936 937 938
    info(HA_STATUS_CONST);
    thd->proc_info=save_proc_info;
  }
  else
  {
    /* mode not implemented */
    error= HA_ERR_WRONG_COMMAND;
  }
939 940 941
  return error;
}

942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964

/*
  Test if indexes are disabled.


  SYNOPSIS
    indexes_are_disabled()
      no parameters


  RETURN
    0  indexes are not disabled
    1  all indexes are disabled
   [2  non-unique indexes are disabled - NOT YET IMPLEMENTED]
*/

int ha_myisam::indexes_are_disabled(void)
{
  
  return mi_indexes_are_disabled(file);
}


965
/*
966 967 968
  prepare for a many-rows insert operation
  e.g. - disable indexes (if they can be recreated fast) or
  activate special bulk-insert optimizations
969 970

  SYNOPSIS
971 972 973
    start_bulk_insert(rows)
    rows        Rows to be inserted
                0 if we don't know
974 975 976

  NOTICE
    Do not forget to call end_bulk_insert() later!
977
*/
978

979
void ha_myisam::start_bulk_insert(ha_rows rows)
980
{
981
  DBUG_ENTER("ha_myisam::start_bulk_insert");
unknown's avatar
unknown committed
982 983
  THD *thd=current_thd;
  ulong size= min(thd->variables.read_buff_size, table->avg_row_length*rows);
984 985
  DBUG_PRINT("info",("start_bulk_insert: rows %lu size %lu",
                     (ulong) rows, size));
unknown's avatar
unknown committed
986

987
  /* don't enable row cache if too few rows */
988
  if (! rows || (rows > MI_MIN_ROWS_TO_USE_WRITE_CACHE))
989
    mi_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size);
unknown's avatar
unknown committed
990 991 992 993

  can_enable_indexes= (file->s->state.key_map ==
                       set_bits(ulonglong, file->s->base.keys));

994
  if (!(specialflag & SPECIAL_SAFE_MODE))
995
  {
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
    /*
      Only disable old index if the table was empty and we are inserting
      a lot of rows.
      We should not do this for only a few rows as this is slower and
      we don't want to update the key statistics based of only a few rows.
    */
    if (file->state->records == 0 && can_enable_indexes &&
        (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES))
      mi_disable_non_unique_index(file,rows);
    else
    if (!file->bulk_insert &&
        (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT))
unknown's avatar
unknown committed
1008
    {
unknown's avatar
unknown committed
1009
      mi_init_bulk_insert(file, thd->variables.bulk_insert_buff_size, rows);
unknown's avatar
unknown committed
1010
    }
1011
  }
1012
  DBUG_VOID_RETURN;
1013 1014
}

1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
/*
  end special bulk-insert optimizations,
  which have been activated by start_bulk_insert().

  SYNOPSIS
    end_bulk_insert()
    no arguments

  RETURN
    0     OK
    != 0  Error
*/

1028
int ha_myisam::end_bulk_insert()
1029
{
unknown's avatar
unknown committed
1030
  mi_end_bulk_insert(file);
unknown's avatar
unknown committed
1031
  int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0);
1032 1033
  return err ? err : can_enable_indexes ?
                     enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE) : 0;
1034
}
unknown's avatar
unknown committed
1035

1036

1037
bool ha_myisam::check_and_repair(THD *thd)
1038 1039
{
  int error=0;
unknown's avatar
unknown committed
1040
  int marked_crashed;
1041 1042
  char *old_query;
  uint old_query_length;
1043
  HA_CHECK_OPT check_opt;
1044
  DBUG_ENTER("ha_myisam::check_and_repair");
1045 1046

  check_opt.init();
1047 1048 1049
  check_opt.flags= T_MEDIUM | T_AUTO_REPAIR;
  // Don't use quick if deleted rows
  if (!file->state->del && (myisam_recover_options & HA_RECOVER_QUICK))
unknown's avatar
unknown committed
1050
    check_opt.flags|=T_QUICK;
unknown's avatar
unknown committed
1051
  sql_print_warning("Checking table:   '%s'",table->path);
1052 1053 1054 1055 1056 1057 1058 1059 1060

  old_query= thd->query;
  old_query_length= thd->query_length;
  pthread_mutex_lock(&LOCK_thread_count);
  thd->query= table->real_name;
  thd->query_length= strlen(table->real_name);
  pthread_mutex_unlock(&LOCK_thread_count);

  if ((marked_crashed= mi_is_crashed(file)) || check(thd, &check_opt))
1061
  {
unknown's avatar
unknown committed
1062
    sql_print_warning("Recovering table: '%s'",table->path);
1063 1064 1065 1066 1067 1068 1069
    check_opt.flags=
      ((myisam_recover_options & HA_RECOVER_BACKUP ? T_BACKUP_DATA : 0) |
       (marked_crashed                             ? 0 : T_QUICK) |
       (myisam_recover_options & HA_RECOVER_FORCE  ? 0 : T_SAFE_REPAIR) |
       T_AUTO_REPAIR);
    if (repair(thd, &check_opt))
      error=1;
1070
  }
1071 1072 1073 1074
  pthread_mutex_lock(&LOCK_thread_count);
  thd->query= old_query;
  thd->query_length= old_query_length;
  pthread_mutex_unlock(&LOCK_thread_count);
1075 1076 1077
  DBUG_RETURN(error);
}

1078 1079 1080 1081 1082
bool ha_myisam::is_crashed() const
{
  return (file->s->state.changed & STATE_CRASHED ||
	  (my_disable_locking && file->s->state.open_count));
}
1083

unknown's avatar
unknown committed
1084 1085 1086
int ha_myisam::update_row(const byte * old_data, byte * new_data)
{
  statistic_increment(ha_update_count,&LOCK_status);
1087 1088
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
    table->timestamp_field->set_time();
unknown's avatar
unknown committed
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
  return mi_update(file,old_data,new_data);
}

int ha_myisam::delete_row(const byte * buf)
{
  statistic_increment(ha_delete_count,&LOCK_status);
  return mi_delete(file,buf);
}

int ha_myisam::index_read(byte * buf, const byte * key,
			  uint key_len, enum ha_rkey_function find_flag)
{
unknown's avatar
unknown committed
1101
  DBUG_ASSERT(inited==INDEX);
unknown's avatar
unknown committed
1102
  statistic_increment(ha_read_key_count,&LOCK_status);
1103
  int error=mi_rkey(file,buf,active_index, key, key_len, find_flag);
unknown's avatar
unknown committed
1104 1105 1106 1107 1108 1109 1110 1111
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}

int ha_myisam::index_read_idx(byte * buf, uint index, const byte * key,
			      uint key_len, enum ha_rkey_function find_flag)
{
  statistic_increment(ha_read_key_count,&LOCK_status);
1112
  int error=mi_rkey(file,buf,index, key, key_len, find_flag);
unknown's avatar
unknown committed
1113 1114 1115 1116
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}

1117 1118
int ha_myisam::index_read_last(byte * buf, const byte * key, uint key_len)
{
unknown's avatar
unknown committed
1119
  DBUG_ASSERT(inited==INDEX);
1120 1121 1122 1123 1124 1125
  statistic_increment(ha_read_key_count,&LOCK_status);
  int error=mi_rkey(file,buf,active_index, key, key_len, HA_READ_PREFIX_LAST);
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}

unknown's avatar
unknown committed
1126 1127
int ha_myisam::index_next(byte * buf)
{
unknown's avatar
unknown committed
1128
  DBUG_ASSERT(inited==INDEX);
unknown's avatar
unknown committed
1129 1130 1131 1132 1133 1134 1135 1136
  statistic_increment(ha_read_next_count,&LOCK_status);
  int error=mi_rnext(file,buf,active_index);
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}

int ha_myisam::index_prev(byte * buf)
{
unknown's avatar
unknown committed
1137
  DBUG_ASSERT(inited==INDEX);
unknown's avatar
unknown committed
1138 1139 1140 1141 1142
  statistic_increment(ha_read_prev_count,&LOCK_status);
  int error=mi_rprev(file,buf, active_index);
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}
1143

unknown's avatar
unknown committed
1144 1145
int ha_myisam::index_first(byte * buf)
{
unknown's avatar
unknown committed
1146
  DBUG_ASSERT(inited==INDEX);
unknown's avatar
unknown committed
1147 1148 1149 1150 1151 1152 1153 1154
  statistic_increment(ha_read_first_count,&LOCK_status);
  int error=mi_rfirst(file, buf, active_index);
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}

int ha_myisam::index_last(byte * buf)
{
unknown's avatar
unknown committed
1155
  DBUG_ASSERT(inited==INDEX);
unknown's avatar
unknown committed
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
  statistic_increment(ha_read_last_count,&LOCK_status);
  int error=mi_rlast(file, buf, active_index);
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}

int ha_myisam::index_next_same(byte * buf,
			       const byte *key __attribute__((unused)),
			       uint length __attribute__((unused)))
{
unknown's avatar
unknown committed
1166
  DBUG_ASSERT(inited==INDEX);
unknown's avatar
unknown committed
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177
  statistic_increment(ha_read_next_count,&LOCK_status);
  int error=mi_rnext_same(file,buf);
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}


int ha_myisam::rnd_init(bool scan)
{
  if (scan)
    return mi_scan_init(file);
unknown's avatar
unknown committed
1178
  return mi_extra(file, HA_EXTRA_RESET, 0);
unknown's avatar
unknown committed
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
}

int ha_myisam::rnd_next(byte *buf)
{
  statistic_increment(ha_read_rnd_next_count,&LOCK_status);
  int error=mi_scan(file, buf);
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}

int ha_myisam::restart_rnd_next(byte *buf, byte *pos)
{
  return rnd_pos(buf,pos);
}

int ha_myisam::rnd_pos(byte * buf, byte *pos)
{
  statistic_increment(ha_read_rnd_count,&LOCK_status);
  int error=mi_rrnd(file, buf, ha_get_ptr(pos,ref_length));
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}

void ha_myisam::position(const byte* record)
{
  my_off_t position=mi_position(file);
  ha_store_ptr(ref, ref_length, position);
}

void ha_myisam::info(uint flag)
{
  MI_ISAMINFO info;
1211 1212
  char name_buff[FN_REFLEN];

unknown's avatar
unknown committed
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232
  (void) mi_status(file,&info,flag);
  if (flag & HA_STATUS_VARIABLE)
  {
    records = info.records;
    deleted = info.deleted;
    data_file_length=info.data_file_length;
    index_file_length=info.index_file_length;
    delete_length = info.delete_length;
    check_time  = info.check_time;
    mean_rec_length=info.mean_reclength;
  }
  if (flag & HA_STATUS_CONST)
  {
    max_data_file_length=info.max_data_file_length;
    max_index_file_length=info.max_index_file_length;
    create_time = info.create_time;
    sortkey = info.sortkey;
    ref_length=info.reflength;
    table->db_options_in_use    = info.options;
    block_size=myisam_block_size;
unknown's avatar
unknown committed
1233 1234
    table->keys_in_use.set_prefix(table->keys);
    table->keys_in_use.intersect(info.key_map);
1235 1236
    table->keys_for_keyread= table->keys_in_use;
    table->keys_for_keyread.subtract(table->read_only_keys);
unknown's avatar
unknown committed
1237 1238 1239 1240
    table->db_record_offset=info.record_offset;
    if (table->key_parts)
      memcpy((char*) table->key_info[0].rec_per_key,
	     (char*) info.rec_per_key,
unknown's avatar
unknown committed
1241
	     sizeof(table->key_info[0].rec_per_key)*table->key_parts);
unknown's avatar
unknown committed
1242 1243 1244
    raid_type=info.raid_type;
    raid_chunks=info.raid_chunks;
    raid_chunksize=info.raid_chunksize;
unknown's avatar
unknown committed
1245

1246 1247
   /*
     Set data_file_name and index_file_name to point at the symlink value
1248
     if table is symlinked (Ie;  Real name is not same as generated name)
1249 1250
   */
    data_file_name=index_file_name=0;
1251 1252
    fn_format(name_buff, file->filename, "", MI_NAME_DEXT, 2);
    if (strcmp(name_buff, info.data_file_name))
1253
      data_file_name=info.data_file_name;
1254 1255
    strmov(fn_ext(name_buff),MI_NAME_IEXT);
    if (strcmp(name_buff, info.index_file_name))
1256
      index_file_name=info.index_file_name;
unknown's avatar
unknown committed
1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
  }
  if (flag & HA_STATUS_ERRKEY)
  {
    errkey  = info.errkey;
    ha_store_ptr(dupp_ref, ref_length, info.dupp_key_pos);
  }
  if (flag & HA_STATUS_TIME)
    update_time = info.update_time;
  if (flag & HA_STATUS_AUTO)
    auto_increment_value= info.auto_increment;
}


int ha_myisam::extra(enum ha_extra_function operation)
{
unknown's avatar
unknown committed
1272 1273 1274 1275 1276 1277
  if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_KEYREAD)
    return 0;
  return mi_extra(file, operation, 0);
}


unknown's avatar
unknown committed
1278
/* To be used with WRITE_CACHE and EXTRA_CACHE */
unknown's avatar
unknown committed
1279 1280 1281

int ha_myisam::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
unknown's avatar
unknown committed
1282
  if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE)
unknown's avatar
unknown committed
1283
    return 0;
unknown's avatar
unknown committed
1284
  return mi_extra(file, operation, (void*) &cache_size);
unknown's avatar
unknown committed
1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
}

int ha_myisam::delete_all_rows()
{
  return mi_delete_all_rows(file);
}

int ha_myisam::delete_table(const char *name)
{
  return mi_delete_table(name);
}

1297

unknown's avatar
unknown committed
1298 1299
int ha_myisam::external_lock(THD *thd, int lock_type)
{
unknown's avatar
unknown committed
1300 1301
  return mi_lock_database(file, !table->tmp_table ?
			  lock_type : ((lock_type == F_UNLCK) ?
1302
				       F_UNLCK : F_EXTRA_LCK));
1303
}
unknown's avatar
unknown committed
1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316

THR_LOCK_DATA **ha_myisam::store_lock(THD *thd,
				      THR_LOCK_DATA **to,
				      enum thr_lock_type lock_type)
{
  if (lock_type != TL_IGNORE && file->lock.type == TL_UNLOCK)
    file->lock.type=lock_type;
  *to++= &file->lock;
  return to;
}

void ha_myisam::update_create_info(HA_CREATE_INFO *create_info)
{
1317
  ha_myisam::info(HA_STATUS_AUTO | HA_STATUS_CONST);
unknown's avatar
unknown committed
1318 1319 1320 1321 1322 1323 1324 1325 1326 1327
  if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
  {
    create_info->auto_increment_value=auto_increment_value;
  }
  if (!(create_info->used_fields & HA_CREATE_USED_RAID))
  {
    create_info->raid_type= raid_type;
    create_info->raid_chunks= raid_chunks;
    create_info->raid_chunksize= raid_chunksize;
  }
1328 1329
  create_info->data_file_name=data_file_name;
  create_info->index_file_name=index_file_name;
unknown's avatar
unknown committed
1330 1331 1332
}


1333
int ha_myisam::create(const char *name, register TABLE *table_arg,
unknown's avatar
unknown committed
1334 1335 1336
		      HA_CREATE_INFO *info)
{
  int error;
unknown's avatar
unknown committed
1337
  uint i,j,recpos,minpos,fieldpos,temp_length,length, create_flags= 0;
1338
  bool found_real_auto_increment=0;
unknown's avatar
unknown committed
1339 1340 1341 1342 1343
  enum ha_base_keytype type;
  char buff[FN_REFLEN];
  KEY *pos;
  MI_KEYDEF *keydef;
  MI_COLUMNDEF *recinfo,*recinfo_pos;
unknown's avatar
unknown committed
1344
  HA_KEYSEG *keyseg;
1345
  uint options=table_arg->db_options_in_use;
unknown's avatar
unknown committed
1346 1347 1348 1349
  DBUG_ENTER("ha_myisam::create");

  type=HA_KEYTYPE_BINARY;				// Keep compiler happy
  if (!(my_multi_malloc(MYF(MY_WME),
1350 1351
			&recinfo,(table_arg->fields*2+2)*sizeof(MI_COLUMNDEF),
			&keydef, table_arg->keys*sizeof(MI_KEYDEF),
unknown's avatar
unknown committed
1352
			&keyseg,
unknown's avatar
unknown committed
1353 1354
			((table_arg->key_parts + table_arg->keys) *
			 sizeof(HA_KEYSEG)),
1355
			NullS)))
unknown's avatar
unknown committed
1356
    DBUG_RETURN(HA_ERR_OUT_OF_MEM);
unknown's avatar
unknown committed
1357

1358 1359
  pos=table_arg->key_info;
  for (i=0; i < table_arg->keys ; i++, pos++)
unknown's avatar
unknown committed
1360
  {
unknown's avatar
unknown committed
1361
    keydef[i].flag= (pos->flags & (HA_NOSAME | HA_FULLTEXT | HA_SPATIAL));
unknown's avatar
SCRUM  
unknown committed
1362 1363 1364
    keydef[i].key_alg= pos->algorithm == HA_KEY_ALG_UNDEF ? 
      (pos->flags & HA_SPATIAL ? HA_KEY_ALG_RTREE : HA_KEY_ALG_BTREE) :
      pos->algorithm;
unknown's avatar
unknown committed
1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
    keydef[i].seg=keyseg;
    keydef[i].keysegs=pos->key_parts;
    for (j=0 ; j < pos->key_parts ; j++)
    {
      keydef[i].seg[j].flag=pos->key_part[j].key_part_flag;
      Field *field=pos->key_part[j].field;
      type=field->key_type();

      if (options & HA_OPTION_PACK_KEYS ||
	  (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
			 HA_SPACE_PACK_USED)))
      {
	if (pos->key_part[j].length > 8 &&
	    (type == HA_KEYTYPE_TEXT ||
	     type == HA_KEYTYPE_NUM ||
	     (type == HA_KEYTYPE_BINARY && !field->zero_pack())))
	{
	  /* No blobs here */
	  if (j == 0)
	    keydef[i].flag|=HA_PACK_KEY;
	  if (!(field->flags & ZEROFILL_FLAG) &&
	      (field->type() == FIELD_TYPE_STRING ||
	       field->type() == FIELD_TYPE_VAR_STRING ||
	       ((int) (pos->key_part[j].length - field->decimals()))
	       >= 4))
	    keydef[i].seg[j].flag|=HA_SPACE_PACK;
	}
	else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16))
	  keydef[i].flag|= HA_BINARY_PACK_KEY;
      }
      keydef[i].seg[j].type=   (int) type;
      keydef[i].seg[j].start=  pos->key_part[j].offset;
      keydef[i].seg[j].length= pos->key_part[j].length;
      keydef[i].seg[j].bit_start=keydef[i].seg[j].bit_end=0;
unknown's avatar
unknown committed
1399
      keydef[i].seg[j].language = field->charset()->number;
unknown's avatar
unknown committed
1400 1401 1402 1403 1404

      if (field->null_ptr)
      {
	keydef[i].seg[j].null_bit=field->null_bit;
	keydef[i].seg[j].null_pos= (uint) (field->null_ptr-
1405
					   (uchar*) table_arg->record[0]);
unknown's avatar
unknown committed
1406 1407 1408 1409 1410 1411
      }
      else
      {
	keydef[i].seg[j].null_bit=0;
	keydef[i].seg[j].null_pos=0;
      }
unknown's avatar
unknown committed
1412 1413
      if (field->type() == FIELD_TYPE_BLOB ||
	  field->type() == FIELD_TYPE_GEOMETRY)
unknown's avatar
unknown committed
1414 1415 1416 1417
      {
	keydef[i].seg[j].flag|=HA_BLOB_PART;
	/* save number of bytes used to pack length */
	keydef[i].seg[j].bit_start= (uint) (field->pack_length() -
1418
					    table_arg->blob_ptr_size);
unknown's avatar
unknown committed
1419 1420 1421 1422 1423
      }
    }
    keyseg+=pos->key_parts;
  }

1424 1425 1426 1427 1428 1429
  if (table_arg->found_next_number_field)
  {
    keydef[table_arg->next_number_index].flag|= HA_AUTO_KEY;
    found_real_auto_increment= table_arg->next_number_key_offset == 0;
  }

unknown's avatar
unknown committed
1430
  recpos=0; recinfo_pos=recinfo;
1431
  while (recpos < (uint) table_arg->reclength)
unknown's avatar
unknown committed
1432 1433
  {
    Field **field,*found=0;
1434
    minpos=table_arg->reclength; length=0;
unknown's avatar
unknown committed
1435

1436
    for (field=table_arg->field ; *field ; field++)
unknown's avatar
unknown committed
1437 1438 1439 1440 1441 1442
    {
      if ((fieldpos=(*field)->offset()) >= recpos &&
	  fieldpos <= minpos)
      {
	/* skip null fields */
	if (!(temp_length= (*field)->pack_length()))
unknown's avatar
unknown committed
1443
	  continue;				/* Skip null-fields */
unknown's avatar
unknown committed
1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468
	if (! found || fieldpos < minpos ||
	    (fieldpos == minpos && temp_length < length))
	{
	  minpos=fieldpos; found= *field; length=temp_length;
	}
      }
    }
    DBUG_PRINT("loop",("found: %lx  recpos: %d  minpos: %d  length: %d",
		       found,recpos,minpos,length));
    if (recpos != minpos)
    {						// Reserved space (Null bits?)
      bzero((char*) recinfo_pos,sizeof(*recinfo_pos));
      recinfo_pos->type=(int) FIELD_NORMAL;
      recinfo_pos++->length= (uint16) (minpos-recpos);
    }
    if (! found)
      break;

    if (found->flags & BLOB_FLAG)
    {
      recinfo_pos->type= (int) FIELD_BLOB;
    }
    else if (!(options & HA_OPTION_PACK_RECORD))
      recinfo_pos->type= (int) FIELD_NORMAL;
    else if (found->zero_pack())
unknown's avatar
unknown committed
1469
      recinfo_pos->type= (int) FIELD_SKIP_ZERO;
unknown's avatar
unknown committed
1470 1471 1472 1473 1474 1475
    else
      recinfo_pos->type= (int) ((length <= 3 ||
				      (found->flags & ZEROFILL_FLAG)) ?
				     FIELD_NORMAL :
				     found->type() == FIELD_TYPE_STRING ||
				     found->type() == FIELD_TYPE_VAR_STRING ?
unknown's avatar
unknown committed
1476 1477
				     FIELD_SKIP_ENDSPACE :
				     FIELD_SKIP_PRESPACE);
unknown's avatar
unknown committed
1478 1479 1480 1481
    if (found->null_ptr)
    {
      recinfo_pos->null_bit=found->null_bit;
      recinfo_pos->null_pos= (uint) (found->null_ptr-
1482
				  (uchar*) table_arg->record[0]);
unknown's avatar
unknown committed
1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
    }
    else
    {
      recinfo_pos->null_bit=0;
      recinfo_pos->null_pos=0;
    }
    (recinfo_pos++) ->length=(uint16) length;
    recpos=minpos+length;
    DBUG_PRINT("loop",("length: %d  type: %d",
		       recinfo_pos[-1].length,recinfo_pos[-1].type));

  }
  MI_CREATE_INFO create_info;
  bzero((char*) &create_info,sizeof(create_info));
1497 1498
  create_info.max_rows=table_arg->max_rows;
  create_info.reloc_rows=table_arg->min_rows;
1499
  create_info.with_auto_increment=found_real_auto_increment;
unknown's avatar
unknown committed
1500 1501 1502
  create_info.auto_increment=(info->auto_increment_value ?
			      info->auto_increment_value -1 :
			      (ulonglong) 0);
1503 1504
  create_info.data_file_length= ((ulonglong) table_arg->max_rows *
				 table_arg->avg_row_length);
unknown's avatar
unknown committed
1505
  create_info.raid_type=info->raid_type;
1506 1507 1508 1509
  create_info.raid_chunks= (info->raid_chunks ? info->raid_chunks :
			    RAID_DEFAULT_CHUNKS);
  create_info.raid_chunksize=(info->raid_chunksize ? info->raid_chunksize :
			      RAID_DEFAULT_CHUNKSIZE);
1510 1511
  create_info.data_file_name= info->data_file_name;
  create_info.index_file_name=info->index_file_name;
unknown's avatar
unknown committed
1512

1513 1514 1515 1516 1517 1518 1519 1520 1521
  if (info->options & HA_LEX_CREATE_TMP_TABLE)
    create_flags|= HA_CREATE_TMP_TABLE;
  if (options & HA_OPTION_PACK_RECORD)
    create_flags|= HA_PACK_RECORD;
  if (options & HA_OPTION_CHECKSUM)
    create_flags|= HA_CREATE_CHECKSUM;
  if (options & HA_OPTION_DELAY_KEY_WRITE)
    create_flags|= HA_CREATE_DELAY_KEY_WRITE;

1522
  /* TODO: Check that the following fn_format is really needed */
1523
  error=mi_create(fn_format(buff,name,"","",2+4),
1524
		  table_arg->keys,keydef,
unknown's avatar
unknown committed
1525 1526
		  (uint) (recinfo_pos-recinfo), recinfo,
		  0, (MI_UNIQUEDEF*) 0,
1527
		  &create_info, create_flags);
1528

unknown's avatar
unknown committed
1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547
  my_free((gptr) recinfo,MYF(0));
  DBUG_RETURN(error);
}


int ha_myisam::rename_table(const char * from, const char * to)
{
  return mi_rename(from,to);
}


longlong ha_myisam::get_auto_increment()
{
  if (!table->next_number_key_offset)
  {						// Autoincrement at key-start
    ha_myisam::info(HA_STATUS_AUTO);
    return auto_increment_value;
  }

1548 1549
  /* it's safe to call the following if bulk_insert isn't on */
  mi_flush_bulk_insert(file, table->next_number_index);
1550

unknown's avatar
unknown committed
1551 1552
  longlong nr;
  int error;
1553
  byte key[MI_MAX_KEY_LENGTH];
unknown's avatar
unknown committed
1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568
  (void) extra(HA_EXTRA_KEYREAD);
  key_copy(key,table,table->next_number_index,
	   table->next_number_key_offset);
  error=mi_rkey(file,table->record[1],(int) table->next_number_index,
		key,table->next_number_key_offset,HA_READ_PREFIX_LAST);
  if (error)
    nr=1;
  else
    nr=(longlong)
      table->next_number_field->val_int_offset(table->rec_buff_length)+1;
  extra(HA_EXTRA_NO_KEYREAD);
  return nr;
}


unknown's avatar
unknown committed
1569 1570 1571 1572 1573 1574
/*
  Find out how many rows there is in the given range

  SYNOPSIS
    records_in_range()
    inx			Index to use
unknown's avatar
unknown committed
1575 1576
    min_key		Start of range.  Null pointer if from first key
    max_key		End of range. Null pointer if to last key
unknown's avatar
unknown committed
1577 1578

  NOTES
unknown's avatar
unknown committed
1579
    min_key.flag can have one of the following values:
unknown's avatar
unknown committed
1580 1581 1582
      HA_READ_KEY_EXACT		Include the key in the range
      HA_READ_AFTER_KEY		Don't include key in range

unknown's avatar
unknown committed
1583
    max_key.flag can have one of the following values:  
unknown's avatar
unknown committed
1584 1585 1586 1587 1588 1589 1590 1591 1592 1593
      HA_READ_BEFORE_KEY	Don't include key in range
      HA_READ_AFTER_KEY		Include all 'end_key' values in the range

  RETURN
   HA_POS_ERROR		Something is wrong with the index tree.
   0			There is no matching keys in the given range
   number > 0		There is approximately 'number' matching rows in
			the range.
*/

unknown's avatar
unknown committed
1594 1595
ha_rows ha_myisam::records_in_range(uint inx, key_range *min_key,
                                    key_range *max_key)
unknown's avatar
unknown committed
1596
{
unknown's avatar
unknown committed
1597
  return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key);
unknown's avatar
unknown committed
1598 1599
}

unknown's avatar
unknown committed
1600

unknown's avatar
unknown committed
1601 1602 1603 1604
int ha_myisam::ft_read(byte * buf)
{
  int error;

1605 1606 1607
  if (!ft_handler)
    return -1;

unknown's avatar
unknown committed
1608 1609
  thread_safe_increment(ha_read_next_count,&LOCK_status); // why ?

unknown's avatar
unknown committed
1610
  error=ft_handler->please->read_next(ft_handler,(char*) buf);
unknown's avatar
unknown committed
1611

unknown's avatar
unknown committed
1612 1613 1614
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}
unknown's avatar
unknown committed
1615 1616 1617 1618 1619 1620

uint ha_myisam::checksum() const
{
  return (uint)file->s->state.checksum;
}