handler.cc 24.6 KB
Newer Older
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2

bk@work.mysql.com's avatar
bk@work.mysql.com 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.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7

bk@work.mysql.com's avatar
bk@work.mysql.com 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.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
12

bk@work.mysql.com's avatar
bk@work.mysql.com committed
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   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 */


/* Handler-calling-functions */

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

#include "mysql_priv.h"
#include "ha_heap.h"
#include "ha_myisam.h"
#include "ha_myisammrg.h"
28
#ifdef HAVE_ISAM
bk@work.mysql.com's avatar
bk@work.mysql.com committed
29 30 31 32 33 34
#include "ha_isam.h"
#include "ha_isammrg.h"
#endif
#ifdef HAVE_BERKELEY_DB
#include "ha_berkeley.h"
#endif
35
#ifdef HAVE_INNOBASE_DB
36
#include "ha_innodb.h"
37 38
#else
#define innobase_query_caching_of_table_permitted(X,Y,Z) 1
39
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
40 41 42 43 44 45 46 47 48 49
#include <myisampack.h>
#include <errno.h>

	/* static functions defined in this file */

static int NEAR_F delete_file(const char *name,const char *ext,int extflag);

ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count,
      ha_read_key_count, ha_read_next_count, ha_read_prev_count,
      ha_read_first_count, ha_read_last_count,
50
      ha_commit_count, ha_rollback_count,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
51 52 53 54
      ha_read_rnd_count, ha_read_rnd_next_count;

const char *ha_table_type[] = {
  "", "DIAB_ISAM","HASH","MISAM","PISAM","RMS_ISAM","HEAP", "ISAM",
55
  "MRG_ISAM","MYISAM", "MRG_MYISAM", "BDB", "INNODB", "GEMINI", "?", "?",NullS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
56 57
};

58 59 60 61
TYPELIB ha_table_typelib=
{
  array_elements(ha_table_type)-3, "", ha_table_type
};
62

bk@work.mysql.com's avatar
bk@work.mysql.com committed
63 64 65 66
const char *ha_row_type[] = {
  "", "FIXED", "DYNAMIC", "COMPRESSED","?","?","?"
};

monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
67
const char *tx_isolation_names[] =
68 69 70
{ "READ-UNCOMMITTED", "READ-COMMITTED", "REPEATABLE-READ", "SERIALIZABLE",
  NullS};
TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"",
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
71
			       tx_isolation_names};
bk@work.mysql.com's avatar
bk@work.mysql.com committed
72 73 74 75 76 77 78 79 80 81

	/* Use other database handler if databasehandler is not incompiled */

enum db_type ha_checktype(enum db_type database_type)
{
  switch (database_type) {
#ifdef HAVE_BERKELEY_DB
  case DB_TYPE_BERKELEY_DB:
    return(berkeley_skip ? DB_TYPE_MYISAM : database_type);
#endif
82
#ifdef HAVE_INNOBASE_DB
83
  case DB_TYPE_INNODB:
84
    return(innodb_skip ? DB_TYPE_MYISAM : database_type);
85
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
86 87 88
#ifndef NO_HASH
  case DB_TYPE_HASH:
#endif
89
#ifdef HAVE_ISAM
bk@work.mysql.com's avatar
bk@work.mysql.com committed
90
  case DB_TYPE_ISAM:
91
    return (isam_skip ? DB_TYPE_MYISAM : database_type);
92
  case DB_TYPE_MRG_ISAM:
93 94 95 96
    return (isam_skip ? DB_TYPE_MRG_MYISAM : database_type);
#else
  case DB_TYPE_MRG_ISAM:
    return (DB_TYPE_MRG_MYISAM);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
#endif
  case DB_TYPE_HEAP:
  case DB_TYPE_MYISAM:
  case DB_TYPE_MRG_MYISAM:
    return (database_type);			/* Database exists on system */
  default:
    break;
  }
  return(DB_TYPE_MYISAM);			/* Use this as default */
} /* ha_checktype */


handler *get_new_handler(TABLE *table, enum db_type db_type)
{
  switch (db_type) {
#ifndef NO_HASH
  return new ha_hash(table);
#endif
115
#ifdef HAVE_ISAM
bk@work.mysql.com's avatar
bk@work.mysql.com committed
116 117 118 119
  case DB_TYPE_MRG_ISAM:
    return new ha_isammrg(table);
  case DB_TYPE_ISAM:
    return new ha_isam(table);
120 121 122
#else
  case DB_TYPE_MRG_ISAM:
    return new ha_myisammrg(table);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
123 124 125 126
#endif
#ifdef HAVE_BERKELEY_DB
  case DB_TYPE_BERKELEY_DB:
    return new ha_berkeley(table);
127 128
#endif
#ifdef HAVE_INNOBASE_DB
129
  case DB_TYPE_INNODB:
130
    return new ha_innobase(table);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
131 132 133 134
#endif
  case DB_TYPE_HEAP:
    return new ha_heap(table);
  default:					// should never happen
135 136 137 138 139 140 141 142
  {
    enum db_type def=(enum db_type) current_thd->variables.table_type;
    /* Try first with 'default table type' */
    if (db_type != def)
      return get_new_handler(table, def);
  }
  /* Fall back to MyISAM */
  case DB_TYPE_MYISAM:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
143 144 145 146 147 148 149 150 151 152 153 154 155 156
    return new ha_myisam(table);
  case DB_TYPE_MRG_MYISAM:
    return new ha_myisammrg(table);
  }
}

int ha_init()
{
#ifdef HAVE_BERKELEY_DB
  if (!berkeley_skip)
  {
    int error;
    if ((error=berkeley_init()))
      return error;
157 158
    if (!berkeley_skip)				// If we couldn't use handler
      opt_using_transactions=1;
159 160
    else
      have_berkeley_db=SHOW_OPTION_DISABLED;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
161
  }
162 163
#endif
#ifdef HAVE_INNOBASE_DB
164
  if (!innodb_skip)
165
  {
monty@donna.mysql.fi's avatar
monty@donna.mysql.fi committed
166 167
    if (innobase_init())
      return -1;
168
    if (!innodb_skip)				// If we couldn't use handler
169
      opt_using_transactions=1;
170
    else
171
      have_innodb=SHOW_OPTION_DISABLED;
172
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
173 174 175 176 177 178 179 180 181 182 183 184 185
#endif
  return 0;
}

	/* close, flush or restart databases */
	/* Ignore this for other databases than ours */

int ha_panic(enum ha_panic_function flag)
{
  int error=0;
#ifndef NO_HASH
  error|=h_panic(flag);			/* fix hash */
#endif
186 187
#ifdef HAVE_ISAM
  error|=mrg_panic(flag);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
188
  error|=nisam_panic(flag);
189 190
#endif
  error|=heap_panic(flag);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
191 192 193 194 195
  error|=mi_panic(flag);
  error|=myrg_panic(flag);
#ifdef HAVE_BERKELEY_DB
  if (!berkeley_skip)
    error|=berkeley_end();
196 197
#endif
#ifdef HAVE_INNOBASE_DB
198
  if (!innodb_skip)
199
    error|=innobase_end();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
200 201 202 203
#endif
  return error;
} /* ha_panic */

204 205 206 207 208 209 210
void ha_drop_database(char* path)
{
#ifdef HAVE_INNOBASE_DB
  if (!innodb_skip)
    innobase_drop_database(path);
#endif
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
211

212 213 214
void ha_close_connection(THD* thd)
{
#ifdef HAVE_INNOBASE_DB
215
  if (!innodb_skip)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
216
    innobase_close_connection(thd);
217 218 219
#endif
}

220
/*
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
221
  This is used to commit or rollback a single statement depending on the value
222 223 224 225 226
  of error. Note that if the autocommit is on, then the following call inside
  InnoDB will commit or rollback the whole transaction (= the statement). The
  autocommit mechanism built into InnoDB is based on counting locks, but if
  the user has used LOCK TABLES then that mechanism does not know to do the
  commit.
227 228
*/

bk@work.mysql.com's avatar
bk@work.mysql.com committed
229 230 231
int ha_autocommit_or_rollback(THD *thd, int error)
{
  DBUG_ENTER("ha_autocommit_or_rollback");
232
#ifdef USING_TRANSACTIONS
233
  if (opt_using_transactions)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
234
  {
235 236 237 238 239 240 241
    if (!error)
    {
      if (ha_commit_stmt(thd))
	error=1;
    }
    else
      (void) ha_rollback_stmt(thd);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
242

243
    thd->variables.tx_isolation=thd->session_tx_isolation;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
244 245 246 247 248
  }
#endif
  DBUG_RETURN(error);
}

249 250 251
/*
  This function is called when MySQL writes the log segment of a
  transaction to the binlog. It is called when the LOCK_log mutex is
252
  reserved. Here we communicate to transactional table handlers what
253 254 255 256 257
  binlog position corresponds to the current transaction. The handler
  can store it and in recovery print to the user, so that the user
  knows from what position in the binlog to start possible
  roll-forward, for example, if the crashed server was a slave in
  replication. This function also calls the commit of the table
258
  handler, because the order of transactions in the log of the table
259
  handler must be the same as in the binlog.
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
260 261 262
  NOTE that to eliminate the bottleneck of the group commit, we do not
  flush the handler log files here, but only later in a call of
  ha_commit_complete().
263 264

  arguments:
265
  thd:           the thread handle of the current connection
266 267
  log_file_name: latest binlog file name
  end_offset:	 the offset in the binlog file up to which we wrote
268
  return value:  0 if success, 1 if error
269
*/
270

271 272 273
int ha_report_binlog_offset_and_commit(THD *thd,
				       char *log_file_name,
				       my_off_t end_offset)
274
{
275 276
  int  error= 0;
#ifdef HAVE_INNOBASE_DB
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
  THD_TRANS *trans;
  trans = &thd->transaction.all;
  if (trans->innobase_tid)
  {
    if ((error=innobase_report_binlog_offset_and_commit(thd,
							trans->innobase_tid,
							log_file_name,
							end_offset)))
    {
      my_error(ER_ERROR_DURING_COMMIT, MYF(0), error);
      error=1;
    }
  }
#endif
  return error;
}
293

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
/*
  Flushes the handler log files (if my.cnf settings do not free us from it)
  after we have called ha_report_binlog_offset_and_commit(). To eliminate
  the bottleneck from the group commit, this should be called when
  LOCK_log has been released in log.cc.

  arguments:
  thd:           the thread handle of the current connection
  return value:  always 0
*/

int ha_commit_complete(THD *thd)
{
#ifdef HAVE_INNOBASE_DB
  THD_TRANS *trans;
  trans = &thd->transaction.all;
  if (trans->innobase_tid)
  {
    innobase_commit_complete(trans->innobase_tid);

    trans->innodb_active_trans=0;
  }
#endif
  return 0;
}
319

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
/*
  This function should be called when MySQL sends rows of a SELECT result set
  or the EOF mark to the client. It releases a possible adaptive hash index
  S-latch held by thd in InnoDB and also releases a possible InnoDB query
  FIFO ticket to enter InnoDB. To save CPU time, InnoDB allows a thd to
  keep them over several calls of the InnoDB handler interface when a join
  is executed. But when we let the control to pass to the client they have
  to be released because if the application program uses mysql_use_result(),
  it may deadlock on the S-latch if the application on another connection
  performs another SQL query. In MySQL-4.1 this is even more important because
  there a connection can have several SELECT queries open at the same time.

  arguments:
  thd:           the thread handle of the current connection
  return value:  always 0
*/

int ha_release_temporary_latches(THD *thd)
{
#ifdef HAVE_INNOBASE_DB
  THD_TRANS *trans;
  trans = &thd->transaction.all;
  if (trans->innobase_tid)
    innobase_release_temporary_latches(trans->innobase_tid);
#endif
  return 0;
}

348
int ha_commit_trans(THD *thd, THD_TRANS* trans)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
349 350
{
  int error=0;
351
  DBUG_ENTER("ha_commit");
352
#ifdef USING_TRANSACTIONS
353
  if (opt_using_transactions)
354
  {
355 356
    bool operation_done= 0;
    bool transaction_commited= 0;
357

358 359 360 361
    /* Update the binary log if we have cached some queries */
    if (trans == &thd->transaction.all && mysql_bin_log.is_open() &&
	my_b_tell(&thd->transaction.trans_log))
    {
362
      mysql_bin_log.write(thd, &thd->transaction.trans_log);
363 364 365 366
      reinit_io_cache(&thd->transaction.trans_log,
		      WRITE_CACHE, (my_off_t) 0, 0, 1);
      thd->transaction.trans_log.end_of_file= max_binlog_cache_size;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
367
#ifdef HAVE_BERKELEY_DB
368
    if (trans->bdb_tid)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
369
    {
370 371 372 373 374
      if ((error=berkeley_commit(thd,trans->bdb_tid)))
      {
	my_error(ER_ERROR_DURING_COMMIT, MYF(0), error);
	error=1;
      }
375
      else
376 377
	if (!(thd->options & OPTION_BEGIN))
	  transaction_commited= 1; 
378
      trans->bdb_tid=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
379
    }
380 381
#endif
#ifdef HAVE_INNOBASE_DB
382
    if (trans->innobase_tid)
383
    {
384 385 386 387 388
      if ((error=innobase_commit(thd,trans->innobase_tid)))
      {
	my_error(ER_ERROR_DURING_COMMIT, MYF(0), error);
	error=1;
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
389
      trans->innodb_active_trans=0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
390
      if (trans == &thd->transaction.all)
391 392
	operation_done= transaction_commited= 1;
	
393
    }
394
#endif
395
#ifdef HAVE_QUERY_CACHE
396
    if (transaction_commited && thd->transaction.changed_tables)
397
      query_cache.invalidate(thd->transaction.changed_tables);
398
#endif /*HAVE_QUERY_CACHE*/
399 400
    if (error && trans == &thd->transaction.all && mysql_bin_log.is_open())
      sql_print_error("Error: Got error during commit;  Binlog is not up to date!");
401
    thd->variables.tx_isolation=thd->session_tx_isolation;
402
    if (operation_done)
403
    {
404
      statistic_increment(ha_commit_count,&LOCK_status);
405 406
      thd->transaction.cleanup();
    }
407
  }
408
#endif // using transactions
bk@work.mysql.com's avatar
bk@work.mysql.com committed
409 410 411
  DBUG_RETURN(error);
}

412

413
int ha_rollback_trans(THD *thd, THD_TRANS *trans)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
414 415
{
  int error=0;
416
  DBUG_ENTER("ha_rollback");
417 418
#ifdef USING_TRANSACTIONS
  if (opt_using_transactions)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
419
  {
420
    bool operation_done=0;
421 422
#ifdef HAVE_BERKELEY_DB
    if (trans->bdb_tid)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
423
    {
424 425 426 427 428 429
      if ((error=berkeley_rollback(thd, trans->bdb_tid)))
      {
	my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), error);
	error=1;
      }
      trans->bdb_tid=0;
430
      operation_done=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
431
    }
432 433
#endif
#ifdef HAVE_INNOBASE_DB
434
    if (trans->innobase_tid)
435
    {
436 437 438 439 440
      if ((error=innobase_rollback(thd, trans->innobase_tid)))
      {
	my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), error);
	error=1;
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
441
      trans->innodb_active_trans=0;
442
      operation_done=1;
443
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
444
#endif
445 446 447 448
    if (trans == &thd->transaction.all)
      reinit_io_cache(&thd->transaction.trans_log,
		      WRITE_CACHE, (my_off_t) 0, 0, 1);
    thd->transaction.trans_log.end_of_file= max_binlog_cache_size;
449
    thd->variables.tx_isolation=thd->session_tx_isolation;
450
    if (operation_done)
451
    {
452
      statistic_increment(ha_rollback_count,&LOCK_status);
453 454
      thd->transaction.cleanup();
    }
455 456
  }
#endif /* USING_TRANSACTIONS */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
457 458 459 460 461 462 463 464 465
  DBUG_RETURN(error);
}

bool ha_flush_logs()
{
  bool result=0;
#ifdef HAVE_BERKELEY_DB
  if (!berkeley_skip && berkeley_flush_logs())
    result=1;
466 467
#endif
#ifdef HAVE_INNOBASE_DB
468
  if (!innodb_skip && innobase_flush_logs())
469
    result=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
470 471 472 473
#endif
  return result;
}

474 475 476 477
/*
  This should return ENOENT if the file doesn't exists.
  The .frm file will be deleted only if we return 0 or ENOENT
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
478 479 480 481 482

int ha_delete_table(enum db_type table_type, const char *path)
{
  handler *file=get_new_handler((TABLE*) 0, table_type);
  if (!file)
483
    return ENOENT;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
484 485 486 487
  int error=file->delete_table(path);
  delete file;
  return error;
}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
488

bk@work.mysql.com's avatar
bk@work.mysql.com committed
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
void ha_store_ptr(byte *buff, uint pack_length, my_off_t pos)
{
  switch (pack_length) {
#if SIZEOF_OFF_T > 4
  case 8: mi_int8store(buff,pos); break;
  case 7: mi_int7store(buff,pos); break;
  case 6: mi_int6store(buff,pos); break;
  case 5: mi_int5store(buff,pos); break;
#endif
  case 4: mi_int4store(buff,pos); break;
  case 3: mi_int3store(buff,pos); break;
  case 2: mi_int2store(buff,(uint) pos); break;
  case 1: buff[0]= (uchar) pos; break;
  }
  return;
}

my_off_t ha_get_ptr(byte *ptr, uint pack_length)
{
  my_off_t pos;
  switch (pack_length) {
#if SIZEOF_OFF_T > 4
  case 8:
    pos= (my_off_t) mi_uint8korr(ptr);
    break;
  case 7:
    pos= (my_off_t) mi_uint7korr(ptr);
    break;
  case 6:
    pos= (my_off_t) mi_uint6korr(ptr);
    break;
  case 5:
    pos= (my_off_t) mi_uint5korr(ptr);
    break;
#endif
  case 4:
    pos= (my_off_t) mi_uint4korr(ptr);
    break;
  case 3:
    pos= (my_off_t) mi_uint3korr(ptr);
    break;
  case 2:
    pos= (my_off_t) mi_uint2korr(ptr);
    break;
  case 1:
    pos= (my_off_t) mi_uint2korr(ptr);
    break;
  default:
    pos=0;					// Impossible
    break;
  }
 return pos;
}

/****************************************************************************
** General handler functions
****************************************************************************/

	/* Open database-handler. Try O_RDONLY if can't open as O_RDWR */
	/* Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set */

int handler::ha_open(const char *name, int mode, int test_if_locked)
{
  int error;
  DBUG_ENTER("handler::open");
554
  DBUG_PRINT("enter",("name: %s  db_type: %d  db_stat: %d  mode: %d  lock_test: %d",
555 556
		      name, table->db_type, table->db_stat, mode,
		      test_if_locked));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575

  if ((error=open(name,mode,test_if_locked)))
  {
    if ((error == EACCES || error == EROFS) && mode == O_RDWR &&
	(table->db_stat & HA_TRY_READ_ONLY))
    {
      table->db_stat|=HA_READ_ONLY;
      error=open(name,O_RDONLY,test_if_locked);
    }
  }
  if (error)
  {
    my_errno=error;			/* Safeguard */
    DBUG_PRINT("error",("error: %d  errno: %d",error,errno));
  }
  else
  {
    if (table->db_options_in_use & HA_OPTION_READ_ONLY_DATA)
      table->db_stat|=HA_READ_ONLY;
576 577
    (void) extra(HA_EXTRA_NO_READCHECK);	// Not needed in SQL

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
578
    if (!alloc_root_inited(&table->mem_root))	// If temporary table
579
      ref=(byte*) sql_alloc(ALIGN_SIZE(ref_length)*2);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
580 581 582
    else
      ref=(byte*) alloc_root(&table->mem_root, ALIGN_SIZE(ref_length)*2);
    if (!ref)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
583 584 585 586 587 588 589 590 591 592 593 594
    {
      close();
      error=HA_ERR_OUT_OF_MEM;
    }
    else
      dupp_ref=ref+ALIGN_SIZE(ref_length);
  }
  DBUG_RETURN(error);
}

int handler::check(THD* thd, HA_CHECK_OPT* check_opt)
{
595
  return HA_ADMIN_NOT_IMPLEMENTED;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
596 597
}

598 599 600 601 602 603 604 605 606 607
int handler::backup(THD* thd, HA_CHECK_OPT* check_opt)
{
  return HA_ADMIN_NOT_IMPLEMENTED;
}

int handler::restore(THD* thd, HA_CHECK_OPT* check_opt)
{
  return HA_ADMIN_NOT_IMPLEMENTED;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
608 609
int handler::repair(THD* thd, HA_CHECK_OPT* check_opt)
{
610
  return HA_ADMIN_NOT_IMPLEMENTED;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
611 612
}

613
int handler::optimize(THD* thd, HA_CHECK_OPT* check_opt)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
614
{
615
  return HA_ADMIN_NOT_IMPLEMENTED;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
616 617
}

618
int handler::analyze(THD* thd, HA_CHECK_OPT* check_opt)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
619
{
620
  return HA_ADMIN_NOT_IMPLEMENTED;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
621 622
}

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
623 624 625 626 627
int handler::preload_keys(THD* thd, HA_CHECK_OPT* check_opt)
{
  return HA_ADMIN_NOT_IMPLEMENTED;
}

628 629 630 631 632
/*
  Read first row (only) from a table
  This is never called for InnoDB or BDB tables, as these table types
  has the HA_NOT_EXACT_COUNT set.
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
633

634
int handler::read_first_row(byte * buf, uint primary_key)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
635 636
{
  register int error;
637
  DBUG_ENTER("handler::read_first_row");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
638 639

  statistic_increment(ha_read_first_count,&LOCK_status);
640 641 642 643 644

  /*
    If there is very few deleted rows in the table, find the first row by
    scanning the table.
  */
645
  if (deleted < 10 || primary_key >= MAX_KEY || 
646
      !(index_flags(primary_key) & HA_READ_ORDER))
647 648 649 650 651 652 653 654 655 656 657 658
  {
    (void) rnd_init();
    while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ;
    (void) rnd_end();
  }
  else
  {
    /* Find the first row through the primary key */
    (void) index_init(primary_key);
    error=index_first(buf);
    (void) index_end();
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
  DBUG_RETURN(error);
}


/*
  The following function is only needed for tables that may be temporary tables
  during joins
*/

int handler::restart_rnd_next(byte *buf, byte *pos)
{
  return HA_ERR_WRONG_COMMAND;
}


674
/* Set a timestamp in record */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689

void handler::update_timestamp(byte *record)
{
  long skr= (long) current_thd->query_start();
#ifdef WORDS_BIGENDIAN
  if (table->db_low_byte_first)
  {
    int4store(record,skr);
  }
  else
#endif
  longstore(record,skr);
  return;
}

690 691 692 693
/*
  Updates field with field_type NEXT_NUMBER according to following:
  if field = 0 change field to the next free key in database.
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
694 695 696 697 698 699 700

void handler::update_auto_increment()
{
  longlong nr;
  THD *thd;
  DBUG_ENTER("update_auto_increment");
  if (table->next_number_field->val_int() != 0)
701 702
  {
    auto_increment_column_changed=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
703
    DBUG_VOID_RETURN;
704
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
705 706 707 708 709
  thd=current_thd;
  if ((nr=thd->next_insert_id))
    thd->next_insert_id=0;			// Clear after use
  else
    nr=get_auto_increment();
710 711
  if (!table->next_number_field->store(nr))
    thd->insert_id((ulonglong) nr);
712 713
  else
    thd->insert_id(table->next_number_field->val_int());
714
  auto_increment_column_changed=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
715 716 717 718 719 720 721 722
  DBUG_VOID_RETURN;
}


longlong handler::get_auto_increment()
{
  longlong nr;
  int error;
723

bk@work.mysql.com's avatar
bk@work.mysql.com committed
724 725
  (void) extra(HA_EXTRA_KEYREAD);
  index_init(table->next_number_index);
726 727 728 729 730 731 732 733 734 735 736 737 738
  if (!table->next_number_key_offset)
  {						// Autoincrement at key-start
    error=index_last(table->record[1]);
  }
  else
  {
    byte key[MAX_KEY_LENGTH];
    key_copy(key,table,table->next_number_index,
             table->next_number_key_offset);
    error=index_read(table->record[1], key, table->next_number_key_offset,
                     HA_READ_PREFIX_LAST);
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
739 740 741 742 743 744
  if (error)
    nr=1;
  else
    nr=(longlong) table->next_number_field->
      val_int_offset(table->rec_buff_length)+1;
  index_end();
745
  (void) extra(HA_EXTRA_NO_KEYREAD);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
  return nr;
}

	/* Print error that we got from handler function */

void handler::print_error(int error, myf errflag)
{
  DBUG_ENTER("print_error");
  DBUG_PRINT("enter",("error: %d",error));

  int textno=ER_GET_ERRNO;
  switch (error) {
  case EAGAIN:
    textno=ER_FILE_USED;
    break;
  case ENOENT:
    textno=ER_FILE_NOT_FOUND;
    break;
  case HA_ERR_KEY_NOT_FOUND:
  case HA_ERR_NO_ACTIVE_RECORD:
  case HA_ERR_END_OF_FILE:
    textno=ER_KEY_NOT_FOUND;
    break;
769
  case HA_ERR_WRONG_MRG_TABLE_DEF:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
770 771 772 773 774 775 776 777 778
    textno=ER_WRONG_MRG_TABLE;
    break;
  case HA_ERR_FOUND_DUPP_KEY:
  {
    uint key_nr=get_dup_key(error);
    if ((int) key_nr >= 0)
    {
      /* Write the dupplicated key in the error message */
      char key[MAX_KEY_LENGTH];
779
      String str(key,sizeof(key),system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
780
      key_unpack(&str,table,(uint) key_nr);
781
      uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(ER(ER_DUP_ENTRY));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
      if (str.length() >= max_length)
      {
	str.length(max_length-4);
	str.append("...");
      }
      my_error(ER_DUP_ENTRY,MYF(0),str.c_ptr(),key_nr+1);
      DBUG_VOID_RETURN;
    }
    textno=ER_DUP_KEY;
    break;
  }
  case HA_ERR_FOUND_DUPP_UNIQUE:
    textno=ER_DUP_UNIQUE;
    break;
  case HA_ERR_RECORD_CHANGED:
    textno=ER_CHECKREAD;
    break;
  case HA_ERR_CRASHED:
    textno=ER_NOT_KEYFILE;
    break;
802 803 804 805 806 807
  case HA_ERR_CRASHED_ON_USAGE:
    textno=ER_CRASHED_ON_USAGE;
    break;
  case HA_ERR_CRASHED_ON_REPAIR:
    textno=ER_CRASHED_ON_REPAIR;
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
  case HA_ERR_OUT_OF_MEM:
    my_error(ER_OUT_OF_RESOURCES,errflag);
    DBUG_VOID_RETURN;
  case HA_ERR_WRONG_COMMAND:
    textno=ER_ILLEGAL_HA;
    break;
  case HA_ERR_OLD_FILE:
    textno=ER_OLD_KEYFILE;
    break;
  case HA_ERR_UNSUPPORTED:
    textno=ER_UNSUPPORTED_EXTENSION;
    break;
  case HA_ERR_RECORD_FILE_FULL:
    textno=ER_RECORD_FILE_FULL;
    break;
823 824 825 826 827 828
  case HA_ERR_LOCK_WAIT_TIMEOUT:
    textno=ER_LOCK_WAIT_TIMEOUT;
    break;
  case HA_ERR_LOCK_TABLE_FULL:
    textno=ER_LOCK_TABLE_FULL;
    break;
829 830 831
  case HA_ERR_LOCK_DEADLOCK:
    textno=ER_LOCK_DEADLOCK;
    break;
832 833 834
  case HA_ERR_READ_ONLY_TRANSACTION:
    textno=ER_READ_ONLY_TRANSACTION;
    break;
monty@donna.mysql.fi's avatar
Merge  
monty@donna.mysql.fi committed
835 836 837 838 839 840 841 842 843
  case HA_ERR_CANNOT_ADD_FOREIGN:
    textno=ER_CANNOT_ADD_FOREIGN;
    break;
  case HA_ERR_ROW_IS_REFERENCED:
    textno=ER_ROW_IS_REFERENCED;
    break;
  case HA_ERR_NO_REFERENCED_ROW:
    textno=ER_NO_REFERENCED_ROW;
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
844 845 846 847 848 849 850 851 852 853
  default:
    {
      my_error(ER_GET_ERRNO,errflag,error);
      DBUG_VOID_RETURN;
    }
  }
  my_error(textno,errflag,table->table_name,error);
  DBUG_VOID_RETURN;
}

854 855

/* Return key if error because of duplicated keys */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
856 857 858

uint handler::get_dup_key(int error)
{
859
  DBUG_ENTER("get_dup_key");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
860 861 862 863 864 865
  table->file->errkey  = (uint) -1;
  if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE)
    info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK);
  DBUG_RETURN(table->file->errkey);
}

866

bk@work.mysql.com's avatar
bk@work.mysql.com committed
867 868
int handler::delete_table(const char *name)
{
869
  int error=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
870 871 872
  for (const char **ext=bas_ext(); *ext ; ext++)
  {
    if (delete_file(name,*ext,2))
873 874 875 876
    {
      if ((error=errno) != ENOENT)
	break;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
877
  }
878
  return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
879 880 881 882 883 884 885 886 887 888 889 890 891 892
}


int handler::rename_table(const char * from, const char * to)
{
  DBUG_ENTER("handler::rename_table");
  for (const char **ext=bas_ext(); *ext ; ext++)
  {
    if (rename_file_ext(from,to,*ext))
      DBUG_RETURN(my_errno);
  }
  DBUG_RETURN(0);
}

893 894
/*
  Tell the handler to turn on or off logging to the handler's recovery log
895
*/
896

897 898 899 900 901 902 903 904
int ha_recovery_logging(THD *thd, bool on)
{
  int error=0;

  DBUG_ENTER("ha_recovery_logging");
  DBUG_RETURN(error);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
int handler::index_next_same(byte *buf, const byte *key, uint keylen)
{
  int error;
  if (!(error=index_next(buf)))
  {
    if (key_cmp(table, key, active_index, keylen))
    {
      table->status=STATUS_NOT_FOUND;
      error=HA_ERR_END_OF_FILE;
    }
  }
  return error;
}


/*
921 922 923 924
  This is called to delete all rows in a table
  If the handler don't support this, then this function will
  return HA_ERR_WRONG_COMMAND and MySQL will delete the rows one
  by one.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
925 926 927 928 929 930 931
*/

int handler::delete_all_rows()
{
  return (my_errno=HA_ERR_WRONG_COMMAND);
}

932
bool handler::caching_allowed(THD* thd, char* table_key,
933 934 935 936 937 938 939 940 941
			      uint key_length, uint8 cache_type)
{
  if (cache_type == HA_CACHE_TBL_ASKTRANSACT)
    return innobase_query_caching_of_table_permitted(thd, table_key,
						     key_length);
  else
    return 1;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
942 943 944 945 946 947 948 949 950 951 952 953 954 955
/****************************************************************************
** Some general functions that isn't in the handler class
****************************************************************************/

	/* Initiates table-file and calls apropriate database-creator */
	/* Returns 1 if something got wrong */

int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
		    bool update_create_info)
{
  int error;
  TABLE table;
  DBUG_ENTER("ha_create_table");

956
  if (openfrm(name,"",0,(uint) READ_ALL, 0, &table))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
957 958 959 960
    DBUG_RETURN(1);
  if (update_create_info)
  {
    update_create_info_from_table(create_info, &table);
961
    if (table.file->table_flags() & HA_DROP_BEFORE_CREATE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
962 963 964 965
      table.file->delete_table(name);		// Needed for BDB tables
  }
  error=table.file->create(name,&table,create_info);
  VOID(closefrm(&table));
966 967 968 969
  if (error)
  {
    if (table.db_type == DB_TYPE_INNODB)
    {
970 971 972 973
      /* Creation of InnoDB table cannot fail because of an OS error:
	 put error as the number */
      my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,error);
    }
974 975
    else
      my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,my_errno);
976
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
977 978 979 980 981 982 983 984
  DBUG_RETURN(error != 0);
}

	/* Use key cacheing on all databases */

void ha_key_cache(void)
{
  if (keybuff_size)
985
    (void) init_key_cache((ulong) keybuff_size);
986 987 988 989 990
}


void ha_resize_key_cache(void)
{
991
  (void) resize_key_cache((ulong) keybuff_size);
992
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
993 994 995 996 997 998


static int NEAR_F delete_file(const char *name,const char *ext,int extflag)
{
  char buff[FN_REFLEN];
  VOID(fn_format(buff,name,"",ext,extflag | 4));
999
  return(my_delete_with_symlink(buff,MYF(MY_WME)));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1000
}
1001 1002 1003 1004 1005 1006

void st_ha_check_opt::init()
{
  flags= sql_flags= 0;
  sort_buffer_size = current_thd->variables.myisam_sort_buff_size;
}