summaryrefslogtreecommitdiff
path: root/storage/innobase/row/row0mysql.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/row/row0mysql.c')
-rw-r--r--storage/innobase/row/row0mysql.c4199
1 files changed, 0 insertions, 4199 deletions
diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c
deleted file mode 100644
index 2d9ed4fc944..00000000000
--- a/storage/innobase/row/row0mysql.c
+++ /dev/null
@@ -1,4199 +0,0 @@
-/******************************************************
-Interface between Innobase row operations and MySQL.
-Contains also create table and other data dictionary operations.
-
-(c) 2000 Innobase Oy
-
-Created 9/17/2000 Heikki Tuuri
-*******************************************************/
-
-#include "row0mysql.h"
-
-#ifdef UNIV_NONINL
-#include "row0mysql.ic"
-#endif
-
-#include "row0ins.h"
-#include "row0sel.h"
-#include "row0upd.h"
-#include "row0row.h"
-#include "que0que.h"
-#include "pars0pars.h"
-#include "dict0dict.h"
-#include "dict0crea.h"
-#include "dict0load.h"
-#include "dict0boot.h"
-#include "trx0roll.h"
-#include "trx0purge.h"
-#include "lock0lock.h"
-#include "rem0cmp.h"
-#include "log0log.h"
-#include "btr0sea.h"
-#include "fil0fil.h"
-#include "ibuf0ibuf.h"
-
-/* A dummy variable used to fool the compiler */
-ibool row_mysql_identically_false = FALSE;
-
-/* Provide optional 4.x backwards compatibility for 5.0 and above */
-ibool row_rollback_on_timeout = FALSE;
-
-/* List of tables we should drop in background. ALTER TABLE in MySQL requires
-that the table handler can drop the table in background when there are no
-queries to it any more. Protected by the kernel mutex. */
-typedef struct row_mysql_drop_struct row_mysql_drop_t;
-struct row_mysql_drop_struct{
- char* table_name;
- UT_LIST_NODE_T(row_mysql_drop_t) row_mysql_drop_list;
-};
-
-UT_LIST_BASE_NODE_T(row_mysql_drop_t) row_mysql_drop_list;
-ibool row_mysql_drop_list_inited = FALSE;
-
-/* Magic table names for invoking various monitor threads */
-static const char S_innodb_monitor[] = "innodb_monitor";
-static const char S_innodb_lock_monitor[] = "innodb_lock_monitor";
-static const char S_innodb_tablespace_monitor[] = "innodb_tablespace_monitor";
-static const char S_innodb_table_monitor[] = "innodb_table_monitor";
-static const char S_innodb_mem_validate[] = "innodb_mem_validate";
-
-/* Evaluates to true if str1 equals str2_onstack, used for comparing
-the above strings. */
-#define STR_EQ(str1, str1_len, str2_onstack) \
- ((str1_len) == sizeof(str2_onstack) \
- && memcmp(str1, str2_onstack, sizeof(str2_onstack)) == 0)
-
-/***********************************************************************
-Determine if the given name is a name reserved for MySQL system tables. */
-static
-ibool
-row_mysql_is_system_table(
-/*======================*/
- /* out: TRUE if name is a MySQL
- system table name */
- const char* name)
-{
- if (strncmp(name, "mysql/", 6) != 0) {
-
- return(FALSE);
- }
-
- return(0 == strcmp(name + 6, "host")
- || 0 == strcmp(name + 6, "user")
- || 0 == strcmp(name + 6, "db"));
-}
-
-/***********************************************************************
-Delays an INSERT, DELETE or UPDATE operation if the purge is lagging. */
-static
-void
-row_mysql_delay_if_needed(void)
-/*===========================*/
-{
- if (srv_dml_needed_delay) {
- os_thread_sleep(srv_dml_needed_delay);
- }
-}
-
-/***********************************************************************
-Frees the blob heap in prebuilt when no longer needed. */
-
-void
-row_mysql_prebuilt_free_blob_heap(
-/*==============================*/
- row_prebuilt_t* prebuilt) /* in: prebuilt struct of a
- ha_innobase:: table handle */
-{
- mem_heap_free(prebuilt->blob_heap);
- prebuilt->blob_heap = NULL;
-}
-
-/***********************************************************************
-Stores a >= 5.0.3 format true VARCHAR length to dest, in the MySQL row
-format. */
-
-byte*
-row_mysql_store_true_var_len(
-/*=========================*/
- /* out: pointer to the data, we skip the 1 or 2 bytes
- at the start that are used to store the len */
- byte* dest, /* in: where to store */
- ulint len, /* in: length, must fit in two bytes */
- ulint lenlen) /* in: storage length of len: either 1 or 2 bytes */
-{
- if (lenlen == 2) {
- ut_a(len < 256 * 256);
-
- mach_write_to_2_little_endian(dest, len);
-
- return(dest + 2);
- }
-
- ut_a(lenlen == 1);
- ut_a(len < 256);
-
- mach_write_to_1(dest, len);
-
- return(dest + 1);
-}
-
-/***********************************************************************
-Reads a >= 5.0.3 format true VARCHAR length, in the MySQL row format, and
-returns a pointer to the data. */
-
-byte*
-row_mysql_read_true_varchar(
-/*========================*/
- /* out: pointer to the data, we skip the 1 or 2 bytes
- at the start that are used to store the len */
- ulint* len, /* out: variable-length field length */
- byte* field, /* in: field in the MySQL format */
- ulint lenlen) /* in: storage length of len: either 1 or 2 bytes */
-{
- if (lenlen == 2) {
- *len = mach_read_from_2_little_endian(field);
-
- return(field + 2);
- }
-
- ut_a(lenlen == 1);
-
- *len = mach_read_from_1(field);
-
- return(field + 1);
-}
-
-/***********************************************************************
-Stores a reference to a BLOB in the MySQL format. */
-
-void
-row_mysql_store_blob_ref(
-/*=====================*/
- byte* dest, /* in: where to store */
- ulint col_len, /* in: dest buffer size: determines into
- how many bytes the BLOB length is stored,
- the space for the length may vary from 1
- to 4 bytes */
- byte* data, /* in: BLOB data; if the value to store
- is SQL NULL this should be NULL pointer */
- ulint len) /* in: BLOB length; if the value to store
- is SQL NULL this should be 0; remember
- also to set the NULL bit in the MySQL record
- header! */
-{
- /* MySQL might assume the field is set to zero except the length and
- the pointer fields */
-
- memset(dest, '\0', col_len);
-
- /* In dest there are 1 - 4 bytes reserved for the BLOB length,
- and after that 8 bytes reserved for the pointer to the data.
- In 32-bit architectures we only use the first 4 bytes of the pointer
- slot. */
-
- ut_a(col_len - 8 > 1 || len < 256);
- ut_a(col_len - 8 > 2 || len < 256 * 256);
- ut_a(col_len - 8 > 3 || len < 256 * 256 * 256);
-
- mach_write_to_n_little_endian(dest, col_len - 8, len);
-
- ut_memcpy(dest + col_len - 8, &data, sizeof(byte*));
-}
-
-/***********************************************************************
-Reads a reference to a BLOB in the MySQL format. */
-
-byte*
-row_mysql_read_blob_ref(
-/*====================*/
- /* out: pointer to BLOB data */
- ulint* len, /* out: BLOB length */
- byte* ref, /* in: BLOB reference in the MySQL format */
- ulint col_len) /* in: BLOB reference length (not BLOB
- length) */
-{
- byte* data;
-
- *len = mach_read_from_n_little_endian(ref, col_len - 8);
-
- ut_memcpy(&data, ref + col_len - 8, sizeof(byte*));
-
- return(data);
-}
-
-/******************************************************************
-Stores a non-SQL-NULL field given in the MySQL format in the InnoDB format.
-The counterpart of this function is row_sel_field_store_in_mysql_format() in
-row0sel.c. */
-
-byte*
-row_mysql_store_col_in_innobase_format(
-/*===================================*/
- /* out: up to which byte we used
- buf in the conversion */
- dfield_t* dfield, /* in/out: dfield where dtype
- information must be already set when
- this function is called! */
- byte* buf, /* in/out: buffer for a converted
- integer value; this must be at least
- col_len long then! */
- ibool row_format_col, /* TRUE if the mysql_data is from
- a MySQL row, FALSE if from a MySQL
- key value;
- in MySQL, a true VARCHAR storage
- format differs in a row and in a
- key value: in a key value the length
- is always stored in 2 bytes! */
- byte* mysql_data, /* in: MySQL column value, not
- SQL NULL; NOTE that dfield may also
- get a pointer to mysql_data,
- therefore do not discard this as long
- as dfield is used! */
- ulint col_len, /* in: MySQL column length; NOTE that
- this is the storage length of the
- column in the MySQL format row, not
- necessarily the length of the actual
- payload data; if the column is a true
- VARCHAR then this is irrelevant */
- ulint comp) /* in: nonzero=compact format */
-{
- byte* ptr = mysql_data;
- dtype_t* dtype;
- ulint type;
- ulint lenlen;
-
- dtype = dfield_get_type(dfield);
-
- type = dtype->mtype;
-
- if (type == DATA_INT) {
- /* Store integer data in Innobase in a big-endian format,
- sign bit negated if the data is a signed integer. In MySQL,
- integers are stored in a little-endian format. */
-
- ptr = buf + col_len;
-
- for (;;) {
- ptr--;
- *ptr = *mysql_data;
- if (ptr == buf) {
- break;
- }
- mysql_data++;
- }
-
- if (!(dtype->prtype & DATA_UNSIGNED)) {
-
- *ptr = (byte) (*ptr ^ 128);
- }
-
- buf += col_len;
- } else if ((type == DATA_VARCHAR
- || type == DATA_VARMYSQL
- || type == DATA_BINARY)) {
-
- if (dtype_get_mysql_type(dtype) == DATA_MYSQL_TRUE_VARCHAR) {
- /* The length of the actual data is stored to 1 or 2
- bytes at the start of the field */
-
- if (row_format_col) {
- if (dtype->prtype & DATA_LONG_TRUE_VARCHAR) {
- lenlen = 2;
- } else {
- lenlen = 1;
- }
- } else {
- /* In a MySQL key value, lenlen is always 2 */
- lenlen = 2;
- }
-
- ptr = row_mysql_read_true_varchar(&col_len, mysql_data,
- lenlen);
- } else {
- /* Remove trailing spaces from old style VARCHAR
- columns. */
-
- /* Handle UCS2 strings differently. */
- ulint mbminlen = dtype_get_mbminlen(dtype);
-
- ptr = mysql_data;
-
- if (mbminlen == 2) {
- /* space=0x0020 */
- /* Trim "half-chars", just in case. */
- col_len &= ~1;
-
- while (col_len >= 2 && ptr[col_len - 2] == 0x00
- && ptr[col_len - 1] == 0x20) {
- col_len -= 2;
- }
- } else {
- ut_a(mbminlen == 1);
- /* space=0x20 */
- while (col_len > 0
- && ptr[col_len - 1] == 0x20) {
- col_len--;
- }
- }
- }
- } else if (comp && type == DATA_MYSQL
- && dtype_get_mbminlen(dtype) == 1
- && dtype_get_mbmaxlen(dtype) > 1) {
- /* In some cases we strip trailing spaces from UTF-8 and other
- multibyte charsets, from FIXED-length CHAR columns, to save
- space. UTF-8 would otherwise normally use 3 * the string length
- bytes to store an ASCII string! */
-
- /* We assume that this CHAR field is encoded in a
- variable-length character set where spaces have
- 1:1 correspondence to 0x20 bytes, such as UTF-8.
-
- Consider a CHAR(n) field, a field of n characters.
- It will contain between n * mbminlen and n * mbmaxlen bytes.
- We will try to truncate it to n bytes by stripping
- space padding. If the field contains single-byte
- characters only, it will be truncated to n characters.
- Consider a CHAR(5) field containing the string ".a "
- where "." denotes a 3-byte character represented by
- the bytes "$%&". After our stripping, the string will
- be stored as "$%&a " (5 bytes). The string ".abc "
- will be stored as "$%&abc" (6 bytes).
-
- The space padding will be restored in row0sel.c, function
- row_sel_field_store_in_mysql_format(). */
-
- ulint n_chars;
-
- ut_a(!(dtype_get_len(dtype) % dtype_get_mbmaxlen(dtype)));
-
- n_chars = dtype_get_len(dtype) / dtype_get_mbmaxlen(dtype);
-
- /* Strip space padding. */
- while (col_len > n_chars && ptr[col_len - 1] == 0x20) {
- col_len--;
- }
- } else if (type == DATA_BLOB && row_format_col) {
-
- ptr = row_mysql_read_blob_ref(&col_len, mysql_data, col_len);
- }
-
- dfield_set_data(dfield, ptr, col_len);
-
- return(buf);
-}
-
-/******************************************************************
-Convert a row in the MySQL format to a row in the Innobase format. Note that
-the function to convert a MySQL format key value to an InnoDB dtuple is
-row_sel_convert_mysql_key_to_innobase() in row0sel.c. */
-static
-void
-row_mysql_convert_row_to_innobase(
-/*==============================*/
- dtuple_t* row, /* in/out: Innobase row where the
- field type information is already
- copied there! */
- row_prebuilt_t* prebuilt, /* in: prebuilt struct where template
- must be of type ROW_MYSQL_WHOLE_ROW */
- byte* mysql_rec) /* in: row in the MySQL format;
- NOTE: do not discard as long as
- row is used, as row may contain
- pointers to this record! */
-{
- mysql_row_templ_t* templ;
- dfield_t* dfield;
- ulint i;
-
- ut_ad(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
- ut_ad(prebuilt->mysql_template);
-
- for (i = 0; i < prebuilt->n_template; i++) {
-
- templ = prebuilt->mysql_template + i;
- dfield = dtuple_get_nth_field(row, i);
-
- if (templ->mysql_null_bit_mask != 0) {
- /* Column may be SQL NULL */
-
- if (mysql_rec[templ->mysql_null_byte_offset]
- & (byte) (templ->mysql_null_bit_mask)) {
-
- /* It is SQL NULL */
-
- dfield_set_data(dfield, NULL, UNIV_SQL_NULL);
-
- goto next_column;
- }
- }
-
- row_mysql_store_col_in_innobase_format(
- dfield,
- prebuilt->ins_upd_rec_buff + templ->mysql_col_offset,
- TRUE, /* MySQL row format data */
- mysql_rec + templ->mysql_col_offset,
- templ->mysql_col_len,
- dict_table_is_comp(prebuilt->table));
-next_column:
- ;
- }
-}
-
-/********************************************************************
-Handles user errors and lock waits detected by the database engine. */
-
-ibool
-row_mysql_handle_errors(
-/*====================*/
- /* out: TRUE if it was a lock wait and
- we should continue running the query thread */
- ulint* new_err,/* out: possible new error encountered in
- lock wait, or if no new error, the value
- of trx->error_state at the entry of this
- function */
- trx_t* trx, /* in: transaction */
- que_thr_t* thr, /* in: query thread */
- trx_savept_t* savept) /* in: savepoint or NULL */
-{
-#ifndef UNIV_HOTBACKUP
- ulint err;
-
-handle_new_error:
- err = trx->error_state;
-
- ut_a(err != DB_SUCCESS);
-
- trx->error_state = DB_SUCCESS;
-
- if ((err == DB_DUPLICATE_KEY)
- || (err == DB_FOREIGN_DUPLICATE_KEY)) {
- if (savept) {
- /* Roll back the latest, possibly incomplete
- insertion or update */
-
- trx_general_rollback_for_mysql(trx, TRUE, savept);
- }
- } else if (err == DB_TOO_BIG_RECORD) {
- if (savept) {
- /* Roll back the latest, possibly incomplete
- insertion or update */
-
- trx_general_rollback_for_mysql(trx, TRUE, savept);
- }
- /* MySQL will roll back the latest SQL statement */
- } else if (err == DB_ROW_IS_REFERENCED
- || err == DB_NO_REFERENCED_ROW
- || err == DB_CANNOT_ADD_CONSTRAINT
- || err == DB_TOO_MANY_CONCURRENT_TRXS) {
- if (savept) {
- /* Roll back the latest, possibly incomplete
- insertion or update */
-
- trx_general_rollback_for_mysql(trx, TRUE, savept);
- }
- /* MySQL will roll back the latest SQL statement */
- } else if (err == DB_LOCK_WAIT) {
-
- srv_suspend_mysql_thread(thr);
-
- if (trx->error_state != DB_SUCCESS) {
- que_thr_stop_for_mysql(thr);
-
- goto handle_new_error;
- }
-
- *new_err = err;
-
- return(TRUE);
-
- } else if (err == DB_DEADLOCK
- || err == DB_LOCK_TABLE_FULL
- || (err == DB_LOCK_WAIT_TIMEOUT
- && row_rollback_on_timeout)) {
- /* Roll back the whole transaction; this resolution was added
- to version 3.23.43 */
-
- trx_general_rollback_for_mysql(trx, FALSE, NULL);
-
- } else if (err == DB_OUT_OF_FILE_SPACE
- || err == DB_LOCK_WAIT_TIMEOUT) {
-
- ut_ad(!(err == DB_LOCK_WAIT_TIMEOUT
- && row_rollback_on_timeout));
-
- if (savept) {
- /* Roll back the latest, possibly incomplete
- insertion or update */
-
- trx_general_rollback_for_mysql(trx, TRUE, savept);
- }
- /* MySQL will roll back the latest SQL statement */
-
- } else if (err == DB_MUST_GET_MORE_FILE_SPACE) {
-
- fputs("InnoDB: The database cannot continue"
- " operation because of\n"
- "InnoDB: lack of space. You must add"
- " a new data file to\n"
- "InnoDB: my.cnf and restart the database.\n", stderr);
-
- exit(1);
- } else if (err == DB_CORRUPTION) {
-
- fputs("InnoDB: We detected index corruption"
- " in an InnoDB type table.\n"
- "InnoDB: You have to dump + drop + reimport"
- " the table or, in\n"
- "InnoDB: a case of widespread corruption,"
- " dump all InnoDB\n"
- "InnoDB: tables and recreate the"
- " whole InnoDB tablespace.\n"
- "InnoDB: If the mysqld server crashes"
- " after the startup or when\n"
- "InnoDB: you dump the tables, look at\n"
- "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
- "forcing-recovery.html"
- " for help.\n", stderr);
-
- } else {
- fprintf(stderr, "InnoDB: unknown error code %lu\n",
- (ulong) err);
- ut_error;
- }
-
- if (trx->error_state != DB_SUCCESS) {
- *new_err = trx->error_state;
- } else {
- *new_err = err;
- }
-
- trx->error_state = DB_SUCCESS;
-
- return(FALSE);
-#else /* UNIV_HOTBACKUP */
- /* This function depends on MySQL code that is not included in
- InnoDB Hot Backup builds. Besides, this function should never
- be called in InnoDB Hot Backup. */
- ut_error;
- return(FALSE);
-#endif /* UNIV_HOTBACKUP */
-}
-
-/************************************************************************
-Create a prebuilt struct for a MySQL table handle. */
-
-row_prebuilt_t*
-row_create_prebuilt(
-/*================*/
- /* out, own: a prebuilt struct */
- dict_table_t* table) /* in: Innobase table handle */
-{
- row_prebuilt_t* prebuilt;
- mem_heap_t* heap;
- dict_index_t* clust_index;
- dtuple_t* ref;
- ulint ref_len;
- ulint i;
-
- heap = mem_heap_create(128);
-
- prebuilt = mem_heap_alloc(heap, sizeof(row_prebuilt_t));
-
- prebuilt->magic_n = ROW_PREBUILT_ALLOCATED;
- prebuilt->magic_n2 = ROW_PREBUILT_ALLOCATED;
-
- prebuilt->table = table;
-
- prebuilt->trx = NULL;
-
- prebuilt->sql_stat_start = TRUE;
-
- prebuilt->mysql_has_locked = FALSE;
-
- prebuilt->index = NULL;
-
- prebuilt->used_in_HANDLER = FALSE;
-
- prebuilt->n_template = 0;
- prebuilt->mysql_template = NULL;
-
- prebuilt->heap = heap;
- prebuilt->ins_node = NULL;
-
- prebuilt->ins_upd_rec_buff = NULL;
- prebuilt->default_rec = NULL;
-
- prebuilt->upd_node = NULL;
- prebuilt->ins_graph = NULL;
- prebuilt->upd_graph = NULL;
-
- prebuilt->pcur = btr_pcur_create_for_mysql();
- prebuilt->clust_pcur = btr_pcur_create_for_mysql();
-
- prebuilt->select_lock_type = LOCK_NONE;
- prebuilt->stored_select_lock_type = 99999999;
-
- prebuilt->row_read_type = ROW_READ_WITH_LOCKS;
-
- prebuilt->sel_graph = NULL;
-
- prebuilt->search_tuple = dtuple_create(
- heap, 2 * dict_table_get_n_cols(table));
-
- clust_index = dict_table_get_first_index(table);
-
- /* Make sure that search_tuple is long enough for clustered index */
- ut_a(2 * dict_table_get_n_cols(table) >= clust_index->n_fields);
-
- ref_len = dict_index_get_n_unique(clust_index);
-
- ref = dtuple_create(heap, ref_len);
-
- dict_index_copy_types(ref, clust_index, ref_len);
-
- prebuilt->clust_ref = ref;
-
- for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) {
- prebuilt->fetch_cache[i] = NULL;
- }
-
- prebuilt->n_fetch_cached = 0;
-
- prebuilt->blob_heap = NULL;
-
- prebuilt->old_vers_heap = NULL;
-
- prebuilt->autoinc_error = 0;
- prebuilt->autoinc_offset = 0;
-
- /* Default to 1, we will set the actual value later in
- ha_innobase::get_auto_increment(). */
- prebuilt->autoinc_increment = 1;
-
- prebuilt->autoinc_last_value = 0;
-
- return(prebuilt);
-}
-
-/************************************************************************
-Free a prebuilt struct for a MySQL table handle. */
-
-void
-row_prebuilt_free(
-/*==============*/
- row_prebuilt_t* prebuilt) /* in, own: prebuilt struct */
-{
- ulint i;
-
- if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED
- || prebuilt->magic_n2 != ROW_PREBUILT_ALLOCATED) {
- fprintf(stderr,
- "InnoDB: Error: trying to free a corrupt\n"
- "InnoDB: table handle. Magic n %lu,"
- " magic n2 %lu, table name",
- (ulong) prebuilt->magic_n,
- (ulong) prebuilt->magic_n2);
- ut_print_name(stderr, NULL, TRUE, prebuilt->table->name);
- putc('\n', stderr);
-
- mem_analyze_corruption(prebuilt);
-
- ut_error;
- }
-
- prebuilt->magic_n = ROW_PREBUILT_FREED;
- prebuilt->magic_n2 = ROW_PREBUILT_FREED;
-
- btr_pcur_free_for_mysql(prebuilt->pcur);
- btr_pcur_free_for_mysql(prebuilt->clust_pcur);
-
- if (prebuilt->mysql_template) {
- mem_free(prebuilt->mysql_template);
- }
-
- if (prebuilt->ins_graph) {
- que_graph_free_recursive(prebuilt->ins_graph);
- }
-
- if (prebuilt->sel_graph) {
- que_graph_free_recursive(prebuilt->sel_graph);
- }
-
- if (prebuilt->upd_graph) {
- que_graph_free_recursive(prebuilt->upd_graph);
- }
-
- if (prebuilt->blob_heap) {
- mem_heap_free(prebuilt->blob_heap);
- }
-
- if (prebuilt->old_vers_heap) {
- mem_heap_free(prebuilt->old_vers_heap);
- }
-
- for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) {
- if (prebuilt->fetch_cache[i] != NULL) {
-
- if ((ROW_PREBUILT_FETCH_MAGIC_N != mach_read_from_4(
- (prebuilt->fetch_cache[i]) - 4))
- || (ROW_PREBUILT_FETCH_MAGIC_N != mach_read_from_4(
- (prebuilt->fetch_cache[i])
- + prebuilt->mysql_row_len))) {
- fputs("InnoDB: Error: trying to free"
- " a corrupt fetch buffer.\n", stderr);
-
- mem_analyze_corruption(
- prebuilt->fetch_cache[i]);
-
- ut_error;
- }
-
- mem_free((prebuilt->fetch_cache[i]) - 4);
- }
- }
-
- dict_table_decrement_handle_count(prebuilt->table);
-
- mem_heap_free(prebuilt->heap);
-}
-
-/*************************************************************************
-Updates the transaction pointers in query graphs stored in the prebuilt
-struct. */
-
-void
-row_update_prebuilt_trx(
-/*====================*/
- /* out: prebuilt dtuple */
- row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL
- handle */
- trx_t* trx) /* in: transaction handle */
-{
- if (trx->magic_n != TRX_MAGIC_N) {
- fprintf(stderr,
- "InnoDB: Error: trying to use a corrupt\n"
- "InnoDB: trx handle. Magic n %lu\n",
- (ulong) trx->magic_n);
-
- mem_analyze_corruption(trx);
-
- ut_error;
- }
-
- if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
- fprintf(stderr,
- "InnoDB: Error: trying to use a corrupt\n"
- "InnoDB: table handle. Magic n %lu, table name",
- (ulong) prebuilt->magic_n);
- ut_print_name(stderr, NULL, TRUE, prebuilt->table->name);
- putc('\n', stderr);
-
- mem_analyze_corruption(prebuilt);
-
- ut_error;
- }
-
- prebuilt->trx = trx;
-
- if (prebuilt->ins_graph) {
- prebuilt->ins_graph->trx = trx;
- }
-
- if (prebuilt->upd_graph) {
- prebuilt->upd_graph->trx = trx;
- }
-
- if (prebuilt->sel_graph) {
- prebuilt->sel_graph->trx = trx;
- }
-}
-
-/*************************************************************************
-Gets pointer to a prebuilt dtuple used in insertions. If the insert graph
-has not yet been built in the prebuilt struct, then this function first
-builds it. */
-static
-dtuple_t*
-row_get_prebuilt_insert_row(
-/*========================*/
- /* out: prebuilt dtuple; the column
- type information is also set in it */
- row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
- handle */
-{
- ins_node_t* node;
- dtuple_t* row;
- dict_table_t* table = prebuilt->table;
- ulint i;
-
- ut_ad(prebuilt && table && prebuilt->trx);
-
- if (prebuilt->ins_node == NULL) {
-
- /* Not called before for this handle: create an insert node
- and query graph to the prebuilt struct */
-
- node = ins_node_create(INS_DIRECT, table, prebuilt->heap);
-
- prebuilt->ins_node = node;
-
- if (prebuilt->ins_upd_rec_buff == NULL) {
- prebuilt->ins_upd_rec_buff = mem_heap_alloc(
- prebuilt->heap, prebuilt->mysql_row_len);
- }
-
- row = dtuple_create(prebuilt->heap,
- dict_table_get_n_cols(table));
-
- dict_table_copy_types(row, table);
-
- /* We init the value of every field to the SQL NULL to avoid
- a debug assertion from failing */
-
- for (i = 0; i < dtuple_get_n_fields(row); i++) {
-
- dtuple_get_nth_field(row, i)->len = UNIV_SQL_NULL;
- }
-
- ins_node_set_new_row(node, row);
-
- prebuilt->ins_graph = que_node_get_parent(
- pars_complete_graph_for_exec(node,
- prebuilt->trx,
- prebuilt->heap));
- prebuilt->ins_graph->state = QUE_FORK_ACTIVE;
- }
-
- return(prebuilt->ins_node->row);
-}
-
-/*************************************************************************
-Updates the table modification counter and calculates new estimates
-for table and index statistics if necessary. */
-UNIV_INLINE
-void
-row_update_statistics_if_needed(
-/*============================*/
- dict_table_t* table) /* in: table */
-{
- ulint counter;
-
- counter = table->stat_modified_counter;
-
- table->stat_modified_counter = counter + 1;
-
- /* Calculate new statistics if 1 / 16 of table has been modified
- since the last time a statistics batch was run, or if
- stat_modified_counter > 2 000 000 000 (to avoid wrap-around).
- We calculate statistics at most every 16th round, since we may have
- a counter table which is very small and updated very often. */
-
- if (counter > 2000000000
- || ((ib_longlong)counter > 16 + table->stat_n_rows / 16)) {
-
- dict_update_statistics(table);
- }
-}
-
-/*************************************************************************
-Unlocks an AUTO_INC type lock possibly reserved by trx. */
-
-void
-row_unlock_table_autoinc_for_mysql(
-/*===============================*/
- trx_t* trx) /* in: transaction */
-{
- if (!trx->auto_inc_lock) {
-
- return;
- }
-
- lock_table_unlock_auto_inc(trx);
-}
-
-/*************************************************************************
-Sets an AUTO_INC type lock on the table mentioned in prebuilt. The
-AUTO_INC lock gives exclusive access to the auto-inc counter of the
-table. The lock is reserved only for the duration of an SQL statement.
-It is not compatible with another AUTO_INC or exclusive lock on the
-table. */
-
-int
-row_lock_table_autoinc_for_mysql(
-/*=============================*/
- /* out: error code or DB_SUCCESS */
- row_prebuilt_t* prebuilt) /* in: prebuilt struct in the MySQL
- table handle */
-{
- trx_t* trx = prebuilt->trx;
- ins_node_t* node = prebuilt->ins_node;
- que_thr_t* thr;
- ulint err;
- ibool was_lock_wait;
-
- ut_ad(trx);
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-
- if (trx->auto_inc_lock) {
-
- return(DB_SUCCESS);
- }
-
- trx->op_info = "setting auto-inc lock";
-
- if (node == NULL) {
- row_get_prebuilt_insert_row(prebuilt);
- node = prebuilt->ins_node;
- }
-
- /* We use the insert query graph as the dummy graph needed
- in the lock module call */
-
- thr = que_fork_get_first_thr(prebuilt->ins_graph);
-
- que_thr_move_to_run_state_for_mysql(thr, trx);
-
-run_again:
- thr->run_node = node;
- thr->prev_node = node;
-
- /* It may be that the current session has not yet started
- its transaction, or it has been committed: */
-
- trx_start_if_not_started(trx);
-
- err = lock_table(0, prebuilt->table, LOCK_AUTO_INC, thr);
-
- trx->error_state = err;
-
- if (err != DB_SUCCESS) {
- que_thr_stop_for_mysql(thr);
-
- was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);
-
- if (was_lock_wait) {
- goto run_again;
- }
-
- trx->op_info = "";
-
- return((int) err);
- }
-
- que_thr_stop_for_mysql_no_error(thr, trx);
-
- trx->op_info = "";
-
- return((int) err);
-}
-
-/*************************************************************************
-Sets a table lock on the table mentioned in prebuilt. */
-
-int
-row_lock_table_for_mysql(
-/*=====================*/
- /* out: error code or DB_SUCCESS */
- row_prebuilt_t* prebuilt, /* in: prebuilt struct in the MySQL
- table handle */
- dict_table_t* table, /* in: table to lock, or NULL
- if prebuilt->table should be
- locked as
- prebuilt->select_lock_type */
- ulint mode) /* in: lock mode of table
- (ignored if table==NULL) */
-{
- trx_t* trx = prebuilt->trx;
- que_thr_t* thr;
- ulint err;
- ibool was_lock_wait;
-
- ut_ad(trx);
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-
- trx->op_info = "setting table lock";
-
- if (prebuilt->sel_graph == NULL) {
- /* Build a dummy select query graph */
- row_prebuild_sel_graph(prebuilt);
- }
-
- /* We use the select query graph as the dummy graph needed
- in the lock module call */
-
- thr = que_fork_get_first_thr(prebuilt->sel_graph);
-
- que_thr_move_to_run_state_for_mysql(thr, trx);
-
-run_again:
- thr->run_node = thr;
- thr->prev_node = thr->common.parent;
-
- /* It may be that the current session has not yet started
- its transaction, or it has been committed: */
-
- trx_start_if_not_started(trx);
-
- if (table) {
- err = lock_table(0, table, mode, thr);
- } else {
- err = lock_table(0, prebuilt->table,
- prebuilt->select_lock_type, thr);
- }
-
- trx->error_state = err;
-
- if (err != DB_SUCCESS) {
- que_thr_stop_for_mysql(thr);
-
- was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);
-
- if (was_lock_wait) {
- goto run_again;
- }
-
- trx->op_info = "";
-
- return((int) err);
- }
-
- que_thr_stop_for_mysql_no_error(thr, trx);
-
- trx->op_info = "";
-
- return((int) err);
-}
-
-/*************************************************************************
-Does an insert for MySQL. */
-
-int
-row_insert_for_mysql(
-/*=================*/
- /* out: error code or DB_SUCCESS */
- byte* mysql_rec, /* in: row in the MySQL format */
- row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
- handle */
-{
- trx_savept_t savept;
- que_thr_t* thr;
- ulint err;
- ibool was_lock_wait;
- trx_t* trx = prebuilt->trx;
- ins_node_t* node = prebuilt->ins_node;
-
- ut_ad(trx);
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-
- if (prebuilt->table->ibd_file_missing) {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB: Error:\n"
- "InnoDB: MySQL is trying to use a table handle"
- " but the .ibd file for\n"
- "InnoDB: table %s does not exist.\n"
- "InnoDB: Have you deleted the .ibd file"
- " from the database directory under\n"
- "InnoDB: the MySQL datadir, or have you"
- " used DISCARD TABLESPACE?\n"
- "InnoDB: Look from\n"
- "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
- "innodb-troubleshooting.html\n"
- "InnoDB: how you can resolve the problem.\n",
- prebuilt->table->name);
- return(DB_ERROR);
- }
-
- if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
- fprintf(stderr,
- "InnoDB: Error: trying to free a corrupt\n"
- "InnoDB: table handle. Magic n %lu, table name",
- (ulong) prebuilt->magic_n);
- ut_print_name(stderr, prebuilt->trx, TRUE,
- prebuilt->table->name);
- putc('\n', stderr);
-
- mem_analyze_corruption(prebuilt);
-
- ut_error;
- }
-
- if (srv_created_new_raw || srv_force_recovery) {
- fputs("InnoDB: A new raw disk partition was initialized or\n"
- "InnoDB: innodb_force_recovery is on: we do not allow\n"
- "InnoDB: database modifications by the user. Shut down\n"
- "InnoDB: mysqld and edit my.cnf so that"
- " newraw is replaced\n"
- "InnoDB: with raw, and innodb_force_... is removed.\n",
- stderr);
-
- return(DB_ERROR);
- }
-
- trx->op_info = "inserting";
-
- row_mysql_delay_if_needed();
-
- trx_start_if_not_started(trx);
-
- if (node == NULL) {
- row_get_prebuilt_insert_row(prebuilt);
- node = prebuilt->ins_node;
- }
-
- row_mysql_convert_row_to_innobase(node->row, prebuilt, mysql_rec);
-
- savept = trx_savept_take(trx);
-
- thr = que_fork_get_first_thr(prebuilt->ins_graph);
-
- if (prebuilt->sql_stat_start) {
- node->state = INS_NODE_SET_IX_LOCK;
- prebuilt->sql_stat_start = FALSE;
- } else {
- node->state = INS_NODE_ALLOC_ROW_ID;
- }
-
- que_thr_move_to_run_state_for_mysql(thr, trx);
-
-run_again:
- thr->run_node = node;
- thr->prev_node = node;
-
- row_ins_step(thr);
-
- err = trx->error_state;
-
- if (err != DB_SUCCESS) {
- que_thr_stop_for_mysql(thr);
-
- /* TODO: what is this? */ thr->lock_state= QUE_THR_LOCK_ROW;
-
- was_lock_wait = row_mysql_handle_errors(&err, trx, thr,
- &savept);
- thr->lock_state= QUE_THR_LOCK_NOLOCK;
-
- if (was_lock_wait) {
- goto run_again;
- }
-
- trx->op_info = "";
-
- return((int) err);
- }
-
- que_thr_stop_for_mysql_no_error(thr, trx);
-
- prebuilt->table->stat_n_rows++;
-
- srv_n_rows_inserted++;
-
- if (prebuilt->table->stat_n_rows == 0) {
- /* Avoid wrap-over */
- prebuilt->table->stat_n_rows--;
- }
-
- row_update_statistics_if_needed(prebuilt->table);
- trx->op_info = "";
-
- return((int) err);
-}
-
-/*************************************************************************
-Builds a dummy query graph used in selects. */
-
-void
-row_prebuild_sel_graph(
-/*===================*/
- row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
- handle */
-{
- sel_node_t* node;
-
- ut_ad(prebuilt && prebuilt->trx);
-
- if (prebuilt->sel_graph == NULL) {
-
- node = sel_node_create(prebuilt->heap);
-
- prebuilt->sel_graph = que_node_get_parent(
- pars_complete_graph_for_exec(node,
- prebuilt->trx,
- prebuilt->heap));
-
- prebuilt->sel_graph->state = QUE_FORK_ACTIVE;
- }
-}
-
-/*************************************************************************
-Creates an query graph node of 'update' type to be used in the MySQL
-interface. */
-
-upd_node_t*
-row_create_update_node_for_mysql(
-/*=============================*/
- /* out, own: update node */
- dict_table_t* table, /* in: table to update */
- mem_heap_t* heap) /* in: mem heap from which allocated */
-{
- upd_node_t* node;
-
- node = upd_node_create(heap);
-
- node->in_mysql_interface = TRUE;
- node->is_delete = FALSE;
- node->searched_update = FALSE;
- node->select_will_do_update = FALSE;
- node->select = NULL;
- node->pcur = btr_pcur_create_for_mysql();
- node->table = table;
-
- node->update = upd_create(dict_table_get_n_cols(table), heap);
-
- node->update_n_fields = dict_table_get_n_cols(table);
-
- UT_LIST_INIT(node->columns);
- node->has_clust_rec_x_lock = TRUE;
- node->cmpl_info = 0;
-
- node->table_sym = NULL;
- node->col_assign_list = NULL;
-
- return(node);
-}
-
-/*************************************************************************
-Gets pointer to a prebuilt update vector used in updates. If the update
-graph has not yet been built in the prebuilt struct, then this function
-first builds it. */
-
-upd_t*
-row_get_prebuilt_update_vector(
-/*===========================*/
- /* out: prebuilt update vector */
- row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
- handle */
-{
- dict_table_t* table = prebuilt->table;
- upd_node_t* node;
-
- ut_ad(prebuilt && table && prebuilt->trx);
-
- if (prebuilt->upd_node == NULL) {
-
- /* Not called before for this handle: create an update node
- and query graph to the prebuilt struct */
-
- node = row_create_update_node_for_mysql(table, prebuilt->heap);
-
- prebuilt->upd_node = node;
-
- prebuilt->upd_graph = que_node_get_parent(
- pars_complete_graph_for_exec(node,
- prebuilt->trx,
- prebuilt->heap));
- prebuilt->upd_graph->state = QUE_FORK_ACTIVE;
- }
-
- return(prebuilt->upd_node->update);
-}
-
-/*************************************************************************
-Does an update or delete of a row for MySQL. */
-
-int
-row_update_for_mysql(
-/*=================*/
- /* out: error code or DB_SUCCESS */
- byte* mysql_rec, /* in: the row to be updated, in
- the MySQL format */
- row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
- handle */
-{
- trx_savept_t savept;
- ulint err;
- que_thr_t* thr;
- ibool was_lock_wait;
- dict_index_t* clust_index;
- /* ulint ref_len; */
- upd_node_t* node;
- dict_table_t* table = prebuilt->table;
- trx_t* trx = prebuilt->trx;
-
- ut_ad(prebuilt && trx);
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
- UT_NOT_USED(mysql_rec);
-
- if (prebuilt->table->ibd_file_missing) {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB: Error:\n"
- "InnoDB: MySQL is trying to use a table handle"
- " but the .ibd file for\n"
- "InnoDB: table %s does not exist.\n"
- "InnoDB: Have you deleted the .ibd file"
- " from the database directory under\n"
- "InnoDB: the MySQL datadir, or have you"
- " used DISCARD TABLESPACE?\n"
- "InnoDB: Look from\n"
- "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
- "innodb-troubleshooting.html\n"
- "InnoDB: how you can resolve the problem.\n",
- prebuilt->table->name);
- return(DB_ERROR);
- }
-
- if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
- fprintf(stderr,
- "InnoDB: Error: trying to free a corrupt\n"
- "InnoDB: table handle. Magic n %lu, table name",
- (ulong) prebuilt->magic_n);
- ut_print_name(stderr, prebuilt->trx, TRUE,
- prebuilt->table->name);
- putc('\n', stderr);
-
- mem_analyze_corruption(prebuilt);
-
- ut_error;
- }
-
- if (srv_created_new_raw || srv_force_recovery) {
- fputs("InnoDB: A new raw disk partition was initialized or\n"
- "InnoDB: innodb_force_recovery is on: we do not allow\n"
- "InnoDB: database modifications by the user. Shut down\n"
- "InnoDB: mysqld and edit my.cnf so that newraw"
- " is replaced\n"
- "InnoDB: with raw, and innodb_force_... is removed.\n",
- stderr);
-
- return(DB_ERROR);
- }
-
- trx->op_info = "updating or deleting";
-
- row_mysql_delay_if_needed();
-
- trx_start_if_not_started(trx);
-
- node = prebuilt->upd_node;
-
- clust_index = dict_table_get_first_index(table);
-
- if (prebuilt->pcur->btr_cur.index == clust_index) {
- btr_pcur_copy_stored_position(node->pcur, prebuilt->pcur);
- } else {
- btr_pcur_copy_stored_position(node->pcur,
- prebuilt->clust_pcur);
- }
-
- ut_a(node->pcur->rel_pos == BTR_PCUR_ON);
-
- /* MySQL seems to call rnd_pos before updating each row it
- has cached: we can get the correct cursor position from
- prebuilt->pcur; NOTE that we cannot build the row reference
- from mysql_rec if the clustered index was automatically
- generated for the table: MySQL does not know anything about
- the row id used as the clustered index key */
-
- savept = trx_savept_take(trx);
-
- thr = que_fork_get_first_thr(prebuilt->upd_graph);
-
- node->state = UPD_NODE_UPDATE_CLUSTERED;
-
- ut_ad(!prebuilt->sql_stat_start);
-
- que_thr_move_to_run_state_for_mysql(thr, trx);
-
-run_again:
- thr->run_node = node;
- thr->prev_node = node;
-
- row_upd_step(thr);
-
- err = trx->error_state;
-
- if (err != DB_SUCCESS) {
- que_thr_stop_for_mysql(thr);
-
- if (err == DB_RECORD_NOT_FOUND) {
- trx->error_state = DB_SUCCESS;
- trx->op_info = "";
-
- return((int) err);
- }
-
- thr->lock_state= QUE_THR_LOCK_ROW;
- was_lock_wait = row_mysql_handle_errors(&err, trx, thr,
- &savept);
- thr->lock_state= QUE_THR_LOCK_NOLOCK;
-
- if (was_lock_wait) {
- goto run_again;
- }
-
- trx->op_info = "";
-
- return((int) err);
- }
-
- que_thr_stop_for_mysql_no_error(thr, trx);
-
- if (node->is_delete) {
- if (prebuilt->table->stat_n_rows > 0) {
- prebuilt->table->stat_n_rows--;
- }
-
- srv_n_rows_deleted++;
- } else {
- srv_n_rows_updated++;
- }
-
- row_update_statistics_if_needed(prebuilt->table);
-
- trx->op_info = "";
-
- return((int) err);
-}
-
-/*************************************************************************
-This can only be used when srv_locks_unsafe_for_binlog is TRUE or
-this session is using a READ COMMITTED isolation level. Before
-calling this function we must use trx_reset_new_rec_lock_info() and
-trx_register_new_rec_lock() to store the information which new record locks
-really were set. This function removes a newly set lock under prebuilt->pcur,
-and also under prebuilt->clust_pcur. Currently, this is only used and tested
-in the case of an UPDATE or a DELETE statement, where the row lock is of the
-LOCK_X type.
-Thus, this implements a 'mini-rollback' that releases the latest record
-locks we set. */
-
-int
-row_unlock_for_mysql(
-/*=================*/
- /* out: error code or DB_SUCCESS */
- row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL
- handle */
- ibool has_latches_on_recs)/* TRUE if called so that we have
- the latches on the records under pcur
- and clust_pcur, and we do not need to
- reposition the cursors. */
-{
- btr_pcur_t* pcur = prebuilt->pcur;
- btr_pcur_t* clust_pcur = prebuilt->clust_pcur;
- trx_t* trx = prebuilt->trx;
-
- ut_ad(prebuilt && trx);
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-
- if (UNIV_UNLIKELY
- (!srv_locks_unsafe_for_binlog
- && trx->isolation_level != TRX_ISO_READ_COMMITTED)) {
-
- fprintf(stderr,
- "InnoDB: Error: calling row_unlock_for_mysql though\n"
- "InnoDB: innodb_locks_unsafe_for_binlog is FALSE and\n"
- "InnoDB: this session is not using"
- " READ COMMITTED isolation level.\n");
-
- return(DB_SUCCESS);
- }
-
- trx->op_info = "unlock_row";
-
- if (prebuilt->new_rec_locks >= 1) {
-
- rec_t* rec;
- dict_index_t* index;
- dulint rec_trx_id;
- mtr_t mtr;
-
- mtr_start(&mtr);
-
- /* Restore the cursor position and find the record */
-
- if (!has_latches_on_recs) {
- btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr);
- }
-
- rec = btr_pcur_get_rec(pcur);
- index = btr_pcur_get_btr_cur(pcur)->index;
-
- if (prebuilt->new_rec_locks >= 2) {
- /* Restore the cursor position and find the record
- in the clustered index. */
-
- if (!has_latches_on_recs) {
- btr_pcur_restore_position(BTR_SEARCH_LEAF,
- clust_pcur, &mtr);
- }
-
- rec = btr_pcur_get_rec(clust_pcur);
- index = btr_pcur_get_btr_cur(clust_pcur)->index;
- }
-
- /* If the record has been modified by this
- transaction, do not unlock it. */
- ut_a(index->type & DICT_CLUSTERED);
-
- if (index->trx_id_offset) {
- rec_trx_id = trx_read_trx_id(rec
- + index->trx_id_offset);
- } else {
- mem_heap_t* heap = NULL;
- ulint offsets_[REC_OFFS_NORMAL_SIZE];
- ulint* offsets = offsets_;
-
- *offsets_ = (sizeof offsets_) / sizeof *offsets_;
- offsets = rec_get_offsets(rec, index, offsets,
- ULINT_UNDEFINED, &heap);
-
- rec_trx_id = row_get_rec_trx_id(rec, index, offsets);
-
- if (UNIV_LIKELY_NULL(heap)) {
- mem_heap_free(heap);
- }
- }
-
- if (ut_dulint_cmp(rec_trx_id, trx->id) != 0) {
- /* We did not update the record: unlock it */
-
- rec = btr_pcur_get_rec(pcur);
- index = btr_pcur_get_btr_cur(pcur)->index;
-
- lock_rec_unlock(trx, rec, prebuilt->select_lock_type);
-
- if (prebuilt->new_rec_locks >= 2) {
- rec = btr_pcur_get_rec(clust_pcur);
- index = btr_pcur_get_btr_cur(clust_pcur)->index;
-
- lock_rec_unlock(trx, rec,
- prebuilt->select_lock_type);
- }
- }
-
- mtr_commit(&mtr);
- }
-
- trx->op_info = "";
-
- return(DB_SUCCESS);
-}
-
-/**************************************************************************
-Does a cascaded delete or set null in a foreign key operation. */
-
-ulint
-row_update_cascade_for_mysql(
-/*=========================*/
- /* out: error code or DB_SUCCESS */
- que_thr_t* thr, /* in: query thread */
- upd_node_t* node, /* in: update node used in the cascade
- or set null operation */
- dict_table_t* table) /* in: table where we do the operation */
-{
- ulint err;
- trx_t* trx;
-
- trx = thr_get_trx(thr);
-run_again:
- thr->run_node = node;
- thr->prev_node = node;
-
- row_upd_step(thr);
-
- err = trx->error_state;
-
- /* Note that the cascade node is a subnode of another InnoDB
- query graph node. We do a normal lock wait in this node, but
- all errors are handled by the parent node. */
-
- if (err == DB_LOCK_WAIT) {
- /* Handle lock wait here */
-
- que_thr_stop_for_mysql(thr);
-
- srv_suspend_mysql_thread(thr);
-
- /* Note that a lock wait may also end in a lock wait timeout,
- or this transaction is picked as a victim in selective
- deadlock resolution */
-
- if (trx->error_state != DB_SUCCESS) {
-
- return(trx->error_state);
- }
-
- /* Retry operation after a normal lock wait */
-
- goto run_again;
- }
-
- if (err != DB_SUCCESS) {
-
- return(err);
- }
-
- if (node->is_delete) {
- if (table->stat_n_rows > 0) {
- table->stat_n_rows--;
- }
-
- srv_n_rows_deleted++;
- } else {
- srv_n_rows_updated++;
- }
-
- row_update_statistics_if_needed(table);
-
- return(err);
-}
-
-/*************************************************************************
-Checks if a table is such that we automatically created a clustered
-index on it (on row id). */
-
-ibool
-row_table_got_default_clust_index(
-/*==============================*/
- dict_table_t* table)
-{
- const dict_index_t* clust_index;
-
- clust_index = dict_table_get_first_index(table);
-
- return(dict_index_get_nth_col(clust_index, 0)->mtype == DATA_SYS);
-}
-
-/*************************************************************************
-Calculates the key number used inside MySQL for an Innobase index. We have
-to take into account if we generated a default clustered index for the table */
-
-ulint
-row_get_mysql_key_number_for_index(
-/*===============================*/
- dict_index_t* index)
-{
- dict_index_t* ind;
- ulint i;
-
- ut_a(index);
-
- i = 0;
- ind = dict_table_get_first_index(index->table);
-
- while (index != ind) {
- ind = dict_table_get_next_index(ind);
- i++;
- }
-
- if (row_table_got_default_clust_index(index->table)) {
- ut_a(i > 0);
- i--;
- }
-
- return(i);
-}
-
-/*************************************************************************
-Locks the data dictionary in shared mode from modifications, for performing
-foreign key check, rollback, or other operation invisible to MySQL. */
-
-void
-row_mysql_freeze_data_dictionary(
-/*=============================*/
- trx_t* trx) /* in: transaction */
-{
- ut_a(trx->dict_operation_lock_mode == 0);
-
- rw_lock_s_lock(&dict_operation_lock);
-
- trx->dict_operation_lock_mode = RW_S_LATCH;
-}
-
-/*************************************************************************
-Unlocks the data dictionary shared lock. */
-
-void
-row_mysql_unfreeze_data_dictionary(
-/*===============================*/
- trx_t* trx) /* in: transaction */
-{
- ut_a(trx->dict_operation_lock_mode == RW_S_LATCH);
-
- rw_lock_s_unlock(&dict_operation_lock);
-
- trx->dict_operation_lock_mode = 0;
-}
-
-/*************************************************************************
-Locks the data dictionary exclusively for performing a table create or other
-data dictionary modification operation. */
-
-void
-row_mysql_lock_data_dictionary(
-/*===========================*/
- trx_t* trx) /* in: transaction */
-{
- ut_a(trx->dict_operation_lock_mode == 0
- || trx->dict_operation_lock_mode == RW_X_LATCH);
-
- /* Serialize data dictionary operations with dictionary mutex:
- no deadlocks or lock waits can occur then in these operations */
-
- rw_lock_x_lock(&dict_operation_lock);
- trx->dict_operation_lock_mode = RW_X_LATCH;
-
- mutex_enter(&(dict_sys->mutex));
-}
-
-/*************************************************************************
-Unlocks the data dictionary exclusive lock. */
-
-void
-row_mysql_unlock_data_dictionary(
-/*=============================*/
- trx_t* trx) /* in: transaction */
-{
- ut_a(trx->dict_operation_lock_mode == RW_X_LATCH);
-
- /* Serialize data dictionary operations with dictionary mutex:
- no deadlocks can occur then in these operations */
-
- mutex_exit(&(dict_sys->mutex));
- rw_lock_x_unlock(&dict_operation_lock);
-
- trx->dict_operation_lock_mode = 0;
-}
-
-/*************************************************************************
-Creates a table for MySQL. If the name of the table ends in
-one of "innodb_monitor", "innodb_lock_monitor", "innodb_tablespace_monitor",
-"innodb_table_monitor", then this will also start the printing of monitor
-output by the master thread. If the table name ends in "innodb_mem_validate",
-InnoDB will try to invoke mem_validate(). */
-
-int
-row_create_table_for_mysql(
-/*=======================*/
- /* out: error code or DB_SUCCESS */
- dict_table_t* table, /* in: table definition */
- trx_t* trx) /* in: transaction handle */
-{
- tab_node_t* node;
- mem_heap_t* heap;
- que_thr_t* thr;
- const char* table_name;
- ulint table_name_len;
- ulint err;
- ulint i;
-
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-#ifdef UNIV_SYNC_DEBUG
- ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
-#endif /* UNIV_SYNC_DEBUG */
- ut_ad(mutex_own(&(dict_sys->mutex)));
- ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
-
- if (srv_created_new_raw) {
- fputs("InnoDB: A new raw disk partition was initialized:\n"
- "InnoDB: we do not allow database modifications"
- " by the user.\n"
- "InnoDB: Shut down mysqld and edit my.cnf so that newraw"
- " is replaced with raw.\n", stderr);
-
- dict_mem_table_free(table);
- trx_commit_for_mysql(trx);
-
- return(DB_ERROR);
- }
-
- trx->op_info = "creating table";
-
- if (row_mysql_is_system_table(table->name)) {
-
- fprintf(stderr,
- "InnoDB: Error: trying to create a MySQL system"
- " table %s of type InnoDB.\n"
- "InnoDB: MySQL system tables must be"
- " of the MyISAM type!\n",
- table->name);
-
- dict_mem_table_free(table);
- trx_commit_for_mysql(trx);
-
- return(DB_ERROR);
- }
-
- /* Check that no reserved column names are used. */
- for (i = 0; i < dict_table_get_n_user_cols(table); i++) {
- if (dict_col_name_is_reserved(
- dict_table_get_col_name(table, i))) {
-
- dict_mem_table_free(table);
- trx_commit_for_mysql(trx);
-
- return(DB_ERROR);
- }
- }
-
- trx_start_if_not_started(trx);
-
- /* The table name is prefixed with the database name and a '/'.
- Certain table names starting with 'innodb_' have their special
- meaning regardless of the database name. Thus, we need to
- ignore the database name prefix in the comparisons. */
- table_name = strchr(table->name, '/');
- ut_a(table_name);
- table_name++;
- table_name_len = strlen(table_name) + 1;
-
- if (STR_EQ(table_name, table_name_len, S_innodb_monitor)) {
-
- /* Table equals "innodb_monitor":
- start monitor prints */
-
- srv_print_innodb_monitor = TRUE;
-
- /* The lock timeout monitor thread also takes care
- of InnoDB monitor prints */
-
- os_event_set(srv_lock_timeout_thread_event);
- } else if (STR_EQ(table_name, table_name_len,
- S_innodb_lock_monitor)) {
-
- srv_print_innodb_monitor = TRUE;
- srv_print_innodb_lock_monitor = TRUE;
- os_event_set(srv_lock_timeout_thread_event);
- } else if (STR_EQ(table_name, table_name_len,
- S_innodb_tablespace_monitor)) {
-
- srv_print_innodb_tablespace_monitor = TRUE;
- os_event_set(srv_lock_timeout_thread_event);
- } else if (STR_EQ(table_name, table_name_len,
- S_innodb_table_monitor)) {
-
- srv_print_innodb_table_monitor = TRUE;
- os_event_set(srv_lock_timeout_thread_event);
- } else if (STR_EQ(table_name, table_name_len,
- S_innodb_mem_validate)) {
- /* We define here a debugging feature intended for
- developers */
-
- fputs("Validating InnoDB memory:\n"
- "to use this feature you must compile InnoDB with\n"
- "UNIV_MEM_DEBUG defined in univ.i and"
- " the server must be\n"
- "quiet because allocation from a mem heap"
- " is not protected\n"
- "by any semaphore.\n", stderr);
-#ifdef UNIV_MEM_DEBUG
- ut_a(mem_validate());
- fputs("Memory validated\n", stderr);
-#else /* UNIV_MEM_DEBUG */
- fputs("Memory NOT validated (recompile with UNIV_MEM_DEBUG)\n",
- stderr);
-#endif /* UNIV_MEM_DEBUG */
- }
-
- heap = mem_heap_create(512);
-
- trx->dict_operation = TRUE;
-
- node = tab_create_graph_create(table, heap);
-
- thr = pars_complete_graph_for_exec(node, trx, heap);
-
- ut_a(thr == que_fork_start_command(que_node_get_parent(thr)));
- que_run_threads(thr);
-
- err = trx->error_state;
-
- if (err != DB_SUCCESS) {
- /* We have special error handling here */
-
- trx->error_state = DB_SUCCESS;
-
- trx_general_rollback_for_mysql(trx, FALSE, NULL);
-
- if (err == DB_OUT_OF_FILE_SPACE) {
- ut_print_timestamp(stderr);
-
- fputs(" InnoDB: Warning: cannot create table ",
- stderr);
- ut_print_name(stderr, trx, TRUE, table->name);
- fputs(" because tablespace full\n", stderr);
-
- if (dict_table_get_low(table->name)) {
-
- row_drop_table_for_mysql(table->name, trx,
- FALSE);
- }
-
- } else if (err == DB_DUPLICATE_KEY) {
- ut_print_timestamp(stderr);
-
- fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, TRUE, table->name);
- fputs(" already exists in InnoDB internal\n"
- "InnoDB: data dictionary. Have you deleted"
- " the .frm file\n"
- "InnoDB: and not used DROP TABLE?"
- " Have you used DROP DATABASE\n"
- "InnoDB: for InnoDB tables in"
- " MySQL version <= 3.23.43?\n"
- "InnoDB: See the Restrictions section"
- " of the InnoDB manual.\n"
- "InnoDB: You can drop the orphaned table"
- " inside InnoDB by\n"
- "InnoDB: creating an InnoDB table with"
- " the same name in another\n"
- "InnoDB: database and copying the .frm file"
- " to the current database.\n"
- "InnoDB: Then MySQL thinks the table exists,"
- " and DROP TABLE will\n"
- "InnoDB: succeed.\n"
- "InnoDB: You can look for further help from\n"
- "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
- "innodb-troubleshooting.html\n",
- stderr);
- }
-
- /* We may also get err == DB_ERROR if the .ibd file for the
- table already exists */
-
- trx->error_state = DB_SUCCESS;
- }
-
- que_graph_free((que_t*) que_node_get_parent(thr));
-
- trx->op_info = "";
-
- return((int) err);
-}
-
-/*************************************************************************
-Does an index creation operation for MySQL. TODO: currently failure
-to create an index results in dropping the whole table! This is no problem
-currently as all indexes must be created at the same time as the table. */
-
-int
-row_create_index_for_mysql(
-/*=======================*/
- /* out: error number or DB_SUCCESS */
- dict_index_t* index, /* in: index definition */
- trx_t* trx, /* in: transaction handle */
- const ulint* field_lengths) /* in: if not NULL, must contain
- dict_index_get_n_fields(index)
- actual field lengths for the
- index columns, which are
- then checked for not being too
- large. */
-{
- ind_node_t* node;
- mem_heap_t* heap;
- que_thr_t* thr;
- ulint err;
- ulint i, j;
- ulint len;
- char* table_name;
-
-#ifdef UNIV_SYNC_DEBUG
- ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
-#endif /* UNIV_SYNC_DEBUG */
- ut_ad(mutex_own(&(dict_sys->mutex)));
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-
- trx->op_info = "creating index";
-
- /* Copy the table name because we may want to drop the
- table later, after the index object is freed (inside
- que_run_threads()) and thus index->table_name is not available. */
- table_name = mem_strdup(index->table_name);
-
- trx_start_if_not_started(trx);
-
- /* Check that the same column does not appear twice in the index.
- Starting from 4.0.14, InnoDB should be able to cope with that, but
- safer not to allow them. */
-
- for (i = 0; i < dict_index_get_n_fields(index); i++) {
- for (j = 0; j < i; j++) {
- if (0 == ut_strcmp(
- dict_index_get_nth_field(index, j)->name,
- dict_index_get_nth_field(index, i)->name)) {
-
- ut_print_timestamp(stderr);
-
- fputs(" InnoDB: Error: column ", stderr);
- ut_print_name(stderr, trx, FALSE,
- dict_index_get_nth_field(
- index, i)->name);
- fputs(" appears twice in ", stderr);
- dict_index_name_print(stderr, trx, index);
- fputs("\n"
- "InnoDB: This is not allowed"
- " in InnoDB.\n", stderr);
-
- err = DB_COL_APPEARS_TWICE_IN_INDEX;
-
- goto error_handling;
- }
- }
-
- /* Check also that prefix_len and actual length
- < DICT_MAX_INDEX_COL_LEN */
-
- len = dict_index_get_nth_field(index, i)->prefix_len;
-
- if (field_lengths) {
- len = ut_max(len, field_lengths[i]);
- }
-
- if (len >= DICT_MAX_INDEX_COL_LEN) {
- err = DB_TOO_BIG_RECORD;
-
- goto error_handling;
- }
- }
-
- heap = mem_heap_create(512);
-
- trx->dict_operation = TRUE;
-
- /* Note that the space id where we store the index is inherited from
- the table in dict_build_index_def_step() in dict0crea.c. */
-
- node = ind_create_graph_create(index, heap);
-
- thr = pars_complete_graph_for_exec(node, trx, heap);
-
- ut_a(thr == que_fork_start_command(que_node_get_parent(thr)));
- que_run_threads(thr);
-
- err = trx->error_state;
-
- que_graph_free((que_t*) que_node_get_parent(thr));
-
-error_handling:
- if (err != DB_SUCCESS) {
- /* We have special error handling here */
-
- trx->error_state = DB_SUCCESS;
-
- trx_general_rollback_for_mysql(trx, FALSE, NULL);
-
- row_drop_table_for_mysql(table_name, trx, FALSE);
-
- trx->error_state = DB_SUCCESS;
- }
-
- trx->op_info = "";
-
- mem_free(table_name);
-
- return((int) err);
-}
-
-/*************************************************************************
-Scans a table create SQL string and adds to the data dictionary
-the foreign key constraints declared in the string. This function
-should be called after the indexes for a table have been created.
-Each foreign key constraint must be accompanied with indexes in
-bot participating tables. The indexes are allowed to contain more
-fields than mentioned in the constraint. Check also that foreign key
-constraints which reference this table are ok. */
-
-int
-row_table_add_foreign_constraints(
-/*==============================*/
- /* out: error code or DB_SUCCESS */
- trx_t* trx, /* in: transaction */
- const char* sql_string, /* in: table create statement where
- foreign keys are declared like:
- FOREIGN KEY (a, b) REFERENCES table2(c, d),
- table2 can be written also with the
- database name before it: test.table2 */
- const char* name, /* in: table full name in the
- normalized form
- database_name/table_name */
- ibool reject_fks) /* in: if TRUE, fail with error
- code DB_CANNOT_ADD_CONSTRAINT if
- any foreign keys are found. */
-{
- ulint err;
-
- ut_ad(mutex_own(&(dict_sys->mutex)));
-#ifdef UNIV_SYNC_DEBUG
- ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
-#endif /* UNIV_SYNC_DEBUG */
- ut_a(sql_string);
-
- trx->op_info = "adding foreign keys";
-
- trx_start_if_not_started(trx);
-
- trx->dict_operation = TRUE;
-
- err = dict_create_foreign_constraints(trx, sql_string, name,
- reject_fks);
-
- if (err == DB_SUCCESS) {
- /* Check that also referencing constraints are ok */
- err = dict_load_foreigns(name, TRUE);
- }
-
- if (err != DB_SUCCESS) {
- /* We have special error handling here */
-
- trx->error_state = DB_SUCCESS;
-
- trx_general_rollback_for_mysql(trx, FALSE, NULL);
-
- row_drop_table_for_mysql(name, trx, FALSE);
-
- trx->error_state = DB_SUCCESS;
- }
-
- return((int) err);
-}
-
-/*************************************************************************
-Drops a table for MySQL as a background operation. MySQL relies on Unix
-in ALTER TABLE to the fact that the table handler does not remove the
-table before all handles to it has been removed. Furhermore, the MySQL's
-call to drop table must be non-blocking. Therefore we do the drop table
-as a background operation, which is taken care of by the master thread
-in srv0srv.c. */
-static
-int
-row_drop_table_for_mysql_in_background(
-/*===================================*/
- /* out: error code or DB_SUCCESS */
- const char* name) /* in: table name */
-{
- ulint error;
- trx_t* trx;
-
- trx = trx_allocate_for_background();
-
- /* If the original transaction was dropping a table referenced by
- foreign keys, we must set the following to be able to drop the
- table: */
-
- trx->check_foreigns = FALSE;
-
- /* fputs("InnoDB: Error: Dropping table ", stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fputs(" in background drop list\n", stderr); */
-
- /* Try to drop the table in InnoDB */
-
- error = row_drop_table_for_mysql(name, trx, FALSE);
-
- /* 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();
-
- trx_commit_for_mysql(trx);
-
- trx_free_for_background(trx);
-
- return((int) error);
-}
-
-/*************************************************************************
-The master thread in srv0srv.c calls this regularly to drop tables which
-we must drop in background after queries to them have ended. Such lazy
-dropping of tables is needed in ALTER TABLE on Unix. */
-
-ulint
-row_drop_tables_for_mysql_in_background(void)
-/*=========================================*/
- /* out: how many tables dropped
- + remaining tables in list */
-{
- row_mysql_drop_t* drop;
- dict_table_t* table;
- ulint n_tables;
- ulint n_tables_dropped = 0;
-loop:
- mutex_enter(&kernel_mutex);
-
- if (!row_mysql_drop_list_inited) {
-
- UT_LIST_INIT(row_mysql_drop_list);
- row_mysql_drop_list_inited = TRUE;
- }
-
- drop = UT_LIST_GET_FIRST(row_mysql_drop_list);
-
- n_tables = UT_LIST_GET_LEN(row_mysql_drop_list);
-
- mutex_exit(&kernel_mutex);
-
- if (drop == NULL) {
- /* All tables dropped */
-
- return(n_tables + n_tables_dropped);
- }
-
- mutex_enter(&(dict_sys->mutex));
- table = dict_table_get_low(drop->table_name);
- mutex_exit(&(dict_sys->mutex));
-
- if (table == NULL) {
- /* If for some reason the table has already been dropped
- through some other mechanism, do not try to drop it */
-
- goto already_dropped;
- }
-
- if (DB_SUCCESS != row_drop_table_for_mysql_in_background(
- drop->table_name)) {
- /* If the DROP fails for some table, we return, and let the
- main thread retry later */
-
- return(n_tables + n_tables_dropped);
- }
-
- n_tables_dropped++;
-
-already_dropped:
- mutex_enter(&kernel_mutex);
-
- UT_LIST_REMOVE(row_mysql_drop_list, row_mysql_drop_list, drop);
-
- ut_print_timestamp(stderr);
- fprintf(stderr,
- " InnoDB: Dropped table %s in background drop queue.\n",
- drop->table_name);
-
- mem_free(drop->table_name);
-
- mem_free(drop);
-
- mutex_exit(&kernel_mutex);
-
- goto loop;
-}
-
-/*************************************************************************
-Get the background drop list length. NOTE: the caller must own the kernel
-mutex! */
-
-ulint
-row_get_background_drop_list_len_low(void)
-/*======================================*/
- /* out: how many tables in list */
-{
- ut_ad(mutex_own(&kernel_mutex));
-
- if (!row_mysql_drop_list_inited) {
-
- UT_LIST_INIT(row_mysql_drop_list);
- row_mysql_drop_list_inited = TRUE;
- }
-
- return(UT_LIST_GET_LEN(row_mysql_drop_list));
-}
-
-/*************************************************************************
-If a table is not yet in the drop list, adds the table to the list of tables
-which the master thread drops in background. We need this on Unix because in
-ALTER TABLE MySQL may call drop table even if the table has running queries on
-it. Also, if there are running foreign key checks on the table, we drop the
-table lazily. */
-static
-ibool
-row_add_table_to_background_drop_list(
-/*==================================*/
- /* out: TRUE if the table was not yet in the
- drop list, and was added there */
- dict_table_t* table) /* in: table */
-{
- row_mysql_drop_t* drop;
-
- mutex_enter(&kernel_mutex);
-
- if (!row_mysql_drop_list_inited) {
-
- UT_LIST_INIT(row_mysql_drop_list);
- row_mysql_drop_list_inited = TRUE;
- }
-
- /* Look if the table already is in the drop list */
- drop = UT_LIST_GET_FIRST(row_mysql_drop_list);
-
- while (drop != NULL) {
- if (strcmp(drop->table_name, table->name) == 0) {
- /* Already in the list */
-
- mutex_exit(&kernel_mutex);
-
- return(FALSE);
- }
-
- drop = UT_LIST_GET_NEXT(row_mysql_drop_list, drop);
- }
-
- drop = mem_alloc(sizeof(row_mysql_drop_t));
-
- drop->table_name = mem_strdup(table->name);
-
- UT_LIST_ADD_LAST(row_mysql_drop_list, row_mysql_drop_list, drop);
-
- /* fputs("InnoDB: Adding table ", stderr);
- ut_print_name(stderr, trx, TRUE, drop->table_name);
- fputs(" to background drop list\n", stderr); */
-
- mutex_exit(&kernel_mutex);
-
- return(TRUE);
-}
-
-#ifndef UNIV_HOTBACKUP
-/*************************************************************************
-Discards the tablespace of a table which stored in an .ibd file. Discarding
-means that this function deletes the .ibd file and assigns a new table id for
-the table. Also the flag table->ibd_file_missing is set TRUE. */
-
-int
-row_discard_tablespace_for_mysql(
-/*=============================*/
- /* out: error code or DB_SUCCESS */
- const char* name, /* in: table name */
- trx_t* trx) /* in: transaction handle */
-{
- dict_foreign_t* foreign;
- dulint new_id;
- dict_table_t* table;
- ibool success;
- ulint err;
- pars_info_t* info = NULL;
-
- /* How do we prevent crashes caused by ongoing operations on
- the table? Old operations could try to access non-existent
- pages.
-
- 1) SQL queries, INSERT, SELECT, ...: we must get an exclusive
- MySQL table lock on the table before we can do DISCARD
- TABLESPACE. Then there are no running queries on the table.
-
- 2) Purge and rollback: we assign a new table id for the
- table. Since purge and rollback look for the table based on
- the table id, they see the table as 'dropped' and discard
- their operations.
-
- 3) Insert buffer: we remove all entries for the tablespace in
- the insert buffer tree; as long as the tablespace mem object
- does not exist, ongoing insert buffer page merges are
- discarded in buf0rea.c. If we recreate the tablespace mem
- object with IMPORT TABLESPACE later, then the tablespace will
- have the same id, but the tablespace_version field in the mem
- object is different, and ongoing old insert buffer page merges
- get discarded.
-
- 4) Linear readahead and random readahead: we use the same
- method as in 3) to discard ongoing operations.
-
- 5) FOREIGN KEY operations: if
- table->n_foreign_key_checks_running > 0, we do not allow the
- discard. We also reserve the data dictionary latch. */
-
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-
- trx->op_info = "discarding tablespace";
- trx_start_if_not_started(trx);
-
- /* Serialize data dictionary operations with dictionary mutex:
- no deadlocks can occur then in these operations */
-
- row_mysql_lock_data_dictionary(trx);
-
- table = dict_table_get_low(name);
-
- if (!table) {
- err = DB_TABLE_NOT_FOUND;
-
- goto funct_exit;
- }
-
- if (table->space == 0) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fputs("\n"
- "InnoDB: is in the system tablespace 0"
- " which cannot be discarded\n", stderr);
- err = DB_ERROR;
-
- goto funct_exit;
- }
-
- if (table->n_foreign_key_checks_running > 0) {
-
- ut_print_timestamp(stderr);
- fputs(" InnoDB: You are trying to DISCARD table ", stderr);
- ut_print_name(stderr, trx, TRUE, table->name);
- fputs("\n"
- "InnoDB: though there is a foreign key check"
- " running on it.\n"
- "InnoDB: Cannot discard the table.\n",
- stderr);
-
- err = DB_ERROR;
-
- goto funct_exit;
- }
-
- /* Check if the table is referenced by foreign key constraints from
- some other table (not the table itself) */
-
- foreign = UT_LIST_GET_FIRST(table->referenced_list);
-
- while (foreign && foreign->foreign_table == table) {
- foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
- }
-
- if (foreign && trx->check_foreigns) {
-
- FILE* ef = dict_foreign_err_file;
-
- /* We only allow discarding a referenced table if
- FOREIGN_KEY_CHECKS is set to 0 */
-
- err = DB_CANNOT_DROP_CONSTRAINT;
-
- mutex_enter(&dict_foreign_err_mutex);
- rewind(ef);
- ut_print_timestamp(ef);
-
- fputs(" Cannot DISCARD table ", ef);
- ut_print_name(ef, trx, TRUE, name);
- fputs("\n"
- "because it is referenced by ", ef);
- ut_print_name(ef, trx, TRUE, foreign->foreign_table_name);
- putc('\n', ef);
- mutex_exit(&dict_foreign_err_mutex);
-
- goto funct_exit;
- }
-
- new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID);
-
- /* Remove all locks except the table-level S and X locks. */
- lock_remove_all_on_table(table, FALSE);
-
- info = pars_info_create();
-
- pars_info_add_str_literal(info, "table_name", name);
- pars_info_add_dulint_literal(info, "new_id", new_id);
-
- err = que_eval_sql(info,
- "PROCEDURE DISCARD_TABLESPACE_PROC () IS\n"
- "old_id CHAR;\n"
- "BEGIN\n"
- "SELECT ID INTO old_id\n"
- "FROM SYS_TABLES\n"
- "WHERE NAME = :table_name\n"
- "LOCK IN SHARE MODE;\n"
- "IF (SQL % NOTFOUND) THEN\n"
- " COMMIT WORK;\n"
- " RETURN;\n"
- "END IF;\n"
- "UPDATE SYS_TABLES SET ID = :new_id\n"
- " WHERE ID = old_id;\n"
- "UPDATE SYS_COLUMNS SET TABLE_ID = :new_id\n"
- " WHERE TABLE_ID = old_id;\n"
- "UPDATE SYS_INDEXES SET TABLE_ID = :new_id\n"
- " WHERE TABLE_ID = old_id;\n"
- "COMMIT WORK;\n"
- "END;\n"
- , FALSE, trx);
-
- if (err != DB_SUCCESS) {
- trx->error_state = DB_SUCCESS;
- trx_general_rollback_for_mysql(trx, FALSE, NULL);
- trx->error_state = DB_SUCCESS;
- } else {
- dict_table_change_id_in_cache(table, new_id);
-
- success = fil_discard_tablespace(table->space);
-
- if (!success) {
- trx->error_state = DB_SUCCESS;
- trx_general_rollback_for_mysql(trx, FALSE, NULL);
- trx->error_state = DB_SUCCESS;
-
- err = DB_ERROR;
- } else {
- /* Set the flag which tells that now it is legal to
- IMPORT a tablespace for this table */
- table->tablespace_discarded = TRUE;
- table->ibd_file_missing = TRUE;
- }
- }
-
-funct_exit:
- trx_commit_for_mysql(trx);
-
- row_mysql_unlock_data_dictionary(trx);
-
- trx->op_info = "";
-
- return((int) err);
-}
-
-/*********************************************************************
-Imports a tablespace. The space id in the .ibd file must match the space id
-of the table in the data dictionary. */
-
-int
-row_import_tablespace_for_mysql(
-/*============================*/
- /* out: error code or DB_SUCCESS */
- const char* name, /* in: table name */
- trx_t* trx) /* in: transaction handle */
-{
- dict_table_t* table;
- ibool success;
- dulint current_lsn;
- ulint err = DB_SUCCESS;
-
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-
- trx_start_if_not_started(trx);
-
- trx->op_info = "importing tablespace";
-
- current_lsn = log_get_lsn();
-
- /* It is possible, though very improbable, that the lsn's in the
- tablespace to be imported have risen above the current system lsn, if
- a lengthy purge, ibuf merge, or rollback was performed on a backup
- taken with ibbackup. If that is the case, reset page lsn's in the
- file. We assume that mysqld was shut down after it performed these
- cleanup operations on the .ibd file, so that it stamped the latest lsn
- to the FIL_PAGE_FILE_FLUSH_LSN in the first page of the .ibd file.
-
- TODO: reset also the trx id's in clustered index records and write
- a new space id to each data page. That would allow us to import clean
- .ibd files from another MySQL installation. */
-
- success = fil_reset_too_high_lsns(name, current_lsn);
-
- if (!success) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Error: cannot reset lsn's in table ", stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fputs("\n"
- "InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n",
- stderr);
-
- err = DB_ERROR;
-
- row_mysql_lock_data_dictionary(trx);
-
- goto funct_exit;
- }
-
- /* Serialize data dictionary operations with dictionary mutex:
- no deadlocks can occur then in these operations */
-
- row_mysql_lock_data_dictionary(trx);
-
- table = dict_table_get_low(name);
-
- if (!table) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: table ", stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fputs("\n"
- "InnoDB: does not exist in the InnoDB data dictionary\n"
- "InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n",
- stderr);
-
- err = DB_TABLE_NOT_FOUND;
-
- goto funct_exit;
- }
-
- if (table->space == 0) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fputs("\n"
- "InnoDB: is in the system tablespace 0"
- " which cannot be imported\n", stderr);
- err = DB_ERROR;
-
- goto funct_exit;
- }
-
- if (!table->tablespace_discarded) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Error: you are trying to"
- " IMPORT a tablespace\n"
- "InnoDB: ", stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fputs(", though you have not called DISCARD on it yet\n"
- "InnoDB: during the lifetime of the mysqld process!\n",
- stderr);
-
- err = DB_ERROR;
-
- goto funct_exit;
- }
-
- /* Play safe and remove all insert buffer entries, though we should
- have removed them already when DISCARD TABLESPACE was called */
-
- ibuf_delete_for_discarded_space(table->space);
-
- success = fil_open_single_table_tablespace(TRUE, table->space,
- table->name);
- if (success) {
- table->ibd_file_missing = FALSE;
- table->tablespace_discarded = FALSE;
- } else {
- if (table->ibd_file_missing) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: cannot find or open in the"
- " database directory the .ibd file of\n"
- "InnoDB: table ", stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fputs("\n"
- "InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n",
- stderr);
- }
-
- err = DB_ERROR;
- }
-
-funct_exit:
- trx_commit_for_mysql(trx);
-
- row_mysql_unlock_data_dictionary(trx);
-
- trx->op_info = "";
-
- return((int) err);
-}
-
-/*************************************************************************
-Truncates a table for MySQL. */
-
-int
-row_truncate_table_for_mysql(
-/*=========================*/
- /* out: error code or DB_SUCCESS */
- dict_table_t* table, /* in: table handle */
- trx_t* trx) /* in: transaction handle */
-{
- dict_foreign_t* foreign;
- ulint err;
- mem_heap_t* heap;
- byte* buf;
- dtuple_t* tuple;
- dfield_t* dfield;
- dict_index_t* sys_index;
- btr_pcur_t pcur;
- mtr_t mtr;
- dulint new_id;
- pars_info_t* info = NULL;
-
- /* How do we prevent crashes caused by ongoing operations on
- the table? Old operations could try to access non-existent
- pages.
-
- 1) SQL queries, INSERT, SELECT, ...: we must get an exclusive
- MySQL table lock on the table before we can do TRUNCATE
- TABLE. Then there are no running queries on the table. This is
- guaranteed, because in ha_innobase::store_lock(), we do not
- weaken the TL_WRITE lock requested by MySQL when executing
- SQLCOM_TRUNCATE.
-
- 2) Purge and rollback: we assign a new table id for the
- table. Since purge and rollback look for the table based on
- the table id, they see the table as 'dropped' and discard
- their operations.
-
- 3) Insert buffer: TRUNCATE TABLE is analogous to DROP TABLE,
- so we do not have to remove insert buffer records, as the
- insert buffer works at a low level. If a freed page is later
- reallocated, the allocator will remove the ibuf entries for
- it.
-
- TODO: when we truncate *.ibd files (analogous to DISCARD
- TABLESPACE), we will have to remove we remove all entries for
- the table in the insert buffer tree!
-
- 4) Linear readahead and random readahead: we use the same
- method as in 3) to discard ongoing operations. (This will only
- be relevant for TRUNCATE TABLE by DISCARD TABLESPACE.)
-
- 5) FOREIGN KEY operations: if
- table->n_foreign_key_checks_running > 0, we do not allow the
- TRUNCATE. We also reserve the data dictionary latch. */
-
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
- ut_ad(table);
-
- if (srv_created_new_raw) {
- fputs("InnoDB: A new raw disk partition was initialized:\n"
- "InnoDB: we do not allow database modifications"
- " by the user.\n"
- "InnoDB: Shut down mysqld and edit my.cnf so that newraw"
- " is replaced with raw.\n", stderr);
-
- return(DB_ERROR);
- }
-
- trx->op_info = "truncating table";
-
- trx_start_if_not_started(trx);
-
- /* Serialize data dictionary operations with dictionary mutex:
- no deadlocks can occur then in these operations */
-
- ut_a(trx->dict_operation_lock_mode == 0);
- /* Prevent foreign key checks etc. while we are truncating the
- table */
-
- row_mysql_lock_data_dictionary(trx);
-
- ut_ad(mutex_own(&(dict_sys->mutex)));
-#ifdef UNIV_SYNC_DEBUG
- ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
-#endif /* UNIV_SYNC_DEBUG */
-
- /* Check if the table is referenced by foreign key constraints from
- some other table (not the table itself) */
-
- foreign = UT_LIST_GET_FIRST(table->referenced_list);
-
- while (foreign && foreign->foreign_table == table) {
- foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
- }
-
- if (foreign && trx->check_foreigns) {
- FILE* ef = dict_foreign_err_file;
-
- /* We only allow truncating a referenced table if
- FOREIGN_KEY_CHECKS is set to 0 */
-
- mutex_enter(&dict_foreign_err_mutex);
- rewind(ef);
- ut_print_timestamp(ef);
-
- fputs(" Cannot truncate table ", ef);
- ut_print_name(ef, trx, TRUE, table->name);
- fputs(" by DROP+CREATE\n"
- "InnoDB: because it is referenced by ", ef);
- ut_print_name(ef, trx, TRUE, foreign->foreign_table_name);
- putc('\n', ef);
- mutex_exit(&dict_foreign_err_mutex);
-
- err = DB_ERROR;
- goto funct_exit;
- }
-
- /* TODO: could we replace the counter n_foreign_key_checks_running
- with lock checks on the table? Acquire here an exclusive lock on the
- table, and rewrite lock0lock.c and the lock wait in srv0srv.c so that
- they can cope with the table having been truncated here? Foreign key
- checks take an IS or IX lock on the table. */
-
- if (table->n_foreign_key_checks_running > 0) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Cannot truncate table ", stderr);
- ut_print_name(stderr, trx, TRUE, table->name);
- fputs(" by DROP+CREATE\n"
- "InnoDB: because there is a foreign key check"
- " running on it.\n",
- stderr);
- err = DB_ERROR;
-
- goto funct_exit;
- }
-
- /* Remove all locks except the table-level S and X locks. */
- lock_remove_all_on_table(table, FALSE);
-
- trx->table_id = table->id;
-
- /* scan SYS_INDEXES for all indexes of the table */
- heap = mem_heap_create(800);
-
- tuple = dtuple_create(heap, 1);
- dfield = dtuple_get_nth_field(tuple, 0);
-
- buf = mem_heap_alloc(heap, 8);
- mach_write_to_8(buf, table->id);
-
- dfield_set_data(dfield, buf, 8);
- sys_index = dict_table_get_first_index(dict_sys->sys_indexes);
- dict_index_copy_types(tuple, sys_index, 1);
-
- mtr_start(&mtr);
- btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE,
- BTR_MODIFY_LEAF, &pcur, &mtr);
- for (;;) {
- rec_t* rec;
- const byte* field;
- ulint len;
- ulint root_page_no;
-
- if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) {
- /* The end of SYS_INDEXES has been reached. */
- break;
- }
-
- rec = btr_pcur_get_rec(&pcur);
-
- field = rec_get_nth_field_old(rec, 0, &len);
- ut_ad(len == 8);
-
- if (memcmp(buf, field, len) != 0) {
- /* End of indexes for the table (TABLE_ID mismatch). */
- break;
- }
-
- if (rec_get_deleted_flag(rec, FALSE)) {
- /* The index has been dropped. */
- goto next_rec;
- }
-
- /* This call may commit and restart mtr
- and reposition pcur. */
- root_page_no = dict_truncate_index_tree(table, &pcur, &mtr);
-
- rec = btr_pcur_get_rec(&pcur);
-
- if (root_page_no != FIL_NULL) {
- page_rec_write_index_page_no(
- rec, DICT_SYS_INDEXES_PAGE_NO_FIELD,
- root_page_no, &mtr);
- /* We will need to commit and restart the
- mini-transaction in order to avoid deadlocks.
- The dict_truncate_index_tree() call has allocated
- a page in this mini-transaction, and the rest of
- this loop could latch another index page. */
- mtr_commit(&mtr);
- mtr_start(&mtr);
- btr_pcur_restore_position(BTR_MODIFY_LEAF,
- &pcur, &mtr);
- }
-
-next_rec:
- btr_pcur_move_to_next_user_rec(&pcur, &mtr);
- }
-
- btr_pcur_close(&pcur);
- mtr_commit(&mtr);
-
- mem_heap_free(heap);
-
- new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID);
-
- info = pars_info_create();
-
- pars_info_add_dulint_literal(info, "old_id", table->id);
- pars_info_add_dulint_literal(info, "new_id", new_id);
-
- err = que_eval_sql(info,
- "PROCEDURE RENUMBER_TABLESPACE_PROC () IS\n"
- "BEGIN\n"
- "UPDATE SYS_TABLES SET ID = :new_id\n"
- " WHERE ID = :old_id;\n"
- "UPDATE SYS_COLUMNS SET TABLE_ID = :new_id\n"
- " WHERE TABLE_ID = :old_id;\n"
- "UPDATE SYS_INDEXES SET TABLE_ID = :new_id\n"
- " WHERE TABLE_ID = :old_id;\n"
- "COMMIT WORK;\n"
- "END;\n"
- , FALSE, trx);
-
- if (err != DB_SUCCESS) {
- trx->error_state = DB_SUCCESS;
- trx_general_rollback_for_mysql(trx, FALSE, NULL);
- trx->error_state = DB_SUCCESS;
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Unable to assign a new identifier to table ",
- stderr);
- ut_print_name(stderr, trx, TRUE, table->name);
- fputs("\n"
- "InnoDB: after truncating it. Background processes"
- " may corrupt the table!\n", stderr);
- err = DB_ERROR;
- } else {
- dict_table_change_id_in_cache(table, new_id);
- }
-
- /* MySQL calls ha_innobase::reset_auto_increment() which does
- the same thing. */
- dict_table_autoinc_lock(table);
- dict_table_autoinc_initialize(table, 1);
- dict_table_autoinc_unlock(table);
- dict_update_statistics(table);
-
- trx_commit_for_mysql(trx);
-
-funct_exit:
-
- row_mysql_unlock_data_dictionary(trx);
-
- trx->op_info = "";
-
- srv_wake_master_thread();
-
- return((int) err);
-}
-#endif /* !UNIV_HOTBACKUP */
-
-/*************************************************************************
-Drops a table for MySQL. If the name of the dropped table ends in
-one of "innodb_monitor", "innodb_lock_monitor", "innodb_tablespace_monitor",
-"innodb_table_monitor", then this will also stop the printing of monitor
-output by the master thread. */
-
-int
-row_drop_table_for_mysql(
-/*=====================*/
- /* out: error code or DB_SUCCESS */
- const char* name, /* in: table name */
- trx_t* trx, /* in: transaction handle */
- ibool drop_db)/* in: TRUE=dropping whole database */
-{
- dict_foreign_t* foreign;
- dict_table_t* table;
- ulint space_id;
- ulint err;
- const char* table_name;
- ulint namelen;
- ibool locked_dictionary = FALSE;
- pars_info_t* info = NULL;
-
- ut_a(name != NULL);
-
- if (srv_created_new_raw) {
- fputs("InnoDB: A new raw disk partition was initialized:\n"
- "InnoDB: we do not allow database modifications"
- " by the user.\n"
- "InnoDB: Shut down mysqld and edit my.cnf so that newraw"
- " is replaced with raw.\n", stderr);
-
- return(DB_ERROR);
- }
-
- trx->op_info = "dropping table";
-
- trx_start_if_not_started(trx);
-
- /* The table name is prefixed with the database name and a '/'.
- Certain table names starting with 'innodb_' have their special
- meaning regardless of the database name. Thus, we need to
- ignore the database name prefix in the comparisons. */
- table_name = strchr(name, '/');
- ut_a(table_name);
- table_name++;
- namelen = strlen(table_name) + 1;
-
- if (namelen == sizeof S_innodb_monitor
- && !memcmp(table_name, S_innodb_monitor,
- sizeof S_innodb_monitor)) {
-
- /* Table name equals "innodb_monitor":
- stop monitor prints */
-
- srv_print_innodb_monitor = FALSE;
- srv_print_innodb_lock_monitor = FALSE;
- } else if (namelen == sizeof S_innodb_lock_monitor
- && !memcmp(table_name, S_innodb_lock_monitor,
- sizeof S_innodb_lock_monitor)) {
- srv_print_innodb_monitor = FALSE;
- srv_print_innodb_lock_monitor = FALSE;
- } else if (namelen == sizeof S_innodb_tablespace_monitor
- && !memcmp(table_name, S_innodb_tablespace_monitor,
- sizeof S_innodb_tablespace_monitor)) {
-
- srv_print_innodb_tablespace_monitor = FALSE;
- } else if (namelen == sizeof S_innodb_table_monitor
- && !memcmp(table_name, S_innodb_table_monitor,
- sizeof S_innodb_table_monitor)) {
-
- srv_print_innodb_table_monitor = FALSE;
- }
-
- /* Serialize data dictionary operations with dictionary mutex:
- no deadlocks can occur then in these operations */
-
- if (trx->dict_operation_lock_mode != RW_X_LATCH) {
- /* Prevent foreign key checks etc. while we are dropping the
- table */
-
- row_mysql_lock_data_dictionary(trx);
-
- locked_dictionary = TRUE;
- }
-
- ut_ad(mutex_own(&(dict_sys->mutex)));
-#ifdef UNIV_SYNC_DEBUG
- ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
-#endif /* UNIV_SYNC_DEBUG */
-
- table = dict_table_get_low(name);
-
- if (!table) {
- err = DB_TABLE_NOT_FOUND;
- ut_print_timestamp(stderr);
-
- fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fputs(" does not exist in the InnoDB internal\n"
- "InnoDB: data dictionary though MySQL is"
- " trying to drop it.\n"
- "InnoDB: Have you copied the .frm file"
- " of the table to the\n"
- "InnoDB: MySQL database directory"
- " from another database?\n"
- "InnoDB: You can look for further help from\n"
- "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
- "innodb-troubleshooting.html\n",
- stderr);
- goto funct_exit;
- }
-
- /* Check if the table is referenced by foreign key constraints from
- some other table (not the table itself) */
-
- foreign = UT_LIST_GET_FIRST(table->referenced_list);
-
- while (foreign && foreign->foreign_table == table) {
-check_next_foreign:
- foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
- }
-
- if (foreign && trx->check_foreigns
- && !(drop_db && dict_tables_have_same_db(
- name, foreign->foreign_table_name))) {
- FILE* ef = dict_foreign_err_file;
-
- /* We only allow dropping a referenced table if
- FOREIGN_KEY_CHECKS is set to 0 */
-
- err = DB_CANNOT_DROP_CONSTRAINT;
-
- mutex_enter(&dict_foreign_err_mutex);
- rewind(ef);
- ut_print_timestamp(ef);
-
- fputs(" Cannot drop table ", ef);
- ut_print_name(ef, trx, TRUE, name);
- fputs("\n"
- "because it is referenced by ", ef);
- ut_print_name(ef, trx, TRUE, foreign->foreign_table_name);
- putc('\n', ef);
- mutex_exit(&dict_foreign_err_mutex);
-
- goto funct_exit;
- }
-
- if (foreign && trx->check_foreigns) {
- goto check_next_foreign;
- }
-
- if (table->n_mysql_handles_opened > 0) {
- ibool added;
-
- added = row_add_table_to_background_drop_list(table);
-
- if (added) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Warning: MySQL is"
- " trying to drop table ", stderr);
- ut_print_name(stderr, trx, TRUE, table->name);
- fputs("\n"
- "InnoDB: though there are still"
- " open handles to it.\n"
- "InnoDB: Adding the table to the"
- " background drop queue.\n",
- stderr);
-
- /* We return DB_SUCCESS to MySQL though the drop will
- happen lazily later */
-
- err = DB_SUCCESS;
- } else {
- /* The table is already in the background drop list */
- err = DB_ERROR;
- }
-
- goto funct_exit;
- }
-
- /* TODO: could we replace the counter n_foreign_key_checks_running
- with lock checks on the table? Acquire here an exclusive lock on the
- table, and rewrite lock0lock.c and the lock wait in srv0srv.c so that
- they can cope with the table having been dropped here? Foreign key
- checks take an IS or IX lock on the table. */
-
- if (table->n_foreign_key_checks_running > 0) {
-
- ibool added;
-
- added = row_add_table_to_background_drop_list(table);
-
- if (added) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: You are trying to drop table ",
- stderr);
- ut_print_name(stderr, trx, TRUE, table->name);
- fputs("\n"
- "InnoDB: though there is a"
- " foreign key check running on it.\n"
- "InnoDB: Adding the table to"
- " the background drop queue.\n",
- stderr);
-
- /* We return DB_SUCCESS to MySQL though the drop will
- happen lazily later */
-
- err = DB_SUCCESS;
- } else {
- /* The table is already in the background drop list */
- err = DB_ERROR;
- }
-
- goto funct_exit;
- }
-
- /* Remove all locks there are on the table or its records */
- lock_remove_all_on_table(table, TRUE);
-
- trx->dict_operation = TRUE;
- trx->table_id = table->id;
-
- /* We use the private SQL parser of Innobase to generate the
- query graphs needed in deleting the dictionary data from system
- tables in Innobase. Deleting a row from SYS_INDEXES table also
- frees the file segments of the B-tree associated with the index. */
-
- info = pars_info_create();
-
- pars_info_add_str_literal(info, "table_name", name);
-
- err = que_eval_sql(info,
- "PROCEDURE DROP_TABLE_PROC () IS\n"
- "sys_foreign_id CHAR;\n"
- "table_id CHAR;\n"
- "index_id CHAR;\n"
- "foreign_id CHAR;\n"
- "found INT;\n"
- "BEGIN\n"
- "SELECT ID INTO table_id\n"
- "FROM SYS_TABLES\n"
- "WHERE NAME = :table_name\n"
- "LOCK IN SHARE MODE;\n"
- "IF (SQL % NOTFOUND) THEN\n"
- " COMMIT WORK;\n"
- " RETURN;\n"
- "END IF;\n"
- "found := 1;\n"
- "SELECT ID INTO sys_foreign_id\n"
- "FROM SYS_TABLES\n"
- "WHERE NAME = 'SYS_FOREIGN'\n"
- "LOCK IN SHARE MODE;\n"
- "IF (SQL % NOTFOUND) THEN\n"
- " found := 0;\n"
- "END IF;\n"
- "IF (:table_name = 'SYS_FOREIGN') THEN\n"
- " found := 0;\n"
- "END IF;\n"
- "IF (:table_name = 'SYS_FOREIGN_COLS') THEN\n"
- " found := 0;\n"
- "END IF;\n"
- "WHILE found = 1 LOOP\n"
- " SELECT ID INTO foreign_id\n"
- " FROM SYS_FOREIGN\n"
- " WHERE FOR_NAME = :table_name\n"
- " AND TO_BINARY(FOR_NAME)\n"
- " = TO_BINARY(:table_name)\n"
- " LOCK IN SHARE MODE;\n"
- " IF (SQL % NOTFOUND) THEN\n"
- " found := 0;\n"
- " ELSE\n"
- " DELETE FROM SYS_FOREIGN_COLS\n"
- " WHERE ID = foreign_id;\n"
- " DELETE FROM SYS_FOREIGN\n"
- " WHERE ID = foreign_id;\n"
- " END IF;\n"
- "END LOOP;\n"
- "found := 1;\n"
- "WHILE found = 1 LOOP\n"
- " SELECT ID INTO index_id\n"
- " FROM SYS_INDEXES\n"
- " WHERE TABLE_ID = table_id\n"
- " LOCK IN SHARE MODE;\n"
- " IF (SQL % NOTFOUND) THEN\n"
- " found := 0;\n"
- " ELSE\n"
- " DELETE FROM SYS_FIELDS\n"
- " WHERE INDEX_ID = index_id;\n"
- " DELETE FROM SYS_INDEXES\n"
- " WHERE ID = index_id\n"
- " AND TABLE_ID = table_id;\n"
- " END IF;\n"
- "END LOOP;\n"
- "DELETE FROM SYS_COLUMNS\n"
- "WHERE TABLE_ID = table_id;\n"
- "DELETE FROM SYS_TABLES\n"
- "WHERE ID = table_id;\n"
- "COMMIT WORK;\n"
- "END;\n"
- , FALSE, trx);
-
- if (err != DB_SUCCESS) {
- ut_a(err == DB_OUT_OF_FILE_SPACE);
-
- err = DB_MUST_GET_MORE_FILE_SPACE;
-
- row_mysql_handle_errors(&err, trx, NULL, NULL);
-
- ut_error;
- } else {
- ibool is_path;
- const char* name_or_path;
- mem_heap_t* heap;
-
- heap = mem_heap_create(200);
-
- /* Clone the name, in case it has been allocated
- from table->heap, which will be freed by
- dict_table_remove_from_cache(table) below. */
- name = mem_heap_strdup(heap, name);
- space_id = table->space;
-
- if (table->dir_path_of_temp_table != NULL) {
- is_path = TRUE;
- name_or_path = mem_heap_strdup(
- heap, table->dir_path_of_temp_table);
- } else {
- is_path = FALSE;
- name_or_path = name;
- }
-
- dict_table_remove_from_cache(table);
-
- if (dict_load_table(name) != NULL) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Error: not able to remove table ",
- stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fputs(" from the dictionary cache!\n", stderr);
- err = DB_ERROR;
- }
-
- /* Do not drop possible .ibd tablespace if something went
- wrong: we do not want to delete valuable data of the user */
-
- if (err == DB_SUCCESS && space_id > 0) {
- if (!fil_space_for_table_exists_in_mem(space_id,
- name_or_path,
- is_path,
- FALSE, TRUE)) {
- err = DB_SUCCESS;
-
- fprintf(stderr,
- "InnoDB: We removed now the InnoDB"
- " internal data dictionary entry\n"
- "InnoDB: of table ");
- ut_print_name(stderr, trx, TRUE, name);
- fprintf(stderr, ".\n");
- } else if (!fil_delete_tablespace(space_id)) {
- fprintf(stderr,
- "InnoDB: We removed now the InnoDB"
- " internal data dictionary entry\n"
- "InnoDB: of table ");
- ut_print_name(stderr, trx, TRUE, name);
- fprintf(stderr, ".\n");
-
- ut_print_timestamp(stderr);
- fprintf(stderr,
- " InnoDB: Error: not able to"
- " delete tablespace %lu of table ",
- (ulong) space_id);
- ut_print_name(stderr, trx, TRUE, name);
- fputs("!\n", stderr);
- err = DB_ERROR;
- }
- }
-
- mem_heap_free(heap);
- }
-funct_exit:
-
- trx_commit_for_mysql(trx);
-
- if (locked_dictionary) {
- row_mysql_unlock_data_dictionary(trx);
- }
-
- trx->op_info = "";
-
-#ifndef UNIV_HOTBACKUP
- srv_wake_master_thread();
-#endif /* !UNIV_HOTBACKUP */
-
- return((int) err);
-}
-
-/***********************************************************************
-Drop all foreign keys in a database, see Bug#18942.
-Called at the end of row_drop_database_for_mysql(). */
-static
-ulint
-drop_all_foreign_keys_in_db(
-/*========================*/
- /* out: error code or DB_SUCCESS */
- const char* name, /* in: database name which ends to '/' */
- trx_t* trx) /* in: transaction handle */
-{
- pars_info_t* pinfo;
- ulint err;
-
- ut_a(name[strlen(name) - 1] == '/');
-
- pinfo = pars_info_create();
-
- pars_info_add_str_literal(pinfo, "dbname", name);
-
-/* true if for_name is not prefixed with dbname */
-#define TABLE_NOT_IN_THIS_DB \
-"SUBSTR(for_name, 0, LENGTH(:dbname)) <> :dbname"
-
- err = que_eval_sql(pinfo,
- "PROCEDURE DROP_ALL_FOREIGN_KEYS_PROC () IS\n"
- "foreign_id CHAR;\n"
- "for_name CHAR;\n"
- "found INT;\n"
- "DECLARE CURSOR cur IS\n"
- "SELECT ID, FOR_NAME FROM SYS_FOREIGN\n"
- "WHERE FOR_NAME >= :dbname\n"
- "LOCK IN SHARE MODE\n"
- "ORDER BY FOR_NAME;\n"
- "BEGIN\n"
- "found := 1;\n"
- "OPEN cur;\n"
- "WHILE found = 1 LOOP\n"
- " FETCH cur INTO foreign_id, for_name;\n"
- " IF (SQL % NOTFOUND) THEN\n"
- " found := 0;\n"
- " ELSIF (" TABLE_NOT_IN_THIS_DB ") THEN\n"
- " found := 0;\n"
- " ELSIF (1=1) THEN\n"
- " DELETE FROM SYS_FOREIGN_COLS\n"
- " WHERE ID = foreign_id;\n"
- " DELETE FROM SYS_FOREIGN\n"
- " WHERE ID = foreign_id;\n"
- " END IF;\n"
- "END LOOP;\n"
- "CLOSE cur;\n"
- "COMMIT WORK;\n"
- "END;\n",
- FALSE, /* do not reserve dict mutex,
- we are already holding it */
- trx);
-
- return(err);
-}
-
-/*************************************************************************
-Drops a database for MySQL. */
-
-int
-row_drop_database_for_mysql(
-/*========================*/
- /* out: error code or DB_SUCCESS */
- const char* name, /* in: database name which ends to '/' */
- trx_t* trx) /* in: transaction handle */
-{
- dict_table_t* table;
- char* table_name;
- int err = DB_SUCCESS;
- ulint namelen = strlen(name);
-
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
- ut_a(name != NULL);
- ut_a(name[namelen - 1] == '/');
-
- trx->op_info = "dropping database";
-
- trx_start_if_not_started(trx);
-loop:
- row_mysql_lock_data_dictionary(trx);
-
- while ((table_name = dict_get_first_table_name_in_db(name))) {
- ut_a(memcmp(table_name, name, namelen) == 0);
-
- table = dict_table_get_low(table_name);
-
- ut_a(table);
-
- /* Wait until MySQL does not have any queries running on
- the table */
-
- if (table->n_mysql_handles_opened > 0) {
- row_mysql_unlock_data_dictionary(trx);
-
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Warning: MySQL is trying to"
- " drop database ", stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fputs("\n"
- "InnoDB: though there are still"
- " open handles to table ", stderr);
- ut_print_name(stderr, trx, TRUE, table_name);
- fputs(".\n", stderr);
-
- os_thread_sleep(1000000);
-
- mem_free(table_name);
-
- goto loop;
- }
-
- err = row_drop_table_for_mysql(table_name, trx, TRUE);
-
- if (err != DB_SUCCESS) {
- fputs("InnoDB: DROP DATABASE ", stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fprintf(stderr, " failed with error %lu for table ",
- (ulint) err);
- ut_print_name(stderr, trx, TRUE, table_name);
- putc('\n', stderr);
- mem_free(table_name);
- break;
- }
-
- mem_free(table_name);
- }
-
- if (err == DB_SUCCESS) {
- /* after dropping all tables try to drop all leftover
- foreign keys in case orphaned ones exist */
- err = (int) drop_all_foreign_keys_in_db(name, trx);
-
- if (err != DB_SUCCESS) {
- fputs("InnoDB: DROP DATABASE ", stderr);
- ut_print_name(stderr, trx, TRUE, name);
- fprintf(stderr, " failed with error %d while "
- "dropping all foreign keys", err);
- }
- }
-
- trx_commit_for_mysql(trx);
-
- row_mysql_unlock_data_dictionary(trx);
-
- trx->op_info = "";
-
- return(err);
-}
-
-/*************************************************************************
-Checks if a table name contains the string "/#sql" which denotes temporary
-tables in MySQL. */
-static
-ibool
-row_is_mysql_tmp_table_name(
-/*========================*/
- /* out: TRUE if temporary table */
- const char* name) /* in: table name in the form
- 'database/tablename' */
-{
- return(strstr(name, "/#sql") != NULL);
- /* return(strstr(name, "/@0023sql") != NULL); */
-}
-
-/********************************************************************
-Delete a single constraint. */
-static
-int
-row_delete_constraint_low(
-/*======================*/
- /* out: error code or DB_SUCCESS */
- const char* id, /* in: constraint id */
- trx_t* trx) /* in: transaction handle */
-{
- pars_info_t* info = pars_info_create();
-
- pars_info_add_str_literal(info, "id", id);
-
- return((int) que_eval_sql(info,
- "PROCEDURE DELETE_CONSTRAINT () IS\n"
- "BEGIN\n"
- "DELETE FROM SYS_FOREIGN_COLS WHERE ID = :id;\n"
- "DELETE FROM SYS_FOREIGN WHERE ID = :id;\n"
- "END;\n"
- , FALSE, trx));
-}
-
-/********************************************************************
-Delete a single constraint. */
-static
-int
-row_delete_constraint(
-/*==================*/
- /* out: error code or DB_SUCCESS */
- const char* id, /* in: constraint id */
- const char* database_name, /* in: database name, with the
- trailing '/' */
- mem_heap_t* heap, /* in: memory heap */
- trx_t* trx) /* in: transaction handle */
-{
- ulint err;
-
- /* New format constraints have ids <databasename>/<constraintname>. */
- err = row_delete_constraint_low(
- mem_heap_strcat(heap, database_name, id), trx);
-
- if ((err == DB_SUCCESS) && !strchr(id, '/')) {
- /* Old format < 4.0.18 constraints have constraint ids
- <number>_<number>. We only try deleting them if the
- constraint name does not contain a '/' character, otherwise
- deleting a new format constraint named 'foo/bar' from
- database 'baz' would remove constraint 'bar' from database
- 'foo', if it existed. */
-
- err = row_delete_constraint_low(id, trx);
- }
-
- return((int) err);
-}
-
-/*************************************************************************
-Renames a table for MySQL. */
-
-int
-row_rename_table_for_mysql(
-/*=======================*/
- /* out: error code or DB_SUCCESS */
- const char* old_name, /* in: old table name */
- const char* new_name, /* in: new table name */
- trx_t* trx) /* in: transaction handle */
-{
- dict_table_t* table;
- ulint err;
- mem_heap_t* heap = NULL;
- const char** constraints_to_drop = NULL;
- ulint n_constraints_to_drop = 0;
- ibool old_is_tmp, new_is_tmp;
- pars_info_t* info = NULL;
-
- ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
- ut_a(old_name != NULL);
- ut_a(new_name != NULL);
-
- if (srv_created_new_raw || srv_force_recovery) {
- fputs("InnoDB: A new raw disk partition was initialized or\n"
- "InnoDB: innodb_force_recovery is on: we do not allow\n"
- "InnoDB: database modifications by the user. Shut down\n"
- "InnoDB: mysqld and edit my.cnf so that newraw"
- " is replaced\n"
- "InnoDB: with raw, and innodb_force_... is removed.\n",
- stderr);
-
- trx_commit_for_mysql(trx);
- return(DB_ERROR);
- }
-
- if (row_mysql_is_system_table(new_name)) {
-
- fprintf(stderr,
- "InnoDB: Error: trying to create a MySQL"
- " system table %s of type InnoDB.\n"
- "InnoDB: MySQL system tables must be"
- " of the MyISAM type!\n",
- new_name);
-
- trx_commit_for_mysql(trx);
- return(DB_ERROR);
- }
-
- trx->op_info = "renaming table";
- trx_start_if_not_started(trx);
-
- old_is_tmp = row_is_mysql_tmp_table_name(old_name);
- new_is_tmp = row_is_mysql_tmp_table_name(new_name);
-
- /* Serialize data dictionary operations with dictionary mutex:
- no deadlocks can occur then in these operations */
-
- row_mysql_lock_data_dictionary(trx);
-
- table = dict_table_get_low(old_name);
-
- if (!table) {
- err = DB_TABLE_NOT_FOUND;
- ut_print_timestamp(stderr);
-
- fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, TRUE, old_name);
- fputs(" does not exist in the InnoDB internal\n"
- "InnoDB: data dictionary though MySQL is"
- " trying to rename the table.\n"
- "InnoDB: Have you copied the .frm file"
- " of the table to the\n"
- "InnoDB: MySQL database directory"
- " from another database?\n"
- "InnoDB: You can look for further help from\n"
- "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
- "innodb-troubleshooting.html\n",
- stderr);
- goto funct_exit;
- }
-
- if (table->ibd_file_missing) {
- err = DB_TABLE_NOT_FOUND;
- ut_print_timestamp(stderr);
-
- fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, TRUE, old_name);
- fputs(" does not have an .ibd file"
- " in the database directory.\n"
- "InnoDB: You can look for further help from\n"
- "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
- "innodb-troubleshooting.html\n",
- stderr);
- goto funct_exit;
- }
-
- if (new_is_tmp) {
- /* MySQL is doing an ALTER TABLE command and it renames the
- original table to a temporary table name. We want to preserve
- the original foreign key constraint definitions despite the
- name change. An exception is those constraints for which
- the ALTER TABLE contained DROP FOREIGN KEY <foreign key id>.*/
-
- heap = mem_heap_create(100);
-
- err = dict_foreign_parse_drop_constraints(
- heap, trx, table, &n_constraints_to_drop,
- &constraints_to_drop);
-
- if (err != DB_SUCCESS) {
-
- goto funct_exit;
- }
- }
-
- /* We use the private SQL parser of Innobase to generate the query
- graphs needed in deleting the dictionary data from system tables in
- Innobase. Deleting a row from SYS_INDEXES table also frees the file
- segments of the B-tree associated with the index. */
-
- info = pars_info_create();
-
- pars_info_add_str_literal(info, "new_table_name", new_name);
- pars_info_add_str_literal(info, "old_table_name", old_name);
-
- err = que_eval_sql(info,
- "PROCEDURE RENAME_TABLE () IS\n"
- "BEGIN\n"
- "UPDATE SYS_TABLES SET NAME = :new_table_name\n"
- " WHERE NAME = :old_table_name;\n"
- "END;\n"
- , FALSE, trx);
-
- if (err != DB_SUCCESS) {
-
- goto end;
- }
-
- if (!new_is_tmp) {
- /* Rename all constraints. */
-
- info = pars_info_create();
-
- pars_info_add_str_literal(info, "new_table_name", new_name);
- pars_info_add_str_literal(info, "old_table_name", old_name);
-
- err = que_eval_sql(
- info,
- "PROCEDURE RENAME_CONSTRAINT_IDS () IS\n"
- "gen_constr_prefix CHAR;\n"
- "new_db_name CHAR;\n"
- "foreign_id CHAR;\n"
- "new_foreign_id CHAR;\n"
- "old_db_name_len INT;\n"
- "old_t_name_len INT;\n"
- "new_db_name_len INT;\n"
- "id_len INT;\n"
- "found INT;\n"
- "BEGIN\n"
- "found := 1;\n"
- "old_db_name_len := INSTR(:old_table_name, '/')-1;\n"
- "new_db_name_len := INSTR(:new_table_name, '/')-1;\n"
- "new_db_name := SUBSTR(:new_table_name, 0,\n"
- " new_db_name_len);\n"
- "old_t_name_len := LENGTH(:old_table_name);\n"
- "gen_constr_prefix := CONCAT(:old_table_name,\n"
- " '_ibfk_');\n"
- "WHILE found = 1 LOOP\n"
- " SELECT ID INTO foreign_id\n"
- " FROM SYS_FOREIGN\n"
- " WHERE FOR_NAME = :old_table_name\n"
- " AND TO_BINARY(FOR_NAME)\n"
- " = TO_BINARY(:old_table_name)\n"
- " LOCK IN SHARE MODE;\n"
- " IF (SQL % NOTFOUND) THEN\n"
- " found := 0;\n"
- " ELSE\n"
- " UPDATE SYS_FOREIGN\n"
- " SET FOR_NAME = :new_table_name\n"
- " WHERE ID = foreign_id;\n"
- " id_len := LENGTH(foreign_id);\n"
- " IF (INSTR(foreign_id, '/') > 0) THEN\n"
- " IF (INSTR(foreign_id,\n"
- " gen_constr_prefix) > 0)\n"
- " THEN\n"
- " new_foreign_id :=\n"
- " CONCAT(:new_table_name,\n"
- " SUBSTR(foreign_id, old_t_name_len,\n"
- " id_len - old_t_name_len));\n"
- " ELSE\n"
- " new_foreign_id :=\n"
- " CONCAT(new_db_name,\n"
- " SUBSTR(foreign_id,\n"
- " old_db_name_len,\n"
- " id_len - old_db_name_len));\n"
- " END IF;\n"
- " UPDATE SYS_FOREIGN\n"
- " SET ID = new_foreign_id\n"
- " WHERE ID = foreign_id;\n"
- " UPDATE SYS_FOREIGN_COLS\n"
- " SET ID = new_foreign_id\n"
- " WHERE ID = foreign_id;\n"
- " END IF;\n"
- " END IF;\n"
- "END LOOP;\n"
- "UPDATE SYS_FOREIGN SET REF_NAME = :new_table_name\n"
- "WHERE REF_NAME = :old_table_name\n"
- " AND TO_BINARY(REF_NAME)\n"
- " = TO_BINARY(:old_table_name);\n"
- "END;\n"
- , FALSE, trx);
-
- } else if (n_constraints_to_drop > 0) {
- /* Drop some constraints of tmp tables. */
-
- ulint db_name_len = dict_get_db_name_len(old_name) + 1;
- char* db_name = mem_heap_strdupl(heap, old_name,
- db_name_len);
- ulint i;
-
- for (i = 0; i < n_constraints_to_drop; i++) {
- err = row_delete_constraint(constraints_to_drop[i],
- db_name, heap, trx);
-
- if (err != DB_SUCCESS) {
- break;
- }
- }
- }
-
-end:
- if (err != DB_SUCCESS) {
- if (err == DB_DUPLICATE_KEY) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Error; possible reasons:\n"
- "InnoDB: 1) Table rename would cause"
- " two FOREIGN KEY constraints\n"
- "InnoDB: to have the same internal name"
- " in case-insensitive comparison.\n"
- "InnoDB: 2) table ", stderr);
- ut_print_name(stderr, trx, TRUE, new_name);
- fputs(" exists in the InnoDB internal data\n"
- "InnoDB: dictionary though MySQL is"
- " trying to rename table ", stderr);
- ut_print_name(stderr, trx, TRUE, old_name);
- fputs(" to it.\n"
- "InnoDB: Have you deleted the .frm file"
- " and not used DROP TABLE?\n"
- "InnoDB: You can look for further help from\n"
- "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
- "innodb-troubleshooting.html\n"
- "InnoDB: If table ", stderr);
- ut_print_name(stderr, trx, TRUE, new_name);
- fputs(" is a temporary table #sql..., then"
- " it can be that\n"
- "InnoDB: there are still queries running"
- " on the table, and it will be\n"
- "InnoDB: dropped automatically when"
- " the queries end.\n"
- "InnoDB: You can drop the orphaned table"
- " inside InnoDB by\n"
- "InnoDB: creating an InnoDB table with"
- " the same name in another\n"
- "InnoDB: database and copying the .frm file"
- " to the current database.\n"
- "InnoDB: Then MySQL thinks the table exists,"
- " and DROP TABLE will\n"
- "InnoDB: succeed.\n", stderr);
- }
- trx->error_state = DB_SUCCESS;
- trx_general_rollback_for_mysql(trx, FALSE, NULL);
- trx->error_state = DB_SUCCESS;
- } else {
- /* The following call will also rename the .ibd data file if
- the table is stored in a single-table tablespace */
-
- ibool success = dict_table_rename_in_cache(table, new_name,
- !new_is_tmp);
-
- if (!success) {
- trx->error_state = DB_SUCCESS;
- trx_general_rollback_for_mysql(trx, FALSE, NULL);
- trx->error_state = DB_SUCCESS;
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Error in table rename,"
- " cannot rename ", stderr);
- ut_print_name(stderr, trx, TRUE, old_name);
- fputs(" to ", stderr);
- ut_print_name(stderr, trx, TRUE, new_name);
- putc('\n', stderr);
- err = DB_ERROR;
-
- goto funct_exit;
- }
-
- /* We only want to switch off some of the type checking in
- an ALTER, not in a RENAME. */
-
- err = dict_load_foreigns(
- new_name, old_is_tmp ? trx->check_foreigns : TRUE);
-
- if (err != DB_SUCCESS) {
- ut_print_timestamp(stderr);
-
- if (old_is_tmp) {
- fputs(" InnoDB: Error: in ALTER TABLE ",
- stderr);
- ut_print_name(stderr, trx, TRUE, new_name);
- fputs("\n"
- "InnoDB: has or is referenced"
- " in foreign key constraints\n"
- "InnoDB: which are not compatible"
- " with the new table definition.\n",
- stderr);
- } else {
- fputs(" InnoDB: Error: in RENAME TABLE"
- " table ",
- stderr);
- ut_print_name(stderr, trx, TRUE, new_name);
- fputs("\n"
- "InnoDB: is referenced in"
- " foreign key constraints\n"
- "InnoDB: which are not compatible"
- " with the new table definition.\n",
- stderr);
- }
-
- ut_a(dict_table_rename_in_cache(table,
- old_name, FALSE));
- trx->error_state = DB_SUCCESS;
- trx_general_rollback_for_mysql(trx, FALSE, NULL);
- trx->error_state = DB_SUCCESS;
- }
- }
-
-funct_exit:
- trx_commit_for_mysql(trx);
- row_mysql_unlock_data_dictionary(trx);
-
- if (UNIV_LIKELY_NULL(heap)) {
- mem_heap_free(heap);
- }
-
- trx->op_info = "";
-
- return((int) err);
-}
-
-/*************************************************************************
-Checks that the index contains entries in an ascending order, unique
-constraint is not broken, and calculates the number of index entries
-in the read view of the current transaction. */
-static
-ibool
-row_scan_and_check_index(
-/*=====================*/
- /* out: TRUE if ok */
- row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL */
- dict_index_t* index, /* in: index */
- ulint* n_rows) /* out: number of entries seen in the
- current consistent read */
-{
- dtuple_t* prev_entry = NULL;
- ulint matched_fields;
- ulint matched_bytes;
- byte* buf;
- ulint ret;
- rec_t* rec;
- ibool is_ok = TRUE;
- int cmp;
- ibool contains_null;
- ulint i;
- ulint cnt;
- mem_heap_t* heap = NULL;
- ulint offsets_[REC_OFFS_NORMAL_SIZE];
- ulint* offsets = offsets_;
- *offsets_ = (sizeof offsets_) / sizeof *offsets_;
-
- *n_rows = 0;
-
- buf = mem_alloc(UNIV_PAGE_SIZE);
- heap = mem_heap_create(100);
-
- /* Make a dummy template in prebuilt, which we will use
- in scanning the index entries */
-
- prebuilt->index = index;
- prebuilt->sql_stat_start = TRUE;
- prebuilt->template_type = ROW_MYSQL_DUMMY_TEMPLATE;
- prebuilt->n_template = 0;
- prebuilt->need_to_access_clustered = FALSE;
-
- dtuple_set_n_fields(prebuilt->search_tuple, 0);
-
- prebuilt->select_lock_type = LOCK_NONE;
- cnt = 1000;
-
- ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, 0);
-loop:
- /* Check thd->killed every 1,000 scanned rows */
- if (--cnt == 0) {
- if (trx_is_interrupted(prebuilt->trx)) {
- goto func_exit;
- }
- cnt = 1000;
- }
- if (ret != DB_SUCCESS) {
-func_exit:
- mem_free(buf);
- mem_heap_free(heap);
-
- return(is_ok);
- }
-
- *n_rows = *n_rows + 1;
-
- /* row_search... returns the index record in buf, record origin offset
- within buf stored in the first 4 bytes, because we have built a dummy
- template */
-
- rec = buf + mach_read_from_4(buf);
-
- if (prev_entry != NULL) {
- matched_fields = 0;
- matched_bytes = 0;
-
- offsets = rec_get_offsets(rec, index, offsets,
- ULINT_UNDEFINED, &heap);
- cmp = cmp_dtuple_rec_with_match(prev_entry, rec, offsets,
- &matched_fields,
- &matched_bytes);
- contains_null = FALSE;
-
- /* In a unique secondary index we allow equal key values if
- they contain SQL NULLs */
-
- for (i = 0;
- i < dict_index_get_n_ordering_defined_by_user(index);
- i++) {
- if (UNIV_SQL_NULL == dfield_get_len(
- dtuple_get_nth_field(prev_entry, i))) {
-
- contains_null = TRUE;
- }
- }
-
- if (cmp > 0) {
- fputs("InnoDB: index records in a wrong order in ",
- stderr);
-not_ok:
- dict_index_name_print(stderr,
- prebuilt->trx, index);
- fputs("\n"
- "InnoDB: prev record ", stderr);
- dtuple_print(stderr, prev_entry);
- fputs("\n"
- "InnoDB: record ", stderr);
- rec_print_new(stderr, rec, offsets);
- putc('\n', stderr);
- is_ok = FALSE;
- } else if ((index->type & DICT_UNIQUE)
- && !contains_null
- && matched_fields
- >= dict_index_get_n_ordering_defined_by_user(
- index)) {
-
- fputs("InnoDB: duplicate key in ", stderr);
- goto not_ok;
- }
- }
-
- mem_heap_empty(heap);
- offsets = offsets_;
-
- prev_entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap);
-
- ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, ROW_SEL_NEXT);
-
- goto loop;
-}
-
-/*************************************************************************
-Checks a table for corruption. */
-
-ulint
-row_check_table_for_mysql(
-/*======================*/
- /* out: DB_ERROR or DB_SUCCESS */
- row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
- handle */
-{
- dict_table_t* table = prebuilt->table;
- dict_index_t* index;
- ulint n_rows;
- ulint n_rows_in_table = ULINT_UNDEFINED;
- ulint ret = DB_SUCCESS;
- ulint old_isolation_level;
-
- if (prebuilt->table->ibd_file_missing) {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB: Error:\n"
- "InnoDB: MySQL is trying to use a table handle"
- " but the .ibd file for\n"
- "InnoDB: table %s does not exist.\n"
- "InnoDB: Have you deleted the .ibd file"
- " from the database directory under\n"
- "InnoDB: the MySQL datadir, or have you"
- " used DISCARD TABLESPACE?\n"
- "InnoDB: Look from\n"
- "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
- "innodb-troubleshooting.html\n"
- "InnoDB: how you can resolve the problem.\n",
- prebuilt->table->name);
- return(DB_ERROR);
- }
-
- prebuilt->trx->op_info = "checking table";
-
- old_isolation_level = prebuilt->trx->isolation_level;
-
- /* We must run the index record counts at an isolation level
- >= READ COMMITTED, because a dirty read can see a wrong number
- of records in some index; to play safe, we use always
- REPEATABLE READ here */
-
- prebuilt->trx->isolation_level = TRX_ISO_REPEATABLE_READ;
-
- /* Enlarge the fatal lock wait timeout during CHECK TABLE. */
- mutex_enter(&kernel_mutex);
- srv_fatal_semaphore_wait_threshold += 7200; /* 2 hours */
- mutex_exit(&kernel_mutex);
-
- index = dict_table_get_first_index(table);
-
- while (index != NULL) {
- /* fputs("Validating index ", stderr);
- ut_print_name(stderr, trx, FALSE, index->name);
- putc('\n', stderr); */
-
- if (!btr_validate_index(index, prebuilt->trx)) {
- ret = DB_ERROR;
- } else {
- if (!row_scan_and_check_index(prebuilt,
- index, &n_rows)) {
- ret = DB_ERROR;
- }
-
- if (trx_is_interrupted(prebuilt->trx)) {
- break;
- }
-
- /* fprintf(stderr, "%lu entries in index %s\n", n_rows,
- index->name); */
-
- if (index == dict_table_get_first_index(table)) {
- n_rows_in_table = n_rows;
- } else if (n_rows != n_rows_in_table) {
-
- ret = DB_ERROR;
-
- fputs("Error: ", stderr);
- dict_index_name_print(stderr,
- prebuilt->trx, index);
- fprintf(stderr,
- " contains %lu entries,"
- " should be %lu\n",
- (ulong) n_rows,
- (ulong) n_rows_in_table);
- }
- }
-
- index = dict_table_get_next_index(index);
- }
-
- /* Restore the original isolation level */
- prebuilt->trx->isolation_level = old_isolation_level;
-
- /* We validate also the whole adaptive hash index for all tables
- at every CHECK TABLE */
-
- if (!btr_search_validate()) {
-
- ret = DB_ERROR;
- }
-
- /* Restore the fatal lock wait timeout after CHECK TABLE. */
- mutex_enter(&kernel_mutex);
- srv_fatal_semaphore_wait_threshold -= 7200; /* 2 hours */
- mutex_exit(&kernel_mutex);
-
- prebuilt->trx->op_info = "";
-
- return(ret);
-}
-
-/*************************************************************************
-Determines if a table is a magic monitor table. */
-
-ibool
-row_is_magic_monitor_table(
-/*=======================*/
- /* out: TRUE if monitor table */
- const char* table_name) /* in: name of the table, in the
- form database/table_name */
-{
- const char* name; /* table_name without database/ */
- ulint len;
-
- name = strchr(table_name, '/');
- ut_a(name != NULL);
- name++;
- len = strlen(name) + 1;
-
- if (STR_EQ(name, len, S_innodb_monitor)
- || STR_EQ(name, len, S_innodb_lock_monitor)
- || STR_EQ(name, len, S_innodb_tablespace_monitor)
- || STR_EQ(name, len, S_innodb_table_monitor)
- || STR_EQ(name, len, S_innodb_mem_validate)) {
-
- return(TRUE);
- }
-
- return(FALSE);
-}