ha_myisam.cc 45.4 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" {

unknown's avatar
unknown committed
90
volatile int *killed_ptr(MI_CHECK *param)
unknown's avatar
unknown committed
91
{
unknown's avatar
unknown committed
92 93
  /* In theory Unsafe conversion, but should be ok for now */
  return (int*) &(((THD *)(param->thd))->killed);
unknown's avatar
unknown committed
94 95
}

unknown's avatar
unknown committed
96 97 98
void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
{
  param->error_printed|=1;
99
  param->out_flag|= O_DATA_LOST;
unknown's avatar
unknown committed
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
  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;
117
  param->out_flag|= O_DATA_LOST;
unknown's avatar
unknown committed
118 119 120 121 122 123 124 125 126
  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
127
{ static const char *ext[]= { ".MYI",".MYD", NullS }; return ext; }
unknown's avatar
unknown committed
128 129


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

unknown's avatar
SCRUM  
unknown committed
141
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
142 143 144 145 146 147
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));
148
  for (;;)
unknown's avatar
unknown committed
149
  {
unknown's avatar
unknown committed
150
    ulong packet_len = my_net_read(net);
unknown's avatar
unknown committed
151 152 153 154 155 156 157 158
    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
159
    if (my_write(data_fd, (byte*)net->read_pos, (uint) packet_len,
unknown's avatar
unknown committed
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
		 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));
179
  if (!buf)
unknown's avatar
unknown committed
180 181 182 183
    return ENOMEM;

  int error = 0;
  my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME));
184
  for (; bytes_to_read > 0;)
unknown's avatar
unknown committed
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 216
  {
    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);
  }
217

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

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

226
int ha_myisam::open(const char *name, int mode, uint test_if_locked)
unknown's avatar
unknown committed
227
{
228
  if (!(file=mi_open(name, mode, test_if_locked)))
unknown's avatar
unknown committed
229
    return (my_errno ? my_errno : -1);
unknown's avatar
unknown committed
230
  
231
  if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE))
unknown's avatar
unknown committed
232
    VOID(mi_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0));
unknown's avatar
unknown committed
233 234
  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
235
    VOID(mi_extra(file, HA_EXTRA_WAIT_LOCK, 0));
unknown's avatar
unknown committed
236
  if (!table->db_record_offset)
237
    int_table_flags|=HA_REC_NOT_IN_SEQ;
unknown's avatar
unknown committed
238 239
  if (file->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
    int_table_flags|=HA_HAS_CHECKSUM;
unknown's avatar
unknown committed
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)
{
252
  statistic_increment(table->in_use->status_var.ha_write_count,&LOCK_status);
unknown's avatar
unknown committed
253 254

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

  /*
    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
262 263 264 265 266 267 268
  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)
{
269
  if (!file) return HA_ADMIN_INTERNAL_ERROR;
unknown's avatar
unknown committed
270
  int error;
unknown's avatar
unknown committed
271 272
  MI_CHECK param;
  MYISAM_SHARE* share = file->s;
unknown's avatar
unknown committed
273
  const char *old_proc_info=thd->proc_info;
274

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

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

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

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

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


/*
  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!
*/

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

unknown's avatar
unknown committed
363 364
  myisamchk_init(&param);
  param.thd = thd;
365
  param.op_name = (char*) "analyze";
366
  param.db_name    = table->table_cache_key;
unknown's avatar
unknown committed
367 368 369 370 371
  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;

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

  error = chk_key(&param, file);
  if (!error)
377
  {
378 379 380
    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
381
  }
382
  else if (!mi_is_crashed(file) && !thd->killed)
383 384
    mi_mark_crashed(file);
  return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
unknown's avatar
unknown committed
385 386
}

387

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

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

402 403 404 405
  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
406
    errmsg = "Failed in my_copy (Error %d)";
407 408
    goto err;
  }
409

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

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

428

unknown's avatar
unknown committed
429 430
int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt)
{
431
  char* backup_dir= thd->lex->backup_dir;
unknown's avatar
unknown committed
432 433
  char src_path[FN_REFLEN], dst_path[FN_REFLEN];
  char* table_name = table->real_name;
434 435 436 437 438 439 440
  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))
  {
441
    errmsg = "Failed in fn_format() for .frm file (errno: %d)";
442 443 444
    error = HA_ADMIN_INVALID;
    goto err;
  }
unknown's avatar
unknown committed
445

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

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

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

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

unknown's avatar
unknown committed
489

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

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

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

int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt)
{
541
  int error;
542 543 544 545 546 547 548 549 550
  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;
551 552 553 554 555 556 557 558
  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;
559 560 561
}


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

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

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

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


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

int ha_myisam::assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt)
{
unknown's avatar
unknown committed
706
  KEY_CACHE *new_key_cache= check_opt->key_cache;
707
  const char *errmsg= 0;
unknown's avatar
unknown committed
708
  int error= HA_ADMIN_OK;
unknown's avatar
unknown committed
709 710 711 712
  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
713
  /* Check validity of the index references */
714
  if (table_list->use_index)
unknown's avatar
unknown committed
715
  {
716
    /* We only come here when the user did specify an index map */
unknown's avatar
unknown committed
717
    key_map kmap;
718
    if (get_key_map_from_key_list(&kmap, table, table_list->use_index))
unknown's avatar
unknown committed
719 720 721 722 723
    {
      errmsg= thd->net.last_error;
      error= HA_ADMIN_FAILED;
      goto err;
    }
724
    map= kmap.to_ulonglong();
unknown's avatar
unknown committed
725 726
  }

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

unknown's avatar
unknown committed
736
 err:
737
  if (error != HA_ADMIN_OK)
unknown's avatar
unknown committed
738
  {
739
    /* Send error to user */
unknown's avatar
unknown committed
740 741 742 743 744 745 746 747 748
    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
749
  DBUG_RETURN(error);
unknown's avatar
unknown committed
750 751 752
}


unknown's avatar
unknown committed
753 754 755 756 757 758 759 760 761 762 763 764 765 766
/*
  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");

767
  /* Check validity of the index references */
unknown's avatar
unknown committed
768 769
  if (table_list->use_index)
  {
770 771 772
    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
773 774 775 776 777
    {
      errmsg= thd->net.last_error;
      error= HA_ADMIN_FAILED;
      goto err;
    }
778 779
    if (!kmap.is_clear_all())
      map= kmap.to_ulonglong();
unknown's avatar
unknown committed
780
  }
781

unknown's avatar
unknown committed
782 783 784 785 786 787 788 789 790 791 792 793
  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;
794
    default:
unknown's avatar
unknown committed
795
      char buf[ERRMSGSIZE+20];
796
      my_snprintf(buf, ERRMSGSIZE,
unknown's avatar
unknown committed
797 798 799 800 801 802
                  "Failed to read from index file (errno: %d)", my_errno);
      errmsg= buf;
    }
    error= HA_ADMIN_FAILED;
    goto err;
  }
803

unknown's avatar
unknown committed
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819
  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);
  }
}

820

821
/*
822 823
  Disable indexes, making it persistent if requested.

824
  SYNOPSIS
825 826 827 828 829 830 831 832 833 834 835 836 837 838
    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.
839
*/
840 841

int ha_myisam::disable_indexes(uint mode)
842
{
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
  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;
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 893

/*
  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)
894
{
895 896
  int error;

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

903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
  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;
925 926 927 928 929 930 931
    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);
    }
932 933 934 935 936 937 938 939
    info(HA_STATUS_CONST);
    thd->proc_info=save_proc_info;
  }
  else
  {
    /* mode not implemented */
    error= HA_ERR_WRONG_COMMAND;
  }
940 941 942
  return error;
}

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

/*
  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);
}


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

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

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

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

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

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

995
  if (!(specialflag & SPECIAL_SAFE_MODE))
996
  {
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
    /*
      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
1009
    {
unknown's avatar
unknown committed
1010
      mi_init_bulk_insert(file, thd->variables.bulk_insert_buff_size, rows);
unknown's avatar
unknown committed
1011
    }
1012
  }
1013
  DBUG_VOID_RETURN;
1014 1015
}

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

  SYNOPSIS
    end_bulk_insert()
    no arguments

  RETURN
    0     OK
    != 0  Error
*/

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

1037

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

  check_opt.init();
1048 1049 1050
  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
1051
    check_opt.flags|=T_QUICK;
unknown's avatar
unknown committed
1052
  sql_print_warning("Checking table:   '%s'",table->path);
1053 1054 1055 1056 1057 1058 1059 1060 1061

  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))
1062
  {
unknown's avatar
unknown committed
1063
    sql_print_warning("Recovering table: '%s'",table->path);
unknown's avatar
unknown committed
1064
    check_opt.flags=
1065 1066 1067 1068
      ((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);
1069 1070 1071
    if (repair(thd, &check_opt))
      error=1;
  }
1072 1073 1074 1075
  pthread_mutex_lock(&LOCK_thread_count);
  thd->query= old_query;
  thd->query_length= old_query_length;
  pthread_mutex_unlock(&LOCK_thread_count);
1076 1077 1078
  DBUG_RETURN(error);
}

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

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

int ha_myisam::delete_row(const byte * buf)
{
1095
  statistic_increment(table->in_use->status_var.ha_delete_count,&LOCK_status);
unknown's avatar
unknown committed
1096 1097 1098 1099 1100 1101
  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
1102
  DBUG_ASSERT(inited==INDEX);
1103 1104
  statistic_increment(table->in_use->status_var.ha_read_key_count,
		      &LOCK_status);
1105
  int error=mi_rkey(file,buf,active_index, key, key_len, find_flag);
unknown's avatar
unknown committed
1106 1107 1108 1109 1110 1111 1112
  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)
{
1113 1114
  statistic_increment(table->in_use->status_var.ha_read_key_count,
		      &LOCK_status);
1115
  int error=mi_rkey(file,buf,index, key, key_len, find_flag);
unknown's avatar
unknown committed
1116 1117 1118 1119
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}

1120 1121
int ha_myisam::index_read_last(byte * buf, const byte * key, uint key_len)
{
unknown's avatar
unknown committed
1122
  DBUG_ASSERT(inited==INDEX);
1123 1124
  statistic_increment(table->in_use->status_var.ha_read_key_count,
		      &LOCK_status);
1125 1126 1127 1128 1129
  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
1130 1131
int ha_myisam::index_next(byte * buf)
{
unknown's avatar
unknown committed
1132
  DBUG_ASSERT(inited==INDEX);
1133 1134
  statistic_increment(table->in_use->status_var.ha_read_next_count,
		      &LOCK_status);
unknown's avatar
unknown committed
1135 1136 1137 1138 1139 1140 1141
  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
1142
  DBUG_ASSERT(inited==INDEX);
1143 1144
  statistic_increment(table->in_use->status_var.ha_read_prev_count,
		      &LOCK_status);
unknown's avatar
unknown committed
1145 1146 1147 1148
  int error=mi_rprev(file,buf, active_index);
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}
1149

unknown's avatar
unknown committed
1150 1151
int ha_myisam::index_first(byte * buf)
{
unknown's avatar
unknown committed
1152
  DBUG_ASSERT(inited==INDEX);
1153
  statistic_increment(table->in_use->status_var.ha_read_first_count,
1154
		      &LOCK_status);
unknown's avatar
unknown committed
1155 1156 1157 1158 1159 1160 1161
  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
1162
  DBUG_ASSERT(inited==INDEX);
1163 1164
  statistic_increment(table->in_use->status_var.ha_read_last_count,
		      &LOCK_status);
unknown's avatar
unknown committed
1165 1166 1167 1168 1169 1170 1171 1172 1173
  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
1174
  DBUG_ASSERT(inited==INDEX);
1175 1176
  statistic_increment(table->in_use->status_var.ha_read_next_count,
		      &LOCK_status);
unknown's avatar
unknown committed
1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
  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
1187
  return mi_extra(file, HA_EXTRA_RESET, 0);
unknown's avatar
unknown committed
1188 1189 1190 1191
}

int ha_myisam::rnd_next(byte *buf)
{
1192
  statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
1193
		      &LOCK_status);
unknown's avatar
unknown committed
1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
  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)
{
1206 1207
  statistic_increment(table->in_use->status_var.ha_read_rnd_count,
		      &LOCK_status);
unknown's avatar
unknown committed
1208
  int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length));
unknown's avatar
unknown committed
1209 1210 1211 1212 1213 1214 1215
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}

void ha_myisam::position(const byte* record)
{
  my_off_t position=mi_position(file);
unknown's avatar
unknown committed
1216
  my_store_ptr(ref, ref_length, position);
unknown's avatar
unknown committed
1217 1218 1219 1220 1221
}

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

unknown's avatar
unknown committed
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
  (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
1244 1245
    table->keys_in_use.set_prefix(table->keys);
    table->keys_in_use.intersect(info.key_map);
1246 1247
    table->keys_for_keyread= table->keys_in_use;
    table->keys_for_keyread.subtract(table->read_only_keys);
unknown's avatar
unknown committed
1248 1249 1250 1251
    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
1252
	     sizeof(table->key_info[0].rec_per_key)*table->key_parts);
unknown's avatar
unknown committed
1253 1254 1255
    raid_type=info.raid_type;
    raid_chunks=info.raid_chunks;
    raid_chunksize=info.raid_chunksize;
unknown's avatar
unknown committed
1256

1257 1258
   /*
     Set data_file_name and index_file_name to point at the symlink value
1259
     if table is symlinked (Ie;  Real name is not same as generated name)
1260 1261
   */
    data_file_name=index_file_name=0;
1262 1263
    fn_format(name_buff, file->filename, "", MI_NAME_DEXT, 2);
    if (strcmp(name_buff, info.data_file_name))
1264
      data_file_name=info.data_file_name;
1265 1266
    strmov(fn_ext(name_buff),MI_NAME_IEXT);
    if (strcmp(name_buff, info.index_file_name))
1267
      index_file_name=info.index_file_name;
unknown's avatar
unknown committed
1268 1269 1270 1271
  }
  if (flag & HA_STATUS_ERRKEY)
  {
    errkey  = info.errkey;
unknown's avatar
unknown committed
1272
    my_store_ptr(dupp_ref, ref_length, info.dupp_key_pos);
unknown's avatar
unknown committed
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
  }
  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
1283 1284 1285 1286 1287 1288
  if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_KEYREAD)
    return 0;
  return mi_extra(file, operation, 0);
}


unknown's avatar
unknown committed
1289
/* To be used with WRITE_CACHE and EXTRA_CACHE */
unknown's avatar
unknown committed
1290 1291 1292

int ha_myisam::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
unknown's avatar
unknown committed
1293
  if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE)
unknown's avatar
unknown committed
1294
    return 0;
unknown's avatar
unknown committed
1295
  return mi_extra(file, operation, (void*) &cache_size);
unknown's avatar
unknown committed
1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
}

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);
}

1308

unknown's avatar
unknown committed
1309 1310
int ha_myisam::external_lock(THD *thd, int lock_type)
{
unknown's avatar
unknown committed
1311 1312
  return mi_lock_database(file, !table->tmp_table ?
			  lock_type : ((lock_type == F_UNLCK) ?
1313
				       F_UNLCK : F_EXTRA_LCK));
1314
}
unknown's avatar
unknown committed
1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327

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)
{
1328
  ha_myisam::info(HA_STATUS_AUTO | HA_STATUS_CONST);
unknown's avatar
unknown committed
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338
  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;
  }
1339 1340
  create_info->data_file_name=data_file_name;
  create_info->index_file_name=index_file_name;
unknown's avatar
unknown committed
1341 1342 1343
}


1344
int ha_myisam::create(const char *name, register TABLE *table_arg,
unknown's avatar
unknown committed
1345 1346 1347 1348
		      HA_CREATE_INFO *info)
{
  int error;
  uint i,j,recpos,minpos,fieldpos,temp_length,length;
1349
  bool found_real_auto_increment=0;
unknown's avatar
unknown committed
1350 1351 1352 1353 1354
  enum ha_base_keytype type;
  char buff[FN_REFLEN];
  KEY *pos;
  MI_KEYDEF *keydef;
  MI_COLUMNDEF *recinfo,*recinfo_pos;
unknown's avatar
unknown committed
1355
  HA_KEYSEG *keyseg;
1356
  uint options=table_arg->db_options_in_use;
unknown's avatar
unknown committed
1357 1358 1359 1360
  DBUG_ENTER("ha_myisam::create");

  type=HA_KEYTYPE_BINARY;				// Keep compiler happy
  if (!(my_multi_malloc(MYF(MY_WME),
1361 1362
			&recinfo,(table_arg->fields*2+2)*sizeof(MI_COLUMNDEF),
			&keydef, table_arg->keys*sizeof(MI_KEYDEF),
unknown's avatar
unknown committed
1363
			&keyseg,
unknown's avatar
unknown committed
1364 1365
			((table_arg->key_parts + table_arg->keys) *
			 sizeof(HA_KEYSEG)),
1366
			NullS)))
unknown's avatar
unknown committed
1367
    DBUG_RETURN(HA_ERR_OUT_OF_MEM);
unknown's avatar
unknown committed
1368

1369 1370
  pos=table_arg->key_info;
  for (i=0; i < table_arg->keys ; i++, pos++)
unknown's avatar
unknown committed
1371
  {
unknown's avatar
unknown committed
1372
    keydef[i].flag= (pos->flags & (HA_NOSAME | HA_FULLTEXT | HA_SPATIAL));
unknown's avatar
SCRUM  
unknown committed
1373 1374 1375
    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
1376 1377 1378 1379 1380 1381
    keydef[i].seg=keyseg;
    keydef[i].keysegs=pos->key_parts;
    for (j=0 ; j < pos->key_parts ; j++)
    {
      Field *field=pos->key_part[j].field;
      type=field->key_type();
1382
      keydef[i].seg[j].flag=pos->key_part[j].key_part_flag;
unknown's avatar
unknown committed
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396

      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) &&
1397 1398
	      (field->type() == MYSQL_TYPE_STRING ||
	       field->type() == MYSQL_TYPE_VAR_STRING ||
unknown's avatar
unknown committed
1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409
	       ((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
1410
      keydef[i].seg[j].language = field->charset()->number;
unknown's avatar
unknown committed
1411 1412 1413 1414 1415

      if (field->null_ptr)
      {
	keydef[i].seg[j].null_bit=field->null_bit;
	keydef[i].seg[j].null_pos= (uint) (field->null_ptr-
1416
					   (uchar*) table_arg->record[0]);
unknown's avatar
unknown committed
1417 1418 1419 1420 1421 1422
      }
      else
      {
	keydef[i].seg[j].null_bit=0;
	keydef[i].seg[j].null_pos=0;
      }
unknown's avatar
unknown committed
1423 1424
      if (field->type() == FIELD_TYPE_BLOB ||
	  field->type() == FIELD_TYPE_GEOMETRY)
unknown's avatar
unknown committed
1425 1426 1427 1428
      {
	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() -
1429
					    table_arg->blob_ptr_size);
unknown's avatar
unknown committed
1430 1431 1432 1433 1434
      }
    }
    keyseg+=pos->key_parts;
  }

1435 1436 1437 1438 1439 1440
  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
1441
  recpos=0; recinfo_pos=recinfo;
1442
  while (recpos < (uint) table_arg->reclength)
unknown's avatar
unknown committed
1443 1444
  {
    Field **field,*found=0;
1445
    minpos=table_arg->reclength; length=0;
unknown's avatar
unknown committed
1446

1447
    for (field=table_arg->field ; *field ; field++)
unknown's avatar
unknown committed
1448 1449 1450 1451 1452 1453
    {
      if ((fieldpos=(*field)->offset()) >= recpos &&
	  fieldpos <= minpos)
      {
	/* skip null fields */
	if (!(temp_length= (*field)->pack_length()))
unknown's avatar
unknown committed
1454
	  continue;				/* Skip null-fields */
unknown's avatar
unknown committed
1455 1456 1457 1458 1459 1460 1461
	if (! found || fieldpos < minpos ||
	    (fieldpos == minpos && temp_length < length))
	{
	  minpos=fieldpos; found= *field; length=temp_length;
	}
      }
    }
1462
    DBUG_PRINT("loop",("found: 0x%lx  recpos: %d  minpos: %d  length: %d",
unknown's avatar
unknown committed
1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476
		       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;
    }
1477 1478
    else if (!(options & HA_OPTION_PACK_RECORD) ||
             found->type() == MYSQL_TYPE_VARCHAR)
unknown's avatar
unknown committed
1479 1480
      recinfo_pos->type= (int) FIELD_NORMAL;
    else if (found->zero_pack())
unknown's avatar
unknown committed
1481
      recinfo_pos->type= (int) FIELD_SKIP_ZERO;
unknown's avatar
unknown committed
1482 1483
    else
      recinfo_pos->type= (int) ((length <= 3 ||
1484 1485 1486 1487 1488 1489
                                 (found->flags & ZEROFILL_FLAG)) ?
                                FIELD_NORMAL :
                                found->type() == MYSQL_TYPE_STRING ||
                                found->type() == MYSQL_TYPE_VAR_STRING ?
                                FIELD_SKIP_ENDSPACE :
                                FIELD_SKIP_PRESPACE);
unknown's avatar
unknown committed
1490 1491 1492 1493
    if (found->null_ptr)
    {
      recinfo_pos->null_bit=found->null_bit;
      recinfo_pos->null_pos= (uint) (found->null_ptr-
1494
                                     (uchar*) table_arg->record[0]);
unknown's avatar
unknown committed
1495 1496 1497 1498 1499 1500
    }
    else
    {
      recinfo_pos->null_bit=0;
      recinfo_pos->null_pos=0;
    }
1501
    (recinfo_pos++)->length= (uint16) length;
unknown's avatar
unknown committed
1502 1503 1504 1505 1506 1507 1508
    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));
1509 1510
  create_info.max_rows=table_arg->max_rows;
  create_info.reloc_rows=table_arg->min_rows;
1511
  create_info.with_auto_increment=found_real_auto_increment;
unknown's avatar
unknown committed
1512 1513 1514
  create_info.auto_increment=(info->auto_increment_value ?
			      info->auto_increment_value -1 :
			      (ulonglong) 0);
1515 1516
  create_info.data_file_length= ((ulonglong) table_arg->max_rows *
				 table_arg->avg_row_length);
unknown's avatar
unknown committed
1517
  create_info.raid_type=info->raid_type;
1518 1519 1520 1521
  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);
1522 1523
  create_info.data_file_name= info->data_file_name;
  create_info.index_file_name=info->index_file_name;
unknown's avatar
unknown committed
1524

1525
  /* TODO: Check that the following fn_format is really needed */
1526
  error=mi_create(fn_format(buff,name,"","",2+4),
1527
		  table_arg->keys,keydef,
unknown's avatar
unknown committed
1528 1529 1530 1531 1532 1533 1534
		  (uint) (recinfo_pos-recinfo), recinfo,
		  0, (MI_UNIQUEDEF*) 0,
		  &create_info,
		  (((options & HA_OPTION_PACK_RECORD) ? HA_PACK_RECORD : 0) |
		   ((options & HA_OPTION_CHECKSUM) ? HA_CREATE_CHECKSUM : 0) |
		   ((options & HA_OPTION_DELAY_KEY_WRITE) ?
		    HA_CREATE_DELAY_KEY_WRITE : 0)));
1535 1536


unknown's avatar
unknown committed
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);
}


1548
ulonglong ha_myisam::get_auto_increment()
unknown's avatar
unknown committed
1549
{
1550 1551 1552 1553
  ulonglong nr;
  int error;
  byte key[MI_MAX_KEY_LENGTH];

unknown's avatar
unknown committed
1554 1555 1556 1557 1558 1559
  if (!table->next_number_key_offset)
  {						// Autoincrement at key-start
    ha_myisam::info(HA_STATUS_AUTO);
    return auto_increment_value;
  }

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

unknown's avatar
unknown committed
1563
  (void) extra(HA_EXTRA_KEYREAD);
1564 1565 1566
  key_copy(key, table->record[0],
           table->key_info + table->next_number_index,
           table->next_number_key_offset);
unknown's avatar
unknown committed
1567 1568 1569
  error=mi_rkey(file,table->record[1],(int) table->next_number_index,
		key,table->next_number_key_offset,HA_READ_PREFIX_LAST);
  if (error)
1570
    nr= 1;
unknown's avatar
unknown committed
1571
  else
1572 1573
    nr= ((ulonglong) table->next_number_field->
         val_int_offset(table->rec_buff_length)+1);
unknown's avatar
unknown committed
1574 1575 1576 1577 1578
  extra(HA_EXTRA_NO_KEYREAD);
  return nr;
}


unknown's avatar
unknown committed
1579 1580 1581 1582 1583 1584
/*
  Find out how many rows there is in the given range

  SYNOPSIS
    records_in_range()
    inx			Index to use
unknown's avatar
unknown committed
1585 1586
    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
1587 1588

  NOTES
unknown's avatar
unknown committed
1589
    min_key.flag can have one of the following values:
unknown's avatar
unknown committed
1590 1591 1592
      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
1593
    max_key.flag can have one of the following values:  
unknown's avatar
unknown committed
1594 1595 1596 1597 1598 1599 1600 1601 1602 1603
      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
1604 1605
ha_rows ha_myisam::records_in_range(uint inx, key_range *min_key,
                                    key_range *max_key)
unknown's avatar
unknown committed
1606
{
unknown's avatar
unknown committed
1607
  return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key);
unknown's avatar
unknown committed
1608 1609
}

unknown's avatar
unknown committed
1610

unknown's avatar
unknown committed
1611 1612 1613 1614
int ha_myisam::ft_read(byte * buf)
{
  int error;

1615 1616 1617
  if (!ft_handler)
    return -1;

1618
  thread_safe_increment(table->in_use->status_var.ha_read_next_count,
1619
			&LOCK_status); // why ?
unknown's avatar
unknown committed
1620

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

unknown's avatar
unknown committed
1623 1624 1625
  table->status=error ? STATUS_NOT_FOUND: 0;
  return error;
}
unknown's avatar
unknown committed
1626 1627 1628 1629 1630 1631

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