Commit 52a89286 authored by marko's avatar marko

branches/zip: Simplify crash recovery in fast index creation.

trx_t: Remove dict_undo_list and dict_redo_list.

innobase_create_temporary_tablename(): Replace TEMP_TABLE_PREFIX with
a table name suffix "#1" or "#2".  In this way, the user can restore
precious data, should anything go wrong.  It is possible to reach an
inconsistent state, because the creation, deletion and renaming of
single-table tablespaces are not transactional.

ut_print_namel(), fil_make_ibd_name(), innobase_rename_table(): Remove
the special treatment of TEMP_TABLE_PREFIX.

Introduce TEMP_INDEX_PREFIX == 0xff for temporary indexes.  This byte
cannot occur in index names since MySQL 4.1.  However, it might have
been possible to use this byte in MySQL 4.0.

recv_recovery_from_checkpoint_finish(): Call the new function
row_merge_drop_temp_indexes(), to drop all indexes whose name starts
with the byte 0xff.

row_merge_rename_indexes(): Renamed from row_merge_rename_index().
Remove the parameter "index".

row_drop_table_for_mysql(): Unconditionally call trx_commit_for_mysql().

row_drop_table_for_mysql_no_commit(): Correct the function commit,
based on the corrected comment of row_drop_table_for_mysql().  Rely on
table->to_be_dropped instead of TEMP_TABLE_PREFIX.

ha_innobase::add_index(): Simplify the control flow.
parent 63805a30
......@@ -4493,224 +4493,3 @@ dict_table_check_for_dup_indexes(
}
}
#endif /* UNIV_DEBUG */
/**************************************************************************
Create an undo list for the trx.*/
void
dict_undo_create_list(
/*==================*/
trx_t* trx) /* out: dict_undo_t list */
{
ut_a(!trx->dict_undo_list);
trx->dict_undo_list = mem_alloc(sizeof(*trx->dict_undo_list));
UT_LIST_INIT(*trx->dict_undo_list);
}
/**************************************************************************
Create an dict_undo_t element and append to the undo list of the trx.*/
dict_undo_t*
dict_undo_create_element(
/*=====================*/ /* out: dict_undo_t element*/
trx_t* trx) /* in: create & add elem to this trx */
{
dict_undo_t* dict_undo;
ut_a(trx->dict_undo_list);
dict_undo = mem_zalloc(sizeof *dict_undo);
UT_LIST_ADD_LAST(node, *trx->dict_undo_list, dict_undo);
return(dict_undo);
}
/**************************************************************************
Free all the nodes on the undo list and free list.*/
void
dict_undo_free_list(
/*================*/
trx_t* trx)
{
dict_undo_t* dict_undo;
ut_a(trx->dict_undo_list);
dict_undo = UT_LIST_GET_FIRST(*trx->dict_undo_list);
while (dict_undo) {
UT_LIST_REMOVE(node, *trx->dict_undo_list, dict_undo);
mem_free(dict_undo);
dict_undo = UT_LIST_GET_FIRST(*trx->dict_undo_list);
}
mem_free(trx->dict_undo_list);
trx->dict_undo_list = NULL;
}
/**************************************************************************
Create an undo list for the trx.*/
void
dict_redo_create_list(
/*==================*/
trx_t* trx) /* out: dict_undo_t list */
{
ut_a(!trx->dict_redo_list);
trx->dict_redo_list = mem_alloc(sizeof(*trx->dict_redo_list));
UT_LIST_INIT(*trx->dict_redo_list);
}
/**************************************************************************
Create an dict_undo_t element and append to the undo list of the trx.*/
dict_redo_t*
dict_redo_create_element(
/*=====================*/ /* out: dict_undo_t element*/
trx_t* trx) /* in: create & add elem to this trx */
{
dict_redo_t* dict_redo;
ut_a(trx->dict_redo_list);
dict_redo = mem_zalloc(sizeof *dict_redo);
UT_LIST_ADD_LAST(node, *trx->dict_redo_list, dict_redo);
return(dict_redo);
}
/**************************************************************************
Free all the nodes on the undo list and free list.*/
void
dict_redo_free_list(
/*================*/
trx_t* trx)
{
dict_redo_t* dict_redo;
ut_a(trx->dict_redo_list);
dict_redo = UT_LIST_GET_FIRST(*trx->dict_redo_list);
while (dict_redo) {
UT_LIST_REMOVE(node, *trx->dict_redo_list, dict_redo);
mem_free(dict_redo);
dict_redo = UT_LIST_GET_FIRST(*trx->dict_redo_list);
}
mem_free(trx->dict_redo_list);
trx->dict_redo_list = NULL;
}
/**************************************************************************
Get the index by name from the transaction's REDO list.*/
dict_index_t*
dict_redo_get_index_on_name(
/*========================*/
trx_t* trx, /* in: transaction */
dict_table_t* table, /* in: table the index belongs to */
const char* name) /* in: index name */
{
dict_redo_t* dict_redo;
ut_a(trx->dict_redo_list);
dict_redo = UT_LIST_GET_FIRST(*trx->dict_redo_list);
while (dict_redo) {
if (dict_redo->index->table == table
&& ut_strcmp(dict_redo->index->name, name) == 0) {
return(dict_redo->index);
}
dict_redo = UT_LIST_GET_NEXT(node, dict_redo);
}
return(NULL);
}
/**************************************************************************
Remove the index from the transaction's REDO list.*/
void
dict_redo_remove_index(
/*===================*/
trx_t* trx, /* in: transaction */
dict_index_t* index) /* in: index to remove */
{
dict_redo_t* dict_redo;
ut_a(trx->dict_redo_list);
dict_redo = UT_LIST_GET_FIRST(*trx->dict_redo_list);
while (dict_redo) {
if (dict_redo->index == index) {
UT_LIST_REMOVE(node, *trx->dict_redo_list, dict_redo);
break;
}
dict_redo = UT_LIST_GET_NEXT(node, dict_redo);
}
}
/**************************************************************************
Add the indexes to SYS_INDEX. */
ulint
dict_rename_indexes(
/*================*/
trx_t* trx) /* in: transaction */
{
dict_redo_t* dict_redo;
ulint err = DB_SUCCESS;
ut_a(trx->dict_redo_list);
dict_redo = UT_LIST_GET_FIRST(*trx->dict_redo_list);
while (dict_redo && err == DB_SUCCESS) {
dict_index_t* index;
index = dict_redo->index;
ut_a(index->table);
ut_a(!ut_dulint_is_zero(index->id));
ut_a(index->space == index->table->space);
#if 0
fprintf(stderr, "Renaming index: %s\n", index->name);
#endif
err = row_merge_rename_index(trx, index->table, index);
dict_redo = UT_LIST_GET_NEXT(node, dict_redo);
}
/* We free the list anyway - even if there is an error of some sort,
let the UNDO code handle the errors.*/
dict_redo_free_list(trx);
return(err);
}
......@@ -2398,16 +2398,6 @@ fil_make_ibd_name(
if (is_temp) {
memcpy(filename, name, namelen);
memcpy(filename + namelen, ".ibd", sizeof ".ibd");
} else if (name[0] == TEMP_TABLE_PREFIX) {
/* Create a temporary tablespace for fast index creation.
See innobase_create_temporary_tablename(). */
memcpy(filename, fil_path_to_mysql_datadir, dirlen);
filename[dirlen] = '/';
memcpy(filename + dirlen + 1, name + 2, namelen - 2);
memcpy(filename + dirlen + namelen - 1, ".ibd", sizeof ".ibd");
/* Replace the 'd' in ".ibd" with the tablename prefix. */
filename[dirlen + namelen + 2] = name[1];
} else {
memcpy(filename, fil_path_to_mysql_datadir, dirlen);
filename[dirlen] = '/';
......
......@@ -5372,17 +5372,8 @@ innobase_rename_table(
norm_to = (char*) my_malloc(strlen(to) + 64, MYF(0));
norm_from = (char*) my_malloc(strlen(from) + 64, MYF(0));
if (*to != TEMP_TABLE_PREFIX) {
normalize_table_name(norm_to, to);
} else {
strcpy(norm_to, to);
}
if (*from != TEMP_TABLE_PREFIX) {
normalize_table_name(norm_from, from);
} else {
strcpy(norm_from, from);
}
normalize_table_name(norm_to, to);
normalize_table_name(norm_from, from);
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
......@@ -8216,20 +8207,19 @@ innobase_create_temporary_tablename(
/*================================*/
/* out: temporary tablename */
mem_heap_t* heap, /* in: memory heap */
char id, /* in: identifier */
char id, /* in: identifier [0-9a-zA-Z] */
const char* table_name) /* in: table name */
{
char* name;
ulint len;
char* name;
ulint len;
static const char suffix[] = "@0023 "; /* "# " */
len = strlen(table_name) + 3;
len = strlen(table_name);
name = (char*) mem_heap_alloc(heap, len);
/* The prefix must be 2 bytes, and the second byte must not be 'd'.
See fil_make_ibd_name(). */
name[0] = TEMP_TABLE_PREFIX;
name[1] = id;
memcpy(name + 2, table_name, len - 2);
name = (char*) mem_heap_alloc(heap, len + sizeof suffix);
memcpy(name, table_name, len);
memcpy(name + len, suffix, sizeof suffix);
name[len + (sizeof suffix - 2)] = id;
return(name);
}
......@@ -8304,11 +8294,26 @@ err_exit:
index_defs = innobase_create_key_def(
trx, innodb_table, heap, key_info, num_of_idx);
ut_a(!trx->dict_redo_list);
/* Allocate memory for dictionary index definitions */
index = (dict_index_t**) mem_heap_alloc(
heap, num_of_idx * sizeof *index);
/* Latch the InnoDB data dictionary exclusively so that no deadlocks
or lock waits can happen in it during an index create operation. */
row_mysql_lock_data_dictionary(trx);
dict_locked = TRUE;
/* Flag this transaction as a dictionary operation, so that the
data dictionary will be locked in crash recovery. Clear the
table_id, so that no table will be dropped in crash recovery,
unless a new primary key is defined. */
trx->dict_operation = TRUE;
trx->table_id = ut_dulint_zero;
/* If a new primary key is defined for the table we need
to drop all original secondary indexes from the table. These
indexes will be rebuilt below. */
to drop the original table and rebuild all indexes. */
new_primary = DICT_CLUSTERED & index_defs[0].ind_type;
......@@ -8316,10 +8321,7 @@ err_exit:
char* new_table_name = innobase_create_temporary_tablename(
heap, '1', innodb_table->name);
row_mysql_lock_data_dictionary(trx);
dict_locked = TRUE;
/* Clone table and write UNDO log record */
/* Clone the table. */
indexed_table = row_merge_create_temporary_table(
new_table_name, index_defs, innodb_table, trx);
......@@ -8330,26 +8332,13 @@ err_exit:
row_mysql_unlock_data_dictionary(trx);
goto err_exit;
}
} else {
dict_redo_create_list(trx);
}
/* Allocate memory for dictionary index definitions */
index = (dict_index_t**) mem_heap_alloc(
heap, num_of_idx * sizeof *index);
/* Latch the InnoDB data dictionary exclusively so that no deadlocks
or lock waits can happen in it during an index create operation. */
if (UNIV_LIKELY(!dict_locked)) {
row_mysql_lock_data_dictionary(trx);
dict_locked = TRUE;
trx->table_id = indexed_table->id;
}
num_created = 0;
/* Create the indexes in SYS_INDEXES and load into dictionary.*/
/* Create the indexes in SYS_INDEXES and load into dictionary. */
for (ulint i = 0; i < num_of_idx; i++) {
......@@ -8419,61 +8408,42 @@ error_handling:
dictionary which were defined. */
switch (error) {
const char* old_name;
const char* tmp_name;
case DB_SUCCESS:
ut_ad(!dict_locked);
break;
case DB_PRIMARY_KEY_IS_NULL:
case DB_DUPLICATE_KEY:
prebuilt->trx->error_info = NULL;
prebuilt->trx->error_key_num = trx->error_key_num;
/* fall through */
default:
if (new_primary) {
row_merge_drop_table(trx, indexed_table);
} else {
row_merge_drop_indexes(trx, indexed_table,
index, num_created);
}
goto func_exit;
}
if (!new_primary) {
error = row_merge_rename_indexes(trx, indexed_table);
/* If a new primary key was defined for the table and
there was no error at this point, we can now rename the
old table as a temporary table, rename the new temporary
table as a old table and drop the old table. */
if (error != DB_SUCCESS) {
row_merge_drop_indexes(trx, indexed_table,
index, num_created);
}
if (new_primary) {
const char* old_name
= innodb_table->name;
const char* tmp_name
= innobase_create_temporary_tablename(heap, '2',
old_name);
break;
}
trx_start_if_not_started(trx);
/* If a new primary key was defined for the table and
there was no error at this point, we can now rename
the old table as a temporary table, rename the new
temporary table as the old table and drop the old table. */
old_name = innodb_table->name;
tmp_name = innobase_create_temporary_tablename(heap, '2',
old_name);
row_mysql_lock_data_dictionary(trx);
dict_locked = TRUE;
row_prebuilt_table_obsolete(innodb_table);
/* Write entry for UNDO */
error = row_undo_report_rename_table_dict_operation(
trx, old_name, indexed_table->name, tmp_name);
if (error != DB_SUCCESS) {
goto func_exit;
}
log_buffer_flush_to_disk();
error = row_merge_rename_tables(innodb_table, indexed_table,
tmp_name, trx);
if (error != DB_SUCCESS) {
goto func_exit;
row_merge_drop_table(trx, indexed_table);
break;
}
row_prebuilt_free(prebuilt, TRUE);
......@@ -8487,15 +8457,25 @@ error_handling:
are no more references to it. */
error = row_merge_drop_table(trx, innodb_table);
}
break;
func_exit:
mem_heap_free(heap);
if (!new_primary) {
dict_rename_indexes(trx);
case DB_PRIMARY_KEY_IS_NULL:
my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0));
/* fall through */
case DB_DUPLICATE_KEY:
prebuilt->trx->error_info = NULL;
prebuilt->trx->error_key_num = trx->error_key_num;
/* fall through */
default:
if (new_primary) {
row_merge_drop_table(trx, indexed_table);
} else {
row_merge_drop_indexes(trx, indexed_table,
index, num_created);
}
}
ut_ad(!trx->dict_redo_list);
mem_heap_free(heap);
trx_commit_for_mysql(trx);
if (dict_locked) {
......@@ -8505,12 +8485,6 @@ func_exit:
/* There might be work for utility threads.*/
srv_active_wake_master_thread();
switch (error) {
case DB_PRIMARY_KEY_IS_NULL:
my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0));
break;
}
DBUG_RETURN(convert_error_code_to_mysql(error, user_thd));
}
......
......@@ -1067,72 +1067,6 @@ dict_table_get_index_on_name_and_min_id(
/* out: index, NULL if does not exist */
dict_table_t* table, /* in: table */
const char* name); /* in: name of the index to find */
/**************************************************************************
Create and return an undo list. */
void
dict_undo_create_list(
/*==================*/
trx_t* trx); /* in: create undo list for this trx.*/
/**************************************************************************
Create element of the undo list and append to the passed in list. */
dict_undo_t*
dict_undo_create_element(
/*=====================*/ /* out: dict_undo_t element*/
trx_t* trx); /* in: create & add elem to this trx.*/
/**************************************************************************
Free all the nodes on the undo list and free list.*/
void
dict_undo_free_list(
/*================*/
trx_t* trx); /* in: free this trx's undo list */
/**************************************************************************
Create and return a redo list. */
void
dict_redo_create_list(
/*==================*/
trx_t* trx); /* in: create redo list for this trx.*/
/**************************************************************************
Create element of the redo list and append to the passed in transaction. */
dict_redo_t*
dict_redo_create_element(
/*=====================*/ /* out: dict_redo_t element*/
trx_t* trx); /* in: create & add elem to this trx.*/
/**************************************************************************
Free all the nodes on the redo list and free list.*/
void
dict_redo_free_list(
/*================*/
trx_t* trx); /* in: free this trx's redo list */
/**************************************************************************
Add the indexes to SYS_INDEX. */
ulint
dict_rename_indexes(
/*================*/
trx_t* trx); /* in/out: transaction */
/**************************************************************************
Remove the index from the transaction's REDO list.*/
void
dict_redo_remove_index(
/*===================*/
trx_t* trx, /* in: transaction */
dict_index_t* index); /* in: index to remove */
/**************************************************************************
Get the index by name from the transaction's REDO list.*/
dict_index_t*
dict_redo_get_index_on_name(
/*========================*/
trx_t* trx, /* in: transaction */
dict_table_t* table, /* in: the table the index belongs to */
const char* name); /* in: index name */
/* Buffers for storing detailed information about the latest foreign key
and unique key errors */
extern FILE* dict_foreign_err_file;
......
......@@ -26,48 +26,4 @@ typedef dict_table_t dict_cluster_t;
typedef struct ind_node_struct ind_node_t;
typedef struct tab_node_struct tab_node_t;
/* Data types for dict_undo */
union dict_undo_data_union {
dict_index_t* index; /* The index to be dropped */
struct {
dict_table_t* old_table; /* All fields are required only for*/
dict_table_t* tmp_table; /*RENAME, for CREATE and DROP we */
dict_table_t* new_table; /*use only old_table */
} table;
};
typedef union dict_undo_data_union dict_undo_data_t;
/* During recovery these are the operations that need to be undone */
struct dict_undo_struct {
ulint op_type; /* Discriminator one of :
TRX_UNDO_INDEX_CREATE_REC,
TRX_UNDO_TABLE_DROP_REC,
TRX_UNDO_TABLE_CREATE_REC,
TRX_UNDO_TABLE_RENAME_REC.*/
dict_undo_data_t
data; /* Data required for UNDO */
UT_LIST_NODE_T(struct dict_undo_struct)
node; /* UNDO list node */
};
typedef struct dict_undo_struct dict_undo_t;
typedef UT_LIST_BASE_NODE_T(dict_undo_t) dict_undo_list_t;
/* TODO: Currently this data structure is a place holder for indexes
created by a transaction.* The REDO is a misnomer*/
struct dict_redo_struct {
ulint op_type; /* Discriminator one of :
TRX_UNDO_INDEX_CREATE_REC.*/
dict_index_t* index; /* The index created.*/
UT_LIST_NODE_T(struct dict_redo_struct)
node; /* REDO list node */
};
typedef struct dict_redo_struct dict_redo_t;
typedef UT_LIST_BASE_NODE_T(dict_redo_t) dict_redo_list_t;
#endif
......@@ -71,7 +71,12 @@ row_merge_drop_indexes(
dict_table_t* table, /* in: table containing the indexes */
dict_index_t** index, /* in: indexes to drop */
ulint num_created); /* in: number of elements in index[] */
/*************************************************************************
Drop all partially created indexes during crash recovery. */
void
row_merge_drop_temp_indexes(void);
/*=============================*/
/*************************************************************************
Rename the tables in the data dictionary. */
......@@ -102,15 +107,14 @@ row_merge_create_temporary_table(
trx_t* trx); /* in/out: transaction
(sets error_state) */
/*************************************************************************
Rename the indexes in the dictionary. */
Rename the temporary indexes in the dictionary to permanent ones. */
ulint
row_merge_rename_index(
/*===================*/
row_merge_rename_indexes(
/*=====================*/
/* out: DB_SUCCESS if all OK */
trx_t* trx, /* in: Transaction */
dict_table_t* table, /* in: Table for index */
dict_index_t* index); /* in: Index to rename */
trx_t* trx, /* in/out: transaction */
dict_table_t* table); /* in/out: table with new indexes */
/*************************************************************************
Create the index and load in to the dictionary. */
......
......@@ -436,10 +436,11 @@ row_drop_table_for_mysql(
ibool drop_db);/* in: TRUE=dropping whole database */
/*************************************************************************
Drops a table for MySQL. If the name of the dropped table ends to
characters INNODB_MONITOR, then this also stops printing of monitor
output by the master thread. But does not commit the transaction, this
is required for UNDOing dictionary records during recovery.*/
Drops a table for MySQL but does not commit the transaction. 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_no_commit(
......@@ -509,48 +510,6 @@ row_create_index_graph_for_mysql(
trx_t* trx, /* in: trx */
dict_table_t* table, /* in: table */
dict_index_t* index); /* in: index */
/***************************************************************************
Writes information to an undo log about dictionary operation, create_table.
This information is used in a rollback of the transaction. */
ulint
row_undo_report_create_table_dict_operation(
/*========================================*/
/* out: DB_SUCCESS or error code */
trx_t* trx, /* in: transaction */
const char* table_name); /* in: table name created. */
/***************************************************************************
Writes information to an undo log about dictionary operation, rename_table.
This information is used in a rollback of the transaction. */
ulint
row_undo_report_create_index_dict_operation(
/*========================================*/
/* out: DB_SUCCESS or error code */
trx_t* trx, /* in: transaction */
dict_index_t* index); /* in: index created. */
/***************************************************************************
Writes information to an undo log about dictionary operation, rename_table.
This information is used in a rollback of the transaction. */
ulint
row_undo_report_rename_table_dict_operation(
/*========================================*/
/* out: DB_SUCCESS or error code */
trx_t* trx, /* in: transaction */
const char* from_table_name,/* in: rename from table name. */
const char* to_table_name, /* in: rename to table table. */
const char* tmp_table_name);/* in: intermediate table name */
/***************************************************************************
Writes information to an undo log about dictionary operation, drop table.
This information is used in a rollback of the transaction. */
ulint
row_undo_report_drop_table_dict_operation(
/*======================================*/
/* out: DB_SUCCESS or error code */
trx_t* trx, /* in: query thread */
const char* table_name); /* in: table name dropped */
/* A struct describing a place for an individual column in the MySQL
row format which is presented to the table handler in ha_innobase.
......
......@@ -28,16 +28,6 @@ row_undo_ins(
/* out: DB_SUCCESS */
undo_node_t* node); /* in: row undo node */
/***************************************************************
Parses the rec_type undo record. */
byte*
row_undo_ins_parse_rec_type_and_table_id(
/*=====================================*/
/* out: ptr to next field to parse */
undo_node_t* node, /* in: row undo node */
dulint* table_id); /* out: table id */
#ifndef UNIV_NONINL
#include "row0uins.ic"
#endif
......
......@@ -51,23 +51,6 @@ row_undo_step(
/*==========*/
/* out: query thread to run next or NULL */
que_thr_t* thr); /* in: query thread */
/***************************************************************
Build the dict undo list*/
ulint
row_undo_build_dict_undo_list(
/*==========================*/
/* out: DB_SUCCESS or error code */
undo_node_t* node); /* in: row undo node */
/***************************************************************
Undo or redo a dictionary change */
void
row_undo_dictionary(
/*================*/
trx_t* trx, /* in: the transaction */
dict_undo_t* dict_undo); /* in: dict op to undo */
/* A single query thread will try to perform the undo for all successive
versions of a clustered index record, if the transaction has modified it
......@@ -95,20 +78,6 @@ struct undo_node_struct{
dulint undo_no;/* undo number of the record */
ulint rec_type;/* undo log record type: TRX_UNDO_INSERT_REC,
... */
ulint rec_sub_type; /* undo log record subtype:
used when rec_type is
TRX_UNDO_DICTIONARY_REC or 0*/
char* new_table_name;/* table name in
TRX_UNDO_TABLE_CREATE_REC or
TRX_UNDO_TABLE_RENAME_REC or
TRX_UNDO_TABLE_DROP_REC or NULL */
char* old_table_name;/* old table name in
TRX_UNDO_TABLE_RENAME_REC or NULL */
char* tmp_table_name; /* intermediate table name used
during rename & drop operation in
ha_innobase::add_index().*/
dulint index_id;/* index id in TRX_UNDO_INDEX_CREATE_REC
or ut_dulint_zero */
dulint new_roll_ptr; /* roll ptr to restore to clustered index
record */
dulint new_trx_id; /* trx id to restore to clustered index
......
......@@ -212,31 +212,6 @@ trx_undo_report_row_operation(
inserted undo log record,
ut_dulint_zero if BTR_NO_UNDO_LOG
flag was specified */
/***************************************************************************
Writes information to an undo log about dictionary operation e.g.
rename_table, create_table, create_index, drop table. This information
is used in a rollback of the transaction. */
ulint
trx_undo_report_dict_operation(
/*===========================*/
/* out: DB_SUCCESS or error code */
ulint op_type, /* in: TRX_UNDO_TABLE_CREATE_OP,
TRX_UNDO_TABLE_RENAME_OP,
TRX_UNDO_TABLE_DROP_OP, or
TRX_UNDO_INDEX_CREATE_OP */
trx_t* trx, /* in: transaction */
dict_index_t* index, /* in:
if TRX_UNDO_INDEX_CREATE_OP
index to be created*/
const char* table_name, /* in: table name or NULL, used in
create table, rename table and
drop table*/
const char* old_table_name, /* in: old table name or NULL.
used in rename table */
const char* tmp_table_name, /* in: the intermediate name used */
dulint* roll_ptr); /* out: rollback pointer to the
inserted undo log record */
/**********************************************************************
Copies an undo record to heap. This function can be called if we know that
the undo log record exists. */
......@@ -324,9 +299,6 @@ record */
fields of the record can change */
#define TRX_UNDO_DEL_MARK_REC 14 /* delete marking of a record; fields
do not change */
#define TRX_UNDO_DICTIONARY_REC 15 /* dictionary operation, detailed
operation type can be found from
undo log records subtype */
#define TRX_UNDO_CMPL_INFO_MULT 16 /* compilation info is multiplied by
this and ORed to the type above */
#define TRX_UNDO_UPD_EXTERN 128 /* This bit can be ORed to type_cmpl
......@@ -334,21 +306,9 @@ record */
storage fields: used by purge to
free the external storage */
/* Operation type flags used in trx_undo_report_row_operation
and trx_undo_report_dict_operation */
/* Operation type flags used in trx_undo_report_row_operation */
#define TRX_UNDO_INSERT_OP 1
#define TRX_UNDO_MODIFY_OP 2
#define TRX_UNDO_INDEX_CREATE_OP 3 /* alter table add index */
#define TRX_UNDO_TABLE_CREATE_OP 4 /* create table */
#define TRX_UNDO_TABLE_RENAME_OP 5 /* rename table */
#define TRX_UNDO_TABLE_DROP_OP 6 /* drop table */
/* Subtypes for dictionary operation */
#define TRX_UNDO_NULL_REC 0 /* No subtype */
#define TRX_UNDO_INDEX_CREATE_REC 1 /* index create record */
#define TRX_UNDO_TABLE_CREATE_REC 2 /* table create record */
#define TRX_UNDO_TABLE_RENAME_REC 3 /* table rename record */
#define TRX_UNDO_TABLE_DROP_REC 4 /* table drop record */
#ifndef UNIV_NONINL
#include "trx0rec.ic"
......
......@@ -408,8 +408,8 @@ trx_get_que_state_str(
/* Signal to a transaction */
struct trx_sig_struct{
ulint type; /* signal type */
ulint sender; /* TRX_SIG_SELF or
unsigned type:3; /* signal type */
unsigned sender:1; /* TRX_SIG_SELF or
TRX_SIG_OTHER_SESS */
que_thr_t* receiver; /* non-NULL if the sender of the signal
wants reply after the operation induced
......@@ -437,66 +437,78 @@ struct trx_struct{
current operation, or an empty
string */
unsigned is_purge:1; /* 0=user transaction, 1=purge */
ulint conc_state; /* state of the trx from the point
unsigned conc_state:2; /* state of the trx from the point
of view of concurrency control:
TRX_ACTIVE, TRX_COMMITTED_IN_MEMORY,
... */
time_t start_time; /* time the trx object was created
or the state last time became
TRX_ACTIVE */
ulint isolation_level;/* TRX_ISO_REPEATABLE_READ, ... */
ibool check_foreigns; /* normally TRUE, but if the user
unsigned que_state:2; /* valid when conc_state == TRX_ACTIVE:
TRX_QUE_RUNNING, TRX_QUE_LOCK_WAIT,
... */
unsigned isolation_level:2;/* TRX_ISO_REPEATABLE_READ, ... */
unsigned check_foreigns:1;/* normally TRUE, but if the user
wants to suppress foreign key checks,
(in table imports, for example) we
set this FALSE */
ibool check_unique_secondary;
unsigned check_unique_secondary:1;
/* normally TRUE, but if the user
wants to speed up inserts by
suppressing unique key checks
for secondary indexes when we decide
if we can use the insert buffer for
them, we set this FALSE */
dulint id; /* transaction id */
XID xid; /* X/Open XA transaction
identification to identify a
transaction branch */
ibool support_xa; /* normally we do the XA two-phase
unsigned support_xa:1; /* normally we do the XA two-phase
commit steps, but by setting this to
FALSE, one can save CPU time and about
150 bytes in the undo log size as then
we skip XA steps */
dulint no; /* transaction serialization number ==
max trx id when the transaction is
moved to COMMITTED_IN_MEMORY state */
ibool flush_log_later;/* when we commit the transaction
unsigned flush_log_later:1;/* when we commit the transaction
in MySQL's binlog write, we will
flush the log to disk later in
a separate call */
ibool must_flush_log_later;/* this flag is set to TRUE in
unsigned must_flush_log_later:1;/* this flag is set to TRUE in
trx_commit_off_kernel() if
flush_log_later was TRUE, and there
were modifications by the transaction;
in that case we must flush the log
in trx_commit_complete_for_mysql() */
ib_uint64_t commit_lsn; /* lsn at the time of the commit */
ibool dict_operation; /* TRUE if the trx is used to create
a table, create an index, or drop a
table. This is a hint that the table
may need to be dropped in crash
unsigned dict_operation:1;/* nonzero if the trx is used
to create a table, create an
index, or drop a table. This
is a hint that the table may
need to be dropped in crash
recovery. */
dict_undo_list_t*
dict_undo_list; /* List of undo records created
during recovery. */
dict_redo_list_t*
dict_redo_list; /* List of indexes created by this
transaction. */
dulint table_id; /* Table to drop iff dict_operation
is TRUE. */
/*------------------------------*/
unsigned duplicates:2; /* TRX_DUP_IGNORE | TRX_DUP_REPLACE */
unsigned active_trans:2; /* 1 - if a transaction in MySQL
is active. 2 - if prepare_commit_mutex
was taken */
unsigned has_search_latch:1;
/* TRUE if this trx has latched the
search system latch in S-mode */
unsigned declared_to_be_inside_innodb:1;
/* this is TRUE if we have declared
this transaction in
srv_conc_enter_innodb to be inside the
InnoDB engine */
unsigned handling_signals:1;/* this is TRUE as long as the trx
is handling signals */
unsigned dict_operation_lock_mode:2;
/* 0, RW_S_LATCH, or RW_X_LATCH:
the latch mode trx currently holds
on dict_operation_lock */
time_t start_time; /* time the trx object was created
or the state last time became
TRX_ACTIVE */
dulint id; /* transaction id */
XID xid; /* X/Open XA transaction
identification to identify a
transaction branch */
dulint no; /* transaction serialization number ==
max trx id when the transaction is
moved to COMMITTED_IN_MEMORY state */
ib_uint64_t commit_lsn; /* lsn at the time of the commit */
dulint table_id; /* Table to drop iff dict_operation
is TRUE, or ut_dulint_zero. */
/*------------------------------*/
void* mysql_thd; /* MySQL thread handle corresponding
to this trx, or NULL */
char** mysql_query_str;/* pointer to the field in mysqld_thd
......@@ -523,13 +535,6 @@ struct trx_struct{
/* how many tables the current SQL
statement uses, except those
in consistent read */
ibool dict_operation_lock_mode;
/* 0, RW_S_LATCH, or RW_X_LATCH:
the latch mode trx currently holds
on dict_operation_lock */
ibool has_search_latch;
/* TRUE if this trx has latched the
search system latch in S-mode */
ulint search_latch_timeout;
/* If we notice that someone is
waiting for our S-lock on the search
......@@ -541,11 +546,6 @@ struct trx_struct{
to reduce contention on the search
latch */
/*------------------------------*/
ibool declared_to_be_inside_innodb;
/* this is TRUE if we have declared
this transaction in
srv_conc_enter_innodb to be inside the
InnoDB engine */
ulint n_tickets_to_enter_innodb;
/* this can be > 0 only when
declared_to_... is TRUE; when we come
......@@ -587,8 +587,6 @@ struct trx_struct{
duplicate key error, a mysql key
number of that index is stored here */
sess_t* sess; /* session of the trx, NULL if none */
ulint que_state; /* TRX_QUE_RUNNING, TRX_QUE_LOCK_WAIT,
... */
que_t* graph; /* query currently run in the session,
or NULL if none; NOTE that the query
belongs to the session, and it can
......@@ -596,8 +594,6 @@ struct trx_struct{
it is a stored procedure with a COMMIT
WORK statement, for instance */
ulint n_active_thrs; /* number of active query threads */
ibool handling_signals;/* this is TRUE as long as the trx
is handling signals */
que_t* graph_before_signal_handling;
/* value of graph when signal handling
for this trx started: this is used to
......@@ -631,7 +627,10 @@ struct trx_struct{
trx that are in the QUE_THR_LOCK_WAIT
state */
ulint deadlock_mark; /* a mark field used in deadlock
checking algorithm */
checking algorithm. This must be
in its own machine word, because
it can be changed by other
threads while holding kernel_mutex. */
/*------------------------------*/
mem_heap_t* lock_heap; /* memory heap for the locks of the
transaction */
......@@ -701,19 +700,19 @@ struct trx_struct{
transaction, e.g., a parallel
query */
/* Transaction concurrency states (trx->conc_state) */
#define TRX_NOT_STARTED 1
#define TRX_ACTIVE 2
#define TRX_COMMITTED_IN_MEMORY 3
#define TRX_PREPARED 4 /* Support for 2PC/XA */
#define TRX_NOT_STARTED 0
#define TRX_ACTIVE 1
#define TRX_COMMITTED_IN_MEMORY 2
#define TRX_PREPARED 3 /* Support for 2PC/XA */
/* Transaction execution states when trx->conc_state == TRX_ACTIVE */
#define TRX_QUE_RUNNING 1 /* transaction is running */
#define TRX_QUE_LOCK_WAIT 2 /* transaction is waiting for a lock */
#define TRX_QUE_ROLLING_BACK 3 /* transaction is rolling back */
#define TRX_QUE_COMMITTING 4 /* transaction is committing */
#define TRX_QUE_RUNNING 0 /* transaction is running */
#define TRX_QUE_LOCK_WAIT 1 /* transaction is waiting for a lock */
#define TRX_QUE_ROLLING_BACK 2 /* transaction is rolling back */
#define TRX_QUE_COMMITTING 3 /* transaction is committing */
/* Transaction isolation levels (trx->isolation_level) */
#define TRX_ISO_READ_UNCOMMITTED 1 /* dirty read: non-locking
#define TRX_ISO_READ_UNCOMMITTED 0 /* dirty read: non-locking
SELECTs are performed so that
we do not look at a possible
earlier version of a record;
......@@ -722,7 +721,7 @@ struct trx_struct{
level; otherwise like level
2 */
#define TRX_ISO_READ_COMMITTED 2 /* somewhat Oracle-like
#define TRX_ISO_READ_COMMITTED 1 /* somewhat Oracle-like
isolation, except that in
range UPDATE and DELETE we
must block phantom rows
......@@ -735,7 +734,7 @@ struct trx_struct{
each consistent read reads its
own snapshot */
#define TRX_ISO_REPEATABLE_READ 3 /* this is the default;
#define TRX_ISO_REPEATABLE_READ 2 /* this is the default;
all consistent reads in the
same trx read the same
snapshot;
......@@ -743,7 +742,7 @@ struct trx_struct{
in locking reads to block
insertions into gaps */
#define TRX_ISO_SERIALIZABLE 4 /* all plain SELECTs are
#define TRX_ISO_SERIALIZABLE 3 /* all plain SELECTs are
converted to LOCK IN SHARE
MODE reads */
......@@ -754,7 +753,7 @@ Multiple flags can be combined with bitwise OR. */
/* Types of a trx signal */
#define TRX_SIG_NO_SIGNAL 100
#define TRX_SIG_NO_SIGNAL 0
#define TRX_SIG_TOTAL_ROLLBACK 1
#define TRX_SIG_ROLLBACK_TO_SAVEPT 2
#define TRX_SIG_COMMIT 3
......@@ -762,10 +761,10 @@ Multiple flags can be combined with bitwise OR. */
#define TRX_SIG_BREAK_EXECUTION 5
/* Sender types of a signal */
#define TRX_SIG_SELF 1 /* sent by the session itself, or
#define TRX_SIG_SELF 0 /* sent by the session itself, or
by an error occurring within this
session */
#define TRX_SIG_OTHER_SESS 2 /* sent by another session (which
#define TRX_SIG_OTHER_SESS 1 /* sent by another session (which
must hold rights to this) */
/* Commit command node in a query graph */
......
......@@ -15,8 +15,6 @@ Created 1/20/1994 Heikki Tuuri
#include <ctype.h>
#endif
#define TEMP_TABLE_PREFIX '/' /* Table name prefix for temporary
tables used in fast index creation */
#define TEMP_INDEX_PREFIX '\377' /* Index name prefix in fast index
creation */
......
......@@ -35,6 +35,7 @@ Created 9/20/1997 Heikki Tuuri
#include "dict0boot.h"
#include "fil0fil.h"
#include "sync0sync.h"
#include "row0merge.h"
#ifdef UNIV_HOTBACKUP
/* This is set to FALSE if the backup was originally taken with the
......@@ -2933,6 +2934,9 @@ recv_recovery_from_checkpoint_finish(void)
recv_sys_free();
#endif
/* Drop partially created indexes. */
row_merge_drop_temp_indexes();
#ifdef UNIV_SYNC_DEBUG
/* Wait for a while so that created threads have time to suspend
themselves before we switch the latching order checks on */
......
......@@ -1523,11 +1523,6 @@ row_merge_drop_index(
foreign key constraints on this table where this index is used */
dict_table_replace_index_in_foreign_list(table, index);
if (trx->dict_redo_list) {
dict_redo_remove_index(trx, index);
}
dict_index_remove_from_cache(table, index);
if (dict_lock) {
......@@ -1556,6 +1551,53 @@ row_merge_drop_indexes(
}
}
/*************************************************************************
Drop all partially created indexes during crash recovery. */
void
row_merge_drop_temp_indexes(void)
/*=============================*/
{
trx_t* trx;
ulint err;
/* 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. */
#if TEMP_INDEX_PREFIX != '\377'
# error "TEMP_INDEX_PREFIX != '\377'"
#endif
static const char drop_temp_indexes[] =
"PROCEDURE DROP_TEMP_INDEXES_PROC () IS\n"
"indexid CHAR;\n"
"DECLARE CURSOR c IS SELECT ID FROM SYS_INDEXES\n"
"WHERE SUBSTR(NAME,0,1)='\377' FOR UPDATE;\n"
"BEGIN\n"
"\tOPEN c;\n"
"\tWHILE 1 LOOP\n"
"\t\tFETCH c INTO indexid;\n"
"\t\tIF (SQL % NOTFOUND) THEN\n"
"\t\t\tEXIT;\n"
"\t\tEND IF;\n"
"\t\tDELETE FROM SYS_FIELDS WHERE INDEX_ID = indexid;\n"
"\t\tDELETE FROM SYS_INDEXES WHERE CURRENT OF c;\n"
"\tEND LOOP;\n"
"\tCLOSE c;\n"
"END;\n";
trx = trx_allocate_for_background();
trx->op_info = "dropping partially created indexes";
row_mysql_lock_data_dictionary(trx);
err = que_eval_sql(NULL, drop_temp_indexes, FALSE, trx);
ut_a(err == DB_SUCCESS);
row_mysql_unlock_data_dictionary(trx);
trx_commit_for_mysql(trx);
trx_free_for_background(trx);
}
/*************************************************************************
Create a merge file. */
static
......@@ -1638,87 +1680,72 @@ row_merge_create_temporary_table(
dict_table_t* new_table = NULL;
ulint n_cols = dict_table_get_n_user_cols(table);
ulint error;
mem_heap_t* heap = mem_heap_create(1000);
ut_ad(table_name);
ut_ad(index_def);
ut_ad(table);
ut_ad(mutex_own(&dict_sys->mutex));
error = row_undo_report_create_table_dict_operation(trx, table_name);
new_table = dict_mem_table_create(table_name, 0, n_cols, table->flags);
if (error == DB_SUCCESS) {
mem_heap_t* heap = mem_heap_create(1000);
log_buffer_flush_to_disk();
new_table = dict_mem_table_create(
table_name, 0, n_cols, table->flags);
for (i = 0; i < n_cols; i++) {
const dict_col_t* col;
const char* col_name;
col = dict_table_get_nth_col(table, i);
col_name = dict_table_get_col_name(table, i);
dict_mem_table_add_col(
new_table, heap, col_name, col->mtype,
row_merge_col_prtype(col, col_name, index_def),
col->len);
}
for (i = 0; i < n_cols; i++) {
const dict_col_t* col;
const char* col_name;
error = row_create_table_for_mysql(new_table, trx);
mem_heap_free(heap);
col = dict_table_get_nth_col(table, i);
col_name = dict_table_get_col_name(table, i);
if (error != DB_SUCCESS) {
new_table = NULL;
}
dict_mem_table_add_col(new_table, heap, col_name, col->mtype,
row_merge_col_prtype(col, col_name,
index_def),
col->len);
}
error = row_create_table_for_mysql(new_table, trx);
mem_heap_free(heap);
if (error != DB_SUCCESS) {
trx->error_state = error;
new_table = NULL;
}
return(new_table);
}
/*************************************************************************
Rename the indexes in the dictionary. */
Rename the temporary indexes in the dictionary to permanent ones. */
ulint
row_merge_rename_index(
/*===================*/
row_merge_rename_indexes(
/*=====================*/
/* out: DB_SUCCESS if all OK */
trx_t* trx, /* in: Transaction */
dict_table_t* table, /* in: Table for index */
dict_index_t* index) /* in: Index to rename */
trx_t* trx, /* in/out: transaction */
dict_table_t* table) /* in/out: table with new indexes */
{
ibool dict_lock = FALSE;
ulint err = DB_SUCCESS;
pars_info_t* info = pars_info_create();
/* Only rename from temp names */
ut_a(*index->name == TEMP_INDEX_PREFIX);
/* We use the private SQL parser of Innobase to generate the
query graphs needed in renaming index. */
query graphs needed in renaming indexes. */
static const char str1[] =
"PROCEDURE RENAME_INDEX_PROC () IS\n"
#if TEMP_INDEX_PREFIX != '\377'
# error "TEMP_INDEX_PREFIX != '\377'"
#endif
static const char rename_indexes[] =
"PROCEDURE RENAME_INDEXES_PROC () IS\n"
"BEGIN\n"
"UPDATE SYS_INDEXES SET NAME = :name\n"
" WHERE ID = :indexid AND TABLE_ID = :tableid;\n"
"UPDATE SYS_INDEXES SET NAME=SUBSTR(NAME,1,LENGTH(NAME)-1)\n"
"WHERE TABLE_ID = :tableid AND SUBSTR(NAME,0,1)='\377';\n"
"END;\n";
table = index->table;
ut_ad(index && table && trx);
ut_ad(table && trx);
trx_start_if_not_started(trx);
trx->op_info = "renaming index";
trx->op_info = "renaming indexes";
pars_info_add_str_literal(info, "name", index->name + 1);
pars_info_add_dulint_literal(info, "indexid", index->id);
pars_info_add_dulint_literal(info, "tableid", table->id);
if (trx->dict_operation_lock_mode == 0) {
......@@ -1726,10 +1753,16 @@ row_merge_rename_index(
dict_lock = TRUE;
}
err = que_eval_sql(info, str1, FALSE, trx);
err = que_eval_sql(info, rename_indexes, FALSE, trx);
if (err == DB_SUCCESS) {
index->name++;
dict_index_t* index = dict_table_get_first_index(table);
do {
if (*index->name == TEMP_INDEX_PREFIX) {
index->name++;
}
index = dict_table_get_next_index(index);
} while (index);
}
if (dict_lock) {
......@@ -1826,8 +1859,9 @@ row_merge_create_index(
index_def)
{
dict_index_t* index;
ulint err = DB_SUCCESS;
ulint err;
ulint n_fields = index_def->n_fields;
ulint i;
/* Create the index prototype, using the passed in def, this is not
a persistent operation. We pass 0 as the space id, and determine at
......@@ -1846,56 +1880,33 @@ row_merge_create_index(
index->id = dict_hdr_get_new_id(DICT_HDR_INDEX_ID);
index->table = table;
/* Write the UNDO record for the create index */
err = row_undo_report_create_index_dict_operation(trx, index);
if (err == DB_SUCCESS) {
ulint i;
/* Make sure the UNDO record gets to disk */
log_buffer_flush_to_disk();
for (i = 0; i < n_fields; i++) {
merge_index_field_t* ifield;
ifield = &index_def->fields[i];
for (i = 0; i < n_fields; i++) {
merge_index_field_t* ifield = &index_def->fields[i];
dict_mem_index_add_field(index,
ifield->field_name,
ifield->prefix_len);
}
dict_mem_index_add_field(index, ifield->field_name,
ifield->prefix_len);
}
/* Add the index to SYS_INDEXES, this will use the prototype
to create an entry in SYS_INDEXES. */
err = row_create_index_graph_for_mysql(trx, table, index);
/* Add the index to SYS_INDEXES, this will use the prototype
to create an entry in SYS_INDEXES. */
err = row_create_index_graph_for_mysql(trx, table, index);
if (err == DB_SUCCESS) {
if (err == DB_SUCCESS) {
index = row_merge_dict_table_get_index(
table, index_def);
index = row_merge_dict_table_get_index(
table, index_def);
ut_a(index);
ut_a(index);
#ifdef ROW_MERGE_IS_INDEX_USABLE
/* Note the id of the transaction that created this
index, we use it to restrict readers from accessing
this index, to ensure read consistency. */
index->trx_id = trx->id;
/* Note the id of the transaction that created this
index, we use it to restrict readers from accessing
this index, to ensure read consistency. */
index->trx_id = trx->id;
#endif /* ROW_MERGE_IS_INDEX_USABLE */
/* Create element and append to list in trx. So that
we can rename from temp name to real name. */
if (trx->dict_redo_list) {
dict_redo_t* dict_redo;
dict_redo = dict_redo_create_element(trx);
dict_redo->index = index;
}
}
}
if (err != DB_SUCCESS) {
} else {
trx->error_state = err;
index = NULL;
}
return(index);
......@@ -1937,8 +1948,6 @@ row_merge_drop_table(
dict_locked = TRUE;
}
ut_a(*table->name == TEMP_TABLE_PREFIX);
/* Drop the table immediately if it is not referenced by MySQL */
if (table->n_mysql_handles_opened == 0) {
err = row_drop_table_for_mysql_no_commit(table->name, trx,
......
......@@ -732,7 +732,6 @@ row_prebuilt_free(
references to it now.*/
if (prebuilt->table->to_be_dropped
&& prebuilt->table->n_mysql_handles_opened == 0) {
ut_a(*prebuilt->table->name == TEMP_TABLE_PREFIX);
if (!row_add_table_to_background_drop_list(prebuilt->table)) {
ut_print_timestamp(stderr);
......@@ -3045,18 +3044,17 @@ row_drop_table_for_mysql(
ulint err;
err = row_drop_table_for_mysql_no_commit(name, trx, drop_db);
trx_commit_for_mysql(trx);
if (!srv_created_new_raw) {
trx_commit_for_mysql(trx);
}
return err;
return(err);
}
/*************************************************************************
Drops a table for MySQL. If the name of the table to be dropped is equal
with one of the predefined magic table names, then this also stops printing
the corresponding monitor output by the master thread. */
Drops a table for MySQL but does not commit the transaction. 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_no_commit(
......@@ -3212,7 +3210,7 @@ check_next_foreign:
if (added) {
/* Temporary tables can have read views and we don't
print any warning. */
if (*table->name != TEMP_TABLE_PREFIX) {
if (!table->to_be_dropped) {
ut_print_timestamp(stderr);
fputs(" InnoDB: Warning: MySQL is"
" trying to drop table ", stderr);
......@@ -3223,8 +3221,6 @@ check_next_foreign:
"InnoDB: Adding the table to the"
" background drop queue.\n",
stderr);
} else {
ut_a(table->to_be_dropped);
}
/* We return DB_SUCCESS to MySQL though the drop will
......@@ -4171,99 +4167,6 @@ row_check_table_for_mysql(
return(ret);
}
/***************************************************************************
Writes information to an undo log about dictionary operation e.g.
rename_table, create_table, create_index, drop table. This information
is used in a rollback of the transaction. */
static
ulint
row_undo_report_dict_operation(
/*===========================*/
/* out: DB_SUCCESS or error code */
ulint op_type, /* in: TRX_UNDO_TABLE_CREATE_OP,
TRX_UNDO_TABLE_RENAME_OP,
TRX_UNDO_TABLE_DROP_OP, or
TRX_UNDO_INDEX_CREATE_OP */
trx_t* trx, /* in: transaction */
dict_index_t* index, /* in:
if TRX_UNDO_INDEX_CREATE_OP
index to be created*/
const char* table_name, /* in: table name or NULL, used in
create table, rename table and
drop table*/
const char* old_table_name, /* in: old table name or NULL. */
const char* tmp_table_name) /* in: the intermediate name or NULL */
{
dulint roll_ptr;
return trx_undo_report_dict_operation(
op_type, trx, index, table_name, old_table_name,
tmp_table_name, &roll_ptr);
}
/***************************************************************************
Writes information to an undo log about dictionary operation, create_table.
This information is used in a rollback of the transaction. */
ulint
row_undo_report_create_table_dict_operation(
/*========================================*/
/* out: DB_SUCCESS or error code */
trx_t* trx, /* in: transaction */
const char* table_name) /* in: table name to create. */
{
return row_undo_report_dict_operation(
TRX_UNDO_TABLE_CREATE_OP, trx, NULL, table_name, NULL, NULL);
}
/***************************************************************************
Writes information to an undo log about dictionary operation, create_index.
This information is used in a rollback of the transaction. */
ulint
row_undo_report_create_index_dict_operation(
/*========================================*/
/* out: DB_SUCCESS or error code */
trx_t* trx, /* in: transaction */
dict_index_t* index) /* in: index created. */
{
return row_undo_report_dict_operation(
TRX_UNDO_INDEX_CREATE_OP, trx, index, NULL, NULL, NULL);
}
/***************************************************************************
Writes information to an undo log about dictionary operation, rename_table.
This information is used in a rollback of the transaction. */
ulint
row_undo_report_rename_table_dict_operation(
/*========================================*/
/* out: DB_SUCCESS or error code */
trx_t* trx, /* in: transaction */
const char* from_table_name,/* in: rename from table table. */
const char* to_table_name, /* in: rename to table name. */
const char* tmp_table_name) /* in: intermediate name for table */
{
return row_undo_report_dict_operation(
TRX_UNDO_TABLE_RENAME_OP, trx, NULL,
to_table_name, from_table_name, tmp_table_name);
}
/***************************************************************************
Writes information to an undo log about dictionary operation, drop table.
This information is used in a rollback of the transaction. */
ulint
row_undo_report_drop_table_dict_operation(
/*======================================*/
/* out: DB_SUCCESS or error code */
trx_t* trx, /* in: query thread */
const char* table_name) /* in: table name dropped */
{
return row_undo_report_dict_operation(
TRX_UNDO_TABLE_DROP_OP, trx, NULL, table_name, NULL, NULL);
}
/*************************************************************************
Create query graph for an index creation */
......@@ -4278,7 +4181,7 @@ row_create_index_graph_for_mysql(
ind_node_t* node; /* Index creation node */
mem_heap_t* heap; /* Memory heap */
que_thr_t* thr; /* Query thread */
ulint err = DB_SUCCESS;
ulint err;
ut_ad(trx && index);
......
......@@ -28,7 +28,6 @@ Created 2/25/1997 Heikki Tuuri
#include "que0que.h"
#include "ibuf0ibuf.h"
#include "log0log.h"
#include "row0mysql.h"
/*******************************************************************
Removes a clustered index record. The pcur in node was positioned on the
......@@ -213,36 +212,6 @@ retry:
return(err);
}
/***************************************************************
Parses the rec_type undo record. */
byte*
row_undo_ins_parse_rec_type_and_table_id(
/*=====================================*/
/* out: ptr to next field to parse */
undo_node_t* node, /* in: row undo node */
dulint* table_id) /* out: table id */
{
byte* ptr;
dulint undo_no;
ulint type;
ulint dummy;
ibool dummy_extern;
ut_ad(node && node->trx);
ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &dummy,
&dummy_extern, &undo_no, table_id);
node->rec_type = type;
if (node->rec_type == TRX_UNDO_DICTIONARY_REC) {
node->trx->dict_operation = TRUE;
}
return ptr;
}
/***************************************************************
Parses the row reference and other info in a fresh insert undo record. */
static
......@@ -251,33 +220,32 @@ row_undo_ins_parse_undo_rec(
/*========================*/
undo_node_t* node) /* in: row undo node */
{
dict_index_t* clust_index;
byte* ptr;
dulint undo_no;
dulint table_id;
ulint type;
ulint dummy;
ibool dummy_extern;
ut_ad(node);
ptr = row_undo_ins_parse_rec_type_and_table_id(node, &table_id);
ut_ad(node->rec_type == TRX_UNDO_INSERT_REC
|| node->rec_type == TRX_UNDO_DICTIONARY_REC);
if (node->rec_type == TRX_UNDO_INSERT_REC) {
node->table = dict_table_get_on_id(table_id, node->trx);
ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &dummy,
&dummy_extern, &undo_no, &table_id);
ut_ad(type == TRX_UNDO_INSERT_REC);
node->rec_type = type;
/* If we can't find the table or .ibd file is missing,
we skip the UNDO.*/
if (node->table == NULL || node->table->ibd_file_missing) {
node->table = dict_table_get_on_id(table_id, node->trx);
node->table = NULL;
} else {
dict_index_t* clust_index;
clust_index = dict_table_get_first_index(node->table);
/* Skip the UNDO if we can't find the table or the .ibd file. */
if (UNIV_UNLIKELY(node->table == NULL)) {
} else if (UNIV_UNLIKELY(node->table->ibd_file_missing)) {
node->table = NULL;
} else {
clust_index = dict_table_get_first_index(node->table);
ptr = trx_undo_rec_get_row_ref(
ptr, clust_index, &node->ref, node->heap);
}
ptr = trx_undo_rec_get_row_ref(
ptr, clust_index, &node->ref, node->heap);
}
}
......@@ -297,14 +265,6 @@ row_undo_ins(
row_undo_ins_parse_undo_rec(node);
/* Dictionary records are undone in a separate function */
if (node->rec_type == TRX_UNDO_DICTIONARY_REC) {
return(row_undo_build_dict_undo_list(node));
}
if (!node->table || !row_undo_search_clust_to_pcur(node)) {
trx_undo_rec_release(node->trx, node->undo_no);
......
......@@ -125,7 +125,6 @@ row_undo_node_create(
undo->state = UNDO_NODE_FETCH_NEXT;
undo->trx = trx;
undo->rec_sub_type = TRX_UNDO_NULL_REC;
btr_pcur_init(&(undo->pcur));
......@@ -352,360 +351,3 @@ row_undo_step(
return(thr);
}
/***************************************************************
Parses the info in a fresh insert undo record containing a
dictionary change. */
static
ulint
row_undo_dictionary_parse_undo_rec(
/*===============================*/
/* out: DB_SUCCESS or DB_ERROR */
undo_node_t* node) /* in: row undo node */
{
byte* ptr;
dulint table_id;
dulint index_id;
ulint len;
ut_ad(node);
node->rec_type = trx_undo_rec_get_type(node->undo_rec);
node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec);
ptr = trx_undo_rec_get_ptr(node->undo_rec, node->undo_no);
ut_a(node->rec_type == TRX_UNDO_DICTIONARY_REC);
/* Read dictionary rec sub type */
node->rec_sub_type = mach_read_from_1(ptr);
ptr++;
/* Parse subtype parameters */
switch (node->rec_sub_type) {
case TRX_UNDO_INDEX_CREATE_REC:
table_id = mach_dulint_read_much_compressed(ptr);
len = mach_dulint_get_much_compressed_size(table_id);
ptr += len;
index_id = mach_dulint_read_much_compressed(ptr);
len = mach_dulint_get_much_compressed_size(index_id);
ptr += len;
node->table = dict_table_get_on_id(table_id, node->trx);
node->index = NULL;
if (!node->table) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: [Error]: Table %lu %lu not found "
"in index create undo rec\n",
(ulong) ut_dulint_get_high(table_id),
(ulong) ut_dulint_get_low(table_id));
goto err_exit;
} else if (ut_dulint_is_zero(index_id)) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: [Error]: Index id missing from "
"index create undo rec\n");
err_exit:
mutex_enter(&kernel_mutex);
trx_print(stderr, node->trx, 1024);
mutex_exit(&kernel_mutex);
return(DB_ERROR);
} else {
node->index = dict_index_get_on_id_low(
node->table, index_id);
}
if (node->table->ibd_file_missing || !node->index) {
/* We skip undo operations to missing .ibd files
and missing indexes */
node->table = NULL;
node->index = NULL;
return(DB_SUCCESS);
}
break;
case TRX_UNDO_TABLE_CREATE_REC:
case TRX_UNDO_TABLE_DROP_REC:
len = strlen((char *)ptr) + 1;
node->new_table_name = mem_heap_strdup(node->heap, (char *)ptr);
ptr += len;
ut_ad(*node->new_table_name == TEMP_TABLE_PREFIX);
break;
case TRX_UNDO_TABLE_RENAME_REC:
len = strlen((char *)ptr) + 1;
node->new_table_name = mem_heap_strdup(node->heap, (char *)ptr);
ptr += len;
ut_ad(*node->new_table_name == TEMP_TABLE_PREFIX);
len = strlen((char *)ptr) + 1;
node->old_table_name = mem_heap_strdup(node->heap, (char *)ptr);
ptr += len;
len = strlen((char *)ptr) + 1;
node->tmp_table_name = mem_heap_strdup(node->heap, (char *)ptr);
ptr += len;
ut_ad(*node->tmp_table_name == TEMP_TABLE_PREFIX);
break;
default:
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: [Error]: Undefined rec_sub_type = %lu at "
"row_undo_dictionary_parse_undo_rec\n",
(ulong)node->rec_sub_type);
mutex_enter(&kernel_mutex);
trx_print(stderr, node->trx, 1024);
mutex_exit(&kernel_mutex);
return(DB_ERROR);
}
return(DB_SUCCESS);
}
/***************************************************************
Currently we gather all the information that is required to do the
UNDO. The actual UNDO is done later in row_undo_dictionary(). */
ulint
row_undo_build_dict_undo_list(
/*==========================*/
/* out: DB_SUCCESS or error code */
undo_node_t* node) /* in: row undo node */
{
trx_t* trx;
dict_undo_t* dict_undo;
ulint err = DB_SUCCESS;
ibool locked_dictionary = FALSE;
ibool thawed_dictionary = FALSE;
ut_ad(node);
ut_ad(node->state == UNDO_NODE_INSERT);
ut_a(node->trx->dict_operation);
err = row_undo_dictionary_parse_undo_rec(node);
if (err != DB_SUCCESS) {
goto func_exit;
}
trx = node->trx;
if (trx->dict_operation_lock_mode == RW_S_LATCH) {
row_mysql_unfreeze_data_dictionary(trx);
thawed_dictionary = TRUE;
}
if (trx->dict_operation_lock_mode == 0
|| trx->dict_operation_lock_mode != RW_X_LATCH) {
row_mysql_lock_data_dictionary(trx);
locked_dictionary = TRUE;
}
/* We will do our own deletes */
trx->table_id = ut_dulint_zero;
if (trx->dict_undo_list == NULL) {
dict_undo_create_list(trx);
}
/* Create an element and append to the list */
dict_undo = dict_undo_create_element(trx);
dict_undo->op_type = node->rec_sub_type;
switch (node->rec_sub_type) {
case TRX_UNDO_INDEX_CREATE_REC:
if (node->table && node->index) {
ut_a(node->index->table == node->table);
dict_undo->data.index = node->index;
} else {
dict_undo->data.index = NULL;
}
break;
case TRX_UNDO_TABLE_DROP_REC:
case TRX_UNDO_TABLE_CREATE_REC:
dict_undo->data.table.old_table = dict_table_get_low(
node->new_table_name);
break;
case TRX_UNDO_TABLE_RENAME_REC:
dict_undo->data.table.old_table = dict_table_get_low(
node->old_table_name);
dict_undo->data.table.tmp_table = dict_table_get_low(
node->tmp_table_name);
dict_undo->data.table.new_table = dict_table_get_low(
node->new_table_name);
if (dict_undo->data.table.tmp_table
&& dict_undo->data.table.old_table
&& dict_undo->data.table.new_table) {
/* This can't happen */
ut_error;
}
break;
default:
ut_error;
break;
}
if (locked_dictionary) {
row_mysql_unlock_data_dictionary(trx);
}
if (thawed_dictionary) {
row_mysql_freeze_data_dictionary(trx);
}
func_exit:
trx_undo_rec_release(node->trx, node->undo_no);
return(err);
}
/***************************************************************
Undo or redo a dictionary change. */
void
row_undo_dictionary(
/*================*/
trx_t* trx, /* in: transaction */
dict_undo_t* dict_undo) /* in: dict undo info */
{
ulint err;
switch (dict_undo->op_type) {
case TRX_UNDO_INDEX_CREATE_REC:
if (dict_undo->data.index) {
row_merge_drop_index(dict_undo->data.index,
dict_undo->data.index->table,
trx);
}
break;
/* TODO: We are REDOing the DROP ? */
case TRX_UNDO_TABLE_DROP_REC:
case TRX_UNDO_TABLE_CREATE_REC:
if (dict_undo->data.table.old_table) {
err = row_drop_table_for_mysql_no_commit(
dict_undo->data.table.old_table->name,
trx, FALSE);
ut_a(err == DB_SUCCESS);
}
break;
case TRX_UNDO_TABLE_RENAME_REC:
if (!dict_undo->data.table.new_table) {
/* Old name to tmp name succeeded and new name to old
name succeeded too. We have to be very careful here as
the user could loose the entire table if not done
carefully.*/
ut_ad(dict_undo->data.table.old_table);
err = row_rename_table_for_mysql(
dict_undo->data.table.old_table->name,
dict_undo->data.table.new_table->name,
trx, FALSE);
ut_a(err == DB_SUCCESS);
err = row_rename_table_for_mysql(
dict_undo->data.table.tmp_table->name,
dict_undo->data.table.old_table->name,
trx, FALSE);
ut_a(err == DB_SUCCESS);
err = row_drop_table_for_mysql_no_commit(
dict_undo->data.table.new_table->name,
trx, FALSE);
ut_a(err == DB_SUCCESS);
} else if (dict_undo->data.table.old_table) {
/* Rename to tmp failed.*/
ut_a(!dict_undo->data.table.tmp_table);
if (dict_undo->data.table.new_table) {
err = row_drop_table_for_mysql_no_commit(
dict_undo->data.table.new_table->name,
trx, FALSE);
ut_a(err == DB_SUCCESS);
}
} else if (dict_undo->data.table.tmp_table) {
/* Rename to tmp was OK. We need to UNDO it.*/
ut_ad(!dict_undo->data.table.old_table);
err = row_rename_table_for_mysql(
dict_undo->data.table.tmp_table->name,
dict_undo->data.table.old_table->name,
trx, FALSE);
ut_a(err == DB_SUCCESS);
if (dict_undo->data.table.new_table) {
err = row_drop_table_for_mysql_no_commit(
dict_undo->data.table.new_table->name,
trx, FALSE);
ut_a(err == DB_SUCCESS);
}
} else {
/* Shouldn't happen */
ut_error;
}
default:
ut_error;
}
}
This diff is collapsed.
......@@ -447,7 +447,6 @@ trx_rollback_active(
trx->mysql_process_no = os_proc_get_number();
/* TODO: Doesn't seem right */
if (trx->dict_operation) {
row_mysql_lock_data_dictionary(trx);
dictionary_locked = TRUE;
......@@ -495,23 +494,6 @@ trx_rollback_active(
ut_a(err == (int) DB_SUCCESS);
}
} else if (trx->dict_undo_list) {
dict_undo_t* dict_undo;
ut_a(trx->dict_undo_list);
for (dict_undo = UT_LIST_GET_FIRST(*trx->dict_undo_list);
dict_undo;
dict_undo = UT_LIST_GET_NEXT(node, dict_undo)) {
row_undo_dictionary(trx, dict_undo);
}
dict_undo_free_list(trx);
mutex_enter(&kernel_mutex);
trx_commit_off_kernel(trx);
mutex_exit(&kernel_mutex);
}
if (dictionary_locked) {
......@@ -1239,11 +1221,7 @@ trx_finish_rollback_off_kernel(
}
#endif /* UNIV_DEBUG */
/* If there are dict UNDO records that need to be undone then
we commit the transaction after these dictionary changes are undone.*/
if (!trx->dict_undo_list) {
trx_commit_off_kernel(trx);
}
trx_commit_off_kernel(trx);
/* Remove all TRX_SIG_TOTAL_ROLLBACK signals from the signal queue and
send reply messages to them */
......
......@@ -99,8 +99,6 @@ trx_create(
trx->dict_operation = FALSE;
trx->table_id = ut_dulint_zero;
trx->dict_undo_list = NULL;
trx->dict_redo_list = NULL;
trx->mysql_thd = NULL;
trx->mysql_query_str = NULL;
......@@ -306,8 +304,6 @@ trx_free(
trx->global_read_view = NULL;
ut_a(trx->read_view == NULL);
ut_a(trx->dict_undo_list == NULL);
ut_a(trx->dict_redo_list == NULL);
mem_free(trx);
}
......@@ -699,10 +695,6 @@ trx_commit_off_kernel(
ut_ad(mutex_own(&kernel_mutex));
/* Can't commit if we have dictionary UNDO records */
ut_a(!trx->dict_undo_list);
ut_a(!trx->dict_redo_list);
trx->must_flush_log_later = FALSE;
rseg = trx->rseg;
......
......@@ -439,35 +439,13 @@ ut_print_namel(
fwrite(name, 1, namelen, f);
#else
if (table_id) {
char* slash;
if (UNIV_UNLIKELY(*name == TEMP_TABLE_PREFIX)) {
slash = memchr(name + 1, '/', namelen);
ut_ad(slash && slash >= name + 2);
/* Database */
innobase_print_identifier(f, trx, TRUE, name + 2,
slash - (name + 2));
putc('.', f);
/* Table */
innobase_print_identifier(f, trx, TRUE, slash + 1,
namelen
- (slash - (name + 3)));
/* Identifier of temporary table */
fprintf(f, "--temporary %c--", name[1]);
return;
}
slash = memchr(name, '/', namelen);
char* slash = memchr(name, '/', namelen);
if (!slash) {
goto no_db_name;
}
/* Print the database name and table name separately. */
innobase_print_identifier(f, trx, TRUE, name, slash - name);
putc('.', f);
innobase_print_identifier(f, trx, TRUE, slash + 1,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment