summaryrefslogtreecommitdiff
path: root/sql/ha_innodb.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/ha_innodb.cc')
-rw-r--r--sql/ha_innodb.cc7325
1 files changed, 0 insertions, 7325 deletions
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
deleted file mode 100644
index d854c362df8..00000000000
--- a/sql/ha_innodb.cc
+++ /dev/null
@@ -1,7325 +0,0 @@
-/* Copyright (C) 2000-2005 MySQL AB & Innobase Oy
-
- 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; version 2 of the License.
-
- 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.
-
- 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 */
-
-/* This file defines the InnoDB handler: the interface between MySQL and InnoDB
-NOTE: You can only use noninlined InnoDB functions in this file, because we
-have disables the InnoDB inlining in this file. */
-
-/* TODO list for the InnoDB handler in 5.0:
- - Remove the flag trx->active_trans and look at the InnoDB
- trx struct state field
- - fix savepoint functions to use savepoint storage area
- - Find out what kind of problems the OS X case-insensitivity causes to
- table and database names; should we 'normalize' the names like we do
- in Windows?
-*/
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-#include "slave.h"
-
-#ifdef HAVE_INNOBASE_DB
-#include <m_ctype.h>
-#include <hash.h>
-#include <myisampack.h>
-#include <mysys_err.h>
-#include <my_sys.h>
-
-#define MAX_ULONG_BIT ((ulong) 1 << (sizeof(ulong)*8-1))
-
-#include "ha_innodb.h"
-
-pthread_mutex_t innobase_share_mutex, /* to protect innobase_open_files */
- prepare_commit_mutex; /* to force correct commit order in
- binlog */
-ulong commit_threads= 0;
-pthread_mutex_t commit_threads_m;
-pthread_cond_t commit_cond;
-pthread_mutex_t commit_cond_m;
-bool innodb_inited= 0;
-
-/*-----------------------------------------------------------------*/
-/* These variables are used to implement (semi-)synchronous MySQL binlog
-replication for InnoDB tables. */
-
-pthread_cond_t innobase_repl_cond; /* Posix cond variable;
- this variable is signaled
- when enough binlog has been
- sent to slave, so that a
- waiting trx can return the
- 'ok' message to the client
- for a commit */
-pthread_mutex_t innobase_repl_cond_mutex; /* Posix cond variable mutex
- that also protects the next
- innobase_repl_... variables */
-uint innobase_repl_state; /* 1 if synchronous replication
- is switched on and is working
- ok; else 0 */
-uint innobase_repl_file_name_inited = 0; /* This is set to 1 when
- innobase_repl_file_name
- contains meaningful data */
-char* innobase_repl_file_name; /* The binlog name up to which
- we have sent some binlog to
- the slave */
-my_off_t innobase_repl_pos; /* The position in that file
- up to which we have sent the
- binlog to the slave */
-uint innobase_repl_n_wait_threads = 0; /* This tells how many
- transactions currently are
- waiting for the binlog to be
- sent to the client */
-uint innobase_repl_wait_file_name_inited = 0; /* This is set to 1
- when we know the 'smallest'
- wait position */
-char* innobase_repl_wait_file_name; /* NULL, or the 'smallest'
- innobase_repl_file_name that
- a transaction is waiting for */
-my_off_t innobase_repl_wait_pos; /* The smallest position in
- that file that a trx is
- waiting for: the trx can
- proceed and send an 'ok' to
- the client when MySQL has sent
- the binlog up to this position
- to the slave */
-/*-----------------------------------------------------------------*/
-
-
-
-/* Store MySQL definition of 'byte': in Linux it is char while InnoDB
-uses unsigned char; the header univ.i which we include next defines
-'byte' as a macro which expands to 'unsigned char' */
-
-typedef byte mysql_byte;
-
-#define INSIDE_HA_INNOBASE_CC
-
-/* Include necessary InnoDB headers */
-extern "C" {
-#include "../innobase/include/univ.i"
-#include "../innobase/include/os0file.h"
-#include "../innobase/include/os0thread.h"
-#include "../innobase/include/srv0start.h"
-#include "../innobase/include/srv0srv.h"
-#include "../innobase/include/trx0roll.h"
-#include "../innobase/include/trx0trx.h"
-#include "../innobase/include/trx0sys.h"
-#include "../innobase/include/mtr0mtr.h"
-#include "../innobase/include/row0ins.h"
-#include "../innobase/include/row0mysql.h"
-#include "../innobase/include/row0sel.h"
-#include "../innobase/include/row0upd.h"
-#include "../innobase/include/log0log.h"
-#include "../innobase/include/lock0lock.h"
-#include "../innobase/include/dict0crea.h"
-#include "../innobase/include/btr0cur.h"
-#include "../innobase/include/btr0btr.h"
-#include "../innobase/include/fsp0fsp.h"
-#include "../innobase/include/sync0sync.h"
-#include "../innobase/include/fil0fil.h"
-#include "../innobase/include/trx0xa.h"
-}
-
-#define HA_INNOBASE_ROWS_IN_TABLE 10000 /* to get optimization right */
-#define HA_INNOBASE_RANGE_COUNT 100
-
-ulong innobase_large_page_size = 0;
-
-/* The default values for the following, type long or longlong, start-up
-parameters are declared in mysqld.cc: */
-
-long innobase_mirrored_log_groups, innobase_log_files_in_group,
- innobase_log_buffer_size, innobase_buffer_pool_awe_mem_mb,
- innobase_additional_mem_pool_size, innobase_file_io_threads,
- innobase_lock_wait_timeout, innobase_force_recovery,
- innobase_open_files;
-
-longlong innobase_buffer_pool_size, innobase_log_file_size;
-
-/* The default values for the following char* start-up parameters
-are determined in innobase_init below: */
-
-char* innobase_data_home_dir = NULL;
-char* innobase_data_file_path = NULL;
-char* innobase_log_group_home_dir = NULL;
-char* innobase_log_arch_dir = NULL;/* unused */
-/* The following has a misleading name: starting from 4.0.5, this also
-affects Windows: */
-char* innobase_unix_file_flush_method = NULL;
-
-/* Below we have boolean-valued start-up parameters, and their default
-values */
-
-ulong innobase_fast_shutdown = 1;
-my_bool innobase_log_archive = FALSE;/* unused */
-my_bool innobase_use_doublewrite = TRUE;
-my_bool innobase_use_checksums = TRUE;
-my_bool innobase_use_large_pages = FALSE;
-my_bool innobase_use_native_aio = FALSE;
-my_bool innobase_file_per_table = FALSE;
-my_bool innobase_locks_unsafe_for_binlog = FALSE;
-my_bool innobase_rollback_on_timeout = FALSE;
-my_bool innobase_create_status_file = FALSE;
-
-static char *internal_innobase_data_file_path = NULL;
-
-/* The following counter is used to convey information to InnoDB
-about server activity: in selects it is not sensible to call
-srv_active_wake_master_thread after each fetch or search, we only do
-it every INNOBASE_WAKE_INTERVAL'th step. */
-
-#define INNOBASE_WAKE_INTERVAL 32
-ulong innobase_active_counter = 0;
-
-static HASH innobase_open_tables;
-
-#ifdef __NETWARE__ /* some special cleanup for NetWare */
-bool nw_panic = FALSE;
-#endif
-
-static mysql_byte* innobase_get_key(INNOBASE_SHARE *share,uint *length,
- my_bool not_used __attribute__((unused)));
-static INNOBASE_SHARE *get_share(const char *table_name);
-static void free_share(INNOBASE_SHARE *share);
-static int innobase_close_connection(THD* thd);
-static int innobase_commit(THD* thd, bool all);
-static int innobase_rollback(THD* thd, bool all);
-static int innobase_rollback_to_savepoint(THD* thd, void *savepoint);
-static int innobase_savepoint(THD* thd, void *savepoint);
-static int innobase_release_savepoint(THD* thd, void *savepoint);
-
-handlerton innobase_hton = {
- "InnoDB",
- SHOW_OPTION_YES,
- "Supports transactions, row-level locking, and foreign keys",
- DB_TYPE_INNODB,
- innobase_init,
- 0, /* slot */
- sizeof(trx_named_savept_t), /* savepoint size. TODO: use it */
- innobase_close_connection,
- innobase_savepoint,
- innobase_rollback_to_savepoint,
- innobase_release_savepoint,
- innobase_commit, /* commit */
- innobase_rollback, /* rollback */
- innobase_xa_prepare, /* prepare */
- innobase_xa_recover, /* recover */
- innobase_commit_by_xid, /* commit_by_xid */
- innobase_rollback_by_xid, /* rollback_by_xid */
- innobase_create_cursor_view,
- innobase_set_cursor_view,
- innobase_close_cursor_view,
- HTON_NO_FLAGS
-};
-
-/*********************************************************************
-Commits a transaction in an InnoDB database. */
-
-void
-innobase_commit_low(
-/*================*/
- trx_t* trx); /* in: transaction handle */
-
-struct show_var_st innodb_status_variables[]= {
- {"buffer_pool_pages_data",
- (char*) &export_vars.innodb_buffer_pool_pages_data, SHOW_LONG},
- {"buffer_pool_pages_dirty",
- (char*) &export_vars.innodb_buffer_pool_pages_dirty, SHOW_LONG},
- {"buffer_pool_pages_flushed",
- (char*) &export_vars.innodb_buffer_pool_pages_flushed, SHOW_LONG},
- {"buffer_pool_pages_free",
- (char*) &export_vars.innodb_buffer_pool_pages_free, SHOW_LONG},
- {"buffer_pool_pages_latched",
- (char*) &export_vars.innodb_buffer_pool_pages_latched, SHOW_LONG},
- {"buffer_pool_pages_misc",
- (char*) &export_vars.innodb_buffer_pool_pages_misc, SHOW_LONG},
- {"buffer_pool_pages_total",
- (char*) &export_vars.innodb_buffer_pool_pages_total, SHOW_LONG},
- {"buffer_pool_read_ahead_rnd",
- (char*) &export_vars.innodb_buffer_pool_read_ahead_rnd, SHOW_LONG},
- {"buffer_pool_read_ahead_seq",
- (char*) &export_vars.innodb_buffer_pool_read_ahead_seq, SHOW_LONG},
- {"buffer_pool_read_requests",
- (char*) &export_vars.innodb_buffer_pool_read_requests, SHOW_LONG},
- {"buffer_pool_reads",
- (char*) &export_vars.innodb_buffer_pool_reads, SHOW_LONG},
- {"buffer_pool_wait_free",
- (char*) &export_vars.innodb_buffer_pool_wait_free, SHOW_LONG},
- {"buffer_pool_write_requests",
- (char*) &export_vars.innodb_buffer_pool_write_requests, SHOW_LONG},
- {"data_fsyncs",
- (char*) &export_vars.innodb_data_fsyncs, SHOW_LONG},
- {"data_pending_fsyncs",
- (char*) &export_vars.innodb_data_pending_fsyncs, SHOW_LONG},
- {"data_pending_reads",
- (char*) &export_vars.innodb_data_pending_reads, SHOW_LONG},
- {"data_pending_writes",
- (char*) &export_vars.innodb_data_pending_writes, SHOW_LONG},
- {"data_read",
- (char*) &export_vars.innodb_data_read, SHOW_LONG},
- {"data_reads",
- (char*) &export_vars.innodb_data_reads, SHOW_LONG},
- {"data_writes",
- (char*) &export_vars.innodb_data_writes, SHOW_LONG},
- {"data_written",
- (char*) &export_vars.innodb_data_written, SHOW_LONG},
- {"dblwr_pages_written",
- (char*) &export_vars.innodb_dblwr_pages_written, SHOW_LONG},
- {"dblwr_writes",
- (char*) &export_vars.innodb_dblwr_writes, SHOW_LONG},
- {"log_waits",
- (char*) &export_vars.innodb_log_waits, SHOW_LONG},
- {"log_write_requests",
- (char*) &export_vars.innodb_log_write_requests, SHOW_LONG},
- {"log_writes",
- (char*) &export_vars.innodb_log_writes, SHOW_LONG},
- {"os_log_fsyncs",
- (char*) &export_vars.innodb_os_log_fsyncs, SHOW_LONG},
- {"os_log_pending_fsyncs",
- (char*) &export_vars.innodb_os_log_pending_fsyncs, SHOW_LONG},
- {"os_log_pending_writes",
- (char*) &export_vars.innodb_os_log_pending_writes, SHOW_LONG},
- {"os_log_written",
- (char*) &export_vars.innodb_os_log_written, SHOW_LONG},
- {"page_size",
- (char*) &export_vars.innodb_page_size, SHOW_LONG},
- {"pages_created",
- (char*) &export_vars.innodb_pages_created, SHOW_LONG},
- {"pages_read",
- (char*) &export_vars.innodb_pages_read, SHOW_LONG},
- {"pages_written",
- (char*) &export_vars.innodb_pages_written, SHOW_LONG},
- {"row_lock_current_waits",
- (char*) &export_vars.innodb_row_lock_current_waits, SHOW_LONG},
- {"row_lock_time",
- (char*) &export_vars.innodb_row_lock_time, SHOW_LONGLONG},
- {"row_lock_time_avg",
- (char*) &export_vars.innodb_row_lock_time_avg, SHOW_LONG},
- {"row_lock_time_max",
- (char*) &export_vars.innodb_row_lock_time_max, SHOW_LONG},
- {"row_lock_waits",
- (char*) &export_vars.innodb_row_lock_waits, SHOW_LONG},
- {"rows_deleted",
- (char*) &export_vars.innodb_rows_deleted, SHOW_LONG},
- {"rows_inserted",
- (char*) &export_vars.innodb_rows_inserted, SHOW_LONG},
- {"rows_read",
- (char*) &export_vars.innodb_rows_read, SHOW_LONG},
- {"rows_updated",
- (char*) &export_vars.innodb_rows_updated, SHOW_LONG},
- {NullS, NullS, SHOW_LONG}};
-
-/* General functions */
-
-/**********************************************************************
-Save some CPU by testing the value of srv_thread_concurrency in inline
-functions. */
-inline
-void
-innodb_srv_conc_enter_innodb(
-/*=========================*/
- trx_t* trx) /* in: transaction handle */
-{
- if (UNIV_LIKELY(!srv_thread_concurrency)) {
-
- return;
- }
-
- srv_conc_enter_innodb(trx);
-}
-
-/**********************************************************************
-Save some CPU by testing the value of srv_thread_concurrency in inline
-functions. */
-inline
-void
-innodb_srv_conc_exit_innodb(
-/*========================*/
- trx_t* trx) /* in: transaction handle */
-{
- if (UNIV_LIKELY(!srv_thread_concurrency)) {
-
- return;
- }
-
- srv_conc_exit_innodb(trx);
-}
-
-/**********************************************************************
-Releases possible search latch and InnoDB thread FIFO ticket. These should
-be released at each SQL statement end, and also when mysqld passes the
-control to the client. It does no harm to release these also in the middle
-of an SQL statement. */
-inline
-void
-innobase_release_stat_resources(
-/*============================*/
- trx_t* trx) /* in: transaction object */
-{
- if (trx->has_search_latch) {
- trx_search_latch_release_if_reserved(trx);
- }
-
- if (trx->declared_to_be_inside_innodb) {
- /* Release our possible ticket in the FIFO */
-
- srv_conc_force_exit_innodb(trx);
- }
-}
-
-/************************************************************************
-Call this function when mysqld passes control to the client. That is to
-avoid deadlocks on the adaptive hash S-latch possibly held by thd. For more
-documentation, see handler.cc. */
-
-void
-innobase_release_temporary_latches(
-/*===============================*/
- THD *thd)
-{
- trx_t* trx;
-
- if (!innodb_inited) {
-
- return;
- }
-
- trx = (trx_t*) thd->ha_data[innobase_hton.slot];
-
- if (trx) {
- innobase_release_stat_resources(trx);
- }
-}
-
-/************************************************************************
-Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth
-time calls srv_active_wake_master_thread. This function should be used
-when a single database operation may introduce a small need for
-server utility activity, like checkpointing. */
-inline
-void
-innobase_active_small(void)
-/*=======================*/
-{
- innobase_active_counter++;
-
- if ((innobase_active_counter % INNOBASE_WAKE_INTERVAL) == 0) {
- srv_active_wake_master_thread();
- }
-}
-
-/************************************************************************
-Converts an InnoDB error code to a MySQL error code and also tells to MySQL
-about a possible transaction rollback inside InnoDB caused by a lock wait
-timeout or a deadlock. */
-static
-int
-convert_error_code_to_mysql(
-/*========================*/
- /* out: MySQL error code */
- int error, /* in: InnoDB error code */
- THD* thd) /* in: user thread handle or NULL */
-{
- if (error == DB_SUCCESS) {
-
- return(0);
-
- } else if (error == (int) DB_DUPLICATE_KEY) {
-
- return(HA_ERR_FOUND_DUPP_KEY);
-
- } else if (error == (int) DB_RECORD_NOT_FOUND) {
-
- return(HA_ERR_NO_ACTIVE_RECORD);
-
- } else if (error == (int) DB_ERROR) {
-
- return(-1); /* unspecified error */
-
- } else if (error == (int) DB_DEADLOCK) {
- /* Since we rolled back the whole transaction, we must
- tell it also to MySQL so that MySQL knows to empty the
- cached binlog for this transaction */
-
- if (thd) {
- ha_rollback(thd);
- }
-
- return(HA_ERR_LOCK_DEADLOCK);
-
- } else if (error == (int) DB_LOCK_WAIT_TIMEOUT) {
-
- /* Starting from 5.0.13, we let MySQL just roll back the
- latest SQL statement in a lock wait timeout. Previously, we
- rolled back the whole transaction. */
-
- if (thd && row_rollback_on_timeout) {
- ha_rollback(thd);
- }
-
- return(HA_ERR_LOCK_WAIT_TIMEOUT);
-
- } else if (error == (int) DB_NO_REFERENCED_ROW) {
-
- return(HA_ERR_NO_REFERENCED_ROW);
-
- } else if (error == (int) DB_ROW_IS_REFERENCED) {
-
- return(HA_ERR_ROW_IS_REFERENCED);
-
- } else if (error == (int) DB_CANNOT_ADD_CONSTRAINT) {
-
- return(HA_ERR_CANNOT_ADD_FOREIGN);
-
- } else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) {
-
- return(HA_ERR_ROW_IS_REFERENCED); /* TODO: This is a bit
- misleading, a new MySQL error
- code should be introduced */
- } else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) {
-
- return(HA_ERR_CRASHED);
-
- } else if (error == (int) DB_OUT_OF_FILE_SPACE) {
-
- return(HA_ERR_RECORD_FILE_FULL);
-
- } else if (error == (int) DB_TABLE_IS_BEING_USED) {
-
- return(HA_ERR_WRONG_COMMAND);
-
- } else if (error == (int) DB_TABLE_NOT_FOUND) {
-
- return(HA_ERR_KEY_NOT_FOUND);
-
- } else if (error == (int) DB_TOO_BIG_RECORD) {
-
- return(HA_ERR_TO_BIG_ROW);
-
- } else if (error == (int) DB_CORRUPTION) {
-
- return(HA_ERR_CRASHED);
- } else if (error == (int) DB_NO_SAVEPOINT) {
-
- return(HA_ERR_NO_SAVEPOINT);
- } else if (error == (int) DB_LOCK_TABLE_FULL) {
- /* Since we rolled back the whole transaction, we must
- tell it also to MySQL so that MySQL knows to empty the
- cached binlog for this transaction */
-
- if (thd) {
- ha_rollback(thd);
- }
-
- return(HA_ERR_LOCK_TABLE_FULL);
- } else {
- return(-1); // Unknown error
- }
-}
-
-/*****************************************************************
-If you want to print a thd that is not associated with the current thread,
-you must call this function before reserving the InnoDB kernel_mutex, to
-protect MySQL from setting thd->query NULL. If you print a thd of the current
-thread, we know that MySQL cannot modify thd->query, and it is not necessary
-to call this. Call innobase_mysql_end_print_arbitrary_thd() after you release
-the kernel_mutex.
-NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this
-function! */
-extern "C"
-void
-innobase_mysql_prepare_print_arbitrary_thd(void)
-/*============================================*/
-{
- VOID(pthread_mutex_lock(&LOCK_thread_count));
-}
-
-/*****************************************************************
-Releases the mutex reserved by innobase_mysql_prepare_print_arbitrary_thd().
-NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this
-function! */
-extern "C"
-void
-innobase_mysql_end_print_arbitrary_thd(void)
-/*========================================*/
-{
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
-}
-
-/*****************************************************************
-Prints info of a THD object (== user session thread) to the given file.
-NOTE that /mysql/innobase/trx/trx0trx.c must contain the prototype for
-this function! */
-extern "C"
-void
-innobase_mysql_print_thd(
-/*=====================*/
- FILE* f, /* in: output stream */
- void* input_thd, /* in: pointer to a MySQL THD object */
- uint max_query_len) /* in: max query length to print, or 0 to
- use the default max length */
-{
- const THD* thd;
- const Security_context *sctx;
- const char* s;
-
- thd = (const THD*) input_thd;
- /* We probably want to have original user as part of debug output. */
- sctx = &thd->main_security_ctx;
-
-
- fprintf(f, "MySQL thread id %lu, query id %lu",
- thd->thread_id, (ulong) thd->query_id);
- if (sctx->host) {
- putc(' ', f);
- fputs(sctx->host, f);
- }
-
- if (sctx->ip) {
- putc(' ', f);
- fputs(sctx->ip, f);
- }
-
- if (sctx->user) {
- putc(' ', f);
- fputs(sctx->user, f);
- }
-
- if ((s = thd->proc_info)) {
- putc(' ', f);
- fputs(s, f);
- }
-
- if ((s = thd->query)) {
- /* 3100 is chosen because currently 3000 is the maximum
- max_query_len we ever give this. */
- char buf[3100];
- uint len;
-
- /* If buf is too small, we dynamically allocate storage
- in this. */
- char* dyn_str = NULL;
-
- /* Points to buf or dyn_str. */
- char* str = buf;
-
- if (max_query_len == 0)
- {
- /* ADDITIONAL SAFETY: the default is to print at
- most 300 chars to reduce the probability of a
- seg fault if there is a race in
- thd->query_length in MySQL; after May 14, 2004
- probably no race any more, but better be
- safe */
- max_query_len = 300;
- }
-
- len = min(thd->query_length, max_query_len);
-
- if (len > (sizeof(buf) - 1))
- {
- dyn_str = my_malloc(len + 1, MYF(0));
- str = dyn_str;
- }
-
- /* Use strmake to reduce the timeframe for a race,
- compared to fwrite() */
- len = (uint) (strmake(str, s, len) - str);
- putc('\n', f);
- fwrite(str, 1, len, f);
-
- if (dyn_str)
- {
- my_free(dyn_str, MYF(0));
- }
- }
-
- putc('\n', f);
-}
-
-/**********************************************************************
-Get the variable length bounds of the given character set.
-
-NOTE that the exact prototype of this function has to be in
-/innobase/data/data0type.ic! */
-extern "C"
-void
-innobase_get_cset_width(
-/*====================*/
- ulint cset, /* in: MySQL charset-collation code */
- ulint* mbminlen, /* out: minimum length of a char (in bytes) */
- ulint* mbmaxlen) /* out: maximum length of a char (in bytes) */
-{
- CHARSET_INFO* cs;
- ut_ad(cset < 256);
- ut_ad(mbminlen);
- ut_ad(mbmaxlen);
-
- cs = all_charsets[cset];
- if (cs) {
- *mbminlen = cs->mbminlen;
- *mbmaxlen = cs->mbmaxlen;
- } else {
- ut_a(cset == 0);
- *mbminlen = *mbmaxlen = 0;
- }
-}
-
-/**********************************************************************
-Compares NUL-terminated UTF-8 strings case insensitively.
-
-NOTE that the exact prototype of this function has to be in
-/innobase/dict/dict0dict.c! */
-extern "C"
-int
-innobase_strcasecmp(
-/*================*/
- /* out: 0 if a=b, <0 if a<b, >1 if a>b */
- const char* a, /* in: first string to compare */
- const char* b) /* in: second string to compare */
-{
- return(my_strcasecmp(system_charset_info, a, b));
-}
-
-/**********************************************************************
-Makes all characters in a NUL-terminated UTF-8 string lower case.
-
-NOTE that the exact prototype of this function has to be in
-/innobase/dict/dict0dict.c! */
-extern "C"
-void
-innobase_casedn_str(
-/*================*/
- char* a) /* in/out: string to put in lower case */
-{
- my_casedn_str(system_charset_info, a);
-}
-
-/*************************************************************************
-Creates a temporary file. */
-extern "C"
-int
-innobase_mysql_tmpfile(void)
-/*========================*/
- /* out: temporary file descriptor, or < 0 on error */
-{
- char filename[FN_REFLEN];
- int fd2 = -1;
- File fd = create_temp_file(filename, mysql_tmpdir, "ib",
-#ifdef __WIN__
- O_BINARY | O_TRUNC | O_SEQUENTIAL |
- O_TEMPORARY | O_SHORT_LIVED |
-#endif /* __WIN__ */
- O_CREAT | O_EXCL | O_RDWR,
- MYF(MY_WME));
- if (fd >= 0) {
-#ifndef __WIN__
- /* On Windows, open files cannot be removed, but files can be
- created with the O_TEMPORARY flag to the same effect
- ("delete on close"). */
- unlink(filename);
-#endif /* !__WIN__ */
- /* Copy the file descriptor, so that the additional resources
- allocated by create_temp_file() can be freed by invoking
- my_close().
-
- Because the file descriptor returned by this function
- will be passed to fdopen(), it will be closed by invoking
- fclose(), which in turn will invoke close() instead of
- my_close(). */
- fd2 = dup(fd);
- if (fd2 < 0) {
- DBUG_PRINT("error",("Got error %d on dup",fd2));
- my_errno=errno;
- my_error(EE_OUT_OF_FILERESOURCES,
- MYF(ME_BELL+ME_WAITTANG),
- filename, my_errno);
- }
- my_close(fd, MYF(MY_WME));
- }
- return(fd2);
-}
-
-/*************************************************************************
-Gets the InnoDB transaction handle for a MySQL handler object, creates
-an InnoDB transaction struct if the corresponding MySQL thread struct still
-lacks one. */
-static
-trx_t*
-check_trx_exists(
-/*=============*/
- /* out: InnoDB transaction handle */
- THD* thd) /* in: user thread handle */
-{
- trx_t* trx;
-
- ut_ad(thd == current_thd);
-
- trx = (trx_t*) thd->ha_data[innobase_hton.slot];
-
- if (trx == NULL) {
- DBUG_ASSERT(thd != NULL);
- trx = trx_allocate_for_mysql();
-
- trx->mysql_thd = thd;
- trx->mysql_query_str = &(thd->query);
- trx->active_trans = 0;
-
- /* Update the info whether we should skip XA steps that eat
- CPU time */
- trx->support_xa = (ibool)(thd->variables.innodb_support_xa);
-
- thd->ha_data[innobase_hton.slot] = trx;
- } else {
- if (trx->magic_n != TRX_MAGIC_N) {
- mem_analyze_corruption((byte*)trx);
-
- ut_a(0);
- }
- }
-
- if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
- trx->check_foreigns = FALSE;
- } else {
- trx->check_foreigns = TRUE;
- }
-
- if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
- trx->check_unique_secondary = FALSE;
- } else {
- trx->check_unique_secondary = TRUE;
- }
-
- return(trx);
-}
-
-
-/*************************************************************************
-Construct ha_innobase handler. */
-
-ha_innobase::ha_innobase(TABLE *table_arg)
- :handler(&innobase_hton, table_arg),
- int_table_flags(HA_REC_NOT_IN_SEQ |
- HA_NULL_IN_KEY |
- HA_CAN_INDEX_BLOBS |
- HA_CAN_SQL_HANDLER |
- HA_NOT_EXACT_COUNT |
- HA_PRIMARY_KEY_IN_READ_INDEX |
- HA_CAN_GEOMETRY |
- HA_TABLE_SCAN_ON_INDEX),
- start_of_scan(0),
- num_write_row(0)
-{}
-
-/*************************************************************************
-Updates the user_thd field in a handle and also allocates a new InnoDB
-transaction handle if needed, and updates the transaction fields in the
-prebuilt struct. */
-inline
-int
-ha_innobase::update_thd(
-/*====================*/
- /* out: 0 or error code */
- THD* thd) /* in: thd to use the handle */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- trx_t* trx;
-
- trx = check_trx_exists(thd);
-
- if (prebuilt->trx != trx) {
-
- row_update_prebuilt_trx(prebuilt, trx);
- }
-
- user_thd = thd;
-
- return(0);
-}
-
-/*************************************************************************
-Registers that InnoDB takes part in an SQL statement, so that MySQL knows to
-roll back the statement if the statement results in an error. This MUST be
-called for every SQL statement that may be rolled back by MySQL. Calling this
-several times to register the same statement is allowed, too. */
-inline
-void
-innobase_register_stmt(
-/*===================*/
- THD* thd) /* in: MySQL thd (connection) object */
-{
- /* Register the statement */
- trans_register_ha(thd, FALSE, &innobase_hton);
-}
-
-/*************************************************************************
-Registers an InnoDB transaction in MySQL, so that the MySQL XA code knows
-to call the InnoDB prepare and commit, or rollback for the transaction. This
-MUST be called for every transaction for which the user may call commit or
-rollback. Calling this several times to register the same transaction is
-allowed, too.
-This function also registers the current SQL statement. */
-inline
-void
-innobase_register_trx_and_stmt(
-/*===========================*/
- THD* thd) /* in: MySQL thd (connection) object */
-{
- /* NOTE that actually innobase_register_stmt() registers also
- the transaction in the AUTOCOMMIT=1 mode. */
-
- innobase_register_stmt(thd);
-
- if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
-
- /* No autocommit mode, register for a transaction */
- trans_register_ha(thd, TRUE, &innobase_hton);
- }
-}
-
-/* BACKGROUND INFO: HOW THE MYSQL QUERY CACHE WORKS WITH INNODB
- ------------------------------------------------------------
-
-1) The use of the query cache for TBL is disabled when there is an
-uncommitted change to TBL.
-
-2) When a change to TBL commits, InnoDB stores the current value of
-its global trx id counter, let us denote it by INV_TRX_ID, to the table object
-in the InnoDB data dictionary, and does only allow such transactions whose
-id <= INV_TRX_ID to use the query cache.
-
-3) When InnoDB does an INSERT/DELETE/UPDATE to a table TBL, or an implicit
-modification because an ON DELETE CASCADE, we invalidate the MySQL query cache
-of TBL immediately.
-
-How this is implemented inside InnoDB:
-
-1) Since every modification always sets an IX type table lock on the InnoDB
-table, it is easy to check if there can be uncommitted modifications for a
-table: just check if there are locks in the lock list of the table.
-
-2) When a transaction inside InnoDB commits, it reads the global trx id
-counter and stores the value INV_TRX_ID to the tables on which it had a lock.
-
-3) If there is an implicit table change from ON DELETE CASCADE or SET NULL,
-InnoDB calls an invalidate method for the MySQL query cache for that table.
-
-How this is implemented inside sql_cache.cc:
-
-1) The query cache for an InnoDB table TBL is invalidated immediately at an
-INSERT/UPDATE/DELETE, just like in the case of MyISAM. No need to delay
-invalidation to the transaction commit.
-
-2) To store or retrieve a value from the query cache of an InnoDB table TBL,
-any query must first ask InnoDB's permission. We must pass the thd as a
-parameter because InnoDB will look at the trx id, if any, associated with
-that thd.
-
-3) Use of the query cache for InnoDB tables is now allowed also when
-AUTOCOMMIT==0 or we are inside BEGIN ... COMMIT. Thus transactions no longer
-put restrictions on the use of the query cache.
-*/
-
-/**********************************************************************
-The MySQL query cache uses this to check from InnoDB if the query cache at
-the moment is allowed to operate on an InnoDB table. The SQL query must
-be a non-locking SELECT.
-
-The query cache is allowed to operate on certain query only if this function
-returns TRUE for all tables in the query.
-
-If thd is not in the autocommit state, this function also starts a new
-transaction for thd if there is no active trx yet, and assigns a consistent
-read view to it if there is no read view yet.
-
-Why a deadlock of threads is not possible: the query cache calls this function
-at the start of a SELECT processing. Then the calling thread cannot be
-holding any InnoDB semaphores. The calling thread is holding the
-query cache mutex, and this function will reserver the InnoDB kernel mutex.
-Thus, the 'rank' in sync0sync.h of the MySQL query cache mutex is above
-the InnoDB kernel mutex. */
-
-my_bool
-innobase_query_caching_of_table_permitted(
-/*======================================*/
- /* out: TRUE if permitted, FALSE if not;
- note that the value FALSE does not mean
- we should invalidate the query cache:
- invalidation is called explicitly */
- THD* thd, /* in: thd of the user who is trying to
- store a result to the query cache or
- retrieve it */
- char* full_name, /* in: concatenation of database name,
- the null character '\0', and the table
- name */
- uint full_name_len, /* in: length of the full name, i.e.
- len(dbname) + len(tablename) + 1 */
- ulonglong *unused) /* unused for this engine */
-{
- ibool is_autocommit;
- trx_t* trx;
- char norm_name[1000];
-
- ut_a(full_name_len < 999);
-
- if (thd->variables.tx_isolation == ISO_SERIALIZABLE) {
- /* In the SERIALIZABLE mode we add LOCK IN SHARE MODE to every
- plain SELECT if AUTOCOMMIT is not on. */
-
- return((my_bool)FALSE);
- }
-
- trx = check_trx_exists(thd);
- if (trx->has_search_latch) {
- ut_print_timestamp(stderr);
- sql_print_error("The calling thread is holding the adaptive "
- "search, latch though calling "
- "innobase_query_caching_of_table_permitted.");
-
- mutex_enter_noninline(&kernel_mutex);
- trx_print(stderr, trx, 1024);
- mutex_exit_noninline(&kernel_mutex);
- }
-
- innobase_release_stat_resources(trx);
-
- if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
-
- is_autocommit = TRUE;
- } else {
- is_autocommit = FALSE;
-
- }
-
- if (is_autocommit && trx->n_mysql_tables_in_use == 0) {
- /* We are going to retrieve the query result from the query
- cache. This cannot be a store operation to the query cache
- because then MySQL would have locks on tables already.
-
- TODO: if the user has used LOCK TABLES to lock the table,
- then we open a transaction in the call of row_.. below.
- That trx can stay open until UNLOCK TABLES. The same problem
- exists even if we do not use the query cache. MySQL should be
- modified so that it ALWAYS calls some cleanup function when
- the processing of a query ends!
-
- We can imagine we instantaneously serialize this consistent
- read trx to the current trx id counter. If trx2 would have
- changed the tables of a query result stored in the cache, and
- trx2 would have already committed, making the result obsolete,
- then trx2 would have already invalidated the cache. Thus we
- can trust the result in the cache is ok for this query. */
-
- return((my_bool)TRUE);
- }
-
- /* Normalize the table name to InnoDB format */
-
- memcpy(norm_name, full_name, full_name_len);
-
- norm_name[strlen(norm_name)] = '/'; /* InnoDB uses '/' as the
- separator between db and table */
- norm_name[full_name_len] = '\0';
-#ifdef __WIN__
- innobase_casedn_str(norm_name);
-#endif
- /* The call of row_search_.. will start a new transaction if it is
- not yet started */
-
- if (trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(thd);
- trx->active_trans = 1;
- }
-
- if (row_search_check_if_query_cache_permitted(trx, norm_name)) {
-
- /* printf("Query cache for %s permitted\n", norm_name); */
-
- return((my_bool)TRUE);
- }
-
- /* printf("Query cache for %s NOT permitted\n", norm_name); */
-
- return((my_bool)FALSE);
-}
-
-/*********************************************************************
-Invalidates the MySQL query cache for the table.
-NOTE that the exact prototype of this function has to be in
-/innobase/row/row0ins.c! */
-extern "C"
-void
-innobase_invalidate_query_cache(
-/*============================*/
- trx_t* trx, /* in: transaction which modifies the table */
- char* full_name, /* in: concatenation of database name, null
- char '\0', table name, null char'\0';
- NOTE that in Windows this is always
- in LOWER CASE! */
- ulint full_name_len) /* in: full name length where also the null
- chars count */
-{
- /* Note that the sync0sync.h rank of the query cache mutex is just
- above the InnoDB kernel mutex. The caller of this function must not
- have latches of a lower rank. */
-
- /* Argument TRUE below means we are using transactions */
-#ifdef HAVE_QUERY_CACHE
- query_cache.invalidate((THD*)(trx->mysql_thd),
- (const char*)full_name,
- (uint32)full_name_len,
- TRUE);
-#endif
-}
-
-/*********************************************************************
-Get the quote character to be used in SQL identifiers.
-This definition must match the one in innobase/ut/ut0ut.c! */
-extern "C"
-int
-mysql_get_identifier_quote_char(
-/*============================*/
- /* out: quote character to be
- used in SQL identifiers; EOF if none */
- trx_t* trx, /* in: transaction */
- const char* name, /* in: name to print */
- ulint namelen)/* in: length of name */
-{
- if (!trx || !trx->mysql_thd) {
- return(EOF);
- }
- return(get_quote_char_for_identifier((THD*) trx->mysql_thd,
- name, (int) namelen));
-}
-
-/**************************************************************************
-Determines if the currently running transaction has been interrupted. */
-extern "C"
-ibool
-trx_is_interrupted(
-/*===============*/
- /* out: TRUE if interrupted */
- trx_t* trx) /* in: transaction */
-{
- return(trx && trx->mysql_thd && ((THD*) trx->mysql_thd)->killed);
-}
-
-/**************************************************************************
-Obtain a pointer to the MySQL THD object, as in current_thd(). This
-definition must match the one in sql/ha_innodb.cc! */
-extern "C"
-void*
-innobase_current_thd(void)
-/*======================*/
- /* out: MySQL THD object */
-{
- return(current_thd);
-}
-
-/*********************************************************************
-Call this when you have opened a new table handle in HANDLER, before you
-call index_read_idx() etc. Actually, we can let the cursor stay open even
-over a transaction commit! Then you should call this before every operation,
-fetch next etc. This function inits the necessary things even after a
-transaction commit. */
-
-void
-ha_innobase::init_table_handle_for_HANDLER(void)
-/*============================================*/
-{
- row_prebuilt_t* prebuilt;
-
- /* If current thd does not yet have a trx struct, create one.
- If the current handle does not yet have a prebuilt struct, create
- one. Update the trx pointers in the prebuilt struct. Normally
- this operation is done in external_lock. */
-
- update_thd(current_thd);
-
- /* Initialize the prebuilt struct much like it would be inited in
- external_lock */
-
- prebuilt = (row_prebuilt_t*)innobase_prebuilt;
-
- innobase_release_stat_resources(prebuilt->trx);
-
- /* If the transaction is not started yet, start it */
-
- trx_start_if_not_started_noninline(prebuilt->trx);
-
- /* Assign a read view if the transaction does not have it yet */
-
- trx_assign_read_view(prebuilt->trx);
-
- /* Set the MySQL flag to mark that there is an active transaction */
-
- if (prebuilt->trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(current_thd);
-
- prebuilt->trx->active_trans = 1;
- }
-
- /* We did the necessary inits in this function, no need to repeat them
- in row_search_for_mysql */
-
- prebuilt->sql_stat_start = FALSE;
-
- /* We let HANDLER always to do the reads as consistent reads, even
- if the trx isolation level would have been specified as SERIALIZABLE */
-
- prebuilt->select_lock_type = LOCK_NONE;
- prebuilt->stored_select_lock_type = LOCK_NONE;
-
- /* Always fetch all columns in the index record */
-
- prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
-
- /* We want always to fetch all columns in the whole row? Or do
- we???? */
-
- prebuilt->read_just_key = FALSE;
-
- prebuilt->used_in_HANDLER = TRUE;
-
- prebuilt->keep_other_fields_on_keyread = FALSE;
-}
-
-/*************************************************************************
-Opens an InnoDB database. */
-
-bool
-innobase_init(void)
-/*===============*/
- /* out: &innobase_hton, or NULL on error */
-{
- static char current_dir[3]; /* Set if using current lib */
- int err;
- bool ret;
- char *default_path;
-
- DBUG_ENTER("innobase_init");
-
- if (have_innodb != SHOW_OPTION_YES)
- goto error;
-
- ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR);
-
- /* Check that values don't overflow on 32-bit systems. */
- if (sizeof(ulint) == 4) {
- if (innobase_buffer_pool_size > UINT_MAX32) {
- sql_print_error(
- "innobase_buffer_pool_size can't be over 4GB"
- " on 32-bit systems");
-
- goto error;
- }
-
- if (innobase_log_file_size > UINT_MAX32) {
- sql_print_error(
- "innobase_log_file_size can't be over 4GB"
- " on 32-bit systems");
-
- goto error;
- }
- }
-
- os_innodb_umask = (ulint)my_umask;
-
- /* First calculate the default path for innodb_data_home_dir etc.,
- in case the user has not given any value.
-
- Note that when using the embedded server, the datadirectory is not
- necessarily the current directory of this program. */
-
- if (mysqld_embedded) {
- default_path = mysql_real_data_home;
- fil_path_to_mysql_datadir = mysql_real_data_home;
- } else {
- /* It's better to use current lib, to keep paths short */
- current_dir[0] = FN_CURLIB;
- current_dir[1] = FN_LIBCHAR;
- current_dir[2] = 0;
- default_path = current_dir;
- }
-
- ut_a(default_path);
-
- if (specialflag & SPECIAL_NO_PRIOR) {
- srv_set_thread_priorities = FALSE;
- } else {
- srv_set_thread_priorities = TRUE;
- srv_query_thread_priority = QUERY_PRIOR;
- }
-
- /* Set InnoDB initialization parameters according to the values
- read from MySQL .cnf file */
-
- /*--------------- Data files -------------------------*/
-
- /* The default dir for data files is the datadir of MySQL */
-
- srv_data_home = (innobase_data_home_dir ? innobase_data_home_dir :
- default_path);
-
- /* Set default InnoDB data file size to 10 MB and let it be
- auto-extending. Thus users can use InnoDB in >= 4.0 without having
- to specify any startup options. */
-
- if (!innobase_data_file_path) {
- innobase_data_file_path = (char*) "ibdata1:10M:autoextend";
- }
-
- /* Since InnoDB edits the argument in the next call, we make another
- copy of it: */
-
- internal_innobase_data_file_path = my_strdup(innobase_data_file_path,
- MYF(MY_FAE));
-
- ret = (bool) srv_parse_data_file_paths_and_sizes(
- internal_innobase_data_file_path,
- &srv_data_file_names,
- &srv_data_file_sizes,
- &srv_data_file_is_raw_partition,
- &srv_n_data_files,
- &srv_auto_extend_last_data_file,
- &srv_last_file_size_max);
- if (ret == FALSE) {
- sql_print_error(
- "InnoDB: syntax error in innodb_data_file_path");
- my_free(internal_innobase_data_file_path,
- MYF(MY_ALLOW_ZERO_PTR));
- goto error;
- }
-
- /* -------------- Log files ---------------------------*/
-
- /* The default dir for log files is the datadir of MySQL */
-
- if (!innobase_log_group_home_dir) {
- innobase_log_group_home_dir = default_path;
- }
-
-#ifdef UNIV_LOG_ARCHIVE
- /* Since innodb_log_arch_dir has no relevance under MySQL,
- starting from 4.0.6 we always set it the same as
- innodb_log_group_home_dir: */
-
- innobase_log_arch_dir = innobase_log_group_home_dir;
-
- srv_arch_dir = innobase_log_arch_dir;
-#endif /* UNIG_LOG_ARCHIVE */
-
- ret = (bool)
- srv_parse_log_group_home_dirs(innobase_log_group_home_dir,
- &srv_log_group_home_dirs);
-
- if (ret == FALSE || innobase_mirrored_log_groups != 1) {
- sql_print_error("syntax error in innodb_log_group_home_dir, or a "
- "wrong number of mirrored log groups");
-
- my_free(internal_innobase_data_file_path,
- MYF(MY_ALLOW_ZERO_PTR));
- goto error;
- }
-
- /* --------------------------------------------------*/
-
- srv_file_flush_method_str = innobase_unix_file_flush_method;
-
- srv_n_log_groups = (ulint) innobase_mirrored_log_groups;
- srv_n_log_files = (ulint) innobase_log_files_in_group;
- srv_log_file_size = (ulint) innobase_log_file_size;
-
-#ifdef UNIV_LOG_ARCHIVE
- srv_log_archive_on = (ulint) innobase_log_archive;
-#endif /* UNIV_LOG_ARCHIVE */
- srv_log_buffer_size = (ulint) innobase_log_buffer_size;
-
- /* We set srv_pool_size here in units of 1 kB. InnoDB internally
- changes the value so that it becomes the number of database pages. */
-
- if (innobase_buffer_pool_awe_mem_mb == 0) {
- /* Careful here: we first convert the signed long int to ulint
- and only after that divide */
-
- srv_pool_size = ((ulint) innobase_buffer_pool_size) / 1024;
- } else {
- srv_use_awe = TRUE;
- srv_pool_size = (ulint)
- (1024 * innobase_buffer_pool_awe_mem_mb);
- srv_awe_window_size = (ulint) innobase_buffer_pool_size;
-
- /* Note that what the user specified as
- innodb_buffer_pool_size is actually the AWE memory window
- size in this case, and the real buffer pool size is
- determined by .._awe_mem_mb. */
- }
-
- srv_mem_pool_size = (ulint) innobase_additional_mem_pool_size;
-
- srv_n_file_io_threads = (ulint) innobase_file_io_threads;
-
- srv_lock_wait_timeout = (ulint) innobase_lock_wait_timeout;
- srv_force_recovery = (ulint) innobase_force_recovery;
-
- srv_use_doublewrite_buf = (ibool) innobase_use_doublewrite;
- srv_use_checksums = (ibool) innobase_use_checksums;
-
- os_use_large_pages = (ibool) innobase_use_large_pages;
- os_large_page_size = (ulint) innobase_large_page_size;
-
- row_rollback_on_timeout = (ibool) innobase_rollback_on_timeout;
-
- srv_file_per_table = (ibool) innobase_file_per_table;
- srv_locks_unsafe_for_binlog = (ibool) innobase_locks_unsafe_for_binlog;
-
- srv_max_n_open_files = (ulint) innobase_open_files;
- srv_innodb_status = (ibool) innobase_create_status_file;
-
- srv_print_verbose_log = mysqld_embedded ? 0 : 1;
-
- /* Store the default charset-collation number of this MySQL
- installation */
-
- data_mysql_default_charset_coll = (ulint)default_charset_info->number;
-
- ut_a(DATA_MYSQL_LATIN1_SWEDISH_CHARSET_COLL ==
- my_charset_latin1.number);
- ut_a(DATA_MYSQL_BINARY_CHARSET_COLL == my_charset_bin.number);
-
- /* Store the latin1_swedish_ci character ordering table to InnoDB. For
- non-latin1_swedish_ci charsets we use the MySQL comparison functions,
- and consequently we do not need to know the ordering internally in
- InnoDB. */
-
- ut_a(0 == strcmp((char*)my_charset_latin1.name,
- (char*)"latin1_swedish_ci"));
- memcpy(srv_latin1_ordering, my_charset_latin1.sort_order, 256);
-
- /* Since we in this module access directly the fields of a trx
- struct, and due to different headers and flags it might happen that
- mutex_t has a different size in this module and in InnoDB
- modules, we check at run time that the size is the same in
- these compilation modules. */
-
- srv_sizeof_trx_t_in_ha_innodb_cc = sizeof(trx_t);
-
- err = innobase_start_or_create_for_mysql();
-
- if (err != DB_SUCCESS) {
- my_free(internal_innobase_data_file_path,
- MYF(MY_ALLOW_ZERO_PTR));
- goto error;
- }
-
- (void) hash_init(&innobase_open_tables,system_charset_info, 32, 0, 0,
- (hash_get_key) innobase_get_key, 0, 0);
- pthread_mutex_init(&innobase_share_mutex, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&prepare_commit_mutex, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&commit_threads_m, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&commit_cond_m, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&commit_cond, NULL);
- innodb_inited= 1;
-
- /* If this is a replication slave and we needed to do a crash recovery,
- set the master binlog position to what InnoDB internally knew about
- how far we got transactions durable inside InnoDB. There is a
- problem here: if the user used also MyISAM tables, InnoDB might not
- know the right position for them.
-
- THIS DOES NOT WORK CURRENTLY because replication seems to initialize
- glob_mi also after innobase_init. */
-
-/* if (trx_sys_mysql_master_log_pos != -1) {
- ut_memcpy(glob_mi.log_file_name, trx_sys_mysql_master_log_name,
- 1 + ut_strlen(trx_sys_mysql_master_log_name));
- glob_mi.pos = trx_sys_mysql_master_log_pos;
- }
-*/
- DBUG_RETURN(FALSE);
-error:
- have_innodb= SHOW_OPTION_DISABLED; // If we couldn't use handler
- DBUG_RETURN(TRUE);
-}
-
-/***********************************************************************
-Closes an InnoDB database. */
-
-bool
-innobase_end(void)
-/*==============*/
- /* out: TRUE if error */
-{
- int err= 0;
-
- DBUG_ENTER("innobase_end");
-
-#ifdef __NETWARE__ /* some special cleanup for NetWare */
- if (nw_panic) {
- set_panic_flag_for_netware();
- }
-#endif
- if (innodb_inited) {
-
- srv_fast_shutdown = (ulint) innobase_fast_shutdown;
- innodb_inited = 0;
- if (innobase_shutdown_for_mysql() != DB_SUCCESS) {
- err = 1;
- }
- hash_free(&innobase_open_tables);
- my_free(internal_innobase_data_file_path,
- MYF(MY_ALLOW_ZERO_PTR));
- pthread_mutex_destroy(&innobase_share_mutex);
- pthread_mutex_destroy(&prepare_commit_mutex);
- pthread_mutex_destroy(&commit_threads_m);
- pthread_mutex_destroy(&commit_cond_m);
- pthread_cond_destroy(&commit_cond);
- }
-
- DBUG_RETURN(err);
-}
-
-/********************************************************************
-Flushes InnoDB logs to disk and makes a checkpoint. Really, a commit flushes
-the logs, and the name of this function should be innobase_checkpoint. */
-
-bool
-innobase_flush_logs(void)
-/*=====================*/
- /* out: TRUE if error */
-{
- bool result = 0;
-
- DBUG_ENTER("innobase_flush_logs");
-
- log_buffer_flush_to_disk();
-
- DBUG_RETURN(result);
-}
-
-/*********************************************************************
-Commits a transaction in an InnoDB database. */
-
-void
-innobase_commit_low(
-/*================*/
- trx_t* trx) /* in: transaction handle */
-{
- if (trx->conc_state == TRX_NOT_STARTED) {
-
- return;
- }
-
-#ifdef HAVE_REPLICATION
- THD *thd=current_thd;
-
- if (thd && thd->slave_thread) {
- /* Update the replication position info inside InnoDB */
-
- trx->mysql_master_log_file_name
- = active_mi->rli.group_master_log_name;
- trx->mysql_master_log_pos = ((ib_longlong)
- active_mi->rli.future_group_master_log_pos);
- }
-#endif /* HAVE_REPLICATION */
-
- trx_commit_for_mysql(trx);
-}
-
-/*********************************************************************
-Creates an InnoDB transaction struct for the thd if it does not yet have one.
-Starts a new InnoDB transaction if a transaction is not yet started. And
-assigns a new snapshot for a consistent read if the transaction does not yet
-have one. */
-
-int
-innobase_start_trx_and_assign_read_view(
-/*====================================*/
- /* out: 0 */
- THD* thd) /* in: MySQL thread handle of the user for whom
- the transaction should be committed */
-{
- trx_t* trx;
-
- DBUG_ENTER("innobase_start_trx_and_assign_read_view");
-
- /* Create a new trx struct for thd, if it does not yet have one */
-
- trx = check_trx_exists(thd);
-
- /* This is just to play safe: release a possible FIFO ticket and
- search latch. Since we will reserve the kernel mutex, we have to
- release the search system latch first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- /* If the transaction is not started yet, start it */
-
- trx_start_if_not_started_noninline(trx);
-
- /* Assign a read view if the transaction does not have it yet */
-
- trx_assign_read_view(trx);
-
- /* Set the MySQL flag to mark that there is an active transaction */
-
- if (trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(current_thd);
-
- trx->active_trans = 1;
- }
-
- DBUG_RETURN(0);
-}
-
-/*********************************************************************
-Commits a transaction in an InnoDB database or marks an SQL statement
-ended. */
-static
-int
-innobase_commit(
-/*============*/
- /* out: 0 */
- THD* thd, /* in: MySQL thread handle of the user for whom
- the transaction should be committed */
- bool all) /* in: TRUE - commit transaction
- FALSE - the current SQL statement ended */
-{
- trx_t* trx;
-
- DBUG_ENTER("innobase_commit");
- DBUG_PRINT("trans", ("ending transaction"));
-
- trx = check_trx_exists(thd);
-
- /* Update the info whether we should skip XA steps that eat CPU time */
- trx->support_xa = (ibool)(thd->variables.innodb_support_xa);
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- if (trx->has_search_latch) {
- trx_search_latch_release_if_reserved(trx);
- }
-
- /* The flag trx->active_trans is set to 1 in
-
- 1. ::external_lock(),
- 2. ::start_stmt(),
- 3. innobase_query_caching_of_table_permitted(),
- 4. innobase_savepoint(),
- 5. ::init_table_handle_for_HANDLER(),
- 6. innobase_start_trx_and_assign_read_view(),
- 7. ::transactional_table_lock()
-
- and it is only set to 0 in a commit or a rollback. If it is 0 we know
- there cannot be resources to be freed and we could return immediately.
- For the time being, we play safe and do the cleanup though there should
- be nothing to clean up. */
-
- if (trx->active_trans == 0
- && trx->conc_state != TRX_NOT_STARTED) {
-
- sql_print_error("trx->active_trans == 0, but trx->conc_state != "
- "TRX_NOT_STARTED");
- }
- if (all
- || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
-
- /* We were instructed to commit the whole transaction, or
- this is an SQL statement end and autocommit is on */
-
- /* We need current binlog position for ibbackup to work.
- Note, the position is current because of prepare_commit_mutex */
-retry:
- if (srv_commit_concurrency > 0)
- {
- pthread_mutex_lock(&commit_cond_m);
- commit_threads++;
- if (commit_threads > srv_commit_concurrency)
- {
- commit_threads--;
- pthread_cond_wait(&commit_cond, &commit_cond_m);
- pthread_mutex_unlock(&commit_cond_m);
- goto retry;
- }
- else
- pthread_mutex_unlock(&commit_cond_m);
- }
-
- trx->mysql_log_file_name = mysql_bin_log.get_log_fname();
- trx->mysql_log_offset =
- (ib_longlong)mysql_bin_log.get_log_file()->pos_in_file;
-
- innobase_commit_low(trx);
-
- if (srv_commit_concurrency > 0)
- {
- pthread_mutex_lock(&commit_cond_m);
- commit_threads--;
- pthread_cond_signal(&commit_cond);
- pthread_mutex_unlock(&commit_cond_m);
- }
-
- if (trx->active_trans == 2) {
-
- pthread_mutex_unlock(&prepare_commit_mutex);
- }
-
- trx->active_trans = 0;
-
- } else {
- /* We just mark the SQL statement ended and do not do a
- transaction commit */
-
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some
- table in this SQL statement we release it now */
-
- row_unlock_table_autoinc_for_mysql(trx);
- }
- /* Store the current undo_no of the transaction so that we
- know where to roll back if we have to roll back the next
- SQL statement */
-
- trx_mark_sql_stat_end(trx);
- }
-
- /* Tell the InnoDB server that there might be work for utility
- threads: */
- if (trx->declared_to_be_inside_innodb) {
- /* Release our possible ticket in the FIFO */
-
- srv_conc_force_exit_innodb(trx);
- }
- srv_active_wake_master_thread();
-
- DBUG_RETURN(0);
-}
-
-/* TODO: put the
-MySQL-4.1 functionality back to 5.0. This is needed to get InnoDB Hot Backup
-to work. */
-
-/*********************************************************************
-This is called when MySQL writes the binlog entry for the current
-transaction. Writes to the InnoDB tablespace info which tells where the
-MySQL binlog entry for the current transaction ended. Also commits the
-transaction inside InnoDB but does NOT flush InnoDB log files to disk.
-To flush you have to call innobase_commit_complete(). We have separated
-flushing to eliminate the bottleneck of LOCK_log in log.cc which disabled
-InnoDB's group commit capability. */
-
-int
-innobase_report_binlog_offset_and_commit(
-/*=====================================*/
- /* out: 0 */
- THD* thd, /* in: user thread */
- void* trx_handle, /* in: InnoDB trx handle */
- char* log_file_name, /* in: latest binlog file name */
- my_off_t end_offset) /* in: the offset in the binlog file
- up to which we wrote */
-{
- trx_t* trx;
-
- trx = (trx_t*)trx_handle;
-
- ut_a(trx != NULL);
-
- trx->mysql_log_file_name = log_file_name;
- trx->mysql_log_offset = (ib_longlong)end_offset;
-
- trx->flush_log_later = TRUE;
-
- innobase_commit(thd, TRUE);
-
- trx->flush_log_later = FALSE;
-
- return(0);
-}
-
-#if 0
-/***********************************************************************
-This function stores the binlog offset and flushes logs. */
-
-void
-innobase_store_binlog_offset_and_flush_log(
-/*=======================================*/
- char *binlog_name, /* in: binlog name */
- longlong offset) /* in: binlog offset */
-{
- mtr_t mtr;
-
- assert(binlog_name != NULL);
-
- /* Start a mini-transaction */
- mtr_start_noninline(&mtr);
-
- /* Update the latest MySQL binlog name and offset info
- in trx sys header */
-
- trx_sys_update_mysql_binlog_offset(
- binlog_name,
- offset,
- TRX_SYS_MYSQL_LOG_INFO, &mtr);
-
- /* Commits the mini-transaction */
- mtr_commit(&mtr);
-
- /* Synchronous flush of the log buffer to disk */
- log_buffer_flush_to_disk();
-}
-#endif
-
-/*********************************************************************
-This is called after MySQL has written the binlog entry for the current
-transaction. Flushes the InnoDB log files to disk if required. */
-
-int
-innobase_commit_complete(
-/*=====================*/
- /* out: 0 */
- THD* thd) /* in: user thread */
-{
- trx_t* trx;
-
- trx = (trx_t*) thd->ha_data[innobase_hton.slot];
-
- if (trx && trx->active_trans) {
-
- trx->active_trans = 0;
-
- if (UNIV_UNLIKELY(srv_flush_log_at_trx_commit == 0)) {
-
- return(0);
- }
-
- trx_commit_complete_for_mysql(trx);
- }
-
- return(0);
-}
-
-/*********************************************************************
-Rolls back a transaction or the latest SQL statement. */
-
-static int
-innobase_rollback(
-/*==============*/
- /* out: 0 or error number */
- THD* thd, /* in: handle to the MySQL thread of the user
- whose transaction should be rolled back */
- bool all) /* in: TRUE - commit transaction
- FALSE - the current SQL statement ended */
-{
- int error = 0;
- trx_t* trx;
-
- DBUG_ENTER("innobase_rollback");
- DBUG_PRINT("trans", ("aborting transaction"));
-
- trx = check_trx_exists(thd);
-
- /* Update the info whether we should skip XA steps that eat CPU time */
- trx->support_xa = (ibool)(thd->variables.innodb_support_xa);
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some table (if
- we come here to roll back the latest SQL statement) we
- release it now before a possibly lengthy rollback */
-
- row_unlock_table_autoinc_for_mysql(trx);
- }
-
- if (all
- || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
-
- error = trx_rollback_for_mysql(trx);
- trx->active_trans = 0;
- } else {
- error = trx_rollback_last_sql_stat_for_mysql(trx);
- }
-
- DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
-}
-
-/*********************************************************************
-Rolls back a transaction */
-
-int
-innobase_rollback_trx(
-/*==================*/
- /* out: 0 or error number */
- trx_t* trx) /* in: transaction */
-{
- int error = 0;
-
- DBUG_ENTER("innobase_rollback_trx");
- DBUG_PRINT("trans", ("aborting transaction"));
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some table (if
- we come here to roll back the latest SQL statement) we
- release it now before a possibly lengthy rollback */
-
- row_unlock_table_autoinc_for_mysql(trx);
- }
-
- error = trx_rollback_for_mysql(trx);
-
- DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
-}
-
-/*********************************************************************
-Rolls back a transaction to a savepoint. */
-
-static int
-innobase_rollback_to_savepoint(
-/*===========================*/
- /* out: 0 if success, HA_ERR_NO_SAVEPOINT if
- no savepoint with the given name */
- THD* thd, /* in: handle to the MySQL thread of the user
- whose transaction should be rolled back */
- void *savepoint) /* in: savepoint data */
-{
- ib_longlong mysql_binlog_cache_pos;
- int error = 0;
- trx_t* trx;
- char name[64];
-
- DBUG_ENTER("innobase_rollback_to_savepoint");
-
- trx = check_trx_exists(thd);
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- /* TODO: use provided savepoint data area to store savepoint data */
-
- longlong2str((ulint)savepoint, name, 36);
-
- error = (int) trx_rollback_to_savepoint_for_mysql(trx, name,
- &mysql_binlog_cache_pos);
- DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
-}
-
-/*********************************************************************
-Release transaction savepoint name. */
-static
-int
-innobase_release_savepoint(
-/*=======================*/
- /* out: 0 if success, HA_ERR_NO_SAVEPOINT if
- no savepoint with the given name */
- THD* thd, /* in: handle to the MySQL thread of the user
- whose transaction should be rolled back */
- void* savepoint) /* in: savepoint data */
-{
- int error = 0;
- trx_t* trx;
- char name[64];
-
- DBUG_ENTER("innobase_release_savepoint");
-
- trx = check_trx_exists(thd);
-
- /* TODO: use provided savepoint data area to store savepoint data */
-
- longlong2str((ulint)savepoint, name, 36);
-
- error = (int) trx_release_savepoint_for_mysql(trx, name);
-
- DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
-}
-
-/*********************************************************************
-Sets a transaction savepoint. */
-static
-int
-innobase_savepoint(
-/*===============*/
- /* out: always 0, that is, always succeeds */
- THD* thd, /* in: handle to the MySQL thread */
- void* savepoint) /* in: savepoint data */
-{
- int error = 0;
- trx_t* trx;
-
- DBUG_ENTER("innobase_savepoint");
-
- /*
- In the autocommit mode there is no sense to set a savepoint
- (unless we are in sub-statement), so SQL layer ensures that
- this method is never called in such situation.
- */
- DBUG_ASSERT(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
- thd->in_sub_stmt);
-
- trx = check_trx_exists(thd);
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- /* cannot happen outside of transaction */
- DBUG_ASSERT(trx->active_trans);
-
- /* TODO: use provided savepoint data area to store savepoint data */
- char name[64];
- longlong2str((ulint)savepoint,name,36);
-
- error = (int) trx_savepoint_for_mysql(trx, name, (ib_longlong)0);
-
- DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
-}
-
-/*********************************************************************
-Frees a possible InnoDB trx object associated with the current THD. */
-static
-int
-innobase_close_connection(
-/*======================*/
- /* out: 0 or error number */
- THD* thd) /* in: handle to the MySQL thread of the user
- whose resources should be free'd */
-{
- trx_t* trx;
-
- trx = (trx_t*)thd->ha_data[innobase_hton.slot];
-
- ut_a(trx);
-
- if (trx->active_trans == 0
- && trx->conc_state != TRX_NOT_STARTED) {
-
- sql_print_error("trx->active_trans == 0, but trx->conc_state != "
- "TRX_NOT_STARTED");
- }
-
-
- if (trx->conc_state != TRX_NOT_STARTED &&
- global_system_variables.log_warnings)
- sql_print_warning("MySQL is closing a connection that has an active "
- "InnoDB transaction. %lu row modifications will "
- "roll back.",
- (ulong)trx->undo_no.low);
-
- innobase_rollback_trx(trx);
-
- trx_free_for_mysql(trx);
-
- return(0);
-}
-
-
-/*****************************************************************************
-** InnoDB database tables
-*****************************************************************************/
-
-/********************************************************************
-Get the record format from the data dictionary. */
-enum row_type
-ha_innobase::get_row_type() const
-/*=============================*/
- /* out: ROW_TYPE_REDUNDANT or ROW_TYPE_COMPACT */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
-
- if (prebuilt && prebuilt->table) {
- if (prebuilt->table->comp) {
- return(ROW_TYPE_COMPACT);
- } else {
- return(ROW_TYPE_REDUNDANT);
- }
- }
- ut_ad(0);
- return(ROW_TYPE_NOT_USED);
-}
-
-/********************************************************************
-Gives the file extension of an InnoDB single-table tablespace. */
-static const char* ha_innobase_exts[] = {
- ".ibd",
- NullS
-};
-
-const char**
-ha_innobase::bas_ext() const
-/*========================*/
- /* out: file extension string */
-{
- return ha_innobase_exts;
-}
-
-
-/*********************************************************************
-Normalizes a table name string. A normalized name consists of the
-database name catenated to '/' and table name. An example:
-test/mytable. On Windows normalization puts both the database name and the
-table name always to lower case. */
-static
-void
-normalize_table_name(
-/*=================*/
- char* norm_name, /* out: normalized name as a
- null-terminated string */
- const char* name) /* in: table name string */
-{
- char* name_ptr;
- char* db_ptr;
- char* ptr;
-
- /* Scan name from the end */
-
- ptr = strend(name)-1;
-
- while (ptr >= name && *ptr != '\\' && *ptr != '/') {
- ptr--;
- }
-
- name_ptr = ptr + 1;
-
- DBUG_ASSERT(ptr > name);
-
- ptr--;
-
- while (ptr >= name && *ptr != '\\' && *ptr != '/') {
- ptr--;
- }
-
- db_ptr = ptr + 1;
-
- memcpy(norm_name, db_ptr, strlen(name) + 1 - (db_ptr - name));
-
- norm_name[name_ptr - db_ptr - 1] = '/';
-
-#ifdef __WIN__
- innobase_casedn_str(norm_name);
-#endif
-}
-
-/*********************************************************************
-Creates and opens a handle to a table which already exists in an InnoDB
-database. */
-
-int
-ha_innobase::open(
-/*==============*/
- /* out: 1 if error, 0 if success */
- const char* name, /* in: table name */
- int mode, /* in: not used */
- uint test_if_locked) /* in: not used */
-{
- dict_table_t* ib_table;
- char norm_name[1000];
- THD* thd;
-
- DBUG_ENTER("ha_innobase::open");
-
- UT_NOT_USED(mode);
- UT_NOT_USED(test_if_locked);
-
- thd = current_thd;
- normalize_table_name(norm_name, name);
-
- user_thd = NULL;
-
- last_query_id = (ulong)-1;
-
- if (!(share=get_share(name))) {
-
- DBUG_RETURN(1);
- }
-
- /* Create buffers for packing the fields of a record. Why
- table->reclength did not work here? Obviously, because char
- fields when packed actually became 1 byte longer, when we also
- stored the string length as the first byte. */
-
- upd_and_key_val_buff_len =
- table->s->reclength + table->s->max_key_length
- + MAX_REF_PARTS * 3;
- if (!(mysql_byte*) my_multi_malloc(MYF(MY_WME),
- &upd_buff, upd_and_key_val_buff_len,
- &key_val_buff, upd_and_key_val_buff_len,
- NullS)) {
- free_share(share);
-
- DBUG_RETURN(1);
- }
-
- /* Get pointer to a table object in InnoDB dictionary cache */
-
- ib_table = dict_table_get_and_increment_handle_count(
- norm_name, NULL);
- if (NULL == ib_table) {
- ut_print_timestamp(stderr);
- sql_print_error("Cannot find table %s from the internal data "
- "dictionary\nof InnoDB though the .frm file "
- "for the table exists. Maybe you\nhave "
- "deleted and recreated InnoDB data files but "
- "have forgotten\nto delete the corresponding "
- ".frm files of InnoDB tables, or you\n"
- "have moved .frm files to another database?\n"
- "See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
- "how you can resolve the problem.\n",
- norm_name);
- free_share(share);
- my_free((gptr) upd_buff, MYF(0));
- my_errno = ENOENT;
-
- DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
- }
-
- if (ib_table->ibd_file_missing && !thd->tablespace_op) {
- ut_print_timestamp(stderr);
- sql_print_error("MySQL is trying to open a table handle but "
- "the .ibd file for\ntable %s does not exist.\n"
- "Have you deleted the .ibd file from the "
- "database directory under\nthe MySQL datadir, "
- "or have you used DISCARD TABLESPACE?\n"
- "See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
- "how you can resolve the problem.\n",
- norm_name);
- free_share(share);
- my_free((gptr) upd_buff, MYF(0));
- my_errno = ENOENT;
-
- dict_table_decrement_handle_count(ib_table);
- DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
- }
-
- innobase_prebuilt = row_create_prebuilt(ib_table);
-
- ((row_prebuilt_t*)innobase_prebuilt)->mysql_row_len =
- table->s->reclength;
-
- /* Looks like MySQL-3.23 sometimes has primary key number != 0 */
-
- primary_key = table->s->primary_key;
- key_used_on_scan = primary_key;
-
- /* Allocate a buffer for a 'row reference'. A row reference is
- a string of bytes of length ref_length which uniquely specifies
- a row in our table. Note that MySQL may also compare two row
- references for equality by doing a simple memcmp on the strings
- of length ref_length! */
-
- if (!row_table_got_default_clust_index(ib_table)) {
- if (primary_key >= MAX_KEY) {
- sql_print_error("Table %s has a primary key in InnoDB data "
- "dictionary, but not in MySQL!", name);
- }
-
- ((row_prebuilt_t*)innobase_prebuilt)
- ->clust_index_was_generated = FALSE;
- /* MySQL allocates the buffer for ref. key_info->key_length
- includes space for all key columns + one byte for each column
- that may be NULL. ref_length must be as exact as possible to
- save space, because all row reference buffers are allocated
- based on ref_length. */
-
- ref_length = table->key_info[primary_key].key_length;
- } else {
- if (primary_key != MAX_KEY) {
- sql_print_error("Table %s has no primary key in InnoDB data "
- "dictionary, but has one in MySQL! If you "
- "created the table with a MySQL version < "
- "3.23.54 and did not define a primary key, "
- "but defined a unique key with all non-NULL "
- "columns, then MySQL internally treats that "
- "key as the primary key. You can fix this "
- "error by dump + DROP + CREATE + reimport "
- "of the table.", name);
- }
-
- ((row_prebuilt_t*)innobase_prebuilt)
- ->clust_index_was_generated = TRUE;
-
- ref_length = DATA_ROW_ID_LEN;
-
- /* If we automatically created the clustered index, then
- MySQL does not know about it, and MySQL must NOT be aware
- of the index used on scan, to make it avoid checking if we
- update the column of the index. That is why we assert below
- that key_used_on_scan is the undefined value MAX_KEY.
- The column is the row id in the automatical generation case,
- and it will never be updated anyway. */
-
- if (key_used_on_scan != MAX_KEY) {
- sql_print_warning("Table %s key_used_on_scan is %lu even "
- "though there is no primary key inside "
- "InnoDB.", name, (ulong) key_used_on_scan);
- }
- }
-
- block_size = 16 * 1024; /* Index block size in InnoDB: used by MySQL
- in query optimization */
-
- /* Init table lock structure */
- thr_lock_data_init(&share->lock,&lock,(void*) 0);
-
- info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
-
- DBUG_RETURN(0);
-}
-
-uint
-ha_innobase::max_supported_key_part_length() const
-{
- return(DICT_MAX_INDEX_COL_LEN - 1);
-}
-
-/**********************************************************************
-Closes a handle to an InnoDB table. */
-
-int
-ha_innobase::close(void)
-/*====================*/
- /* out: 0 */
-{
- DBUG_ENTER("ha_innobase::close");
-
- row_prebuilt_free((row_prebuilt_t*) innobase_prebuilt);
-
- my_free((gptr) upd_buff, MYF(0));
- free_share(share);
-
- /* Tell InnoDB server that there might be work for
- utility threads: */
-
- srv_active_wake_master_thread();
-
- DBUG_RETURN(0);
-}
-
-/* The following accessor functions should really be inside MySQL code! */
-
-/******************************************************************
-Gets field offset for a field in a table. */
-inline
-uint
-get_field_offset(
-/*=============*/
- /* out: offset */
- TABLE* table, /* in: MySQL table object */
- Field* field) /* in: MySQL field object */
-{
- return((uint) (field->ptr - (char*) table->record[0]));
-}
-
-/******************************************************************
-Checks if a field in a record is SQL NULL. Uses the record format
-information in table to track the null bit in record. */
-inline
-uint
-field_in_record_is_null(
-/*====================*/
- /* out: 1 if NULL, 0 otherwise */
- TABLE* table, /* in: MySQL table object */
- Field* field, /* in: MySQL field object */
- char* record) /* in: a row in MySQL format */
-{
- int null_offset;
-
- if (!field->null_ptr) {
-
- return(0);
- }
-
- null_offset = (uint) ((char*) field->null_ptr
- - (char*) table->record[0]);
-
- if (record[null_offset] & field->null_bit) {
-
- return(1);
- }
-
- return(0);
-}
-
-/******************************************************************
-Sets a field in a record to SQL NULL. Uses the record format
-information in table to track the null bit in record. */
-inline
-void
-set_field_in_record_to_null(
-/*========================*/
- TABLE* table, /* in: MySQL table object */
- Field* field, /* in: MySQL field object */
- char* record) /* in: a row in MySQL format */
-{
- int null_offset;
-
- null_offset = (uint) ((char*) field->null_ptr
- - (char*) table->record[0]);
-
- record[null_offset] = record[null_offset] | field->null_bit;
-}
-
-extern "C" {
-/*****************************************************************
-InnoDB uses this function to compare two data fields for which the data type
-is such that we must use MySQL code to compare them. NOTE that the prototype
-of this function is in rem0cmp.c in InnoDB source code! If you change this
-function, remember to update the prototype there! */
-
-int
-innobase_mysql_cmp(
-/*===============*/
- /* out: 1, 0, -1, if a is greater,
- equal, less than b, respectively */
- int mysql_type, /* in: MySQL type */
- uint charset_number, /* in: number of the charset */
- unsigned char* a, /* in: data field */
- unsigned int a_length, /* in: data field length,
- not UNIV_SQL_NULL */
- unsigned char* b, /* in: data field */
- unsigned int b_length) /* in: data field length,
- not UNIV_SQL_NULL */
-{
- CHARSET_INFO* charset;
- enum_field_types mysql_tp;
- int ret;
-
- DBUG_ASSERT(a_length != UNIV_SQL_NULL);
- DBUG_ASSERT(b_length != UNIV_SQL_NULL);
-
- mysql_tp = (enum_field_types) mysql_type;
-
- switch (mysql_tp) {
-
- case MYSQL_TYPE_BIT:
- case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_VAR_STRING:
- case FIELD_TYPE_TINY_BLOB:
- case FIELD_TYPE_MEDIUM_BLOB:
- case FIELD_TYPE_BLOB:
- case FIELD_TYPE_LONG_BLOB:
- case MYSQL_TYPE_VARCHAR:
- /* Use the charset number to pick the right charset struct for
- the comparison. Since the MySQL function get_charset may be
- slow before Bar removes the mutex operation there, we first
- look at 2 common charsets directly. */
-
- if (charset_number == default_charset_info->number) {
- charset = default_charset_info;
- } else if (charset_number == my_charset_latin1.number) {
- charset = &my_charset_latin1;
- } else {
- charset = get_charset(charset_number, MYF(MY_WME));
-
- if (charset == NULL) {
- sql_print_error("InnoDB needs charset %lu for doing "
- "a comparison, but MySQL cannot "
- "find that charset.",
- (ulong) charset_number);
- ut_a(0);
- }
- }
-
- /* Starting from 4.1.3, we use strnncollsp() in comparisons of
- non-latin1_swedish_ci strings. NOTE that the collation order
- changes then: 'b\0\0...' is ordered BEFORE 'b ...'. Users
- having indexes on such data need to rebuild their tables! */
-
- ret = charset->coll->strnncollsp(charset,
- a, a_length,
- b, b_length, 0);
- if (ret < 0) {
- return(-1);
- } else if (ret > 0) {
- return(1);
- } else {
- return(0);
- }
- default:
- assert(0);
- }
-
- return(0);
-}
-}
-
-/******************************************************************
-Converts a MySQL type to an InnoDB type. Note that this function returns
-the 'mtype' of InnoDB. InnoDB differentiates between MySQL's old <= 4.1
-VARCHAR and the new true VARCHAR in >= 5.0.3 by the 'prtype'. */
-inline
-ulint
-get_innobase_type_from_mysql_type(
-/*==============================*/
- /* out: DATA_BINARY, DATA_VARCHAR, ... */
- ulint* unsigned_flag, /* out: DATA_UNSIGNED if an 'unsigned type';
- at least ENUM and SET, and unsigned integer
- types are 'unsigned types' */
- Field* field) /* in: MySQL field */
-{
- /* The following asserts try to check that the MySQL type code fits in
- 8 bits: this is used in ibuf and also when DATA_NOT_NULL is ORed to
- the type */
-
- DBUG_ASSERT((ulint)FIELD_TYPE_STRING < 256);
- DBUG_ASSERT((ulint)FIELD_TYPE_VAR_STRING < 256);
- DBUG_ASSERT((ulint)FIELD_TYPE_DOUBLE < 256);
- DBUG_ASSERT((ulint)FIELD_TYPE_FLOAT < 256);
- DBUG_ASSERT((ulint)FIELD_TYPE_DECIMAL < 256);
-
- if (field->flags & UNSIGNED_FLAG) {
-
- *unsigned_flag = DATA_UNSIGNED;
- } else {
- *unsigned_flag = 0;
- }
-
- if (field->real_type() == FIELD_TYPE_ENUM
- || field->real_type() == FIELD_TYPE_SET) {
-
- /* MySQL has field->type() a string type for these, but the
- data is actually internally stored as an unsigned integer
- code! */
-
- *unsigned_flag = DATA_UNSIGNED; /* MySQL has its own unsigned
- flag set to zero, even though
- internally this is an unsigned
- integer type */
- return(DATA_INT);
- }
-
- switch (field->type()) {
- /* NOTE that we only allow string types in DATA_MYSQL
- and DATA_VARMYSQL */
- case MYSQL_TYPE_VAR_STRING: /* old <= 4.1 VARCHAR */
- case MYSQL_TYPE_VARCHAR: /* new >= 5.0.3 true VARCHAR */
- if (field->binary()) {
- return(DATA_BINARY);
- } else if (strcmp(
- field->charset()->name,
- "latin1_swedish_ci") == 0) {
- return(DATA_VARCHAR);
- } else {
- return(DATA_VARMYSQL);
- }
- case MYSQL_TYPE_BIT:
- case MYSQL_TYPE_STRING: if (field->binary()) {
-
- return(DATA_FIXBINARY);
- } else if (strcmp(
- field->charset()->name,
- "latin1_swedish_ci") == 0) {
- return(DATA_CHAR);
- } else {
- return(DATA_MYSQL);
- }
- case FIELD_TYPE_NEWDECIMAL:
- return(DATA_FIXBINARY);
- case FIELD_TYPE_LONG:
- case FIELD_TYPE_LONGLONG:
- case FIELD_TYPE_TINY:
- case FIELD_TYPE_SHORT:
- case FIELD_TYPE_INT24:
- case FIELD_TYPE_DATE:
- case FIELD_TYPE_DATETIME:
- case FIELD_TYPE_YEAR:
- case FIELD_TYPE_NEWDATE:
- case FIELD_TYPE_TIME:
- case FIELD_TYPE_TIMESTAMP:
- return(DATA_INT);
- case FIELD_TYPE_FLOAT:
- return(DATA_FLOAT);
- case FIELD_TYPE_DOUBLE:
- return(DATA_DOUBLE);
- case FIELD_TYPE_DECIMAL:
- return(DATA_DECIMAL);
- case FIELD_TYPE_GEOMETRY:
- case FIELD_TYPE_TINY_BLOB:
- case FIELD_TYPE_MEDIUM_BLOB:
- case FIELD_TYPE_BLOB:
- case FIELD_TYPE_LONG_BLOB:
- return(DATA_BLOB);
- default:
- assert(0);
- }
-
- return(0);
-}
-
-/***********************************************************************
-Writes an unsigned integer value < 64k to 2 bytes, in the little-endian
-storage format. */
-inline
-void
-innobase_write_to_2_little_endian(
-/*==============================*/
- byte* buf, /* in: where to store */
- ulint val) /* in: value to write, must be < 64k */
-{
- ut_a(val < 256 * 256);
-
- buf[0] = (byte)(val & 0xFF);
- buf[1] = (byte)(val / 256);
-}
-
-/***********************************************************************
-Reads an unsigned integer value < 64k from 2 bytes, in the little-endian
-storage format. */
-inline
-uint
-innobase_read_from_2_little_endian(
-/*===============================*/
- /* out: value */
- const mysql_byte* buf) /* in: from where to read */
-{
- return (uint) ((ulint)(buf[0]) + 256 * ((ulint)(buf[1])));
-}
-
-/***********************************************************************
-Stores a key value for a row to a buffer. */
-
-uint
-ha_innobase::store_key_val_for_row(
-/*===============================*/
- /* out: key value length as stored in buff */
- uint keynr, /* in: key number */
- char* buff, /* in/out: buffer for the key value (in MySQL
- format) */
- uint buff_len,/* in: buffer length */
- const mysql_byte* record)/* in: row in MySQL format */
-{
- KEY* key_info = table->key_info + keynr;
- KEY_PART_INFO* key_part = key_info->key_part;
- KEY_PART_INFO* end = key_part + key_info->key_parts;
- char* buff_start = buff;
- enum_field_types mysql_type;
- Field* field;
- ibool is_null;
-
- DBUG_ENTER("store_key_val_for_row");
-
- /* The format for storing a key field in MySQL is the following:
-
- 1. If the column can be NULL, then in the first byte we put 1 if the
- field value is NULL, 0 otherwise.
-
- 2. If the column is of a BLOB type (it must be a column prefix field
- in this case), then we put the length of the data in the field to the
- next 2 bytes, in the little-endian format. If the field is SQL NULL,
- then these 2 bytes are set to 0. Note that the length of data in the
- field is <= column prefix length.
-
- 3. In a column prefix field, prefix_len next bytes are reserved for
- data. In a normal field the max field length next bytes are reserved
- for data. For a VARCHAR(n) the max field length is n. If the stored
- value is the SQL NULL then these data bytes are set to 0.
-
- 4. We always use a 2 byte length for a true >= 5.0.3 VARCHAR. Note that
- in the MySQL row format, the length is stored in 1 or 2 bytes,
- depending on the maximum allowed length. But in the MySQL key value
- format, the length always takes 2 bytes.
-
- We have to zero-fill the buffer so that MySQL is able to use a
- simple memcmp to compare two key values to determine if they are
- equal. MySQL does this to compare contents of two 'ref' values. */
-
- bzero(buff, buff_len);
-
- for (; key_part != end; key_part++) {
- is_null = FALSE;
-
- if (key_part->null_bit) {
- if (record[key_part->null_offset]
- & key_part->null_bit) {
- *buff = 1;
- is_null = TRUE;
- } else {
- *buff = 0;
- }
- buff++;
- }
-
- field = key_part->field;
- mysql_type = field->type();
-
- if (mysql_type == MYSQL_TYPE_VARCHAR) {
- /* >= 5.0.3 true VARCHAR */
- ulint lenlen;
- ulint len;
- byte* data;
- ulint key_len;
- ulint true_len;
- CHARSET_INFO* cs;
- int error=0;
-
- key_len = key_part->length;
-
- if (is_null) {
- buff += key_len + 2;
-
- continue;
- }
- cs = field->charset();
-
- lenlen = (ulint)
- (((Field_varstring*)field)->length_bytes);
-
- data = row_mysql_read_true_varchar(&len,
- (byte*) (record
- + (ulint)get_field_offset(table, field)),
- lenlen);
-
- true_len = len;
-
- /* For multi byte character sets we need to calculate
- the true length of the key */
-
- if (len > 0 && cs->mbmaxlen > 1) {
- true_len = (ulint) cs->cset->well_formed_len(cs,
- (const char *) data,
- (const char *) data + len,
- key_len / cs->mbmaxlen,
- &error);
- }
-
- /* In a column prefix index, we may need to truncate
- the stored value: */
-
- if (true_len > key_len) {
- true_len = key_len;
- }
-
- /* The length in a key value is always stored in 2
- bytes */
-
- row_mysql_store_true_var_len((byte*)buff, true_len, 2);
- buff += 2;
-
- memcpy(buff, data, true_len);
-
- /* Note that we always reserve the maximum possible
- length of the true VARCHAR in the key value, though
- only len first bytes after the 2 length bytes contain
- actual data. The rest of the space was reset to zero
- in the bzero() call above. */
-
- buff += key_len;
-
- } else if (mysql_type == FIELD_TYPE_TINY_BLOB
- || mysql_type == FIELD_TYPE_MEDIUM_BLOB
- || mysql_type == FIELD_TYPE_BLOB
- || mysql_type == FIELD_TYPE_LONG_BLOB) {
-
- CHARSET_INFO* cs;
- ulint key_len;
- ulint true_len;
- int error=0;
- ulint blob_len;
- byte* blob_data;
-
- ut_a(key_part->key_part_flag & HA_PART_KEY_SEG);
-
- key_len = key_part->length;
-
- if (is_null) {
- buff += key_len + 2;
-
- continue;
- }
-
- cs = field->charset();
-
- blob_data = row_mysql_read_blob_ref(&blob_len,
- (byte*) (record
- + (ulint)get_field_offset(table, field)),
- (ulint) field->pack_length());
-
- true_len = blob_len;
-
- ut_a(get_field_offset(table, field)
- == key_part->offset);
-
- /* For multi byte character sets we need to calculate
- the true length of the key */
-
- if (blob_len > 0 && cs->mbmaxlen > 1) {
- true_len = (ulint) cs->cset->well_formed_len(cs,
- (const char *) blob_data,
- (const char *) blob_data
- + blob_len,
- key_len / cs->mbmaxlen,
- &error);
- }
-
- /* All indexes on BLOB and TEXT are column prefix
- indexes, and we may need to truncate the data to be
- stored in the key value: */
-
- if (true_len > key_len) {
- true_len = key_len;
- }
-
- /* MySQL reserves 2 bytes for the length and the
- storage of the number is little-endian */
-
- innobase_write_to_2_little_endian(
- (byte*)buff, true_len);
- buff += 2;
-
- memcpy(buff, blob_data, true_len);
-
- /* Note that we always reserve the maximum possible
- length of the BLOB prefix in the key value. */
-
- buff += key_len;
- } else {
- /* Here we handle all other data types except the
- true VARCHAR, BLOB and TEXT. Note that the column
- value we store may be also in a column prefix
- index. */
-
- CHARSET_INFO* cs;
- ulint true_len;
- ulint key_len;
- const mysql_byte* src_start;
- int error=0;
- enum_field_types real_type;
-
- key_len = key_part->length;
-
- if (is_null) {
- buff += key_len;
-
- continue;
- }
-
- src_start = record + key_part->offset;
- real_type = field->real_type();
- true_len = key_len;
-
- /* Character set for the field is defined only
- to fields whose type is string and real field
- type is not enum or set. For these fields check
- if character set is multi byte. */
-
- if (real_type != FIELD_TYPE_ENUM
- && real_type != FIELD_TYPE_SET
- && ( mysql_type == MYSQL_TYPE_VAR_STRING
- || mysql_type == MYSQL_TYPE_STRING)) {
-
- cs = field->charset();
-
- /* For multi byte character sets we need to
- calculate the true length of the key */
-
- if (key_len > 0 && cs->mbmaxlen > 1) {
-
- true_len = (ulint)
- cs->cset->well_formed_len(cs,
- (const char *)src_start,
- (const char *)src_start
- + key_len,
- key_len / cs->mbmaxlen,
- &error);
- }
- }
-
- memcpy(buff, src_start, true_len);
- buff += true_len;
-
- /* Pad the unused space with spaces. Note that no
- padding is ever needed for UCS-2 because in MySQL,
- all UCS2 characters are 2 bytes, as MySQL does not
- support surrogate pairs, which are needed to represent
- characters in the range U+10000 to U+10FFFF. */
-
- if (true_len < key_len) {
- ulint pad_len = key_len - true_len;
- memset(buff, ' ', pad_len);
- buff += pad_len;
- }
- }
- }
-
- ut_a(buff <= buff_start + buff_len);
-
- DBUG_RETURN((uint)(buff - buff_start));
-}
-
-/******************************************************************
-Builds a 'template' to the prebuilt struct. The template is used in fast
-retrieval of just those column values MySQL needs in its processing. */
-static
-void
-build_template(
-/*===========*/
- row_prebuilt_t* prebuilt, /* in: prebuilt struct */
- THD* thd, /* in: current user thread, used
- only if templ_type is
- ROW_MYSQL_REC_FIELDS */
- TABLE* table, /* in: MySQL table */
- ulint templ_type) /* in: ROW_MYSQL_WHOLE_ROW or
- ROW_MYSQL_REC_FIELDS */
-{
- dict_index_t* index;
- dict_index_t* clust_index;
- mysql_row_templ_t* templ;
- Field* field;
- ulint n_fields;
- ulint n_requested_fields = 0;
- ibool fetch_all_in_key = FALSE;
- ibool fetch_primary_key_cols = FALSE;
- ulint i;
- /* byte offset of the end of last requested column */
- ulint mysql_prefix_len = 0;
-
- if (prebuilt->select_lock_type == LOCK_X) {
- /* We always retrieve the whole clustered index record if we
- use exclusive row level locks, for example, if the read is
- done in an UPDATE statement. */
-
- templ_type = ROW_MYSQL_WHOLE_ROW;
- }
-
- if (templ_type == ROW_MYSQL_REC_FIELDS) {
- if (prebuilt->hint_need_to_fetch_extra_cols
- == ROW_RETRIEVE_ALL_COLS) {
-
- /* We know we must at least fetch all columns in the key, or
- all columns in the table */
-
- if (prebuilt->read_just_key) {
- /* MySQL has instructed us that it is enough to
- fetch the columns in the key; looks like MySQL
- can set this flag also when there is only a
- prefix of the column in the key: in that case we
- retrieve the whole column from the clustered
- index */
-
- fetch_all_in_key = TRUE;
- } else {
- templ_type = ROW_MYSQL_WHOLE_ROW;
- }
- } else if (prebuilt->hint_need_to_fetch_extra_cols
- == ROW_RETRIEVE_PRIMARY_KEY) {
- /* We must at least fetch all primary key cols. Note that if
- the clustered index was internally generated by InnoDB on the
- row id (no primary key was defined), then
- row_search_for_mysql() will always retrieve the row id to a
- special buffer in the prebuilt struct. */
-
- fetch_primary_key_cols = TRUE;
- }
- }
-
- clust_index = dict_table_get_first_index_noninline(prebuilt->table);
-
- if (templ_type == ROW_MYSQL_REC_FIELDS) {
- index = prebuilt->index;
- } else {
- index = clust_index;
- }
-
- if (index == clust_index) {
- prebuilt->need_to_access_clustered = TRUE;
- } else {
- prebuilt->need_to_access_clustered = FALSE;
- /* Below we check column by column if we need to access
- the clustered index */
- }
-
- n_fields = (ulint)table->s->fields; /* number of columns */
-
- if (!prebuilt->mysql_template) {
- prebuilt->mysql_template = (mysql_row_templ_t*)
- mem_alloc_noninline(
- n_fields * sizeof(mysql_row_templ_t));
- }
-
- prebuilt->template_type = templ_type;
- prebuilt->null_bitmap_len = table->s->null_bytes;
-
- prebuilt->templ_contains_blob = FALSE;
-
- /* Note that in InnoDB, i is the column number. MySQL calls columns
- 'fields'. */
- for (i = 0; i < n_fields; i++) {
- templ = prebuilt->mysql_template + n_requested_fields;
- field = table->field[i];
-
- if (UNIV_LIKELY(templ_type == ROW_MYSQL_REC_FIELDS)) {
- /* Decide which columns we should fetch
- and which we can skip. */
- register const ibool index_contains_field =
- dict_index_contains_col_or_prefix(index, i);
-
- if (!index_contains_field && prebuilt->read_just_key) {
- /* If this is a 'key read', we do not need
- columns that are not in the key */
-
- goto skip_field;
- }
-
- if (index_contains_field && fetch_all_in_key) {
- /* This field is needed in the query */
-
- goto include_field;
- }
-
- if (thd->query_id == field->query_id) {
- /* This field is needed in the query */
-
- goto include_field;
- }
-
- if (fetch_primary_key_cols
- && dict_table_col_in_clustered_key(index->table,
- i)) {
- /* This field is needed in the query */
-
- goto include_field;
- }
-
- /* This field is not needed in the query, skip it */
-
- goto skip_field;
- }
-include_field:
- n_requested_fields++;
-
- templ->col_no = i;
-
- if (index == clust_index) {
- templ->rec_field_no = (index->table->cols + i)
- ->clust_pos;
- } else {
- templ->rec_field_no = dict_index_get_nth_col_pos(
- index, i);
- }
-
- if (templ->rec_field_no == ULINT_UNDEFINED) {
- prebuilt->need_to_access_clustered = TRUE;
- }
-
- if (field->null_ptr) {
- templ->mysql_null_byte_offset =
- (ulint) ((char*) field->null_ptr
- - (char*) table->record[0]);
-
- templ->mysql_null_bit_mask = (ulint) field->null_bit;
- } else {
- templ->mysql_null_bit_mask = 0;
- }
-
- templ->mysql_col_offset = (ulint)
- get_field_offset(table, field);
-
- templ->mysql_col_len = (ulint) field->pack_length();
- if (mysql_prefix_len < templ->mysql_col_offset
- + templ->mysql_col_len) {
- mysql_prefix_len = templ->mysql_col_offset
- + templ->mysql_col_len;
- }
- templ->type = index->table->cols[i].type.mtype;
- templ->mysql_type = (ulint)field->type();
-
- if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
- templ->mysql_length_bytes = (ulint)
- (((Field_varstring*)field)->length_bytes);
- }
-
- templ->charset = dtype_get_charset_coll_noninline(
- index->table->cols[i].type.prtype);
- templ->mbminlen = index->table->cols[i].type.mbminlen;
- templ->mbmaxlen = index->table->cols[i].type.mbmaxlen;
- templ->is_unsigned = index->table->cols[i].type.prtype
- & DATA_UNSIGNED;
- if (templ->type == DATA_BLOB) {
- prebuilt->templ_contains_blob = TRUE;
- }
-skip_field:
- ;
- }
-
- prebuilt->n_template = n_requested_fields;
- prebuilt->mysql_prefix_len = mysql_prefix_len;
-
- if (index != clust_index && prebuilt->need_to_access_clustered) {
- /* Change rec_field_no's to correspond to the clustered index
- record */
- for (i = 0; i < n_requested_fields; i++) {
- templ = prebuilt->mysql_template + i;
-
- templ->rec_field_no =
- (index->table->cols + templ->col_no)->clust_pos;
- }
- }
-}
-
-/************************************************************************
-Stores a row in an InnoDB database, to the table specified in this
-handle. */
-
-int
-ha_innobase::write_row(
-/*===================*/
- /* out: error code */
- mysql_byte* record) /* in: a row in MySQL format */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
- int error;
- longlong auto_inc;
- longlong dummy;
- ibool auto_inc_used= FALSE;
-
- DBUG_ENTER("ha_innobase::write_row");
-
- if (prebuilt->trx !=
- (trx_t*) current_thd->ha_data[innobase_hton.slot]) {
- sql_print_error("The transaction object for the table handle is at "
- "%p, but for the current thread it is at %p",
- prebuilt->trx,
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- fputs("InnoDB: Dump of 200 bytes around prebuilt: ", stderr);
- ut_print_buf(stderr, ((const byte*)prebuilt) - 100, 200);
- fputs("\n"
- "InnoDB: Dump of 200 bytes around transaction.all: ",
- stderr);
- ut_print_buf(stderr,
- ((byte*)(&(current_thd->ha_data[innobase_hton.slot]))) - 100,
- 200);
- putc('\n', stderr);
- ut_error;
- }
-
- statistic_increment(current_thd->status_var.ha_write_count,
- &LOCK_status);
-
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
-
- if ((user_thd->lex->sql_command == SQLCOM_ALTER_TABLE
- || user_thd->lex->sql_command == SQLCOM_OPTIMIZE
- || user_thd->lex->sql_command == SQLCOM_CREATE_INDEX
- || user_thd->lex->sql_command == SQLCOM_DROP_INDEX)
- && num_write_row >= 10000) {
- /* ALTER TABLE is COMMITted at every 10000 copied rows.
- The IX table lock for the original table has to be re-issued.
- As this method will be called on a temporary table where the
- contents of the original table is being copied to, it is
- a bit tricky to determine the source table. The cursor
- position in the source table need not be adjusted after the
- intermediate COMMIT, since writes by other transactions are
- being blocked by a MySQL table lock TL_WRITE_ALLOW_READ. */
-
- dict_table_t* src_table;
- ulint mode;
-
- num_write_row = 0;
-
- /* Commit the transaction. This will release the table
- locks, so they have to be acquired again. */
-
- /* Altering an InnoDB table */
- /* Get the source table. */
- src_table = lock_get_src_table(
- prebuilt->trx, prebuilt->table, &mode);
- if (!src_table) {
-no_commit:
- /* Unknown situation: do not commit */
- /*
- ut_print_timestamp(stderr);
- fprintf(stderr,
- " InnoDB error: ALTER TABLE is holding lock"
- " on %lu tables!\n",
- prebuilt->trx->mysql_n_tables_locked);
- */
- ;
- } else if (src_table == prebuilt->table) {
- /* Source table is not in InnoDB format:
- no need to re-acquire locks on it. */
-
- /* Altering to InnoDB format */
- innobase_commit(user_thd, 1);
- /* Note that this transaction is still active. */
- prebuilt->trx->active_trans = 1;
- /* We will need an IX lock on the destination table. */
- prebuilt->sql_stat_start = TRUE;
- } else {
- /* Ensure that there are no other table locks than
- LOCK_IX and LOCK_AUTO_INC on the destination table. */
-
- if (!lock_is_table_exclusive(prebuilt->table,
- prebuilt->trx)) {
- goto no_commit;
- }
-
- /* Commit the transaction. This will release the table
- locks, so they have to be acquired again. */
- innobase_commit(user_thd, 1);
- /* Note that this transaction is still active. */
- prebuilt->trx->active_trans = 1;
- /* Re-acquire the table lock on the source table. */
- row_lock_table_for_mysql(prebuilt, src_table, mode);
- /* We will need an IX lock on the destination table. */
- prebuilt->sql_stat_start = TRUE;
- }
- }
-
- num_write_row++;
-
- if (last_query_id != user_thd->query_id) {
- prebuilt->sql_stat_start = TRUE;
- last_query_id = user_thd->query_id;
-
- innobase_release_stat_resources(prebuilt->trx);
- }
-
- if (table->next_number_field && record == table->record[0]) {
- /* This is the case where the table has an
- auto-increment column */
-
- /* Initialize the auto-inc counter if it has not been
- initialized yet */
-
- if (0 == dict_table_autoinc_peek(prebuilt->table)) {
-
- /* This call initializes the counter */
- error = innobase_read_and_init_auto_inc(&dummy);
-
- if (error) {
- /* Deadlock or lock wait timeout */
-
- goto func_exit;
- }
-
- /* We have to set sql_stat_start to TRUE because
- the above call probably has called a select, and
- has reset that flag; row_insert_for_mysql has to
- know to set the IX intention lock on the table,
- something it only does at the start of each
- statement */
-
- prebuilt->sql_stat_start = TRUE;
- }
-
- /* We have to use the transactional lock mechanism on the
- auto-inc counter of the table to ensure that replication and
- roll-forward of the binlog exactly imitates also the given
- auto-inc values. The lock is released at each SQL statement's
- end. This lock also prevents a race where two threads would
- call ::get_auto_increment() simultaneously. */
-
- error = row_lock_table_autoinc_for_mysql(prebuilt);
-
- if (error != DB_SUCCESS) {
- /* Deadlock or lock wait timeout */
-
- error = convert_error_code_to_mysql(error, user_thd);
-
- goto func_exit;
- }
-
- /* We must use the handler code to update the auto-increment
- value to be sure that we increment it correctly. */
-
- if ((error= update_auto_increment()))
- goto func_exit;
- auto_inc_used = 1;
-
- }
-
- if (prebuilt->mysql_template == NULL
- || prebuilt->template_type != ROW_MYSQL_WHOLE_ROW) {
- /* Build the template used in converting quickly between
- the two database formats */
-
- build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
- }
-
- innodb_srv_conc_enter_innodb(prebuilt->trx);
-
- error = row_insert_for_mysql((byte*) record, prebuilt);
-
- if (error == DB_SUCCESS && auto_inc_used) {
-
- /* Fetch the value that was set in the autoincrement field */
-
- auto_inc = table->next_number_field->val_int();
-
- if (auto_inc != 0) {
- /* This call will update the counter according to the
- value that was inserted in the table */
-
- dict_table_autoinc_update(prebuilt->table, auto_inc);
- }
- }
-
- /* A REPLACE command and LOAD DATA INFILE REPLACE handle a duplicate
- key error themselves, and we must update the autoinc counter if we are
- performing those statements. */
-
- if (error == DB_DUPLICATE_KEY && auto_inc_used
- && (user_thd->lex->sql_command == SQLCOM_REPLACE
- || user_thd->lex->sql_command == SQLCOM_REPLACE_SELECT
- || (user_thd->lex->sql_command == SQLCOM_LOAD
- && user_thd->lex->duplicates == DUP_REPLACE))) {
-
- auto_inc = table->next_number_field->val_int();
-
- if (auto_inc != 0) {
- dict_table_autoinc_update(prebuilt->table, auto_inc);
- }
- }
-
- innodb_srv_conc_exit_innodb(prebuilt->trx);
-
- error = convert_error_code_to_mysql(error, user_thd);
-
- /* Tell InnoDB server that there might be work for
- utility threads: */
-func_exit:
- innobase_active_small();
-
- DBUG_RETURN(error);
-}
-
-/**************************************************************************
-Checks which fields have changed in a row and stores information
-of them to an update vector. */
-static
-int
-calc_row_difference(
-/*================*/
- /* out: error number or 0 */
- upd_t* uvect, /* in/out: update vector */
- mysql_byte* old_row, /* in: old row in MySQL format */
- mysql_byte* new_row, /* in: new row in MySQL format */
- struct st_table* table, /* in: table in MySQL data
- dictionary */
- mysql_byte* upd_buff, /* in: buffer to use */
- ulint buff_len, /* in: buffer length */
- row_prebuilt_t* prebuilt, /* in: InnoDB prebuilt struct */
- THD* thd) /* in: user thread */
-{
- mysql_byte* original_upd_buff = upd_buff;
- Field* field;
- enum_field_types field_mysql_type;
- uint n_fields;
- ulint o_len;
- ulint n_len;
- ulint col_pack_len;
- byte* new_mysql_row_col;
- byte* o_ptr;
- byte* n_ptr;
- byte* buf;
- upd_field_t* ufield;
- ulint col_type;
- ulint n_changed = 0;
- dfield_t dfield;
- uint i;
-
- n_fields = table->s->fields;
-
- /* We use upd_buff to convert changed fields */
- buf = (byte*) upd_buff;
-
- for (i = 0; i < n_fields; i++) {
- field = table->field[i];
-
- /* if (thd->query_id != field->query_id) { */
- /* TODO: check that these fields cannot have
- changed! */
-
- /* goto skip_field;
- }*/
-
- o_ptr = (byte*) old_row + get_field_offset(table, field);
- n_ptr = (byte*) new_row + get_field_offset(table, field);
-
- /* Use new_mysql_row_col and col_pack_len save the values */
-
- new_mysql_row_col = n_ptr;
- col_pack_len = field->pack_length();
-
- o_len = col_pack_len;
- n_len = col_pack_len;
-
- /* We use o_ptr and n_ptr to dig up the actual data for
- comparison. */
-
- field_mysql_type = field->type();
-
- col_type = prebuilt->table->cols[i].type.mtype;
-
- switch (col_type) {
-
- case DATA_BLOB:
- o_ptr = row_mysql_read_blob_ref(&o_len, o_ptr, o_len);
- n_ptr = row_mysql_read_blob_ref(&n_len, n_ptr, n_len);
-
- break;
-
- case DATA_VARCHAR:
- case DATA_BINARY:
- case DATA_VARMYSQL:
- if (field_mysql_type == MYSQL_TYPE_VARCHAR) {
- /* This is a >= 5.0.3 type true VARCHAR where
- the real payload data length is stored in
- 1 or 2 bytes */
-
- o_ptr = row_mysql_read_true_varchar(
- &o_len, o_ptr,
- (ulint)
- (((Field_varstring*)field)->length_bytes));
-
- n_ptr = row_mysql_read_true_varchar(
- &n_len, n_ptr,
- (ulint)
- (((Field_varstring*)field)->length_bytes));
- }
-
- break;
- default:
- ;
- }
-
- if (field->null_ptr) {
- if (field_in_record_is_null(table, field,
- (char*) old_row)) {
- o_len = UNIV_SQL_NULL;
- }
-
- if (field_in_record_is_null(table, field,
- (char*) new_row)) {
- n_len = UNIV_SQL_NULL;
- }
- }
-
- if (o_len != n_len || (o_len != UNIV_SQL_NULL &&
- 0 != memcmp(o_ptr, n_ptr, o_len))) {
- /* The field has changed */
-
- ufield = uvect->fields + n_changed;
-
- /* Let us use a dummy dfield to make the conversion
- from the MySQL column format to the InnoDB format */
-
- dfield.type = (prebuilt->table->cols + i)->type;
-
- if (n_len != UNIV_SQL_NULL) {
- buf = row_mysql_store_col_in_innobase_format(
- &dfield,
- (byte*)buf,
- TRUE,
- new_mysql_row_col,
- col_pack_len,
- prebuilt->table->comp);
- ufield->new_val.data = dfield.data;
- ufield->new_val.len = dfield.len;
- } else {
- ufield->new_val.data = NULL;
- ufield->new_val.len = UNIV_SQL_NULL;
- }
-
- ufield->exp = NULL;
- ufield->field_no = prebuilt->table->cols[i].clust_pos;
- n_changed++;
- }
- }
-
- uvect->n_fields = n_changed;
- uvect->info_bits = 0;
-
- ut_a(buf <= (byte*)original_upd_buff + buff_len);
-
- return(0);
-}
-
-/**************************************************************************
-Updates a row given as a parameter to a new value. Note that we are given
-whole rows, not just the fields which are updated: this incurs some
-overhead for CPU when we check which fields are actually updated.
-TODO: currently InnoDB does not prevent the 'Halloween problem':
-in a searched update a single row can get updated several times
-if its index columns are updated! */
-
-int
-ha_innobase::update_row(
-/*====================*/
- /* out: error number or 0 */
- const mysql_byte* old_row,/* in: old row in MySQL format */
- mysql_byte* new_row)/* in: new row in MySQL format */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- upd_t* uvect;
- int error = 0;
-
- DBUG_ENTER("ha_innobase::update_row");
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
-
- if (last_query_id != user_thd->query_id) {
- prebuilt->sql_stat_start = TRUE;
- last_query_id = user_thd->query_id;
-
- innobase_release_stat_resources(prebuilt->trx);
- }
-
- if (prebuilt->upd_node) {
- uvect = prebuilt->upd_node->update;
- } else {
- uvect = row_get_prebuilt_update_vector(prebuilt);
- }
-
- /* Build an update vector from the modified fields in the rows
- (uses upd_buff of the handle) */
-
- calc_row_difference(uvect, (mysql_byte*) old_row, new_row, table,
- upd_buff, (ulint)upd_and_key_val_buff_len,
- prebuilt, user_thd);
-
- /* This is not a delete */
- prebuilt->upd_node->is_delete = FALSE;
-
- assert(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
-
- innodb_srv_conc_enter_innodb(prebuilt->trx);
-
- error = row_update_for_mysql((byte*) old_row, prebuilt);
-
- innodb_srv_conc_exit_innodb(prebuilt->trx);
-
- error = convert_error_code_to_mysql(error, user_thd);
-
- /* Tell InnoDB server that there might be work for
- utility threads: */
-
- innobase_active_small();
-
- DBUG_RETURN(error);
-}
-
-/**************************************************************************
-Deletes a row given as the parameter. */
-
-int
-ha_innobase::delete_row(
-/*====================*/
- /* out: error number or 0 */
- const mysql_byte* record) /* in: a row in MySQL format */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- int error = 0;
-
- DBUG_ENTER("ha_innobase::delete_row");
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- if (last_query_id != user_thd->query_id) {
- prebuilt->sql_stat_start = TRUE;
- last_query_id = user_thd->query_id;
-
- innobase_release_stat_resources(prebuilt->trx);
- }
-
- if (!prebuilt->upd_node) {
- row_get_prebuilt_update_vector(prebuilt);
- }
-
- /* This is a delete */
-
- prebuilt->upd_node->is_delete = TRUE;
-
- innodb_srv_conc_enter_innodb(prebuilt->trx);
-
- error = row_update_for_mysql((byte*) record, prebuilt);
-
- innodb_srv_conc_exit_innodb(prebuilt->trx);
-
- error = convert_error_code_to_mysql(error, user_thd);
-
- /* Tell the InnoDB server that there might be work for
- utility threads: */
-
- innobase_active_small();
-
- DBUG_RETURN(error);
-}
-
-/**************************************************************************
-Removes a new lock set on a row. This method does nothing unless the
-option innodb_locks_unsafe_for_binlog is set.*/
-
-void
-ha_innobase::unlock_row(void)
-/*=========================*/
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
-
- DBUG_ENTER("ha_innobase::unlock_row");
-
- if (last_query_id != user_thd->query_id) {
- ut_print_timestamp(stderr);
- sql_print_error("last_query_id is %lu != user_thd_query_id is "
- "%lu", (ulong) last_query_id,
- (ulong) user_thd->query_id);
- mem_analyze_corruption((byte *) prebuilt->trx);
- ut_error;
- }
-
- /* Consistent read does not take any locks, thus there is
- nothing to unlock. */
-
- if (prebuilt->select_lock_type == LOCK_NONE) {
- DBUG_VOID_RETURN;
- }
-
- if (srv_locks_unsafe_for_binlog) {
- row_unlock_for_mysql(prebuilt, FALSE);
- }
-
- DBUG_VOID_RETURN;
-
-}
-
-/**********************************************************************
-Initializes a handle to use an index. */
-
-int
-ha_innobase::index_init(
-/*====================*/
- /* out: 0 or error number */
- uint keynr) /* in: key (index) number */
-{
- int error = 0;
- DBUG_ENTER("index_init");
-
- error = change_active_index(keynr);
-
- DBUG_RETURN(error);
-}
-
-/**********************************************************************
-Currently does nothing. */
-
-int
-ha_innobase::index_end(void)
-/*========================*/
-{
- int error = 0;
- DBUG_ENTER("index_end");
- active_index=MAX_KEY;
- DBUG_RETURN(error);
-}
-
-/*************************************************************************
-Converts a search mode flag understood by MySQL to a flag understood
-by InnoDB. */
-inline
-ulint
-convert_search_mode_to_innobase(
-/*============================*/
- enum ha_rkey_function find_flag)
-{
- switch (find_flag) {
- case HA_READ_KEY_EXACT: return(PAGE_CUR_GE);
- /* the above does not require the index to be UNIQUE */
- case HA_READ_KEY_OR_NEXT: return(PAGE_CUR_GE);
- case HA_READ_KEY_OR_PREV: return(PAGE_CUR_LE);
- case HA_READ_AFTER_KEY: return(PAGE_CUR_G);
- case HA_READ_BEFORE_KEY: return(PAGE_CUR_L);
- case HA_READ_PREFIX: return(PAGE_CUR_GE);
- case HA_READ_PREFIX_LAST: return(PAGE_CUR_LE);
- case HA_READ_PREFIX_LAST_OR_PREV:return(PAGE_CUR_LE);
- /* In MySQL-4.0 HA_READ_PREFIX and HA_READ_PREFIX_LAST always
- pass a complete-field prefix of a key value as the search
- tuple. I.e., it is not allowed that the last field would
- just contain n first bytes of the full field value.
- MySQL uses a 'padding' trick to convert LIKE 'abc%'
- type queries so that it can use as a search tuple
- a complete-field-prefix of a key value. Thus, the InnoDB
- search mode PAGE_CUR_LE_OR_EXTENDS is never used.
- TODO: when/if MySQL starts to use also partial-field
- prefixes, we have to deal with stripping of spaces
- and comparison of non-latin1 char type fields in
- innobase_mysql_cmp() to get PAGE_CUR_LE_OR_EXTENDS to
- work correctly. */
-
- default: assert(0);
- }
-
- return(0);
-}
-
-/*
- BACKGROUND INFO: HOW A SELECT SQL QUERY IS EXECUTED
- ---------------------------------------------------
-The following does not cover all the details, but explains how we determine
-the start of a new SQL statement, and what is associated with it.
-
-For each table in the database the MySQL interpreter may have several
-table handle instances in use, also in a single SQL query. For each table
-handle instance there is an InnoDB 'prebuilt' struct which contains most
-of the InnoDB data associated with this table handle instance.
-
- A) if the user has not explicitly set any MySQL table level locks:
-
- 1) MySQL calls ::external_lock to set an 'intention' table level lock on
-the table of the handle instance. There we set
-prebuilt->sql_stat_start = TRUE. The flag sql_stat_start should be set
-true if we are taking this table handle instance to use in a new SQL
-statement issued by the user. We also increment trx->n_mysql_tables_in_use.
-
- 2) If prebuilt->sql_stat_start == TRUE we 'pre-compile' the MySQL search
-instructions to prebuilt->template of the table handle instance in
-::index_read. The template is used to save CPU time in large joins.
-
- 3) In row_search_for_mysql, if prebuilt->sql_stat_start is true, we
-allocate a new consistent read view for the trx if it does not yet have one,
-or in the case of a locking read, set an InnoDB 'intention' table level
-lock on the table.
-
- 4) We do the SELECT. MySQL may repeatedly call ::index_read for the
-same table handle instance, if it is a join.
-
- 5) When the SELECT ends, MySQL removes its intention table level locks
-in ::external_lock. When trx->n_mysql_tables_in_use drops to zero,
- (a) we execute a COMMIT there if the autocommit is on,
- (b) we also release possible 'SQL statement level resources' InnoDB may
-have for this SQL statement. The MySQL interpreter does NOT execute
-autocommit for pure read transactions, though it should. That is why the
-table handler in that case has to execute the COMMIT in ::external_lock.
-
- B) If the user has explicitly set MySQL table level locks, then MySQL
-does NOT call ::external_lock at the start of the statement. To determine
-when we are at the start of a new SQL statement we at the start of
-::index_read also compare the query id to the latest query id where the
-table handle instance was used. If it has changed, we know we are at the
-start of a new SQL statement. Since the query id can theoretically
-overwrap, we use this test only as a secondary way of determining the
-start of a new SQL statement. */
-
-
-/**************************************************************************
-Positions an index cursor to the index specified in the handle. Fetches the
-row if any. */
-
-int
-ha_innobase::index_read(
-/*====================*/
- /* out: 0, HA_ERR_KEY_NOT_FOUND,
- or error number */
- mysql_byte* buf, /* in/out: buffer for the returned
- row */
- const mysql_byte* key_ptr,/* in: key value; if this is NULL
- we position the cursor at the
- start or end of index; this can
- also contain an InnoDB row id, in
- which case key_len is the InnoDB
- row id length; the key value can
- also be a prefix of a full key value,
- and the last column can be a prefix
- of a full column */
- uint key_len,/* in: key value length */
- enum ha_rkey_function find_flag)/* in: search flags from my_base.h */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- ulint mode;
- dict_index_t* index;
- ulint match_mode = 0;
- int error;
- ulint ret;
-
- DBUG_ENTER("index_read");
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- statistic_increment(current_thd->status_var.ha_read_key_count,
- &LOCK_status);
-
- if (last_query_id != user_thd->query_id) {
- prebuilt->sql_stat_start = TRUE;
- last_query_id = user_thd->query_id;
-
- innobase_release_stat_resources(prebuilt->trx);
- }
-
- index = prebuilt->index;
-
- /* Note that if the index for which the search template is built is not
- necessarily prebuilt->index, but can also be the clustered index */
-
- if (prebuilt->sql_stat_start) {
- build_template(prebuilt, user_thd, table,
- ROW_MYSQL_REC_FIELDS);
- }
-
- if (key_ptr) {
- /* Convert the search key value to InnoDB format into
- prebuilt->search_tuple */
-
- row_sel_convert_mysql_key_to_innobase(prebuilt->search_tuple,
- (byte*) key_val_buff,
- (ulint)upd_and_key_val_buff_len,
- index,
- (byte*) key_ptr,
- (ulint) key_len, prebuilt->trx);
- } else {
- /* We position the cursor to the last or the first entry
- in the index */
-
- dtuple_set_n_fields(prebuilt->search_tuple, 0);
- }
-
- mode = convert_search_mode_to_innobase(find_flag);
-
- match_mode = 0;
-
- if (find_flag == HA_READ_KEY_EXACT) {
- match_mode = ROW_SEL_EXACT;
-
- } else if (find_flag == HA_READ_PREFIX
- || find_flag == HA_READ_PREFIX_LAST) {
- match_mode = ROW_SEL_EXACT_PREFIX;
- }
-
- last_match_mode = (uint) match_mode;
-
- innodb_srv_conc_enter_innodb(prebuilt->trx);
-
- ret = row_search_for_mysql((byte*) buf, mode, prebuilt, match_mode, 0);
-
- innodb_srv_conc_exit_innodb(prebuilt->trx);
-
- if (ret == DB_SUCCESS) {
- error = 0;
- table->status = 0;
-
- } else if (ret == DB_RECORD_NOT_FOUND) {
- error = HA_ERR_KEY_NOT_FOUND;
- table->status = STATUS_NOT_FOUND;
-
- } else if (ret == DB_END_OF_INDEX) {
- error = HA_ERR_KEY_NOT_FOUND;
- table->status = STATUS_NOT_FOUND;
- } else {
- error = convert_error_code_to_mysql((int) ret, user_thd);
- table->status = STATUS_NOT_FOUND;
- }
-
- DBUG_RETURN(error);
-}
-
-/***********************************************************************
-The following functions works like index_read, but it find the last
-row with the current key value or prefix. */
-
-int
-ha_innobase::index_read_last(
-/*=========================*/
- /* out: 0, HA_ERR_KEY_NOT_FOUND, or an
- error code */
- mysql_byte* buf, /* out: fetched row */
- const mysql_byte* key_ptr, /* in: key value, or a prefix of a full
- key value */
- uint key_len) /* in: length of the key val or prefix
- in bytes */
-{
- return(index_read(buf, key_ptr, key_len, HA_READ_PREFIX_LAST));
-}
-
-/************************************************************************
-Changes the active index of a handle. */
-
-int
-ha_innobase::change_active_index(
-/*=============================*/
- /* out: 0 or error code */
- uint keynr) /* in: use this index; MAX_KEY means always clustered
- index, even if it was internally generated by
- InnoDB */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- KEY* key=0;
- statistic_increment(current_thd->status_var.ha_read_key_count,
- &LOCK_status);
- DBUG_ENTER("change_active_index");
-
- ut_ad(user_thd == current_thd);
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- active_index = keynr;
-
- if (keynr != MAX_KEY && table->s->keys > 0) {
- key = table->key_info + active_index;
-
- prebuilt->index = dict_table_get_index_noninline(
- prebuilt->table,
- key->name);
- } else {
- prebuilt->index = dict_table_get_first_index_noninline(
- prebuilt->table);
- }
-
- if (!prebuilt->index) {
- sql_print_error("Innodb could not find key n:o %u with name %s "
- "from dict cache for table %s",
- keynr, key ? key->name : "NULL",
- prebuilt->table->name);
- DBUG_RETURN(1);
- }
-
- assert(prebuilt->search_tuple != 0);
-
- dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields);
-
- dict_index_copy_types(prebuilt->search_tuple, prebuilt->index,
- prebuilt->index->n_fields);
-
- /* MySQL changes the active index for a handle also during some
- queries, for example SELECT MAX(a), SUM(a) first retrieves the MAX()
- and then calculates the sum. Previously we played safe and used
- the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary
- copying. Starting from MySQL-4.1 we use a more efficient flag here. */
-
- build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS);
-
- DBUG_RETURN(0);
-}
-
-/**************************************************************************
-Positions an index cursor to the index specified in keynr. Fetches the
-row if any. */
-/* ??? This is only used to read whole keys ??? */
-
-int
-ha_innobase::index_read_idx(
-/*========================*/
- /* out: error number or 0 */
- mysql_byte* buf, /* in/out: buffer for the returned
- row */
- uint keynr, /* in: use this index */
- const mysql_byte* key, /* in: key value; if this is NULL
- we position the cursor at the
- start or end of index */
- uint key_len, /* in: key value length */
- enum ha_rkey_function find_flag)/* in: search flags from my_base.h */
-{
- if (change_active_index(keynr)) {
-
- return(1);
- }
-
- return(index_read(buf, key, key_len, find_flag));
-}
-
-/***************************************************************************
-Reads the next or previous row from a cursor, which must have previously been
-positioned using index_read. */
-
-int
-ha_innobase::general_fetch(
-/*=======================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error
- number */
- mysql_byte* buf, /* in/out: buffer for next row in MySQL
- format */
- uint direction, /* in: ROW_SEL_NEXT or ROW_SEL_PREV */
- uint match_mode) /* in: 0, ROW_SEL_EXACT, or
- ROW_SEL_EXACT_PREFIX */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- ulint ret;
- int error = 0;
-
- DBUG_ENTER("general_fetch");
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- innodb_srv_conc_enter_innodb(prebuilt->trx);
-
- ret = row_search_for_mysql((byte*)buf, 0, prebuilt, match_mode,
- direction);
- innodb_srv_conc_exit_innodb(prebuilt->trx);
-
- if (ret == DB_SUCCESS) {
- error = 0;
- table->status = 0;
-
- } else if (ret == DB_RECORD_NOT_FOUND) {
- error = HA_ERR_END_OF_FILE;
- table->status = STATUS_NOT_FOUND;
-
- } else if (ret == DB_END_OF_INDEX) {
- error = HA_ERR_END_OF_FILE;
- table->status = STATUS_NOT_FOUND;
- } else {
- error = convert_error_code_to_mysql((int) ret, user_thd);
- table->status = STATUS_NOT_FOUND;
- }
-
- DBUG_RETURN(error);
-}
-
-/***************************************************************************
-Reads the next row from a cursor, which must have previously been
-positioned using index_read. */
-
-int
-ha_innobase::index_next(
-/*====================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error
- number */
- mysql_byte* buf) /* in/out: buffer for next row in MySQL
- format */
-{
- statistic_increment(current_thd->status_var.ha_read_next_count,
- &LOCK_status);
-
- return(general_fetch(buf, ROW_SEL_NEXT, 0));
-}
-
-/***********************************************************************
-Reads the next row matching to the key value given as the parameter. */
-
-int
-ha_innobase::index_next_same(
-/*=========================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error
- number */
- mysql_byte* buf, /* in/out: buffer for the row */
- const mysql_byte* key, /* in: key value */
- uint keylen) /* in: key value length */
-{
- statistic_increment(current_thd->status_var.ha_read_next_count,
- &LOCK_status);
-
- return(general_fetch(buf, ROW_SEL_NEXT, last_match_mode));
-}
-
-/***************************************************************************
-Reads the previous row from a cursor, which must have previously been
-positioned using index_read. */
-
-int
-ha_innobase::index_prev(
-/*====================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error
- number */
- mysql_byte* buf) /* in/out: buffer for previous row in MySQL
- format */
-{
- statistic_increment(current_thd->status_var.ha_read_prev_count,
- &LOCK_status);
-
- return(general_fetch(buf, ROW_SEL_PREV, 0));
-}
-
-/************************************************************************
-Positions a cursor on the first record in an index and reads the
-corresponding row to buf. */
-
-int
-ha_innobase::index_first(
-/*=====================*/
- /* out: 0, HA_ERR_END_OF_FILE,
- or error code */
- mysql_byte* buf) /* in/out: buffer for the row */
-{
- int error;
-
- DBUG_ENTER("index_first");
- statistic_increment(current_thd->status_var.ha_read_first_count,
- &LOCK_status);
-
- error = index_read(buf, NULL, 0, HA_READ_AFTER_KEY);
-
- /* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
-
- if (error == HA_ERR_KEY_NOT_FOUND) {
- error = HA_ERR_END_OF_FILE;
- }
-
- DBUG_RETURN(error);
-}
-
-/************************************************************************
-Positions a cursor on the last record in an index and reads the
-corresponding row to buf. */
-
-int
-ha_innobase::index_last(
-/*====================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error code */
- mysql_byte* buf) /* in/out: buffer for the row */
-{
- int error;
-
- DBUG_ENTER("index_last");
- statistic_increment(current_thd->status_var.ha_read_last_count,
- &LOCK_status);
-
- error = index_read(buf, NULL, 0, HA_READ_BEFORE_KEY);
-
- /* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
-
- if (error == HA_ERR_KEY_NOT_FOUND) {
- error = HA_ERR_END_OF_FILE;
- }
-
- DBUG_RETURN(error);
-}
-
-/********************************************************************
-Initialize a table scan. */
-
-int
-ha_innobase::rnd_init(
-/*==================*/
- /* out: 0 or error number */
- bool scan) /* in: ???????? */
-{
- int err;
-
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
-
- /* Store the active index value so that we can restore the original
- value after a scan */
-
- if (prebuilt->clust_index_was_generated) {
- err = change_active_index(MAX_KEY);
- } else {
- err = change_active_index(primary_key);
- }
-
- start_of_scan = 1;
-
- return(err);
-}
-
-/*********************************************************************
-Ends a table scan. */
-
-int
-ha_innobase::rnd_end(void)
-/*======================*/
- /* out: 0 or error number */
-{
- return(index_end());
-}
-
-/*********************************************************************
-Reads the next row in a table scan (also used to read the FIRST row
-in a table scan). */
-
-int
-ha_innobase::rnd_next(
-/*==================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error number */
- mysql_byte* buf)/* in/out: returns the row in this buffer,
- in MySQL format */
-{
- int error;
-
- DBUG_ENTER("rnd_next");
- statistic_increment(current_thd->status_var.ha_read_rnd_next_count,
- &LOCK_status);
-
- if (start_of_scan) {
- error = index_first(buf);
- if (error == HA_ERR_KEY_NOT_FOUND) {
- error = HA_ERR_END_OF_FILE;
- }
- start_of_scan = 0;
- } else {
- error = general_fetch(buf, ROW_SEL_NEXT, 0);
- }
-
- DBUG_RETURN(error);
-}
-
-/**************************************************************************
-Fetches a row from the table based on a row reference. */
-
-int
-ha_innobase::rnd_pos(
-/*=================*/
- /* out: 0, HA_ERR_KEY_NOT_FOUND,
- or error code */
- mysql_byte* buf, /* in/out: buffer for the row */
- mysql_byte* pos) /* in: primary key value of the row in the
- MySQL format, or the row id if the clustered
- index was internally generated by InnoDB;
- the length of data in pos has to be
- ref_length */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- int error;
- uint keynr = active_index;
- DBUG_ENTER("rnd_pos");
- DBUG_DUMP("key", (char*) pos, ref_length);
-
- statistic_increment(current_thd->status_var.ha_read_rnd_count,
- &LOCK_status);
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- if (prebuilt->clust_index_was_generated) {
- /* No primary key was defined for the table and we
- generated the clustered index from the row id: the
- row reference is the row id, not any key value
- that MySQL knows of */
-
- error = change_active_index(MAX_KEY);
- } else {
- error = change_active_index(primary_key);
- }
-
- if (error) {
- DBUG_PRINT("error", ("Got error: %d", error));
- DBUG_RETURN(error);
- }
-
- /* Note that we assume the length of the row reference is fixed
- for the table, and it is == ref_length */
-
- error = index_read(buf, pos, ref_length, HA_READ_KEY_EXACT);
-
- if (error) {
- DBUG_PRINT("error", ("Got error: %d", error));
- }
-
- change_active_index(keynr);
-
- DBUG_RETURN(error);
-}
-
-/*************************************************************************
-Stores a reference to the current row to 'ref' field of the handle. Note
-that in the case where we have generated the clustered index for the
-table, the function parameter is illogical: we MUST ASSUME that 'record'
-is the current 'position' of the handle, because if row ref is actually
-the row id internally generated in InnoDB, then 'record' does not contain
-it. We just guess that the row id must be for the record where the handle
-was positioned the last time. */
-
-void
-ha_innobase::position(
-/*==================*/
- const mysql_byte* record) /* in: row in MySQL format */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- uint len;
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- if (prebuilt->clust_index_was_generated) {
- /* No primary key was defined for the table and we
- generated the clustered index from row id: the
- row reference will be the row id, not any key value
- that MySQL knows of */
-
- len = DATA_ROW_ID_LEN;
-
- memcpy(ref, prebuilt->row_id, len);
- } else {
- len = store_key_val_for_row(primary_key, (char*)ref,
- ref_length, record);
- }
-
- /* We assume that the 'ref' value len is always fixed for the same
- table. */
-
- if (len != ref_length) {
- sql_print_error("Stored ref len is %lu, but table ref len is %lu",
- (ulong) len, (ulong) ref_length);
- }
-}
-
-/*********************************************************************
-Creates a table definition to an InnoDB database. */
-static
-int
-create_table_def(
-/*=============*/
- trx_t* trx, /* in: InnoDB transaction handle */
- TABLE* form, /* in: information on table
- columns and indexes */
- const char* table_name, /* in: table name */
- const char* path_of_temp_table,/* in: if this is a table explicitly
- created by the user with the
- TEMPORARY keyword, then this
- parameter is the dir path where the
- table should be placed if we create
- an .ibd file for it (no .ibd extension
- in the path, though); otherwise this
- is NULL */
- ibool comp) /* in: TRUE=compact record format */
-{
- Field* field;
- dict_table_t* table;
- ulint n_cols;
- int error;
- ulint col_type;
- ulint col_len;
- ulint nulls_allowed;
- ulint unsigned_type;
- ulint binary_type;
- ulint long_true_varchar;
- ulint charset_no;
- ulint i;
-
- DBUG_ENTER("create_table_def");
- DBUG_PRINT("enter", ("table_name: %s", table_name));
-
- n_cols = form->s->fields;
-
- /* We pass 0 as the space id, and determine at a lower level the space
- id where to store the table */
-
- table = dict_mem_table_create(table_name, 0, n_cols, comp);
-
- if (path_of_temp_table) {
- table->dir_path_of_temp_table =
- mem_heap_strdup(table->heap, path_of_temp_table);
- }
-
- for (i = 0; i < n_cols; i++) {
- field = form->field[i];
-
- col_type = get_innobase_type_from_mysql_type(&unsigned_type,
- field);
- if (field->null_ptr) {
- nulls_allowed = 0;
- } else {
- nulls_allowed = DATA_NOT_NULL;
- }
-
- if (field->binary()) {
- binary_type = DATA_BINARY_TYPE;
- } else {
- binary_type = 0;
- }
-
- charset_no = 0;
-
- if (dtype_is_string_type(col_type)) {
-
- charset_no = (ulint)field->charset()->number;
-
- ut_a(charset_no < 256); /* in data0type.h we assume
- that the number fits in one
- byte */
- }
-
- ut_a(field->type() < 256); /* we assume in dtype_form_prtype()
- that this fits in one byte */
- col_len = field->pack_length();
-
- /* The MySQL pack length contains 1 or 2 bytes length field
- for a true VARCHAR. Let us subtract that, so that the InnoDB
- column length in the InnoDB data dictionary is the real
- maximum byte length of the actual data. */
-
- long_true_varchar = 0;
-
- if (field->type() == MYSQL_TYPE_VARCHAR) {
- col_len -= ((Field_varstring*)field)->length_bytes;
-
- if (((Field_varstring*)field)->length_bytes == 2) {
- long_true_varchar = DATA_LONG_TRUE_VARCHAR;
- }
- }
-
- dict_mem_table_add_col(table,
- (char*) field->field_name,
- col_type,
- dtype_form_prtype(
- (ulint)field->type()
- | nulls_allowed | unsigned_type
- | binary_type | long_true_varchar,
- charset_no),
- col_len,
- 0);
- }
-
- error = row_create_table_for_mysql(table, trx);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- DBUG_RETURN(error);
-}
-
-/*********************************************************************
-Creates an index in an InnoDB database. */
-static
-int
-create_index(
-/*=========*/
- trx_t* trx, /* in: InnoDB transaction handle */
- TABLE* form, /* in: information on table
- columns and indexes */
- const char* table_name, /* in: table name */
- uint key_num) /* in: index number */
-{
- Field* field;
- dict_index_t* index;
- int error;
- ulint n_fields;
- KEY* key;
- KEY_PART_INFO* key_part;
- ulint ind_type;
- ulint col_type;
- ulint prefix_len;
- ulint is_unsigned;
- ulint i;
- ulint j;
- ulint* field_lengths;
-
- DBUG_ENTER("create_index");
-
- key = form->key_info + key_num;
-
- n_fields = key->key_parts;
-
- ind_type = 0;
-
- if (key_num == form->s->primary_key) {
- ind_type = ind_type | DICT_CLUSTERED;
- }
-
- if (key->flags & HA_NOSAME ) {
- ind_type = ind_type | DICT_UNIQUE;
- }
-
- /* We pass 0 as the space id, and determine at a lower level the space
- id where to store the table */
-
- index = dict_mem_index_create((char*) table_name, key->name, 0,
- ind_type, n_fields);
-
- field_lengths = (ulint*) my_malloc(sizeof(ulint) * n_fields,
- MYF(MY_FAE));
-
- for (i = 0; i < n_fields; i++) {
- key_part = key->key_part + i;
-
- /* (The flag HA_PART_KEY_SEG denotes in MySQL a column prefix
- field in an index: we only store a specified number of first
- bytes of the column to the index field.) The flag does not
- seem to be properly set by MySQL. Let us fall back on testing
- the length of the key part versus the column. */
-
- field = NULL;
- for (j = 0; j < form->s->fields; j++) {
-
- field = form->field[j];
-
- if (0 == innobase_strcasecmp(
- field->field_name,
- key_part->field->field_name)) {
- /* Found the corresponding column */
-
- break;
- }
- }
-
- ut_a(j < form->s->fields);
-
- col_type = get_innobase_type_from_mysql_type(
- &is_unsigned, key_part->field);
-
- if (DATA_BLOB == col_type
- || (key_part->length < field->pack_length()
- && field->type() != MYSQL_TYPE_VARCHAR)
- || (field->type() == MYSQL_TYPE_VARCHAR
- && key_part->length < field->pack_length()
- - ((Field_varstring*)field)->length_bytes)) {
-
- prefix_len = key_part->length;
-
- if (col_type == DATA_INT
- || col_type == DATA_FLOAT
- || col_type == DATA_DOUBLE
- || col_type == DATA_DECIMAL) {
- sql_print_error("MySQL is trying to create a column "
- "prefix index field, on an "
- "inappropriate data type. Table "
- "name %s, column name %s.",
- table_name,
- key_part->field->field_name);
-
- prefix_len = 0;
- }
- } else {
- prefix_len = 0;
- }
-
- field_lengths[i] = key_part->length;
-
- /* We assume all fields should be sorted in ascending
- order, hence the '0': */
-
- dict_mem_index_add_field(index,
- (char*) key_part->field->field_name,
- 0, prefix_len);
- }
-
- /* Even though we've defined max_supported_key_part_length, we
- still do our own checking using field_lengths to be absolutely
- sure we don't create too long indexes. */
- error = row_create_index_for_mysql(index, trx, field_lengths);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- my_free((gptr) field_lengths, MYF(0));
-
- DBUG_RETURN(error);
-}
-
-/*********************************************************************
-Creates an index to an InnoDB table when the user has defined no
-primary index. */
-static
-int
-create_clustered_index_when_no_primary(
-/*===================================*/
- trx_t* trx, /* in: InnoDB transaction handle */
- const char* table_name) /* in: table name */
-{
- dict_index_t* index;
- int error;
-
- /* We pass 0 as the space id, and determine at a lower level the space
- id where to store the table */
-
- index = dict_mem_index_create((char*) table_name,
- (char*) "GEN_CLUST_INDEX",
- 0, DICT_CLUSTERED, 0);
- error = row_create_index_for_mysql(index, trx, NULL);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- return(error);
-}
-
-/*********************************************************************
-Creates a new table to an InnoDB database. */
-
-int
-ha_innobase::create(
-/*================*/
- /* out: error number */
- const char* name, /* in: table name */
- TABLE* form, /* in: information on table
- columns and indexes */
- HA_CREATE_INFO* create_info) /* in: more information of the
- created table, contains also the
- create statement string */
-{
- int error;
- dict_table_t* innobase_table;
- trx_t* parent_trx;
- trx_t* trx;
- int primary_key_no;
- uint i;
- char name2[FN_REFLEN];
- char norm_name[FN_REFLEN];
- THD *thd= current_thd;
- ib_longlong auto_inc_value;
-
- DBUG_ENTER("ha_innobase::create");
-
- DBUG_ASSERT(thd != NULL);
-
- if (form->s->fields > 1000) {
- /* The limit probably should be REC_MAX_N_FIELDS - 3 = 1020,
- but we play safe here */
-
- DBUG_RETURN(HA_ERR_TO_BIG_ROW);
- }
-
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- parent_trx = check_trx_exists(current_thd);
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(parent_trx);
-
- trx = trx_allocate_for_mysql();
-
- trx->mysql_thd = thd;
- trx->mysql_query_str = &((*thd).query);
-
- if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
- trx->check_foreigns = FALSE;
- }
-
- if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
- trx->check_unique_secondary = FALSE;
- }
-
- if (lower_case_table_names) {
- srv_lower_case_table_names = TRUE;
- } else {
- srv_lower_case_table_names = FALSE;
- }
-
- fn_format(name2, name, "", "", 2); // Remove the .frm extension
-
- normalize_table_name(norm_name, name2);
-
- /* Latch the InnoDB data dictionary exclusively so that no deadlocks
- or lock waits can happen in it during a table create operation.
- Drop table etc. do this latching in row0mysql.c. */
-
- row_mysql_lock_data_dictionary(trx);
-
- /* Create the table definition in InnoDB */
-
- error = create_table_def(trx, form, norm_name,
- create_info->options & HA_LEX_CREATE_TMP_TABLE ? name2 : NULL,
- form->s->row_type != ROW_TYPE_REDUNDANT);
-
- if (error) {
- goto cleanup;
- }
-
- /* Look for a primary key */
-
- primary_key_no= (table->s->primary_key != MAX_KEY ?
- (int) table->s->primary_key :
- -1);
-
- /* Our function row_get_mysql_key_number_for_index assumes
- the primary key is always number 0, if it exists */
-
- DBUG_ASSERT(primary_key_no == -1 || primary_key_no == 0);
-
- /* Create the keys */
-
- if (form->s->keys == 0 || primary_key_no == -1) {
- /* Create an index which is used as the clustered index;
- order the rows by their row id which is internally generated
- by InnoDB */
-
- error = create_clustered_index_when_no_primary(trx,
- norm_name);
- if (error) {
- goto cleanup;
- }
- }
-
- if (primary_key_no != -1) {
- /* In InnoDB the clustered index must always be created
- first */
- if ((error = create_index(trx, form, norm_name,
- (uint) primary_key_no))) {
- goto cleanup;
- }
- }
-
- for (i = 0; i < form->s->keys; i++) {
-
- if (i != (uint) primary_key_no) {
-
- if ((error = create_index(trx, form, norm_name, i))) {
- goto cleanup;
- }
- }
- }
-
- if (current_thd->query != NULL) {
- LEX_STRING q;
-
- if (thd->convert_string(&q, system_charset_info,
- current_thd->query,
- current_thd->query_length,
- current_thd->charset())) {
- error = HA_ERR_OUT_OF_MEM;
-
- goto cleanup;
- }
-
- error = row_table_add_foreign_constraints(trx,
- q.str, norm_name,
- create_info->options & HA_LEX_CREATE_TMP_TABLE);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- if (error) {
- goto cleanup;
- }
- }
-
- innobase_commit_low(trx);
-
- row_mysql_unlock_data_dictionary(trx);
-
- /* Flush the log to reduce probability that the .frm files and
- the InnoDB data dictionary get out-of-sync if the user runs
- with innodb_flush_log_at_trx_commit = 0 */
-
- log_buffer_flush_to_disk();
-
- innobase_table = dict_table_get(norm_name, NULL);
-
- DBUG_ASSERT(innobase_table != 0);
-
- if ((create_info->used_fields & HA_CREATE_USED_AUTO) &&
- (create_info->auto_increment_value != 0)) {
-
- /* Query was ALTER TABLE...AUTO_INCREMENT = x; or
- CREATE TABLE ...AUTO_INCREMENT = x; Find out a table
- definition from the dictionary and get the current value
- of the auto increment field. Set a new value to the
- auto increment field if the value is greater than the
- maximum value in the column. */
-
- auto_inc_value = create_info->auto_increment_value;
- dict_table_autoinc_initialize(innobase_table, auto_inc_value);
- }
-
- /* Tell the InnoDB server that there might be work for
- utility threads: */
-
- srv_active_wake_master_thread();
-
- trx_free_for_mysql(trx);
-
- DBUG_RETURN(0);
-
-cleanup:
- innobase_commit_low(trx);
-
- row_mysql_unlock_data_dictionary(trx);
-
- trx_free_for_mysql(trx);
-
- DBUG_RETURN(error);
-}
-
-/*********************************************************************
-Discards or imports an InnoDB tablespace. */
-
-int
-ha_innobase::discard_or_import_tablespace(
-/*======================================*/
- /* out: 0 == success, -1 == error */
- my_bool discard) /* in: TRUE if discard, else import */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- dict_table_t* dict_table;
- trx_t* trx;
- int err;
-
- DBUG_ENTER("ha_innobase::discard_or_import_tablespace");
-
- ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
- ut_a(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- dict_table = prebuilt->table;
- trx = prebuilt->trx;
-
- if (discard) {
- err = row_discard_tablespace_for_mysql(dict_table->name, trx);
- } else {
- err = row_import_tablespace_for_mysql(dict_table->name, trx);
- }
-
- err = convert_error_code_to_mysql(err, NULL);
-
- DBUG_RETURN(err);
-}
-
-/*********************************************************************
-Deletes all rows of an InnoDB table. */
-
-int
-ha_innobase::delete_all_rows(void)
-/*==============================*/
- /* out: error number */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
- int error;
- trx_t* trx;
- THD* thd = current_thd;
-
- DBUG_ENTER("ha_innobase::delete_all_rows");
-
- if (thd->lex->sql_command != SQLCOM_TRUNCATE) {
- fallback:
- /* We only handle TRUNCATE TABLE t as a special case.
- DELETE FROM t will have to use ha_innobase::delete_row(). */
- DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND);
- }
-
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- trx = check_trx_exists(thd);
-
- /* Truncate the table in InnoDB */
-
- error = row_truncate_table_for_mysql(prebuilt->table, trx);
- if (error == DB_ERROR) {
- /* Cannot truncate; resort to ha_innobase::delete_row() */
- goto fallback;
- }
-
- error = convert_error_code_to_mysql(error, NULL);
-
- DBUG_RETURN(error);
-}
-
-/*********************************************************************
-Drops a table from an InnoDB database. Before calling this function,
-MySQL calls innobase_commit to commit the transaction of the current user.
-Then the current user cannot have locks set on the table. Drop table
-operation inside InnoDB will remove all locks any user has on the table
-inside InnoDB. */
-
-int
-ha_innobase::delete_table(
-/*======================*/
- /* out: error number */
- const char* name) /* in: table name */
-{
- ulint name_len;
- int error;
- trx_t* parent_trx;
- trx_t* trx;
- THD *thd= current_thd;
- char norm_name[1000];
-
- DBUG_ENTER("ha_innobase::delete_table");
-
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- parent_trx = check_trx_exists(current_thd);
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(parent_trx);
-
- if (lower_case_table_names) {
- srv_lower_case_table_names = TRUE;
- } else {
- srv_lower_case_table_names = FALSE;
- }
-
- trx = trx_allocate_for_mysql();
-
- trx->mysql_thd = current_thd;
- trx->mysql_query_str = &((*current_thd).query);
-
- if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
- trx->check_foreigns = FALSE;
- }
-
- if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
- trx->check_unique_secondary = FALSE;
- }
-
- name_len = strlen(name);
-
- assert(name_len < 1000);
-
- /* Strangely, MySQL passes the table name without the '.frm'
- extension, in contrast to ::create */
-
- normalize_table_name(norm_name, name);
-
- /* Drop the table in InnoDB */
-
- error = row_drop_table_for_mysql(norm_name, trx,
- thd->lex->sql_command == SQLCOM_DROP_DB);
-
- /* Flush the log to reduce probability that the .frm files and
- the InnoDB data dictionary get out-of-sync if the user runs
- with innodb_flush_log_at_trx_commit = 0 */
-
- log_buffer_flush_to_disk();
-
- /* Tell the InnoDB server that there might be work for
- utility threads: */
-
- srv_active_wake_master_thread();
-
- innobase_commit_low(trx);
-
- trx_free_for_mysql(trx);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- DBUG_RETURN(error);
-}
-
-/*********************************************************************
-Removes all tables in the named database inside InnoDB. */
-
-int
-innobase_drop_database(
-/*===================*/
- /* out: error number */
- char* path) /* in: database path; inside InnoDB the name
- of the last directory in the path is used as
- the database name: for example, in 'mysql/data/test'
- the database name is 'test' */
-{
- ulint len = 0;
- trx_t* parent_trx;
- trx_t* trx;
- char* ptr;
- int error;
- char* namebuf;
-
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- parent_trx = check_trx_exists(current_thd);
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(parent_trx);
-
- ptr = strend(path) - 2;
-
- while (ptr >= path && *ptr != '\\' && *ptr != '/') {
- ptr--;
- len++;
- }
-
- ptr++;
- namebuf = my_malloc((uint) len + 2, MYF(0));
-
- memcpy(namebuf, ptr, len);
- namebuf[len] = '/';
- namebuf[len + 1] = '\0';
-#ifdef __WIN__
- innobase_casedn_str(namebuf);
-#endif
- trx = trx_allocate_for_mysql();
- trx->mysql_thd = current_thd;
- trx->mysql_query_str = &((*current_thd).query);
-
- if (current_thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
- trx->check_foreigns = FALSE;
- }
-
- error = row_drop_database_for_mysql(namebuf, trx);
- my_free(namebuf, MYF(0));
-
- /* Flush the log to reduce probability that the .frm files and
- the InnoDB data dictionary get out-of-sync if the user runs
- with innodb_flush_log_at_trx_commit = 0 */
-
- log_buffer_flush_to_disk();
-
- /* Tell the InnoDB server that there might be work for
- utility threads: */
-
- srv_active_wake_master_thread();
-
- innobase_commit_low(trx);
- trx_free_for_mysql(trx);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- return(error);
-}
-
-/*************************************************************************
-Renames an InnoDB table. */
-
-int
-ha_innobase::rename_table(
-/*======================*/
- /* out: 0 or error code */
- const char* from, /* in: old name of the table */
- const char* to) /* in: new name of the table */
-{
- ulint name_len1;
- ulint name_len2;
- int error;
- trx_t* parent_trx;
- trx_t* trx;
- char norm_from[1000];
- char norm_to[1000];
-
- DBUG_ENTER("ha_innobase::rename_table");
-
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- parent_trx = check_trx_exists(current_thd);
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(parent_trx);
-
- if (lower_case_table_names) {
- srv_lower_case_table_names = TRUE;
- } else {
- srv_lower_case_table_names = FALSE;
- }
-
- trx = trx_allocate_for_mysql();
- trx->mysql_thd = current_thd;
- trx->mysql_query_str = &((*current_thd).query);
-
- if (current_thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
- trx->check_foreigns = FALSE;
- }
-
- name_len1 = strlen(from);
- name_len2 = strlen(to);
-
- assert(name_len1 < 1000);
- assert(name_len2 < 1000);
-
- normalize_table_name(norm_from, from);
- normalize_table_name(norm_to, to);
-
- /* Rename the table in InnoDB */
-
- error = row_rename_table_for_mysql(norm_from, norm_to, trx);
-
- /* Flush the log to reduce probability that the .frm files and
- the InnoDB data dictionary get out-of-sync if the user runs
- with innodb_flush_log_at_trx_commit = 0 */
-
- log_buffer_flush_to_disk();
-
- /* Tell the InnoDB server that there might be work for
- utility threads: */
-
- srv_active_wake_master_thread();
-
- innobase_commit_low(trx);
- trx_free_for_mysql(trx);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- DBUG_RETURN(error);
-}
-
-/*************************************************************************
-Estimates the number of index records in a range. */
-
-ha_rows
-ha_innobase::records_in_range(
-/*==========================*/
- /* out: estimated number of
- rows */
- uint keynr, /* in: index number */
- key_range *min_key, /* in: start key value of the
- range, may also be 0 */
- key_range *max_key) /* in: range end key val, may
- also be 0 */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- KEY* key;
- dict_index_t* index;
- mysql_byte* key_val_buff2 = (mysql_byte*) my_malloc(
- table->s->reclength
- + table->s->max_key_length + 100,
- MYF(MY_FAE));
- ulint buff2_len = table->s->reclength
- + table->s->max_key_length + 100;
- dtuple_t* range_start;
- dtuple_t* range_end;
- ib_longlong n_rows;
- ulint mode1;
- ulint mode2;
- void* heap1;
- void* heap2;
-
- DBUG_ENTER("records_in_range");
-
- prebuilt->trx->op_info = (char*)"estimating records in index range";
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
-
- active_index = keynr;
-
- key = table->key_info + active_index;
-
- index = dict_table_get_index_noninline(prebuilt->table, key->name);
-
- range_start = dtuple_create_for_mysql(&heap1, key->key_parts);
- dict_index_copy_types(range_start, index, key->key_parts);
-
- range_end = dtuple_create_for_mysql(&heap2, key->key_parts);
- dict_index_copy_types(range_end, index, key->key_parts);
-
- row_sel_convert_mysql_key_to_innobase(
- range_start, (byte*) key_val_buff,
- (ulint)upd_and_key_val_buff_len,
- index,
- (byte*) (min_key ? min_key->key :
- (const mysql_byte*) 0),
- (ulint) (min_key ? min_key->length : 0),
- prebuilt->trx);
-
- row_sel_convert_mysql_key_to_innobase(
- range_end, (byte*) key_val_buff2,
- buff2_len, index,
- (byte*) (max_key ? max_key->key :
- (const mysql_byte*) 0),
- (ulint) (max_key ? max_key->length : 0),
- prebuilt->trx);
-
- mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag :
- HA_READ_KEY_EXACT);
- mode2 = convert_search_mode_to_innobase(max_key ? max_key->flag :
- HA_READ_KEY_EXACT);
-
- n_rows = btr_estimate_n_rows_in_range(index, range_start,
- mode1, range_end, mode2);
- dtuple_free_for_mysql(heap1);
- dtuple_free_for_mysql(heap2);
-
- my_free((gptr) key_val_buff2, MYF(0));
-
- prebuilt->trx->op_info = (char*)"";
-
- /* The MySQL optimizer seems to believe an estimate of 0 rows is
- always accurate and may return the result 'Empty set' based on that.
- The accuracy is not guaranteed, and even if it were, for a locking
- read we should anyway perform the search to set the next-key lock.
- Add 1 to the value to make sure MySQL does not make the assumption! */
-
- if (n_rows == 0) {
- n_rows = 1;
- }
-
- DBUG_RETURN((ha_rows) n_rows);
-}
-
-/*************************************************************************
-Gives an UPPER BOUND to the number of rows in a table. This is used in
-filesort.cc. */
-
-ha_rows
-ha_innobase::estimate_rows_upper_bound(void)
-/*======================================*/
- /* out: upper bound of rows */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- dict_index_t* index;
- ulonglong estimate;
- ulonglong local_data_file_length;
-
- DBUG_ENTER("estimate_rows_upper_bound");
-
- /* We do not know if MySQL can call this function before calling
- external_lock(). To be safe, update the thd of the current table
- handle. */
-
- update_thd(current_thd);
-
- prebuilt->trx->op_info = (char*)
- "calculating upper bound for table rows";
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
-
- index = dict_table_get_first_index_noninline(prebuilt->table);
-
- local_data_file_length = ((ulonglong) index->stat_n_leaf_pages)
- * UNIV_PAGE_SIZE;
-
- /* Calculate a minimum length for a clustered index record and from
- that an upper bound for the number of rows. Since we only calculate
- new statistics in row0mysql.c when a table has grown by a threshold
- factor, we must add a safety factor 2 in front of the formula below. */
-
- estimate = 2 * local_data_file_length /
- dict_index_calc_min_rec_len(index);
-
- prebuilt->trx->op_info = (char*)"";
-
- DBUG_RETURN((ha_rows) estimate);
-}
-
-/*************************************************************************
-How many seeks it will take to read through the table. This is to be
-comparable to the number returned by records_in_range so that we can
-decide if we should scan the table or use keys. */
-
-double
-ha_innobase::scan_time()
-/*====================*/
- /* out: estimated time measured in disk seeks */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
-
- /* Since MySQL seems to favor table scans too much over index
- searches, we pretend that a sequential read takes the same time
- as a random disk read, that is, we do not divide the following
- by 10, which would be physically realistic. */
-
- return((double) (prebuilt->table->stat_clustered_index_size));
-}
-
-/**********************************************************************
-Calculate the time it takes to read a set of ranges through an index
-This enables us to optimise reads for clustered indexes. */
-
-double
-ha_innobase::read_time(
-/*===================*/
- /* out: estimated time measured in disk seeks */
- uint index, /* in: key number */
- uint ranges, /* in: how many ranges */
- ha_rows rows) /* in: estimated number of rows in the ranges */
-{
- ha_rows total_rows;
- double time_for_scan;
-
- if (index != table->s->primary_key) {
- /* Not clustered */
- return(handler::read_time(index, ranges, rows));
- }
-
- if (rows <= 2) {
-
- return((double) rows);
- }
-
- /* Assume that the read time is proportional to the scan time for all
- rows + at most one seek per range. */
-
- time_for_scan = scan_time();
-
- if ((total_rows = estimate_rows_upper_bound()) < rows) {
-
- return(time_for_scan);
- }
-
- return(ranges + (double) rows / (double) total_rows * time_for_scan);
-}
-
-/*************************************************************************
-Returns statistics information of the table to the MySQL interpreter,
-in various fields of the handle object. */
-
-int
-ha_innobase::info(
-/*==============*/
- uint flag) /* in: what information MySQL requests */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- dict_table_t* ib_table;
- dict_index_t* index;
- ha_rows rec_per_key;
- ib_longlong n_rows;
- ulong j;
- ulong i;
- char path[FN_REFLEN];
- os_file_stat_t stat_info;
-
- DBUG_ENTER("info");
-
- /* If we are forcing recovery at a high level, we will suppress
- statistics calculation on tables, because that may crash the
- server if an index is badly corrupted. */
-
- if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) {
-
- DBUG_RETURN(HA_ERR_CRASHED);
- }
-
- /* We do not know if MySQL can call this function before calling
- external_lock(). To be safe, update the thd of the current table
- handle. */
-
- update_thd(current_thd);
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- prebuilt->trx->op_info = (char*)"returning various info to MySQL";
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
-
- ib_table = prebuilt->table;
-
- if (flag & HA_STATUS_TIME) {
- /* In sql_show we call with this flag: update then statistics
- so that they are up-to-date */
-
- prebuilt->trx->op_info = (char*)"updating table statistics";
-
- dict_update_statistics(ib_table);
-
- prebuilt->trx->op_info = (char*)
- "returning various info to MySQL";
- my_snprintf(path, sizeof(path), "%s/%s%s",
- mysql_data_home, ib_table->name,
- reg_ext);
-
- unpack_filename(path,path);
-
- /* Note that we do not know the access time of the table,
- nor the CHECK TABLE time, nor the UPDATE or INSERT time. */
-
- if (os_file_get_status(path,&stat_info)) {
- create_time = stat_info.ctime;
- }
- }
-
- if (flag & HA_STATUS_VARIABLE) {
- n_rows = ib_table->stat_n_rows;
-
- /* Because we do not protect stat_n_rows by any mutex in a
- delete, it is theoretically possible that the value can be
- smaller than zero! TODO: fix this race.
-
- The MySQL optimizer seems to assume in a left join that n_rows
- is an accurate estimate if it is zero. Of course, it is not,
- since we do not have any locks on the rows yet at this phase.
- Since SHOW TABLE STATUS seems to call this function with the
- HA_STATUS_TIME flag set, while the left join optimizer does not
- set that flag, we add one to a zero value if the flag is not
- set. That way SHOW TABLE STATUS will show the best estimate,
- while the optimizer never sees the table empty. */
-
- if (n_rows < 0) {
- n_rows = 0;
- }
-
- if (n_rows == 0 && !(flag & HA_STATUS_TIME)) {
- n_rows++;
- }
-
- records = (ha_rows)n_rows;
- deleted = 0;
- data_file_length = ((ulonglong)
- ib_table->stat_clustered_index_size)
- * UNIV_PAGE_SIZE;
- index_file_length = ((ulonglong)
- ib_table->stat_sum_of_other_index_sizes)
- * UNIV_PAGE_SIZE;
- delete_length = 0;
- check_time = 0;
-
- if (records == 0) {
- mean_rec_length = 0;
- } else {
- mean_rec_length = (ulong) (data_file_length / records);
- }
- }
-
- if (flag & HA_STATUS_CONST) {
- index = dict_table_get_first_index_noninline(ib_table);
-
- if (prebuilt->clust_index_was_generated) {
- index = dict_table_get_next_index_noninline(index);
- }
-
- for (i = 0; i < table->s->keys; i++) {
- if (index == NULL) {
- ut_print_timestamp(stderr);
- sql_print_error("Table %s contains fewer "
- "indexes inside InnoDB than "
- "are defined in the MySQL "
- ".frm file. Have you mixed up "
- ".frm files from different "
- "installations? See "
-"http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n",
-
- ib_table->name);
- break;
- }
-
- for (j = 0; j < table->key_info[i].key_parts; j++) {
-
- if (j + 1 > index->n_uniq) {
- ut_print_timestamp(stderr);
- sql_print_error(
-"Index %s of %s has %lu columns unique inside InnoDB, but MySQL is asking "
-"statistics for %lu columns. Have you mixed up .frm files from different "
-"installations? "
-"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n",
- index->name,
- ib_table->name,
- (unsigned long)
- index->n_uniq, j + 1);
- break;
- }
-
- if (index->stat_n_diff_key_vals[j + 1] == 0) {
-
- rec_per_key = records;
- } else {
- rec_per_key = (ha_rows)(records /
- index->stat_n_diff_key_vals[j + 1]);
- }
-
- /* Since MySQL seems to favor table scans
- too much over index searches, we pretend
- index selectivity is 2 times better than
- our estimate: */
-
- rec_per_key = rec_per_key / 2;
-
- if (rec_per_key == 0) {
- rec_per_key = 1;
- }
-
- table->key_info[i].rec_per_key[j]=
- rec_per_key >= ~(ulong) 0 ? ~(ulong) 0 :
- rec_per_key;
- }
-
- index = dict_table_get_next_index_noninline(index);
- }
- }
-
- if (flag & HA_STATUS_ERRKEY) {
- ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
-
- errkey = (unsigned int) row_get_mysql_key_number_for_index(
- (dict_index_t*)
- trx_get_error_info(prebuilt->trx));
- }
-
- if (flag & HA_STATUS_AUTO && table->found_next_number_field) {
- longlong auto_inc;
- int ret;
-
- /* The following function call can the first time fail in
- a lock wait timeout error because it reserves the auto-inc
- lock on the table. If it fails, then someone is already initing
- the auto-inc counter, and the second call is guaranteed to
- succeed. */
-
- ret = innobase_read_and_init_auto_inc(&auto_inc);
-
- if (ret != 0) {
- ret = innobase_read_and_init_auto_inc(&auto_inc);
-
- if (ret != 0) {
- ut_print_timestamp(stderr);
- sql_print_error("Cannot get table %s auto-inc"
- "counter value in ::info\n",
- ib_table->name);
- auto_inc = 0;
- }
- }
-
- auto_increment_value = auto_inc;
- }
-
- prebuilt->trx->op_info = (char*)"";
-
- DBUG_RETURN(0);
-}
-
-/**************************************************************************
-Updates index cardinalities of the table, based on 8 random dives into
-each index tree. This does NOT calculate exact statistics on the table. */
-
-int
-ha_innobase::analyze(
-/*=================*/
- /* out: returns always 0 (success) */
- THD* thd, /* in: connection thread handle */
- HA_CHECK_OPT* check_opt) /* in: currently ignored */
-{
- /* Simply call ::info() with all the flags */
- info(HA_STATUS_TIME | HA_STATUS_CONST | HA_STATUS_VARIABLE);
-
- return(0);
-}
-
-/**************************************************************************
-This is mapped to "ALTER TABLE tablename TYPE=InnoDB", which rebuilds
-the table in MySQL. */
-
-int
-ha_innobase::optimize(
-/*==================*/
- THD* thd, /* in: connection thread handle */
- HA_CHECK_OPT* check_opt) /* in: currently ignored */
-{
- return(HA_ADMIN_TRY_ALTER);
-}
-
-/***********************************************************************
-Tries to check that an InnoDB table is not corrupted. If corruption is
-noticed, prints to stderr information about it. In case of corruption
-may also assert a failure and crash the server. */
-
-int
-ha_innobase::check(
-/*===============*/
- /* out: HA_ADMIN_CORRUPT or
- HA_ADMIN_OK */
- THD* thd, /* in: user thread handle */
- HA_CHECK_OPT* check_opt) /* in: check options, currently
- ignored */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- ulint ret;
-
- ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
- ut_a(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- if (prebuilt->mysql_template == NULL) {
- /* Build the template; we will use a dummy template
- in index scans done in checking */
-
- build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
- }
-
- ret = row_check_table_for_mysql(prebuilt);
-
- if (ret == DB_SUCCESS) {
- return(HA_ADMIN_OK);
- }
-
- return(HA_ADMIN_CORRUPT);
-}
-
-/*****************************************************************
-Adds information about free space in the InnoDB tablespace to a table comment
-which is printed out when a user calls SHOW TABLE STATUS. Adds also info on
-foreign keys. */
-
-char*
-ha_innobase::update_table_comment(
-/*==============================*/
- /* out: table comment + InnoDB free space +
- info on foreign keys */
- const char* comment)/* in: table comment defined by user */
-{
- uint length = (uint) strlen(comment);
- char* str;
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
- long flen;
-
- /* We do not know if MySQL can call this function before calling
- external_lock(). To be safe, update the thd of the current table
- handle. */
-
- if (length > 64000 - 3) {
- return((char*)comment); /* string too long */
- }
-
- update_thd(current_thd);
-
- prebuilt->trx->op_info = (char*)"returning table comment";
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
- str = NULL;
-
- /* output the data to a temporary file */
-
- mutex_enter_noninline(&srv_dict_tmpfile_mutex);
- rewind(srv_dict_tmpfile);
-
- fprintf(srv_dict_tmpfile, "InnoDB free: %lu kB",
- (ulong) fsp_get_available_space_in_free_extents(
- prebuilt->table->space));
-
- dict_print_info_on_foreign_keys(FALSE, srv_dict_tmpfile,
- prebuilt->trx, prebuilt->table);
- flen = ftell(srv_dict_tmpfile);
- if (flen < 0) {
- flen = 0;
- } else if (length + flen + 3 > 64000) {
- flen = 64000 - 3 - length;
- }
-
- /* allocate buffer for the full string, and
- read the contents of the temporary file */
-
- str = my_malloc(length + flen + 3, MYF(0));
-
- if (str) {
- char* pos = str + length;
- if (length) {
- memcpy(str, comment, length);
- *pos++ = ';';
- *pos++ = ' ';
- }
- rewind(srv_dict_tmpfile);
- flen = (uint) fread(pos, 1, flen, srv_dict_tmpfile);
- pos[flen] = 0;
- }
-
- mutex_exit_noninline(&srv_dict_tmpfile_mutex);
-
- prebuilt->trx->op_info = (char*)"";
-
- return(str ? str : (char*) comment);
-}
-
-/***********************************************************************
-Gets the foreign key create info for a table stored in InnoDB. */
-
-char*
-ha_innobase::get_foreign_key_create_info(void)
-/*==========================================*/
- /* out, own: character string in the form which
- can be inserted to the CREATE TABLE statement,
- MUST be freed with ::free_foreign_key_create_info */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
- char* str = 0;
- long flen;
-
- ut_a(prebuilt != NULL);
-
- /* We do not know if MySQL can call this function before calling
- external_lock(). To be safe, update the thd of the current table
- handle. */
-
- update_thd(current_thd);
-
- prebuilt->trx->op_info = (char*)"getting info on foreign keys";
-
- /* In case MySQL calls this in the middle of a SELECT query,
- release possible adaptive hash latch to avoid
- deadlocks of threads */
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
-
- mutex_enter_noninline(&srv_dict_tmpfile_mutex);
- rewind(srv_dict_tmpfile);
-
- /* output the data to a temporary file */
- dict_print_info_on_foreign_keys(TRUE, srv_dict_tmpfile,
- prebuilt->trx, prebuilt->table);
- prebuilt->trx->op_info = (char*)"";
-
- flen = ftell(srv_dict_tmpfile);
- if (flen < 0) {
- flen = 0;
- } else if (flen > 64000 - 1) {
- flen = 64000 - 1;
- }
-
- /* allocate buffer for the string, and
- read the contents of the temporary file */
-
- str = my_malloc(flen + 1, MYF(0));
-
- if (str) {
- rewind(srv_dict_tmpfile);
- flen = (uint) fread(str, 1, flen, srv_dict_tmpfile);
- str[flen] = 0;
- }
-
- mutex_exit_noninline(&srv_dict_tmpfile_mutex);
-
- return(str);
-}
-
-
-int
-ha_innobase::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
-{
- dict_foreign_t* foreign;
-
- DBUG_ENTER("get_foreign_key_list");
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
- ut_a(prebuilt != NULL);
- update_thd(current_thd);
- prebuilt->trx->op_info = (char*)"getting list of foreign keys";
- trx_search_latch_release_if_reserved(prebuilt->trx);
- mutex_enter_noninline(&(dict_sys->mutex));
- foreign = UT_LIST_GET_FIRST(prebuilt->table->foreign_list);
-
- while (foreign != NULL)
- {
- uint i;
- FOREIGN_KEY_INFO f_key_info;
- LEX_STRING *name= 0;
- const char *tmp_buff;
-
- tmp_buff= foreign->id;
- i= 0;
- while (tmp_buff[i] != '/')
- i++;
- tmp_buff+= i + 1;
- f_key_info.forein_id= make_lex_string(thd, 0, tmp_buff,
- (uint) strlen(tmp_buff), 1);
- tmp_buff= foreign->referenced_table_name;
- i= 0;
- while (tmp_buff[i] != '/')
- i++;
- f_key_info.referenced_db= make_lex_string(thd, 0,
- tmp_buff, i, 1);
- tmp_buff+= i + 1;
- f_key_info.referenced_table= make_lex_string(thd, 0, tmp_buff,
- (uint) strlen(tmp_buff), 1);
-
- for (i= 0;;)
- {
- tmp_buff= foreign->foreign_col_names[i];
- name= make_lex_string(thd, name, tmp_buff, (uint) strlen(tmp_buff), 1);
- f_key_info.foreign_fields.push_back(name);
- tmp_buff= foreign->referenced_col_names[i];
- name= make_lex_string(thd, name, tmp_buff, (uint) strlen(tmp_buff), 1);
- f_key_info.referenced_fields.push_back(name);
- if (++i >= foreign->n_fields)
- break;
- }
-
- ulong length= 0;
- if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE)
- {
- length=17;
- tmp_buff= "ON DELETE CASCADE";
- }
- else if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL)
- {
- length=18;
- tmp_buff= "ON DELETE SET NULL";
- }
- else if (foreign->type == DICT_FOREIGN_ON_DELETE_NO_ACTION)
- {
- length=19;
- tmp_buff= "ON DELETE NO ACTION";
- }
- else if (foreign->type == DICT_FOREIGN_ON_UPDATE_CASCADE)
- {
- length=17;
- tmp_buff= "ON UPDATE CASCADE";
- }
- else if (foreign->type == DICT_FOREIGN_ON_UPDATE_SET_NULL)
- {
- length=18;
- tmp_buff= "ON UPDATE SET NULL";
- }
- else if (foreign->type == DICT_FOREIGN_ON_UPDATE_NO_ACTION)
- {
- length=19;
- tmp_buff= "ON UPDATE NO ACTION";
- }
- f_key_info.constraint_method= make_lex_string(thd,
- f_key_info.constraint_method,
- tmp_buff, length, 1);
-
- FOREIGN_KEY_INFO *pf_key_info= ((FOREIGN_KEY_INFO *)
- thd->memdup((gptr) &f_key_info,
- sizeof(FOREIGN_KEY_INFO)));
- f_key_list->push_back(pf_key_info);
- foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
- }
- mutex_exit_noninline(&(dict_sys->mutex));
- prebuilt->trx->op_info = (char*)"";
- DBUG_RETURN(0);
-}
-
-/*********************************************************************
-Checks if ALTER TABLE may change the storage engine of the table.
-Changing storage engines is not allowed for tables for which there
-are foreign key constraints (parent or child tables). */
-
-bool
-ha_innobase::can_switch_engines(void)
-/*=================================*/
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- bool can_switch;
-
- DBUG_ENTER("ha_innobase::can_switch_engines");
- prebuilt->trx->op_info =
- "determining if there are foreign key constraints";
- row_mysql_lock_data_dictionary(prebuilt->trx);
-
- can_switch = !UT_LIST_GET_FIRST(prebuilt->table->referenced_list)
- && !UT_LIST_GET_FIRST(prebuilt->table->foreign_list);
-
- row_mysql_unlock_data_dictionary(prebuilt->trx);
- prebuilt->trx->op_info = "";
-
- DBUG_RETURN(can_switch);
-}
-
-/***********************************************************************
-Checks if a table is referenced by a foreign key. The MySQL manual states that
-a REPLACE is either equivalent to an INSERT, or DELETE(s) + INSERT. Only a
-delete is then allowed internally to resolve a duplicate key conflict in
-REPLACE, not an update. */
-
-uint
-ha_innobase::referenced_by_foreign_key(void)
-/*========================================*/
- /* out: > 0 if referenced by a FOREIGN KEY */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
-
- if (dict_table_referenced_by_foreign_key(prebuilt->table)) {
-
- return(1);
- }
-
- return(0);
-}
-
-/***********************************************************************
-Frees the foreign key create info for a table stored in InnoDB, if it is
-non-NULL. */
-
-void
-ha_innobase::free_foreign_key_create_info(
-/*======================================*/
- char* str) /* in, own: create info string to free */
-{
- if (str) {
- my_free(str, MYF(0));
- }
-}
-
-/***********************************************************************
-Tells something additional to the handler about how to do things. */
-
-int
-ha_innobase::extra(
-/*===============*/
- /* out: 0 or error number */
- enum ha_extra_function operation)
- /* in: HA_EXTRA_RETRIEVE_ALL_COLS or some
- other flag */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
-
- /* Warning: since it is not sure that MySQL calls external_lock
- before calling this function, the trx field in prebuilt can be
- obsolete! */
-
- switch (operation) {
- case HA_EXTRA_FLUSH:
- if (prebuilt->blob_heap) {
- row_mysql_prebuilt_free_blob_heap(prebuilt);
- }
- break;
- case HA_EXTRA_RESET:
- if (prebuilt->blob_heap) {
- row_mysql_prebuilt_free_blob_heap(prebuilt);
- }
- prebuilt->keep_other_fields_on_keyread = 0;
- prebuilt->read_just_key = 0;
- break;
- case HA_EXTRA_RESET_STATE:
- prebuilt->keep_other_fields_on_keyread = 0;
- prebuilt->read_just_key = 0;
- break;
- case HA_EXTRA_NO_KEYREAD:
- prebuilt->read_just_key = 0;
- break;
- case HA_EXTRA_RETRIEVE_ALL_COLS:
- prebuilt->hint_need_to_fetch_extra_cols
- = ROW_RETRIEVE_ALL_COLS;
- break;
- case HA_EXTRA_RETRIEVE_PRIMARY_KEY:
- if (prebuilt->hint_need_to_fetch_extra_cols == 0) {
- prebuilt->hint_need_to_fetch_extra_cols
- = ROW_RETRIEVE_PRIMARY_KEY;
- }
- break;
- case HA_EXTRA_KEYREAD:
- prebuilt->read_just_key = 1;
- break;
- case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
- prebuilt->keep_other_fields_on_keyread = 1;
- break;
- default:/* Do nothing */
- ;
- }
-
- return(0);
-}
-
-/**********************************************************************
-MySQL calls this function at the start of each SQL statement inside LOCK
-TABLES. Inside LOCK TABLES the ::external_lock method does not work to
-mark SQL statement borders. Note also a special case: if a temporary table
-is created inside LOCK TABLES, MySQL has not called external_lock() at all
-on that table.
-MySQL-5.0 also calls this before each statement in an execution of a stored
-procedure. To make the execution more deterministic for binlogging, MySQL-5.0
-locks all tables involved in a stored procedure with full explicit table
-locks (thd->in_lock_tables is true in ::store_lock()) before executing the
-procedure. */
-
-int
-ha_innobase::start_stmt(
-/*====================*/
- /* out: 0 or error code */
- THD* thd, /* in: handle to the user thread */
- thr_lock_type lock_type)
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- trx_t* trx;
-
- update_thd(thd);
-
- trx = prebuilt->trx;
-
- /* Here we release the search latch and the InnoDB thread FIFO ticket
- if they were reserved. They should have been released already at the
- end of the previous statement, but because inside LOCK TABLES the
- lock count method does not work to mark the end of a SELECT statement,
- that may not be the case. We MUST release the search latch before an
- INSERT, for example. */
-
- innobase_release_stat_resources(trx);
-
- prebuilt->sql_stat_start = TRUE;
- prebuilt->hint_need_to_fetch_extra_cols = 0;
- prebuilt->read_just_key = 0;
- prebuilt->keep_other_fields_on_keyread = FALSE;
-
- if (!prebuilt->mysql_has_locked) {
- /* This handle is for a temporary table created inside
- this same LOCK TABLES; since MySQL does NOT call external_lock
- in this case, we must use x-row locks inside InnoDB to be
- prepared for an update of a row */
-
- prebuilt->select_lock_type = LOCK_X;
- } else {
- if (trx->isolation_level != TRX_ISO_SERIALIZABLE
- && thd->lex->sql_command == SQLCOM_SELECT
- && lock_type == TL_READ) {
-
- /* For other than temporary tables, we obtain
- no lock for consistent read (plain SELECT). */
-
- prebuilt->select_lock_type = LOCK_NONE;
- } else {
- /* Not a consistent read: restore the
- select_lock_type value. The value of
- stored_select_lock_type was decided in:
- 1) ::store_lock(),
- 2) ::external_lock(),
- 3) ::init_table_handle_for_HANDLER(), and
- 4) :.transactional_table_lock(). */
-
- prebuilt->select_lock_type =
- prebuilt->stored_select_lock_type;
- }
- }
-
- trx->detailed_error[0] = '\0';
-
- /* Set the MySQL flag to mark that there is an active transaction */
- if (trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(thd);
- trx->active_trans = 1;
- } else {
- innobase_register_stmt(thd);
- }
-
- return(0);
-}
-
-/**********************************************************************
-Maps a MySQL trx isolation level code to the InnoDB isolation level code */
-inline
-ulint
-innobase_map_isolation_level(
-/*=========================*/
- /* out: InnoDB isolation level */
- enum_tx_isolation iso) /* in: MySQL isolation level code */
-{
- switch(iso) {
- case ISO_REPEATABLE_READ: return(TRX_ISO_REPEATABLE_READ);
- case ISO_READ_COMMITTED: return(TRX_ISO_READ_COMMITTED);
- case ISO_SERIALIZABLE: return(TRX_ISO_SERIALIZABLE);
- case ISO_READ_UNCOMMITTED: return(TRX_ISO_READ_UNCOMMITTED);
- default: ut_a(0); return(0);
- }
-}
-
-/**********************************************************************
-As MySQL will execute an external lock for every new table it uses when it
-starts to process an SQL statement (an exception is when MySQL calls
-start_stmt for the handle) we can use this function to store the pointer to
-the THD in the handle. We will also use this function to communicate
-to InnoDB that a new SQL statement has started and that we must store a
-savepoint to our transaction handle, so that we are able to roll back
-the SQL statement in case of an error. */
-
-int
-ha_innobase::external_lock(
-/*=======================*/
- /* out: 0 */
- THD* thd, /* in: handle to the user thread */
- int lock_type) /* in: lock type */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- trx_t* trx;
-
- DBUG_ENTER("ha_innobase::external_lock");
- DBUG_PRINT("enter",("lock_type: %d", lock_type));
-
- update_thd(thd);
-
- trx = prebuilt->trx;
-
- prebuilt->sql_stat_start = TRUE;
- prebuilt->hint_need_to_fetch_extra_cols = 0;
-
- prebuilt->read_just_key = 0;
- prebuilt->keep_other_fields_on_keyread = FALSE;
-
- if (lock_type == F_WRLCK) {
-
- /* If this is a SELECT, then it is in UPDATE TABLE ...
- or SELECT ... FOR UPDATE */
- prebuilt->select_lock_type = LOCK_X;
- prebuilt->stored_select_lock_type = LOCK_X;
- }
-
- if (lock_type != F_UNLCK) {
- /* MySQL is setting a new table lock */
-
- trx->detailed_error[0] = '\0';
-
- /* Set the MySQL flag to mark that there is an active
- transaction */
- if (trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(thd);
- trx->active_trans = 1;
- } else if (trx->n_mysql_tables_in_use == 0) {
- innobase_register_stmt(thd);
- }
-
- trx->n_mysql_tables_in_use++;
- prebuilt->mysql_has_locked = TRUE;
-
- if (trx->n_mysql_tables_in_use == 1) {
- trx->isolation_level = innobase_map_isolation_level(
- (enum_tx_isolation)
- thd->variables.tx_isolation);
- }
-
- if (trx->isolation_level == TRX_ISO_SERIALIZABLE
- && prebuilt->select_lock_type == LOCK_NONE
- && (thd->options
- & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
-
- /* To get serializable execution, we let InnoDB
- conceptually add 'LOCK IN SHARE MODE' to all SELECTs
- which otherwise would have been consistent reads. An
- exception is consistent reads in the AUTOCOMMIT=1 mode:
- we know that they are read-only transactions, and they
- can be serialized also if performed as consistent
- reads. */
-
- prebuilt->select_lock_type = LOCK_S;
- prebuilt->stored_select_lock_type = LOCK_S;
- }
-
- /* Starting from 4.1.9, no InnoDB table lock is taken in LOCK
- TABLES if AUTOCOMMIT=1. It does not make much sense to acquire
- an InnoDB table lock if it is released immediately at the end
- of LOCK TABLES, and InnoDB's table locks in that case cause
- VERY easily deadlocks.
-
- We do not set InnoDB table locks if user has not explicitly
- requested a table lock. Note that thd->in_lock_tables
- can be TRUE on some cases e.g. at the start of a stored
- procedure call (SQLCOM_CALL). */
-
- if (prebuilt->select_lock_type != LOCK_NONE) {
-
- if (thd->in_lock_tables &&
- thd->lex->sql_command == SQLCOM_LOCK_TABLES &&
- thd->variables.innodb_table_locks &&
- (thd->options & OPTION_NOT_AUTOCOMMIT)) {
-
- ulint error;
- error = row_lock_table_for_mysql(prebuilt,
- NULL, 0);
-
- if (error != DB_SUCCESS) {
- error = convert_error_code_to_mysql(
- (int) error, user_thd);
- DBUG_RETURN((int) error);
- }
- }
-
- trx->mysql_n_tables_locked++;
- }
-
- DBUG_RETURN(0);
- }
-
- /* MySQL is releasing a table lock */
-
- trx->n_mysql_tables_in_use--;
- prebuilt->mysql_has_locked = FALSE;
-
- /* If the MySQL lock count drops to zero we know that the current SQL
- statement has ended */
-
- if (trx->n_mysql_tables_in_use == 0) {
-
- trx->mysql_n_tables_locked = 0;
- prebuilt->used_in_HANDLER = FALSE;
-
- /* Release a possible FIFO ticket and search latch. Since we
- may reserve the kernel mutex, we have to release the search
- system latch first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
- if (trx->active_trans != 0) {
- innobase_commit(thd, TRUE);
- }
- } else {
- if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
- && trx->global_read_view) {
-
- /* At low transaction isolation levels we let
- each consistent read set its own snapshot */
-
- read_view_close_for_mysql(trx);
- }
- }
- }
-
- DBUG_RETURN(0);
-}
-
-/**********************************************************************
-With this function MySQL request a transactional lock to a table when
-user issued query LOCK TABLES..WHERE ENGINE = InnoDB. */
-
-int
-ha_innobase::transactional_table_lock(
-/*==================================*/
- /* out: error code */
- THD* thd, /* in: handle to the user thread */
- int lock_type) /* in: lock type */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- trx_t* trx;
-
- DBUG_ENTER("ha_innobase::transactional_table_lock");
- DBUG_PRINT("enter",("lock_type: %d", lock_type));
-
- /* We do not know if MySQL can call this function before calling
- external_lock(). To be safe, update the thd of the current table
- handle. */
-
- update_thd(thd);
-
- if (prebuilt->table->ibd_file_missing && !current_thd->tablespace_op) {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB error:\n"
-"MySQL is trying to use a table handle but the .ibd file for\n"
-"table %s does not exist.\n"
-"Have you deleted the .ibd file from the database directory under\n"
-"the MySQL datadir?"
-"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
-"how you can resolve the problem.\n",
- prebuilt->table->name);
- DBUG_RETURN(HA_ERR_CRASHED);
- }
-
- trx = prebuilt->trx;
-
- prebuilt->sql_stat_start = TRUE;
- prebuilt->hint_need_to_fetch_extra_cols = 0;
-
- prebuilt->read_just_key = 0;
- prebuilt->keep_other_fields_on_keyread = FALSE;
-
- if (lock_type == F_WRLCK) {
- prebuilt->select_lock_type = LOCK_X;
- prebuilt->stored_select_lock_type = LOCK_X;
- } else if (lock_type == F_RDLCK) {
- prebuilt->select_lock_type = LOCK_S;
- prebuilt->stored_select_lock_type = LOCK_S;
- } else {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB error:\n"
-"MySQL is trying to set transactional table lock with corrupted lock type\n"
-"to table %s, lock type %d does not exist.\n",
- prebuilt->table->name, lock_type);
- DBUG_RETURN(HA_ERR_CRASHED);
- }
-
- /* MySQL is setting a new transactional table lock */
-
- /* Set the MySQL flag to mark that there is an active transaction */
- if (trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(thd);
- trx->active_trans = 1;
- }
-
- if (thd->in_lock_tables && thd->variables.innodb_table_locks) {
- ulint error = DB_SUCCESS;
-
- error = row_lock_table_for_mysql(prebuilt, NULL, 0);
-
- if (error != DB_SUCCESS) {
- error = convert_error_code_to_mysql((int) error, user_thd);
- DBUG_RETURN((int) error);
- }
-
- if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
-
- /* Store the current undo_no of the transaction
- so that we know where to roll back if we have
- to roll back the next SQL statement */
-
- trx_mark_sql_stat_end(trx);
- }
- }
-
- DBUG_RETURN(0);
-}
-
-/****************************************************************************
-Here we export InnoDB status variables to MySQL. */
-
-void
-innodb_export_status(void)
-/*======================*/
-{
- srv_export_innodb_status();
-}
-
-/****************************************************************************
-Implements the SHOW INNODB STATUS command. Sends the output of the InnoDB
-Monitor to the client. */
-
-bool
-innodb_show_status(
-/*===============*/
- THD* thd) /* in: the MySQL query thread of the caller */
-{
- Protocol* protocol = thd->protocol;
- trx_t* trx;
- static const char truncated_msg[] = "... truncated...\n";
- const long MAX_STATUS_SIZE = 64000;
- ulint trx_list_start = ULINT_UNDEFINED;
- ulint trx_list_end = ULINT_UNDEFINED;
-
- DBUG_ENTER("innodb_show_status");
-
- if (have_innodb != SHOW_OPTION_YES) {
- my_message(ER_NOT_SUPPORTED_YET,
- "Cannot call SHOW INNODB STATUS because skip-innodb is defined",
- MYF(0));
- DBUG_RETURN(TRUE);
- }
-
- trx = check_trx_exists(thd);
-
- innobase_release_stat_resources(trx);
-
- /* We let the InnoDB Monitor to output at most MAX_STATUS_SIZE
- bytes of text. */
-
- long flen, usable_len;
- char* str;
-
- mutex_enter_noninline(&srv_monitor_file_mutex);
- rewind(srv_monitor_file);
- srv_printf_innodb_monitor(srv_monitor_file,
- &trx_list_start, &trx_list_end);
- flen = ftell(srv_monitor_file);
- os_file_set_eof(srv_monitor_file);
-
- if (flen < 0) {
- flen = 0;
- }
-
- if (flen > MAX_STATUS_SIZE) {
- usable_len = MAX_STATUS_SIZE;
- } else {
- usable_len = flen;
- }
-
- /* allocate buffer for the string, and
- read the contents of the temporary file */
-
- if (!(str = my_malloc(usable_len + 1, MYF(0))))
- {
- mutex_exit_noninline(&srv_monitor_file_mutex);
- DBUG_RETURN(TRUE);
- }
-
- rewind(srv_monitor_file);
- if (flen < MAX_STATUS_SIZE) {
- /* Display the entire output. */
- flen = (long) fread(str, 1, flen, srv_monitor_file);
- } else if (trx_list_end < (ulint) flen
- && trx_list_start < trx_list_end
- && trx_list_start + (flen - trx_list_end)
- < MAX_STATUS_SIZE - sizeof truncated_msg - 1) {
- /* Omit the beginning of the list of active transactions. */
- long len = (long) fread(str, 1, trx_list_start, srv_monitor_file);
- memcpy(str + len, truncated_msg, sizeof truncated_msg - 1);
- len += sizeof truncated_msg - 1;
- usable_len = (MAX_STATUS_SIZE - 1) - len;
- fseek(srv_monitor_file, flen - usable_len, SEEK_SET);
- len += (long) fread(str + len, 1, usable_len, srv_monitor_file);
- flen = len;
- } else {
- /* Omit the end of the output. */
- flen = (long) fread(str, 1, MAX_STATUS_SIZE - 1, srv_monitor_file);
- }
-
- mutex_exit_noninline(&srv_monitor_file_mutex);
-
- List<Item> field_list;
-
- field_list.push_back(new Item_empty_string("Status", flen));
-
- if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF)) {
- my_free(str, MYF(0));
-
- DBUG_RETURN(TRUE);
- }
-
- protocol->prepare_for_resend();
- protocol->store(str, flen, system_charset_info);
- my_free(str, MYF(0));
-
- if (protocol->write()) {
-
- DBUG_RETURN(TRUE);
- }
- send_eof(thd);
-
- DBUG_RETURN(FALSE);
-}
-
-/****************************************************************************
-Implements the SHOW MUTEX STATUS command. . */
-
-bool
-innodb_mutex_show_status(
-/*===============*/
- THD* thd) /* in: the MySQL query thread of the caller */
-{
- Protocol *protocol= thd->protocol;
- List<Item> field_list;
- mutex_t* mutex;
-#ifdef UNIV_DEBUG
- ulint rw_lock_count= 0;
- ulint rw_lock_count_spin_loop= 0;
- ulint rw_lock_count_spin_rounds= 0;
- ulint rw_lock_count_os_wait= 0;
- ulint rw_lock_count_os_yield= 0;
- ulonglong rw_lock_wait_time= 0;
-#endif /* UNIV_DEBUG */
- DBUG_ENTER("innodb_mutex_show_status");
-
-#ifdef UNIV_DEBUG
- field_list.push_back(new Item_empty_string("Mutex", FN_REFLEN));
- field_list.push_back(new Item_empty_string("Module", FN_REFLEN));
- field_list.push_back(new Item_uint("Count", 21));
- field_list.push_back(new Item_uint("Spin_waits", 21));
- field_list.push_back(new Item_uint("Spin_rounds", 21));
- field_list.push_back(new Item_uint("OS_waits", 21));
- field_list.push_back(new Item_uint("OS_yields", 21));
- field_list.push_back(new Item_uint("OS_waits_time", 21));
-#else /* UNIV_DEBUG */
- field_list.push_back(new Item_empty_string("File", FN_REFLEN));
- field_list.push_back(new Item_uint("Line", 21));
- field_list.push_back(new Item_uint("OS_waits", 21));
-#endif /* UNIV_DEBUG */
-
- if (protocol->send_fields(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(TRUE);
-
- mutex_enter_noninline(&mutex_list_mutex);
-
- mutex = UT_LIST_GET_FIRST(mutex_list);
-
- while ( mutex != NULL )
- {
-#ifdef UNIV_DEBUG
- if (mutex->mutex_type != 1)
- {
- if (mutex->count_using > 0)
- {
- protocol->prepare_for_resend();
- protocol->store(mutex->cmutex_name, system_charset_info);
- protocol->store(mutex->cfile_name, system_charset_info);
- protocol->store((ulonglong)mutex->count_using);
- protocol->store((ulonglong)mutex->count_spin_loop);
- protocol->store((ulonglong)mutex->count_spin_rounds);
- protocol->store((ulonglong)mutex->count_os_wait);
- protocol->store((ulonglong)mutex->count_os_yield);
- protocol->store((ulonglong)mutex->lspent_time/1000);
-
- if (protocol->write())
- {
- mutex_exit_noninline(&mutex_list_mutex);
- DBUG_RETURN(1);
- }
- }
- }
- else
- {
- rw_lock_count += mutex->count_using;
- rw_lock_count_spin_loop += mutex->count_spin_loop;
- rw_lock_count_spin_rounds += mutex->count_spin_rounds;
- rw_lock_count_os_wait += mutex->count_os_wait;
- rw_lock_count_os_yield += mutex->count_os_yield;
- rw_lock_wait_time += mutex->lspent_time;
- }
-#else /* UNIV_DEBUG */
- protocol->prepare_for_resend();
- protocol->store(mutex->cfile_name, system_charset_info);
- protocol->store((ulonglong)mutex->cline);
- protocol->store((ulonglong)mutex->count_os_wait);
-
- if (protocol->write())
- {
- mutex_exit_noninline(&mutex_list_mutex);
- DBUG_RETURN(1);
- }
-#endif /* UNIV_DEBUG */
-
- mutex = UT_LIST_GET_NEXT(list, mutex);
- }
-
- mutex_exit_noninline(&mutex_list_mutex);
-
-#ifdef UNIV_DEBUG
- protocol->prepare_for_resend();
- protocol->store("rw_lock_mutexes", system_charset_info);
- protocol->store("", system_charset_info);
- protocol->store((ulonglong)rw_lock_count);
- protocol->store((ulonglong)rw_lock_count_spin_loop);
- protocol->store((ulonglong)rw_lock_count_spin_rounds);
- protocol->store((ulonglong)rw_lock_count_os_wait);
- protocol->store((ulonglong)rw_lock_count_os_yield);
- protocol->store((ulonglong)rw_lock_wait_time/1000);
-
- if (protocol->write())
- {
- DBUG_RETURN(1);
- }
-#endif /* UNIV_DEBUG */
-
- send_eof(thd);
- DBUG_RETURN(FALSE);
-}
-
-/****************************************************************************
- Handling the shared INNOBASE_SHARE structure that is needed to provide table
- locking.
-****************************************************************************/
-
-static mysql_byte* innobase_get_key(INNOBASE_SHARE *share,uint *length,
- my_bool not_used __attribute__((unused)))
-{
- *length=share->table_name_length;
- return (mysql_byte*) share->table_name;
-}
-
-static INNOBASE_SHARE *get_share(const char *table_name)
-{
- INNOBASE_SHARE *share;
- pthread_mutex_lock(&innobase_share_mutex);
- uint length=(uint) strlen(table_name);
-
- if (!(share=(INNOBASE_SHARE*) hash_search(&innobase_open_tables,
- (mysql_byte*) table_name,
- length))) {
-
- share = (INNOBASE_SHARE *) my_malloc(sizeof(*share)+length+1,
- MYF(MY_FAE | MY_ZEROFILL));
-
- share->table_name_length=length;
- share->table_name=(char*) (share+1);
- strmov(share->table_name,table_name);
-
- if (my_hash_insert(&innobase_open_tables,
- (mysql_byte*) share)) {
- pthread_mutex_unlock(&innobase_share_mutex);
- my_free((gptr) share,0);
-
- return 0;
- }
-
- thr_lock_init(&share->lock);
- pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
- }
-
- share->use_count++;
- pthread_mutex_unlock(&innobase_share_mutex);
-
- return share;
-}
-
-static void free_share(INNOBASE_SHARE *share)
-{
- pthread_mutex_lock(&innobase_share_mutex);
- if (!--share->use_count)
- {
- hash_delete(&innobase_open_tables, (mysql_byte*) share);
- thr_lock_delete(&share->lock);
- pthread_mutex_destroy(&share->mutex);
- my_free((gptr) share, MYF(0));
- }
- pthread_mutex_unlock(&innobase_share_mutex);
-}
-
-/*********************************************************************
-Converts a MySQL table lock stored in the 'lock' field of the handle to
-a proper type before storing pointer to the lock into an array of pointers.
-MySQL also calls this if it wants to reset some table locks to a not-locked
-state during the processing of an SQL query. An example is that during a
-SELECT the read lock is released early on the 'const' tables where we only
-fetch one row. MySQL does not call this when it releases all locks at the
-end of an SQL statement. */
-
-THR_LOCK_DATA**
-ha_innobase::store_lock(
-/*====================*/
- /* out: pointer to the next
- element in the 'to' array */
- THD* thd, /* in: user thread handle */
- THR_LOCK_DATA** to, /* in: pointer to an array
- of pointers to lock structs;
- pointer to the 'lock' field
- of current handle is stored
- next to this array */
- enum thr_lock_type lock_type) /* in: lock type to store in
- 'lock'; this may also be
- TL_IGNORE */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
-
- /* NOTE: MySQL can call this function with lock 'type' TL_IGNORE!
- Be careful to ignore TL_IGNORE if we are going to do something with
- only 'real' locks! */
-
- if ((lock_type == TL_READ && thd->in_lock_tables) ||
- (lock_type == TL_READ_HIGH_PRIORITY && thd->in_lock_tables) ||
- lock_type == TL_READ_WITH_SHARED_LOCKS ||
- lock_type == TL_READ_NO_INSERT ||
- (thd->lex->sql_command != SQLCOM_SELECT
- && lock_type != TL_IGNORE)) {
-
- /* The OR cases above are in this order:
- 1) MySQL is doing LOCK TABLES ... READ LOCAL, or we
- are processing a stored procedure or function, or
- 2) (we do not know when TL_READ_HIGH_PRIORITY is used), or
- 3) this is a SELECT ... IN SHARE MODE, or
- 4) we are doing a complex SQL statement like
- INSERT INTO ... SELECT ... and the logical logging (MySQL
- binlog) requires the use of a locking read, or
- MySQL is doing LOCK TABLES ... READ.
- 5) we let InnoDB do locking reads for all SQL statements that
- are not simple SELECTs; note that select_lock_type in this
- case may get strengthened in ::external_lock() to LOCK_X.
- Note that we MUST use a locking read in all data modifying
- SQL statements, because otherwise the execution would not be
- serializable, and also the results from the update could be
- unexpected if an obsolete consistent read view would be
- used. */
-
- if (srv_locks_unsafe_for_binlog &&
- prebuilt->trx->isolation_level != TRX_ISO_SERIALIZABLE &&
- (lock_type == TL_READ || lock_type == TL_READ_NO_INSERT) &&
- (thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
- thd->lex->sql_command == SQLCOM_UPDATE ||
- thd->lex->sql_command == SQLCOM_CREATE_TABLE)) {
-
- /* In case we have innobase_locks_unsafe_for_binlog
- option set and isolation level of the transaction
- is not set to serializable and MySQL is doing
- INSERT INTO...SELECT or UPDATE ... = (SELECT ...) or
- CREATE ... SELECT... without FOR UPDATE or
- IN SHARE MODE in select, then we use consistent
- read for select. */
-
- prebuilt->select_lock_type = LOCK_NONE;
- prebuilt->stored_select_lock_type = LOCK_NONE;
- } else if (thd->lex->sql_command == SQLCOM_CHECKSUM) {
- /* Use consistent read for checksum table */
-
- prebuilt->select_lock_type = LOCK_NONE;
- prebuilt->stored_select_lock_type = LOCK_NONE;
- } else {
- prebuilt->select_lock_type = LOCK_S;
- prebuilt->stored_select_lock_type = LOCK_S;
- }
-
- } else if (lock_type != TL_IGNORE) {
-
- /* We set possible LOCK_X value in external_lock, not yet
- here even if this would be SELECT ... FOR UPDATE */
-
- prebuilt->select_lock_type = LOCK_NONE;
- prebuilt->stored_select_lock_type = LOCK_NONE;
- }
-
- if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) {
-
- /* Starting from 5.0.7, we weaken also the table locks
- set at the start of a MySQL stored procedure call, just like
- we weaken the locks set at the start of an SQL statement.
- MySQL does set thd->in_lock_tables TRUE there, but in reality
- we do not need table locks to make the execution of a
- single transaction stored procedure call deterministic
- (if it does not use a consistent read). */
-
- if (lock_type == TL_READ
- && thd->lex->sql_command == SQLCOM_LOCK_TABLES) {
- /* We come here if MySQL is processing LOCK TABLES
- ... READ LOCAL. MyISAM under that table lock type
- reads the table as it was at the time the lock was
- granted (new inserts are allowed, but not seen by the
- reader). To get a similar effect on an InnoDB table,
- we must use LOCK TABLES ... READ. We convert the lock
- type here, so that for InnoDB, READ LOCAL is
- equivalent to READ. This will change the InnoDB
- behavior in mysqldump, so that dumps of InnoDB tables
- are consistent with dumps of MyISAM tables. */
-
- lock_type = TL_READ_NO_INSERT;
- }
-
- /* If we are not doing a LOCK TABLE, DISCARD/IMPORT
- TABLESPACE or TRUNCATE TABLE then allow multiple
- writers. Note that ALTER TABLE uses a TL_WRITE_ALLOW_READ
- < TL_WRITE_CONCURRENT_INSERT.
-
- We especially allow multiple writers if MySQL is at the
- start of a stored procedure call (SQLCOM_CALL) or a
- stored function call (MySQL does have thd->in_lock_tables
- TRUE there). */
-
- if ((lock_type >= TL_WRITE_CONCURRENT_INSERT
- && lock_type <= TL_WRITE)
- && !(thd->in_lock_tables
- && thd->lex->sql_command == SQLCOM_LOCK_TABLES)
- && !thd->tablespace_op
- && thd->lex->sql_command != SQLCOM_TRUNCATE
- && thd->lex->sql_command != SQLCOM_OPTIMIZE
-
-#ifdef __WIN__
- /* For alter table on win32 for succesful operation
- completion it is used TL_WRITE(=10) lock instead of
- TL_WRITE_ALLOW_READ(=6), however here in innodb handler
- TL_WRITE is lifted to TL_WRITE_ALLOW_WRITE, which causes
- race condition when several clients do alter table
- simultaneously (bug #17264). This fix avoids the problem. */
- && thd->lex->sql_command != SQLCOM_ALTER_TABLE
-#endif
-
- && thd->lex->sql_command != SQLCOM_CREATE_TABLE) {
-
- lock_type = TL_WRITE_ALLOW_WRITE;
- }
-
- /* In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
- MySQL would use the lock TL_READ_NO_INSERT on t2, and that
- would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
- to t2. Convert the lock to a normal read lock to allow
- concurrent inserts to t2.
-
- We especially allow concurrent inserts if MySQL is at the
- start of a stored procedure call (SQLCOM_CALL)
- (MySQL does have thd->in_lock_tables TRUE there). */
-
- if (lock_type == TL_READ_NO_INSERT
- && thd->lex->sql_command != SQLCOM_LOCK_TABLES) {
-
- lock_type = TL_READ;
- }
-
- lock.type = lock_type;
- }
-
- *to++= &lock;
-
- return(to);
-}
-
-/***********************************************************************
-This function initializes the auto-inc counter if it has not been
-initialized yet. This function does not change the value of the auto-inc
-counter if it already has been initialized. In parameter ret returns
-the value of the auto-inc counter. */
-
-int
-ha_innobase::innobase_read_and_init_auto_inc(
-/*=========================================*/
- /* out: 0 or error code: deadlock or lock wait
- timeout */
- longlong* ret) /* out: auto-inc value */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- longlong auto_inc;
- ulint old_select_lock_type;
- ibool trx_was_not_started = FALSE;
- int error;
-
- ut_a(prebuilt);
- ut_a(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
- ut_a(prebuilt->table);
-
- if (prebuilt->trx->conc_state == TRX_NOT_STARTED) {
- trx_was_not_started = TRUE;
- }
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
-
- auto_inc = dict_table_autoinc_read(prebuilt->table);
-
- if (auto_inc != 0) {
- /* Already initialized */
- *ret = auto_inc;
-
- error = 0;
-
- goto func_exit_early;
- }
-
- error = row_lock_table_autoinc_for_mysql(prebuilt);
-
- if (error != DB_SUCCESS) {
- error = convert_error_code_to_mysql(error, user_thd);
-
- goto func_exit_early;
- }
-
- /* Check again if someone has initialized the counter meanwhile */
- auto_inc = dict_table_autoinc_read(prebuilt->table);
-
- if (auto_inc != 0) {
- *ret = auto_inc;
-
- error = 0;
-
- goto func_exit_early;
- }
-
- (void) extra(HA_EXTRA_KEYREAD);
- index_init(table->s->next_number_index);
-
- /* Starting from 5.0.9, we use a consistent read to read the auto-inc
- column maximum value. This eliminates the spurious deadlocks caused
- by the row X-lock that we previously used. Note the following flaw
- in our algorithm: if some other user meanwhile UPDATEs the auto-inc
- column, our consistent read will not return the largest value. We
- accept this flaw, since the deadlocks were a bigger trouble. */
-
- /* Fetch all the columns in the key */
-
- prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
-
- old_select_lock_type = prebuilt->select_lock_type;
- prebuilt->select_lock_type = LOCK_NONE;
-
- /* Eliminate an InnoDB error print that happens when we try to SELECT
- from a table when no table has been locked in ::external_lock(). */
- prebuilt->trx->n_mysql_tables_in_use++;
-
- error = index_last(table->record[1]);
-
- prebuilt->trx->n_mysql_tables_in_use--;
- prebuilt->select_lock_type = old_select_lock_type;
-
- if (error) {
- if (error == HA_ERR_END_OF_FILE) {
- /* The table was empty, initialize to 1 */
- auto_inc = 1;
-
- error = 0;
- } else {
- /* This should not happen in a consistent read */
- sql_print_error("Consistent read of auto-inc column "
- "returned %lu", (ulong) error);
- auto_inc = -1;
-
- goto func_exit;
- }
- } else {
- /* Initialize to max(col) + 1; we use
- 'found_next_number_field' below because MySQL in SHOW TABLE
- STATUS does not seem to set 'next_number_field'. The comment
- in table.h says that 'next_number_field' is set when it is
- 'active'. */
-
- auto_inc = (longlong) table->found_next_number_field->
- val_int_offset(table->s->rec_buff_length) + 1;
- }
-
- dict_table_autoinc_initialize(prebuilt->table, auto_inc);
-
-func_exit:
- (void) extra(HA_EXTRA_NO_KEYREAD);
-
- index_end();
-
- *ret = auto_inc;
-
-func_exit_early:
- /* Since MySQL does not seem to call autocommit after SHOW TABLE
- STATUS (even if we would register the trx here), we commit our
- transaction here if it was started here. This is to eliminate a
- dangling transaction. If the user had AUTOCOMMIT=0, then SHOW
- TABLE STATUS does leave a dangling transaction if the user does not
- himself call COMMIT. */
-
- if (trx_was_not_started) {
-
- innobase_commit_low(prebuilt->trx);
- }
-
- return(error);
-}
-
-/***********************************************************************
-This function initializes the auto-inc counter if it has not been
-initialized yet. This function does not change the value of the auto-inc
-counter if it already has been initialized. Returns the value of the
-auto-inc counter. */
-
-ulonglong
-ha_innobase::get_auto_increment()
-/*=============================*/
- /* out: auto-increment column value, -1 if error
- (deadlock or lock wait timeout) */
-{
- longlong nr;
- int error;
-
- error = innobase_read_and_init_auto_inc(&nr);
-
- if (error) {
- /* This should never happen in the current (5.0.6) code, since
- we call this function only after the counter has been
- initialized. */
-
- ut_print_timestamp(stderr);
- sql_print_error("Error %lu in ::get_auto_increment()",
- (ulong) error);
- return(~(ulonglong) 0);
- }
-
- return((ulonglong) nr);
-}
-
-/* See comment in handler.h */
-int
-ha_innobase::reset_auto_increment(ulonglong value)
-{
- DBUG_ENTER("ha_innobase::reset_auto_increment");
-
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- int error;
-
- error = row_lock_table_autoinc_for_mysql(prebuilt);
-
- if (error != DB_SUCCESS) {
- error = convert_error_code_to_mysql(error, user_thd);
-
- DBUG_RETURN(error);
- }
-
- dict_table_autoinc_initialize(prebuilt->table, value);
-
- DBUG_RETURN(0);
-}
-
-/* See comment in handler.cc */
-bool
-ha_innobase::get_error_message(int error, String *buf)
-{
- trx_t* trx = check_trx_exists(current_thd);
-
- buf->copy(trx->detailed_error, strlen(trx->detailed_error),
- system_charset_info);
-
- return FALSE;
-}
-
-/***********************************************************************
-Compares two 'refs'. A 'ref' is the (internal) primary key value of the row.
-If there is no explicitly declared non-null unique key or a primary key, then
-InnoDB internally uses the row id as the primary key. */
-
-int
-ha_innobase::cmp_ref(
-/*=================*/
- /* out: < 0 if ref1 < ref2, 0 if equal, else
- > 0 */
- const mysql_byte* ref1, /* in: an (internal) primary key value in the
- MySQL key value format */
- const mysql_byte* ref2) /* in: an (internal) primary key value in the
- MySQL key value format */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- enum_field_types mysql_type;
- Field* field;
- KEY_PART_INFO* key_part;
- KEY_PART_INFO* key_part_end;
- uint len1;
- uint len2;
- int result;
-
- if (prebuilt->clust_index_was_generated) {
- /* The 'ref' is an InnoDB row id */
-
- return(memcmp(ref1, ref2, DATA_ROW_ID_LEN));
- }
-
- /* Do a type-aware comparison of primary key fields. PK fields
- are always NOT NULL, so no checks for NULL are performed. */
-
- key_part = table->key_info[table->s->primary_key].key_part;
-
- key_part_end = key_part
- + table->key_info[table->s->primary_key].key_parts;
-
- for (; key_part != key_part_end; ++key_part) {
- field = key_part->field;
- mysql_type = field->type();
-
- if (mysql_type == FIELD_TYPE_TINY_BLOB
- || mysql_type == FIELD_TYPE_MEDIUM_BLOB
- || mysql_type == FIELD_TYPE_BLOB
- || mysql_type == FIELD_TYPE_LONG_BLOB) {
-
- /* In the MySQL key value format, a column prefix of
- a BLOB is preceded by a 2-byte length field */
-
- len1 = innobase_read_from_2_little_endian(ref1);
- len2 = innobase_read_from_2_little_endian(ref2);
-
- ref1 += 2;
- ref2 += 2;
- result = ((Field_blob*)field)->cmp(
- (const char*)ref1, len1,
- (const char*)ref2, len2);
- } else {
- result = field->key_cmp(ref1, ref2);
- }
-
- if (result) {
-
- return(result);
- }
-
- ref1 += key_part->store_length;
- ref2 += key_part->store_length;
- }
-
- return(0);
-}
-
-char*
-ha_innobase::get_mysql_bin_log_name()
-{
- return(trx_sys_mysql_bin_log_name);
-}
-
-ulonglong
-ha_innobase::get_mysql_bin_log_pos()
-{
- /* trx... is ib_longlong, which is a typedef for a 64-bit integer
- (__int64 or longlong) so it's ok to cast it to ulonglong. */
-
- return(trx_sys_mysql_bin_log_pos);
-}
-
-extern "C" {
-/**********************************************************************
-This function is used to find the storage length in bytes of the first n
-characters for prefix indexes using a multibyte character set. The function
-finds charset information and returns length of prefix_len characters in the
-index field in bytes.
-
-NOTE: the prototype of this function is copied to data0type.c! If you change
-this function, you MUST change also data0type.c! */
-
-ulint
-innobase_get_at_most_n_mbchars(
-/*===========================*/
- /* out: number of bytes occupied by the first
- n characters */
- ulint charset_id, /* in: character set id */
- ulint prefix_len, /* in: prefix length in bytes of the index
- (this has to be divided by mbmaxlen to get the
- number of CHARACTERS n in the prefix) */
- ulint data_len, /* in: length of the string in bytes */
- const char* str) /* in: character string */
-{
- ulint char_length; /* character length in bytes */
- ulint n_chars; /* number of characters in prefix */
- CHARSET_INFO* charset; /* charset used in the field */
-
- charset = get_charset((uint) charset_id, MYF(MY_WME));
-
- ut_ad(charset);
- ut_ad(charset->mbmaxlen);
-
- /* Calculate how many characters at most the prefix index contains */
-
- n_chars = prefix_len / charset->mbmaxlen;
-
- /* If the charset is multi-byte, then we must find the length of the
- first at most n chars in the string. If the string contains less
- characters than n, then we return the length to the end of the last
- character. */
-
- if (charset->mbmaxlen > 1) {
- /* my_charpos() returns the byte length of the first n_chars
- characters, or a value bigger than the length of str, if
- there were not enough full characters in str.
-
- Why does the code below work:
- Suppose that we are looking for n UTF-8 characters.
-
- 1) If the string is long enough, then the prefix contains at
- least n complete UTF-8 characters + maybe some extra
- characters + an incomplete UTF-8 character. No problem in
- this case. The function returns the pointer to the
- end of the nth character.
-
- 2) If the string is not long enough, then the string contains
- the complete value of a column, that is, only complete UTF-8
- characters, and we can store in the column prefix index the
- whole string. */
-
- char_length = my_charpos(charset, str,
- str + data_len, (int) n_chars);
- if (char_length > data_len) {
- char_length = data_len;
- }
- } else {
- if (data_len < prefix_len) {
- char_length = data_len;
- } else {
- char_length = prefix_len;
- }
- }
-
- return(char_length);
-}
-}
-
-extern "C" {
-/**********************************************************************
-This function returns true if
-
-1) SQL-query in the current thread
-is either REPLACE or LOAD DATA INFILE REPLACE.
-
-2) SQL-query in the current thread
-is INSERT ON DUPLICATE KEY UPDATE.
-
-NOTE that /mysql/innobase/row/row0ins.c must contain the
-prototype for this function ! */
-
-ibool
-innobase_query_is_update(void)
-/*==========================*/
-{
- THD* thd;
-
- thd = (THD *)innobase_current_thd();
-
- if (thd->lex->sql_command == SQLCOM_REPLACE ||
- thd->lex->sql_command == SQLCOM_REPLACE_SELECT ||
- (thd->lex->sql_command == SQLCOM_LOAD &&
- thd->lex->duplicates == DUP_REPLACE)) {
-
- return(1);
- }
-
- if (thd->lex->sql_command == SQLCOM_INSERT &&
- thd->lex->duplicates == DUP_UPDATE) {
-
- return(1);
- }
-
- return(0);
-}
-}
-
-/***********************************************************************
-This function is used to prepare X/Open XA distributed transaction */
-
-int
-innobase_xa_prepare(
-/*================*/
- /* out: 0 or error number */
- THD* thd, /* in: handle to the MySQL thread of the user
- whose XA transaction should be prepared */
- bool all) /* in: TRUE - commit transaction
- FALSE - the current SQL statement ended */
-{
- int error = 0;
- trx_t* trx = check_trx_exists(thd);
-
- if (thd->lex->sql_command != SQLCOM_XA_PREPARE) {
-
- /* For ibbackup to work the order of transactions in binlog
- and InnoDB must be the same. Consider the situation
-
- thread1> prepare; write to binlog; ...
- <context switch>
- thread2> prepare; write to binlog; commit
- thread1> ... commit
-
- To ensure this will not happen we're taking the mutex on
- prepare, and releasing it on commit.
-
- Note: only do it for normal commits, done via ha_commit_trans.
- If 2pc protocol is executed by external transaction
- coordinator, it will be just a regular MySQL client
- executing XA PREPARE and XA COMMIT commands.
- In this case we cannot know how many minutes or hours
- will be between XA PREPARE and XA COMMIT, and we don't want
- to block for undefined period of time.
- */
- pthread_mutex_lock(&prepare_commit_mutex);
- trx->active_trans = 2;
- }
-
- if (!thd->variables.innodb_support_xa) {
-
- return(0);
- }
-
- trx->xid=thd->transaction.xid_state.xid;
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- if (trx->active_trans == 0 && trx->conc_state != TRX_NOT_STARTED) {
-
- sql_print_error("trx->active_trans == 0, but trx->conc_state != "
- "TRX_NOT_STARTED");
- }
-
- if (all
- || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
-
- /* We were instructed to prepare the whole transaction, or
- this is an SQL statement end and autocommit is on */
-
- ut_ad(trx->active_trans);
-
- error = (int) trx_prepare_for_mysql(trx);
- } else {
- /* We just mark the SQL statement ended and do not do a
- transaction prepare */
-
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some
- table in this SQL statement we release it now */
-
- row_unlock_table_autoinc_for_mysql(trx);
- }
- /* Store the current undo_no of the transaction so that we
- know where to roll back if we have to roll back the next
- SQL statement */
-
- trx_mark_sql_stat_end(trx);
- }
-
- /* Tell the InnoDB server that there might be work for utility
- threads: */
-
- srv_active_wake_master_thread();
-
- return error;
-}
-
-/***********************************************************************
-This function is used to recover X/Open XA distributed transactions */
-
-int
-innobase_xa_recover(
-/*================*/
- /* out: number of prepared transactions
- stored in xid_list */
- XID* xid_list, /* in/out: prepared transactions */
- uint len) /* in: number of slots in xid_list */
-{
- if (len == 0 || xid_list == NULL) {
-
- return(0);
- }
-
- return(trx_recover_for_mysql(xid_list, len));
-}
-
-/***********************************************************************
-This function is used to commit one X/Open XA distributed transaction
-which is in the prepared state */
-
-int
-innobase_commit_by_xid(
-/*===================*/
- /* out: 0 or error number */
- XID* xid) /* in: X/Open XA transaction identification */
-{
- trx_t* trx;
-
- trx = trx_get_trx_by_xid(xid);
-
- if (trx) {
- innobase_commit_low(trx);
-
- return(XA_OK);
- } else {
- return(XAER_NOTA);
- }
-}
-
-/***********************************************************************
-This function is used to rollback one X/Open XA distributed transaction
-which is in the prepared state */
-
-int
-innobase_rollback_by_xid(
-/*=====================*/
- /* out: 0 or error number */
- XID *xid) /* in: X/Open XA transaction identification */
-{
- trx_t* trx;
-
- trx = trx_get_trx_by_xid(xid);
-
- if (trx) {
- return(innobase_rollback_trx(trx));
- } else {
- return(XAER_NOTA);
- }
-}
-
-/***********************************************************************
-Create a consistent view for a cursor based on current transaction
-which is created if the corresponding MySQL thread still lacks one.
-This consistent view is then used inside of MySQL when accessing records
-using a cursor. */
-
-void*
-innobase_create_cursor_view(void)
-/*=============================*/
- /* out: Pointer to cursor view or NULL */
-{
- return(read_cursor_view_create_for_mysql(
- check_trx_exists(current_thd)));
-}
-
-/***********************************************************************
-Close the given consistent cursor view of a transaction and restore
-global read view to a transaction read view. Transaction is created if the
-corresponding MySQL thread still lacks one. */
-
-void
-innobase_close_cursor_view(
-/*=======================*/
- void* curview)/* in: Consistent read view to be closed */
-{
- read_cursor_view_close_for_mysql(check_trx_exists(current_thd),
- (cursor_view_t*) curview);
-}
-
-/***********************************************************************
-Set the given consistent cursor view to a transaction which is created
-if the corresponding MySQL thread still lacks one. If the given
-consistent cursor view is NULL global read view of a transaction is
-restored to a transaction read view. */
-
-void
-innobase_set_cursor_view(
-/*=====================*/
- void* curview)/* in: Consistent cursor view to be set */
-{
- read_cursor_set_for_mysql(check_trx_exists(current_thd),
- (cursor_view_t*) curview);
-}
-
-#endif /* HAVE_INNOBASE_DB */