1
0
mirror of https://github.com/MariaDB/server.git synced 2025-12-12 08:01:43 +03:00

branches/zip: Merge branches/fast-index-creation -r1413.

Fix some bugs.  The tests innodb and innodb-index fail, but that might
be due to an old MySQL source tree being used.
This commit is contained in:
marko
2007-04-04 11:05:33 +00:00
parent 4d6805f2a8
commit fc65b08f53
47 changed files with 8334 additions and 672 deletions

View File

@@ -383,6 +383,7 @@ dfield_print_also_hex(
const byte* data; const byte* data;
ulint len; ulint len;
ulint mtype; ulint mtype;
ulint prtype;
ulint i; ulint i;
ibool print_also_hex; ibool print_also_hex;
@@ -396,6 +397,7 @@ dfield_print_also_hex(
} }
mtype = dtype_get_mtype(dfield_get_type(dfield)); mtype = dtype_get_mtype(dfield_get_type(dfield));
prtype = dtype_get_prtype(dfield_get_type(dfield));
if ((mtype == DATA_CHAR) || (mtype == DATA_VARCHAR)) { if ((mtype == DATA_CHAR) || (mtype == DATA_VARCHAR)) {
@@ -403,11 +405,14 @@ dfield_print_also_hex(
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
int c = *data++; int c = *data++;
if (!isprint(c)) { if (!isprint(c)) {
print_also_hex = TRUE; print_also_hex = TRUE;
c = ' ';
fprintf(stderr, "\\x%02x", (unsigned char) c);
} else {
putc(c, stderr);
} }
putc(c, stderr);
} }
if (!print_also_hex) { if (!print_also_hex) {
@@ -422,13 +427,122 @@ dfield_print_also_hex(
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
fprintf(stderr, "%02lx", (ulint)*data); fprintf(stderr, "%02lx", (ulint)*data);
data++;
}
} else if (mtype == DATA_BINARY) {
data = dfield_get_data(dfield);
fputs(" Hex: ",stderr);
for (i = 0; i < len; i++) {
fprintf(stderr, "%02lx", (ulint)*data);
data++; data++;
} }
} else if (mtype == DATA_INT) { } else if (mtype == DATA_INT) {
ut_a(len == 4); /* only works for 32-bit integers */ dulint big_val;
fprintf(stderr, "%d", (int)mach_read_from_4(data));
if (len == 1) {
ulint val;
val = (ulint)mach_read_from_1(data);
if (!(prtype & DATA_UNSIGNED)) {
val &= ~0x80;
fprintf(stderr, "%ld", (long) val);
} else {
fprintf(stderr, "%lu", (ulong) val);
}
} else if (len == 2) {
ulint val;
val = (ulint)mach_read_from_2(data);
if (!(prtype & DATA_UNSIGNED)) {
val &= ~0x8000;
fprintf(stderr, "%ld", (long) val);
} else {
fprintf(stderr, "%lu", (ulong) val);
}
} else if (len == 3) {
ulint val;
val = (ulint)mach_read_from_3(data);
if (!(prtype & DATA_UNSIGNED)) {
val &= ~0x800000;
fprintf(stderr, "%ld", (long) val);
} else {
fprintf(stderr, "%lu", (ulong) val);
}
} else if (len == 4) {
ulint val;
val = (ulint)mach_read_from_4(data);
if (!(prtype & DATA_UNSIGNED)) {
val &= ~0x80000000;
fprintf(stderr, "%ld", (long) val);
} else {
fprintf(stderr, "%lu", (ulong) val);
}
} else if (len == 6) {
big_val = (dulint)mach_read_from_6(data);
fprintf(stderr, "{%lu %lu}",
ut_dulint_get_high(big_val),
ut_dulint_get_low(big_val));
} else if (len == 7) {
big_val = (dulint)mach_read_from_7(data);
fprintf(stderr, "{%lu %lu}",
ut_dulint_get_high(big_val),
ut_dulint_get_low(big_val));
} else if (len == 8) {
big_val = (dulint)mach_read_from_8(data);
fprintf(stderr, "{%lu %lu}",
ut_dulint_get_high(big_val),
ut_dulint_get_low(big_val));
} else {
fputs(" Hex: ",stderr);
for (i = 0; i < len; i++) {
fprintf(stderr, "%02lx", (ulint)*data);
data++;
}
}
} else if (mtype == DATA_SYS) {
dulint id;
if (prtype & DATA_TRX_ID) {
id = mach_read_from_6(data);
fprintf(stderr, "trx_id {%lu %lu}",
ut_dulint_get_high(id), ut_dulint_get_low(id));
} else if (prtype & DATA_ROLL_PTR) {
id = mach_read_from_7(data);
fprintf(stderr, "roll_ptr {%lu %lu}",
ut_dulint_get_high(id), ut_dulint_get_low(id));
} else if (prtype & DATA_ROW_ID) {
id = mach_read_from_6(data);
fprintf(stderr, "row_id {%lu %lu}",
ut_dulint_get_high(id), ut_dulint_get_low(id));
} else {
id = mach_dulint_read_compressed(data);
fprintf(stderr, "mix_id {%lu %lu}",
ut_dulint_get_high(id), ut_dulint_get_low(id));
}
} else { } else {
ut_error; fputs(" Hex: ",stderr);
for (i = 0; i < len; i++) {
fprintf(stderr, "%02lx", (ulint)*data);
data++;
}
} }
} }

View File

@@ -540,7 +540,11 @@ dict_build_index_def_step(
ut_ad((UT_LIST_GET_LEN(table->indexes) > 0) ut_ad((UT_LIST_GET_LEN(table->indexes) > 0)
|| dict_index_is_clust(index)); || dict_index_is_clust(index));
index->id = dict_hdr_get_new_id(DICT_HDR_INDEX_ID); /* For fast index creation we have already allocated an index id
for this index so that we could write an UNDO log record for it.*/
if (ut_dulint_is_zero(index->id)) {
index->id = dict_hdr_get_new_id(DICT_HDR_INDEX_ID);
}
/* Inherit the space id from the table; we store all indexes of a /* Inherit the space id from the table; we store all indexes of a
table in the same tablespace */ table in the same tablespace */
@@ -552,6 +556,9 @@ dict_build_index_def_step(
ins_node_set_new_row(node->ind_def, row); ins_node_set_new_row(node->ind_def, row);
/* Note that the index was created by this transaction. */
index->trx_id = trx->id;
return(DB_SUCCESS); return(DB_SUCCESS);
} }

View File

@@ -26,6 +26,7 @@ Created 1/8/1996 Heikki Tuuri
#include "pars0sym.h" #include "pars0sym.h"
#include "que0que.h" #include "que0que.h"
#include "rem0cmp.h" #include "rem0cmp.h"
#include "row0merge.h"
#ifndef UNIV_HOTBACKUP #ifndef UNIV_HOTBACKUP
# include "m_ctype.h" /* my_isspace() */ # include "m_ctype.h" /* my_isspace() */
#endif /* !UNIV_HOTBACKUP */ #endif /* !UNIV_HOTBACKUP */
@@ -366,19 +367,6 @@ dict_table_get_next_index_noninline(
return(dict_table_get_next_index(index)); return(dict_table_get_next_index(index));
} }
/**************************************************************************
Returns an index object. */
dict_index_t*
dict_table_get_index_noninline(
/*===========================*/
/* out: index, NULL if does not exist */
dict_table_t* table, /* in: table */
const char* name) /* in: index name */
{
return(dict_table_get_index(table, name));
}
/************************************************************************** /**************************************************************************
Returns a column's name. */ Returns a column's name. */
@@ -539,6 +527,33 @@ dict_table_autoinc_update(
mutex_exit(&(table->autoinc_mutex)); mutex_exit(&(table->autoinc_mutex));
} }
/**************************************************************************
Looks for an index with the given table and index id.
NOTE that we do not reserve the dictionary mutex. */
dict_index_t*
dict_index_get_on_id_low(
/*=====================*/
/* out: index or NULL if not found from cache */
dict_table_t* table, /* in: table */
dulint id) /* in: index id */
{
dict_index_t* index;
index = dict_table_get_first_index(table);
while (index) {
if (0 == ut_dulint_cmp(id, index->id)) {
/* Found */
return(index);
}
index = dict_table_get_next_index(index);
}
return(NULL);
}
/************************************************************************ /************************************************************************
Looks for column n in an index. */ Looks for column n in an index. */
@@ -806,7 +821,7 @@ dict_table_get(
does not exist */ does not exist */
const char* table_name, /* in: table name */ const char* table_name, /* in: table name */
ibool inc_mysql_count) ibool inc_mysql_count)
/* in: whether to increment the open /* in: whether to increment the open
handle count on the table */ handle count on the table */
{ {
dict_table_t* table; dict_table_t* table;
@@ -1358,20 +1373,6 @@ dict_index_add_to_cache(
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
ut_ad(mem_heap_validate(index->heap)); ut_ad(mem_heap_validate(index->heap));
#ifdef UNIV_DEBUG
{
dict_index_t* index2;
index2 = UT_LIST_GET_FIRST(table->indexes);
while (index2 != NULL) {
ut_ad(ut_strcmp(index->name, index2->name) != 0);
index2 = UT_LIST_GET_NEXT(indexes, index2);
}
}
#endif /* UNIV_DEBUG */
ut_a(!dict_index_is_clust(index) ut_a(!dict_index_is_clust(index)
|| UT_LIST_GET_LEN(table->indexes) == 0); || UT_LIST_GET_LEN(table->indexes) == 0);
@@ -1412,7 +1413,10 @@ dict_index_add_to_cache(
dict_index_get_nth_field(new_index, i)->col->ord_part = 1; dict_index_get_nth_field(new_index, i)->col->ord_part = 1;
} }
new_index->page = (unsigned int) page_no; new_index->stat_index_size = 1;
new_index->stat_n_leaf_pages = 1;
new_index->page = page_no;
rw_lock_create(&new_index->lock, SYNC_INDEX_TREE); rw_lock_create(&new_index->lock, SYNC_INDEX_TREE);
if (!UNIV_UNLIKELY(new_index->type & DICT_UNIVERSAL)) { if (!UNIV_UNLIKELY(new_index->type & DICT_UNIVERSAL)) {
@@ -1497,7 +1501,7 @@ dict_index_find_cols(
/* It is an error not to find a matching column. */ /* It is an error not to find a matching column. */
ut_error; ut_error;
found: found:
; ;
} }
} }
@@ -1873,18 +1877,92 @@ dict_index_build_internal_non_clust(
Checks if a table is referenced by foreign keys. */ Checks if a table is referenced by foreign keys. */
ibool ibool
dict_table_referenced_by_foreign_key( dict_table_is_referenced_by_foreign_key(
/*=================================*/ /*====================================*/
/* out: TRUE if table is referenced by a /* out: TRUE if table is referenced
foreign key */ by a foreign key */
dict_table_t* table) /* in: InnoDB table */ const dict_table_t* table) /* in: InnoDB table */
{ {
if (UT_LIST_GET_LEN(table->referenced_list) > 0) { return(UT_LIST_GET_LEN(table->referenced_list) > 0);
}
return(TRUE); /*************************************************************************
Check if the index is referenced by a foreign key, if TRUE return foreign
else return NULL */
dict_foreign_t*
dict_table_get_referenced_constraint(
/*=================================*/
/* out: pointer to foreign key struct if index
is defined for foreign key, otherwise NULL */
dict_table_t* table, /* in: InnoDB table */
dict_index_t* index) /* in: InnoDB index */
{
dict_foreign_t* foreign = NULL;
ut_ad(index && table);
/* If the referenced list is empty, nothing to do */
if (UT_LIST_GET_LEN(table->referenced_list) == 0) {
return(NULL);
} }
return(FALSE); foreign = UT_LIST_GET_FIRST(table->referenced_list);
while (foreign) {
if (foreign->referenced_index == index
|| foreign->referenced_index == index) {
return(foreign);
}
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
}
return(NULL);
}
/*************************************************************************
Checks if a index is defined for a foreign key constraint. Index is a part
of a foreign key constraint if the index is referenced by foreign key
or index is a foreign key index. */
dict_foreign_t*
dict_table_get_foreign_constraint(
/*==============================*/
/* out: pointer to foreign key struct if index
is defined for foreign key, otherwise NULL */
dict_table_t* table, /* in: InnoDB table */
dict_index_t* index) /* in: InnoDB index */
{
dict_foreign_t* foreign = NULL;
ut_ad(index && table);
/* If list empty then nothgin to do */
if (UT_LIST_GET_LEN(table->foreign_list) == 0) {
return(NULL);
}
/* Check whether this index is defined for a foreign key */
foreign = UT_LIST_GET_FIRST(table->foreign_list);
while (foreign) {
if (foreign->foreign_index == index
|| foreign->referenced_index == index) {
return(foreign);
}
foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
}
return(NULL);
} }
/************************************************************************* /*************************************************************************
@@ -1967,7 +2045,8 @@ dict_foreign_find(
/************************************************************************* /*************************************************************************
Tries to find an index whose first fields are the columns in the array, Tries to find an index whose first fields are the columns in the array,
in the same order. */ in the same order and is not marked for deletion and is not the same
as types_idx. */
static static
dict_index_t* dict_index_t*
dict_foreign_find_index( dict_foreign_find_index(
@@ -1986,16 +2065,23 @@ dict_foreign_find_index(
be declared NOT NULL */ be declared NOT NULL */
{ {
dict_index_t* index; dict_index_t* index;
dict_field_t* field;
const char* col_name;
ulint i;
index = dict_table_get_first_index(table); index = dict_table_get_first_index(table);
while (index != NULL) { while (index != NULL) {
if (dict_index_get_n_fields(index) >= n_cols) { /* Ignore matches that refer to the same instance
or the index is to be dropped */
if (index->to_be_dropped || types_idx == index) {
goto next_rec;
} else if (dict_index_get_n_fields(index) >= n_cols) {
ulint i;
for (i = 0; i < n_cols; i++) { for (i = 0; i < n_cols; i++) {
dict_field_t* field;
const char* col_name;
field = dict_index_get_nth_field(index, i); field = dict_index_get_nth_field(index, i);
col_name = dict_table_get_col_name( col_name = dict_table_get_col_name(
@@ -2036,12 +2122,73 @@ dict_foreign_find_index(
} }
} }
next_rec:
index = dict_table_get_next_index(index); index = dict_table_get_next_index(index);
} }
return(NULL); return(NULL);
} }
/*************************************************************************
Tries to find an index whose fields match exactly, in the same order. If
more than one index is found then return the index with the higher id.*/
static
dict_index_t*
dict_find_index_by_max_id(
/*======================*/
/* out: matching index, NULL if not found */
dict_table_t* table, /* in: table */
const char* name, /* in: the index name to find */
const char** columns,/* in: array of column names */
ulint n_cols) /* in: number of columns */
{
dict_index_t* index;
dict_index_t* found;
found = NULL;
index = dict_table_get_first_index(table);
while (index != NULL) {
if (ut_strcmp(index->name, name) == 0
&& dict_index_get_n_ordering_defined_by_user(index)
== n_cols) {
ulint i;
for (i = 0; i < n_cols; i++) {
dict_field_t* field;
const char* col_name;
field = dict_index_get_nth_field(index, i);
col_name = dict_table_get_col_name(
table, dict_col_get_no(field->col));
if (0 != innobase_strcasecmp(
columns[i], col_name)) {
break;
}
}
if (i == n_cols) {
/* We found a matching index, select
the index with the higher id*/
if (!found
|| ut_dulint_cmp(index->id, found->id) > 0) {
found = index;
}
}
}
index = dict_table_get_next_index(index);
}
return(found);
}
/************************************************************************** /**************************************************************************
Report an error in a foreign key definition. */ Report an error in a foreign key definition. */
static static
@@ -4013,6 +4160,7 @@ dict_index_print_low(
{ {
ib_longlong n_vals; ib_longlong n_vals;
ulint i; ulint i;
const char* type_string;
ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(mutex_own(&(dict_sys->mutex)));
@@ -4023,6 +4171,14 @@ dict_index_print_low(
n_vals = index->stat_n_diff_key_vals[1]; n_vals = index->stat_n_diff_key_vals[1];
} }
if (index->type & DICT_CLUSTERED) {
type_string = "clustered index";
} else if (index->type & DICT_UNIQUE) {
type_string = "unique index";
} else {
type_string = "secondary index";
}
fprintf(stderr, fprintf(stderr,
" INDEX: name %s, id %lu %lu, fields %lu/%lu," " INDEX: name %s, id %lu %lu, fields %lu/%lu,"
" uniq %lu, type %lu\n" " uniq %lu, type %lu\n"
@@ -4261,6 +4417,7 @@ dict_print_info_on_foreign_keys(
/************************************************************************ /************************************************************************
Displays the names of the index and the table. */ Displays the names of the index and the table. */
void void
dict_index_name_print( dict_index_name_print(
/*==================*/ /*==================*/
@@ -4273,3 +4430,417 @@ dict_index_name_print(
fputs(" of table ", file); fputs(" of table ", file);
ut_print_name(file, trx, TRUE, index->table_name); ut_print_name(file, trx, TRUE, index->table_name);
} }
/**************************************************************************
Get index by name */
dict_index_t*
dict_table_get_index_on_name(
/*=========================*/
/* out: index, NULL if does not exist */
dict_table_t* table, /* in: table */
const char* name) /* in: name of the index to find */
{
dict_index_t* index;
index = dict_table_get_first_index(table);
while (index != NULL) {
if (ut_strcmp(index->name, name) == 0) {
return(index);
}
index = dict_table_get_next_index(index);
}
return(NULL);
}
/**************************************************************************
Find and index that is equivalent to the one passed in. */
dict_index_t*
dict_table_find_equivalent_index(
/*=============================*/
dict_table_t* table, /* in/out: table */
dict_index_t* index) /* in: index to match */
{
ulint i;
const char** column_names;
dict_index_t* equiv_index;
if (UT_LIST_GET_LEN(table->foreign_list) == 0) {
return(NULL);
}
column_names = mem_alloc(index->n_fields * sizeof *column_names);
/* Convert the column names to the format & type accepted by the find
index function */
for (i = 0; i < index->n_fields; i++) {
column_names[i] = index->fields[i].name;
}
equiv_index = dict_foreign_find_index(
table, (const char**)column_names, index->n_fields,
index, TRUE, FALSE);
mem_free(column_names);
return(equiv_index);
}
/**************************************************************************
Replace the index passed in with another equivalent index in the tables
foreign key list. */
void
dict_table_replace_index_in_foreign_list(
/*=====================================*/
dict_table_t* table, /* in/out: table */
dict_index_t* index) /* in: index to be replaced */
{
dict_index_t* new_index;
new_index = dict_table_find_equivalent_index(table, index);
/* If match found */
if (new_index) {
dict_foreign_t* foreign;
ut_a(new_index != index);
foreign = UT_LIST_GET_FIRST(table->foreign_list);
/* If the list is not empty then this should hold */
ut_a(foreign);
/* Iterate over the foreign index list and replace the index
passed in with the new index */
while (foreign) {
if (foreign->foreign_index == index) {
foreign->foreign_index = new_index;
}
foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
}
}
}
/**************************************************************************
In case there is more than one index with the same name return the index
with the min(id). */
dict_index_t*
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 */
{
dict_index_t* index;
dict_index_t* min_index; /* Index with matching name and min(id) */
min_index = NULL;
index = dict_table_get_first_index(table);
while (index != NULL) {
if (ut_strcmp(index->name, name) == 0) {
if (!min_index
|| ut_dulint_cmp(index->id, min_index->id) < 0) {
min_index = index;
}
}
index = dict_table_get_next_index(index);
}
return(min_index);
}
/**************************************************************************
Returns an index object by matching on the name and column names and
if more than one index matches return the index with the max id */
dict_index_t*
dict_table_get_index_by_max_id(
/*===========================*/
/* out: index, NULL if does not exist */
dict_table_t* table, /* in: table */
const char* name, /* in: index name to find*/
const char** column_names, /* in: column names to match */
ulint n_cols) /* in: number of columns */
{
/* Find an exact match with the passed in index */
return(dict_find_index_by_max_id(table, name, column_names, n_cols));
}
/**************************************************************************
Check for duplicate index entries in a table [using the index name] */
#ifdef UNIV_DEBUG
void
dict_table_check_for_dup_indexes(
/*=============================*/
dict_table_t* table) /* in: Check for dup indexes in this table */
{
/* Check for duplicates, ignoring indexes that are marked
as to be dropped */
dict_index_t* index1;
dict_index_t* index2;
/* The primary index _must_ exist */
ut_a(UT_LIST_GET_LEN(table->indexes) > 0);
index1 = UT_LIST_GET_FIRST(table->indexes);
index2 = UT_LIST_GET_NEXT(indexes, index1);
while (index1 && index2) {
while (index2) {
if (!index2->to_be_dropped) {
ut_ad(ut_strcmp(index1->name, index2->name));
}
index2 = UT_LIST_GET_NEXT(indexes, index2);
}
index1 = UT_LIST_GET_NEXT(indexes, index1);
index2 = UT_LIST_GET_NEXT(indexes, index1);
}
}
#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_alloc(sizeof(*dict_undo));
memset(dict_undo, '\0', 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_alloc(sizeof(*dict_redo));
memset(dict_redo, '\0', 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 */
ibool commit_flag) /* in: UNUSED */
{
dict_redo_t* dict_redo;
ulint err = DB_SUCCESS;
ut_a(trx->dict_redo_list);
ut_a(commit_flag);
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);
#ifdef UNIV_DEBUG
fprintf(stderr, "Renaming index: %s\n", index->name);
#endif /* UNIV_DEBUG */
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);
}

View File

@@ -476,32 +476,12 @@ dict_load_columns(
mtr_commit(&mtr); mtr_commit(&mtr);
} }
/************************************************************************
Report that an index field or index for a table has been delete marked. */
static
void
dict_load_report_deleted_index(
/*===========================*/
const char* name, /* in: table name */
ulint field) /* in: index field, or ULINT_UNDEFINED */
{
fprintf(stderr, "InnoDB: Error: data dictionary entry"
" for table %s is corrupt!\n", name);
if (field != ULINT_UNDEFINED) {
fprintf(stderr,
"InnoDB: Index field %lu is delete marked.\n", field);
} else {
fputs("InnoDB: An index is delete marked.\n", stderr);
}
}
/************************************************************************ /************************************************************************
Loads definitions for index fields. */ Loads definitions for index fields. */
static static
void void
dict_load_fields( dict_load_fields(
/*=============*/ /*=============*/
dict_table_t* table, /* in: table */
dict_index_t* index, /* in: index whose fields to load */ dict_index_t* index, /* in: index whose fields to load */
mem_heap_t* heap) /* in: memory heap for temporary storage */ mem_heap_t* heap) /* in: memory heap for temporary storage */
{ {
@@ -543,13 +523,18 @@ dict_load_fields(
rec = btr_pcur_get_rec(&pcur); rec = btr_pcur_get_rec(&pcur);
ut_a(btr_pcur_is_on_user_rec(&pcur, &mtr)); ut_a(btr_pcur_is_on_user_rec(&pcur, &mtr));
/* There could be delete marked records in SYS_FIELDS
because SYS_FIELDS.INDEX_ID can be updated
by ALTER TABLE ADD INDEX. */
if (rec_get_deleted_flag(rec, 0)) { if (rec_get_deleted_flag(rec, 0)) {
dict_load_report_deleted_index(table->name, i);
goto next_rec;
} }
field = rec_get_nth_field_old(rec, 0, &len); field = rec_get_nth_field_old(rec, 0, &len);
ut_ad(len == 8); ut_ad(len == 8);
ut_a(ut_memcmp(buf, field, len) == 0);
field = rec_get_nth_field_old(rec, 1, &len); field = rec_get_nth_field_old(rec, 1, &len);
ut_a(len == 4); ut_a(len == 4);
@@ -584,6 +569,7 @@ dict_load_fields(
(char*) field, len), (char*) field, len),
prefix_len); prefix_len);
next_rec:
btr_pcur_move_to_next_user_rec(&pcur, &mtr); btr_pcur_move_to_next_user_rec(&pcur, &mtr);
} }
@@ -662,16 +648,9 @@ dict_load_indexes(
if (ut_memcmp(buf, field, len) != 0) { if (ut_memcmp(buf, field, len) != 0) {
break; break;
} } else if (rec_get_deleted_flag(rec, 0)) {
/* Skip delete marked records */
if (rec_get_deleted_flag(rec, 0)) { goto next_rec;
dict_load_report_deleted_index(table->name,
ULINT_UNDEFINED);
btr_pcur_close(&pcur);
mtr_commit(&mtr);
return(FALSE);
} }
field = rec_get_nth_field_old(rec, 1, &len); field = rec_get_nth_field_old(rec, 1, &len);
@@ -714,12 +693,15 @@ dict_load_indexes(
if ((type & DICT_CLUSTERED) == 0 if ((type & DICT_CLUSTERED) == 0
&& NULL == dict_table_get_first_index(table)) { && NULL == dict_table_get_first_index(table)) {
fprintf(stderr, if (*table->name != TEMP_TABLE_PREFIX) {
"InnoDB: Error: trying to load index %s"
" for table %s\n" fprintf(stderr,
"InnoDB: but the first index" "InnoDB: Error: trying to"
" is not clustered!\n", " load index %s for table %s\n"
name_buf, table->name); "InnoDB: but the first index"
" is not clustered!\n",
name_buf, table->name);
}
btr_pcur_close(&pcur); btr_pcur_close(&pcur);
mtr_commit(&mtr); mtr_commit(&mtr);
@@ -741,10 +723,11 @@ dict_load_indexes(
space, type, n_fields); space, type, n_fields);
index->id = id; index->id = id;
dict_load_fields(table, index, heap); dict_load_fields(index, heap);
dict_index_add_to_cache(table, index, page_no); dict_index_add_to_cache(table, index, page_no);
} }
next_rec:
btr_pcur_move_to_next_user_rec(&pcur, &mtr); btr_pcur_move_to_next_user_rec(&pcur, &mtr);
} }

View File

@@ -62,6 +62,7 @@ dict_mem_table_create(
table->n_foreign_key_checks_running = 0; table->n_foreign_key_checks_running = 0;
table->cached = FALSE; table->cached = FALSE;
table->to_be_dropped = 0;
table->cols = mem_heap_alloc(heap, (n_cols + DATA_N_SYS_COLS) table->cols = mem_heap_alloc(heap, (n_cols + DATA_N_SYS_COLS)
* sizeof(dict_col_t)); * sizeof(dict_col_t));
@@ -75,6 +76,7 @@ dict_mem_table_create(
UT_LIST_INIT(table->locks); UT_LIST_INIT(table->locks);
UT_LIST_INIT(table->foreign_list); UT_LIST_INIT(table->foreign_list);
UT_LIST_INIT(table->referenced_list); UT_LIST_INIT(table->referenced_list);
UT_LIST_INIT(table->prebuilts);
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
table->does_not_fit_in_memory = FALSE; table->does_not_fit_in_memory = FALSE;
@@ -236,6 +238,7 @@ dict_mem_index_create(
heap = mem_heap_create(DICT_HEAP_SIZE); heap = mem_heap_create(DICT_HEAP_SIZE);
index = mem_heap_alloc(heap, sizeof(dict_index_t)); index = mem_heap_alloc(heap, sizeof(dict_index_t));
index->id = ut_dulint_create(0, 0);
index->heap = heap; index->heap = heap;
index->type = type; index->type = type;
@@ -253,6 +256,8 @@ dict_mem_index_create(
index->stat_n_diff_key_vals = NULL; index->stat_n_diff_key_vals = NULL;
index->cached = FALSE; index->cached = FALSE;
index->to_be_dropped = FALSE;
index->trx_id = ut_dulint_create(0, 0);
memset(&index->lock, 0, sizeof index->lock); memset(&index->lock, 0, sizeof index->lock);
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
index->magic_n = DICT_INDEX_MAGIC_N; index->magic_n = DICT_INDEX_MAGIC_N;

File diff suppressed because it is too large Load Diff

View File

@@ -85,13 +85,9 @@ class ha_innobase: public handler
const char *index_type(uint key_number) { return "BTREE"; } const char *index_type(uint key_number) { return "BTREE"; }
const char** bas_ext() const; const char** bas_ext() const;
ulonglong table_flags() const { return int_table_flags; } ulonglong table_flags() const { return int_table_flags; }
ulong index_flags(uint idx, uint part, bool all_parts) const ulong index_flags(uint idx, uint part, bool all_parts) const {
{ return(HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER
return (HA_READ_NEXT | | HA_READ_RANGE | HA_KEYREAD_ONLY);
HA_READ_PREV |
HA_READ_ORDER |
HA_READ_RANGE |
HA_KEYREAD_ONLY);
} }
uint max_supported_keys() const { return MAX_KEY; } uint max_supported_keys() const { return MAX_KEY; }
/* An InnoDB page must store >= 2 keys; /* An InnoDB page must store >= 2 keys;
@@ -117,6 +113,7 @@ class ha_innobase: public handler
void try_semi_consistent_read(bool yes); void try_semi_consistent_read(bool yes);
void unlock_row(); void unlock_row();
bool is_index_available(uint index);
int index_init(uint index, bool sorted); int index_init(uint index, bool sorted);
int index_end(); int index_end();
int index_read(byte * buf, const byte * key, int index_read(byte * buf, const byte * key,
@@ -185,6 +182,10 @@ class ha_innobase: public handler
static ulonglong get_mysql_bin_log_pos(); static ulonglong get_mysql_bin_log_pos();
bool primary_key_is_clustered() { return true; } bool primary_key_is_clustered() { return true; }
int cmp_ref(const byte *ref1, const byte *ref2); int cmp_ref(const byte *ref1, const byte *ref2);
int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys);
int prepare_drop_index(TABLE *table_arg, uint *key_num,
uint num_of_keys);
int final_drop_index(TABLE *table_arg);
bool check_if_incompatible_data(HA_CREATE_INFO *info, bool check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes); uint table_changes);
}; };

View File

@@ -279,8 +279,13 @@ dtuple_create(
ulint i; ulint i;
for (i = 0; i < n_fields; i++) { for (i = 0; i < n_fields; i++) {
(tuple->fields + i)->data = &data_error; dfield_t* field;
dfield_get_type(tuple->fields + i)->mtype = DATA_ERROR;
field = dtuple_get_nth_field(tuple, i);
dfield_set_len(field, UNIV_SQL_NULL);
field->data = &data_error;
dfield_get_type(field)->mtype = DATA_ERROR;
} }
} }
#endif #endif

View File

@@ -62,6 +62,9 @@ Created 5/24/1996 Heikki Tuuri
activated by the operation would activated by the operation would
lead to a duplicate key in some lead to a duplicate key in some
table */ table */
#define DB_CANNOT_DROP_FOREIGN_INDEX 47 /* we cannot drop an index because
it is needed on foreign key
constraint */
/* The following are partial failure codes */ /* The following are partial failure codes */
#define DB_FAIL 1000 #define DB_FAIL 1000

View File

@@ -53,6 +53,15 @@ dict_remove_db_name(
/* out: table name */ /* out: table name */
const char* name); /* in: table name in the form const char* name); /* in: table name in the form
dbname '/' tablename */ dbname '/' tablename */
/**************************************************************************
Returns a table object based on table id. */
dict_table_t*
dict_table_get_on_id(
/*=================*/
/* out: table, NULL if does not exist */
dulint table_id, /* in: table id */
trx_t* trx); /* in: transaction handle */
/************************************************************************ /************************************************************************
Decrements the count of open MySQL handles to a table. */ Decrements the count of open MySQL handles to a table. */
@@ -248,6 +257,14 @@ dict_table_rename_in_cache(
to preserve the original table name to preserve the original table name
in constraints which reference it */ in constraints which reference it */
/************************************************************************** /**************************************************************************
Removes an index from the dictionary cache. */
void
dict_index_remove_from_cache(
/*=========================*/
dict_table_t* table, /* in: table */
dict_index_t* index); /* in, own: index */
/**************************************************************************
Change the id of a table object in the dictionary cache. This is used in Change the id of a table object in the dictionary cache. This is used in
DISCARD TABLESPACE. */ DISCARD TABLESPACE. */
@@ -270,14 +287,34 @@ dict_foreign_add_to_cache(
ibool check_charsets);/* in: TRUE=check charset ibool check_charsets);/* in: TRUE=check charset
compatibility */ compatibility */
/************************************************************************* /*************************************************************************
Check if the index is referenced by a foreign key, if TRUE return the
matching instance NULL otherwise. */
dict_foreign_t*
dict_table_get_referenced_constraint(
/*=================================*/
/* out: pointer to foreign key struct if index
is defined for foreign key, otherwise NULL */
dict_table_t* table, /* in: InnoDB table */
dict_index_t* index); /* in: InnoDB index */
/*************************************************************************
Checks if a table is referenced by foreign keys. */ Checks if a table is referenced by foreign keys. */
ibool ibool
dict_table_referenced_by_foreign_key( dict_table_is_referenced_by_foreign_key(
/*=================================*/ /*====================================*/
/* out: TRUE if table is referenced by a /* out: TRUE if table is referenced
foreign key */ by a foreign key */
dict_table_t* table); /* in: InnoDB table */ const dict_table_t* table); /* in: InnoDB table */
/**************************************************************************
Replace the index in the foreign key list that matches this index's
definition with an equivalent index. */
void
dict_table_replace_index_in_foreign_list(
/*=====================================*/
dict_table_t* table, /* in/out: table */
dict_index_t* index); /* in: index to be replaced */
/************************************************************************** /**************************************************************************
Determines whether a string starts with the specified keyword. */ Determines whether a string starts with the specified keyword. */
@@ -290,6 +327,18 @@ dict_str_starts_with_keyword(
const char* str, /* in: string to scan for keyword */ const char* str, /* in: string to scan for keyword */
const char* keyword); /* in: keyword to look for */ const char* keyword); /* in: keyword to look for */
/************************************************************************* /*************************************************************************
Checks if a index is defined for a foreign key constraint. Index is a part
of a foreign key constraint if the index is referenced by foreign key
or index is a foreign key index */
dict_foreign_t*
dict_table_get_foreign_constraint(
/*==============================*/
/* out: pointer to foreign key struct if index
is defined for foreign key, otherwise NULL */
dict_table_t* table, /* in: InnoDB table */
dict_index_t* index); /* in: InnoDB index */
/*************************************************************************
Scans a table create SQL string and adds to the data dictionary Scans a table create SQL string and adds to the data dictionary
the foreign key constraints declared in the string. This function the foreign key constraints declared in the string. This function
should be called after the indexes for a table have been created. should be called after the indexes for a table have been created.
@@ -350,24 +399,18 @@ dict_table_get(
/* in: whether to increment the open /* in: whether to increment the open
handle count on the table */ handle count on the table */
/************************************************************************** /**************************************************************************
Returns a table object based on table id. */ Returns a index object, based on table and index id, and memoryfixes it. */
dict_table_t* dict_index_t*
dict_table_get_on_id( dict_index_get_on_id_low(
/*=================*/
/* out: table, NULL if does not exist */
dulint table_id, /* in: table id */
trx_t* trx); /* in: transaction handle */
/**************************************************************************
Returns a table object based on table id. */
UNIV_INLINE
dict_table_t*
dict_table_get_on_id_low(
/*=====================*/ /*=====================*/
/* out: table, NULL if does not exist */ /* out: index, NULL if does not
dulint table_id); /* in: table id */ exist */
dict_table_t* table, /* in: table */
dulint index_id); /* in: index id */
/************************************************************************** /**************************************************************************
Checks if a table is in the dictionary cache. */ Checks if a table is in the dictionary cache. */
UNIV_INLINE UNIV_INLINE
dict_table_t* dict_table_t*
dict_table_check_if_in_cache_low( dict_table_check_if_in_cache_low(
@@ -384,6 +427,14 @@ dict_table_get_low(
/* out: table, NULL if not found */ /* out: table, NULL if not found */
const char* table_name); /* in: table name */ const char* table_name); /* in: table name */
/************************************************************************** /**************************************************************************
Returns a table object based on table id. */
UNIV_INLINE
dict_table_t*
dict_table_get_on_id_low(
/*=====================*/
/* out: table, NULL if does not exist */
dulint table_id); /* in: table id */
/**************************************************************************
A noninlined version of dict_table_get_low. */ A noninlined version of dict_table_get_low. */
dict_table_t* dict_table_t*
@@ -392,23 +443,17 @@ dict_table_get_low_noninlined(
/* out: table, NULL if not found */ /* out: table, NULL if not found */
const char* table_name); /* in: table name */ const char* table_name); /* in: table name */
/************************************************************************** /**************************************************************************
Returns an index object. */ Returns an index object by matching on the name and column names and if
UNIV_INLINE more than index is found return the index with the higher id.*/
dict_index_t*
dict_table_get_index(
/*=================*/
/* out: index, NULL if does not exist */
dict_table_t* table, /* in: table */
const char* name); /* in: index name */
/**************************************************************************
Returns an index object. */
dict_index_t* dict_index_t*
dict_table_get_index_noninline( dict_table_get_index_by_max_id(
/*===========================*/ /*===========================*/
/* out: index, NULL if does not exist */ /* out: index, NULL if does not exist */
dict_table_t* table, /* in: table */ dict_table_t* table, /* in: table */
const char* name); /* in: index name */ const char* name, /* in: index name to find*/
const char** column_names, /* in: column names to match */
ulint n_cols);/* in: number of columns */
/************************************************************************** /**************************************************************************
Returns a column's name. */ Returns a column's name. */
@@ -855,6 +900,14 @@ dict_index_check_search_tuple(
/* out: TRUE if ok */ /* out: TRUE if ok */
dict_index_t* index, /* in: index */ dict_index_t* index, /* in: index */
const dtuple_t* tuple); /* in: tuple used in a search */ const dtuple_t* tuple); /* in: tuple used in a search */
/**************************************************************************
Check for duplicate index entries in a table [using the index name] */
void
dict_table_check_for_dup_indexes(
/*=============================*/
dict_table_t* table); /* in: Check for dup indexes in this table */
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
/************************************************************************** /**************************************************************************
Builds a node pointer out of a physical record and a page number. */ Builds a node pointer out of a physical record and a page number. */
@@ -1012,6 +1065,118 @@ dict_scan_to(
/* out: scanned up to this */ /* out: scanned up to this */
const char* ptr, /* in: scan from */ const char* ptr, /* in: scan from */
const char* string);/* in: look for this */ const char* string);/* in: look for this */
/*************************************************************************
Removes an index from the cache */
void
dict_index_remove_from_cache(
/*=========================*/
dict_table_t* table, /* in: table */
dict_index_t* index); /* in, own: index */
/**************************************************************************
Get index by name */
dict_index_t*
dict_table_get_index_on_name(
/*=========================*/
/* out: index, NULL if does not exist */
dict_table_t* table, /* in: table */
const char* name); /* in: name of the index to find */
/**************************************************************************
Find and index that is equivalent to the one passed in and is not marked
for deletion. */
dict_index_t*
dict_table_find_equivalent_index(
/*=============================*/
dict_table_t* table, /* in/out: table */
dict_index_t* index); /* in: index to match */
/**************************************************************************
Find and return an index in the table that matches the index id.*/
dict_index_t*
dict_table_get_index_on_id_noninline(
/*=================================*/
/* out: index, NULL if does not exist */
dict_table_t* table, /* in: table */
dulint index_id);/* in: table id */
/**************************************************************************
In case there is more than one index with the same name return the index
with the min(id). */
dict_index_t*
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: transaction */
ibool commit_flag); /* in: ignored for now */
/**************************************************************************
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 /* Buffers for storing detailed information about the latest foreign key
and unique key errors */ and unique key errors */
extern FILE* dict_foreign_err_file; extern FILE* dict_foreign_err_file;
@@ -1050,6 +1215,10 @@ struct dict_sys_struct{
dict_table_t* sys_fields; /* SYS_FIELDS table */ dict_table_t* sys_fields; /* SYS_FIELDS table */
}; };
#define TEMP_TABLE_PREFIX '/' /* Table name prefix for temporary
internal tables. Used in fast index
creation etc. */
#ifndef UNIV_NONINL #ifndef UNIV_NONINL
#include "dict0dict.ic" #include "dict0dict.ic"
#endif #endif

View File

@@ -647,28 +647,3 @@ dict_table_get_on_id_low(
return(table); return(table);
} }
/**************************************************************************
Returns an index object. */
UNIV_INLINE
dict_index_t*
dict_table_get_index(
/*=================*/
/* out: index, NULL if does not exist */
dict_table_t* table, /* in: table */
const char* name) /* in: index name */
{
dict_index_t* index = NULL;
index = dict_table_get_first_index(table);
while (index != NULL) {
if (ut_strcmp(name, index->name) == 0) {
break;
}
index = dict_table_get_next_index(index);
}
return(index);
}

View File

@@ -13,6 +13,7 @@ Created 4/24/1996 Heikki Tuuri
#include "univ.i" #include "univ.i"
#include "dict0types.h" #include "dict0types.h"
#include "ut0byte.h" #include "ut0byte.h"
#include "mem0mem.h"
/************************************************************************ /************************************************************************
In a crash recovery we already have all the tablespace objects created. In a crash recovery we already have all the tablespace objects created.

View File

@@ -24,6 +24,7 @@ Created 1/8/1996 Heikki Tuuri
#include "lock0types.h" #include "lock0types.h"
#include "hash0hash.h" #include "hash0hash.h"
#include "que0types.h" #include "que0types.h"
#include "row0types.h"
/* Type flags of an index: OR'ing of the flags is allowed to define a /* Type flags of an index: OR'ing of the flags is allowed to define a
combination of types */ combination of types */
@@ -31,7 +32,8 @@ combination of types */
#define DICT_UNIQUE 2 /* unique index */ #define DICT_UNIQUE 2 /* unique index */
#define DICT_UNIVERSAL 4 /* index which can contain records from any #define DICT_UNIVERSAL 4 /* index which can contain records from any
other index */ other index */
#define DICT_IBUF 8 /* insert buffer tree */ #define DICT_IBUF 8 /* insert buffer tree */
#define DICT_NOT_READY 16 /* this index is being build */
/* Types for a table object */ /* Types for a table object */
#define DICT_TABLE_ORDINARY 1 #define DICT_TABLE_ORDINARY 1
@@ -185,7 +187,7 @@ struct dict_index_struct{
dulint id; /* id of the index */ dulint id; /* id of the index */
mem_heap_t* heap; /* memory heap */ mem_heap_t* heap; /* memory heap */
ulint type; /* index type */ ulint type; /* index type */
const char* name; /* index name */ char* name; /* index name */
const char* table_name; /* table name */ const char* table_name; /* table name */
dict_table_t* table; /* back pointer to table */ dict_table_t* table; /* back pointer to table */
unsigned space:32; unsigned space:32;
@@ -207,6 +209,10 @@ struct dict_index_struct{
unsigned n_nullable:10;/* number of nullable fields */ unsigned n_nullable:10;/* number of nullable fields */
unsigned cached:1;/* TRUE if the index object is in the unsigned cached:1;/* TRUE if the index object is in the
dictionary cache */ dictionary cache */
unsigned to_be_dropped:1;
/* TRUE if this index is marked to be
dropped in ha_innobase::prepare_drop_index(),
otherwise FALSE */
dict_field_t* fields; /* array of field descriptions */ dict_field_t* fields; /* array of field descriptions */
UT_LIST_NODE_T(dict_index_t) UT_LIST_NODE_T(dict_index_t)
indexes;/* list of indexes of the table */ indexes;/* list of indexes of the table */
@@ -224,6 +230,9 @@ struct dict_index_struct{
index tree */ index tree */
rw_lock_t lock; /* read-write lock protecting the upper levels rw_lock_t lock; /* read-write lock protecting the upper levels
of the index tree */ of the index tree */
dulint trx_id; /* id of the transaction that created this
index. It can be zero which implies that
it was created on database startup.*/
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
ulint magic_n;/* magic number */ ulint magic_n;/* magic number */
# define DICT_INDEX_MAGIC_N 76789786 # define DICT_INDEX_MAGIC_N 76789786
@@ -290,6 +299,14 @@ struct dict_table_struct{
innodb_file_per_table is defined in my.cnf; innodb_file_per_table is defined in my.cnf;
in Unix this is usually /tmp/..., in Windows in Unix this is usually /tmp/..., in Windows
\temp\... */ \temp\... */
unsigned version_number:32;
/* version number of this table definition.
Version number is 0 when table is created.
Every schema change implemented without
creating a new table and copying rows from
the old table to new table will increase this
number. For example adding or removing index,
adding or removing a column. */
unsigned space:32; unsigned space:32;
/* space where the clustered index of the /* space where the clustered index of the
table is placed */ table is placed */
@@ -303,6 +320,8 @@ struct dict_table_struct{
calls DISCARD TABLESPACE on this calls DISCARD TABLESPACE on this
table, and reset to FALSE in IMPORT table, and reset to FALSE in IMPORT
TABLESPACE */ TABLESPACE */
unsigned to_be_dropped:1; /* if set then this table will
dropped when n_mysql_handles_opened is 0 */
unsigned cached:1;/* TRUE if the table object has been added unsigned cached:1;/* TRUE if the table object has been added
to the dictionary cache */ to the dictionary cache */
unsigned flags:8;/* DICT_TF_COMPACT, ... */ unsigned flags:8;/* DICT_TF_COMPACT, ... */
@@ -406,6 +425,10 @@ struct dict_table_struct{
SELECT MAX(auto inc column) */ SELECT MAX(auto inc column) */
ib_longlong autoinc;/* autoinc counter value to give to the ib_longlong autoinc;/* autoinc counter value to give to the
next inserted row */ next inserted row */
/*----------------------*/
UT_LIST_BASE_NODE_T(row_prebuilt_t) prebuilts;
/* base node for the prebuilts defined
for the table */
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
ulint magic_n;/* magic number */ ulint magic_n;/* magic number */
# define DICT_TABLE_MAGIC_N 76333786 # define DICT_TABLE_MAGIC_N 76333786

View File

@@ -9,6 +9,8 @@ Created 1/8/1996 Heikki Tuuri
#ifndef dict0types_h #ifndef dict0types_h
#define dict0types_h #define dict0types_h
#include "ut0list.h"
typedef struct dict_sys_struct dict_sys_t; typedef struct dict_sys_struct dict_sys_t;
typedef struct dict_col_struct dict_col_t; typedef struct dict_col_struct dict_col_t;
typedef struct dict_field_struct dict_field_t; typedef struct dict_field_struct dict_field_t;
@@ -24,4 +26,48 @@ typedef dict_table_t dict_cluster_t;
typedef struct ind_node_struct ind_node_t; typedef struct ind_node_struct ind_node_t;
typedef struct tab_node_struct tab_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 #endif

View File

@@ -73,6 +73,12 @@ heap creation. */
Use this macro instead of the corresponding function! Macro for memory Use this macro instead of the corresponding function! Macro for memory
heap creation. */ heap creation. */
#define mem_heap_create_noninline(N) mem_heap_create_func_noninline(\
(N), MEM_HEAP_DYNAMIC, __FILE__, __LINE__)
/******************************************************************
Use this macro instead of the corresponding function! Macro for memory
heap creation. */
#define mem_heap_create_in_buffer(N) mem_heap_create_func(\ #define mem_heap_create_in_buffer(N) mem_heap_create_func(\
(N), MEM_HEAP_BUFFER, __FILE__, __LINE__) (N), MEM_HEAP_BUFFER, __FILE__, __LINE__)
/****************************************************************** /******************************************************************
@@ -89,6 +95,12 @@ heap freeing. */
#define mem_heap_free(heap) mem_heap_free_func(\ #define mem_heap_free(heap) mem_heap_free_func(\
(heap), __FILE__, __LINE__) (heap), __FILE__, __LINE__)
/******************************************************************
Use this macro instead of the corresponding function! Macro for memory
heap freeing. */
#define mem_heap_free_noninline(heap) mem_heap_free_func_noninline(\
(heap), __FILE__, __LINE__)
/********************************************************************* /*********************************************************************
NOTE: Use the corresponding macros instead of this function. Creates a NOTE: Use the corresponding macros instead of this function. Creates a
memory heap. For debugging purposes, takes also the file name and line as memory heap. For debugging purposes, takes also the file name and line as
@@ -118,6 +130,37 @@ mem_heap_free_func(
mem_heap_t* heap, /* in, own: heap to be freed */ mem_heap_t* heap, /* in, own: heap to be freed */
const char* file_name, /* in: file name where freed */ const char* file_name, /* in: file name where freed */
ulint line); /* in: line where freed */ ulint line); /* in: line where freed */
/*********************************************************************
NOTE: Use the corresponding macros instead of this function. Creates a
memory heap. For debugging purposes, takes also the file name and line as
arguments. */
mem_heap_t*
mem_heap_create_func_noninline(
/*===========================*/
/* out, own: memory heap, NULL if
did not succeed (only possible for
MEM_HEAP_BTR_SEARCH type heaps)*/
ulint n, /* in: desired start block size,
this means that a single user buffer
of size n will fit in the block,
0 creates a default size block;
if init_block is not NULL, n tells
its size in bytes */
ulint type, /* in: heap type */
const char* file_name, /* in: file name where created */
ulint line); /* in: line where created */
/*********************************************************************
NOTE: Use the corresponding macro instead of this function. Frees the space
occupied by a memory heap. In the debug version erases the heap memory
blocks. */
void
mem_heap_free_func_noninline(
/*=========================*/
mem_heap_t* heap, /* in, own: heap to be freed */
const char* file_name, /* in: file name where freed */
ulint line); /* in: line where freed */
/******************************************************************* /*******************************************************************
Allocates n bytes of memory from a memory heap. */ Allocates n bytes of memory from a memory heap. */
UNIV_INLINE UNIV_INLINE
@@ -131,6 +174,19 @@ mem_heap_alloc(
ulint n); /* in: number of bytes; if the heap is allowed ulint n); /* in: number of bytes; if the heap is allowed
to grow into the buffer pool, this must be to grow into the buffer pool, this must be
<= MEM_MAX_ALLOC_IN_BUF */ <= MEM_MAX_ALLOC_IN_BUF */
/*******************************************************************
Allocates n bytes of memory from a memory heap. */
void*
mem_heap_alloc_noninline(
/*=====================*/
/* out: allocated storage, NULL if did not
succeed (only possible for
MEM_HEAP_BTR_SEARCH type heaps) */
mem_heap_t* heap, /* in: memory heap */
ulint n); /* in: number of bytes; if the heap is allowed
to grow into the buffer pool, this must be
<= MEM_MAX_ALLOC_IN_BUF */
/********************************************************************* /*********************************************************************
Returns a pointer to the heap top. */ Returns a pointer to the heap top. */
UNIV_INLINE UNIV_INLINE
@@ -193,6 +249,12 @@ Macro for memory buffer allocation */
#define mem_alloc_noninline(N) mem_alloc_func_noninline(\ #define mem_alloc_noninline(N) mem_alloc_func_noninline(\
(N), __FILE__, __LINE__) (N), __FILE__, __LINE__)
/******************************************************************
Use this macro instead of the corresponding function!
Macro for memory buffer allocation */
#define mem_free_noninline(N) mem_free_func_noninline(\
(N), __FILE__, __LINE__)
/******************************************************************* /*******************************************************************
NOTE: Use the corresponding macro instead of this function. NOTE: Use the corresponding macro instead of this function.
Allocates a single buffer of memory from the dynamic memory of Allocates a single buffer of memory from the dynamic memory of
@@ -238,6 +300,18 @@ mem_free_func(
const char* file_name, /* in: file name where created */ const char* file_name, /* in: file name where created */
ulint line /* in: line where created */ ulint line /* in: line where created */
); );
/*******************************************************************
NOTE: Use the corresponding macro instead of this function.
Frees a single buffer of storage from
the dynamic memory of C compiler. Similar to free of C. */
void
mem_free_func_noninline(
/*====================*/
void* ptr, /* in, own: buffer to be freed */
const char* file_name, /* in: file name where created */
ulint line /* in: line where created */
);
/************************************************************************** /**************************************************************************
Duplicates a NUL-terminated string. */ Duplicates a NUL-terminated string. */

View File

@@ -729,4 +729,15 @@ os_file_get_status(
os_file_stat_t* stat_info); /* information of a file in a os_file_stat_t* stat_info); /* information of a file in a
directory */ directory */
#if !defined(UNIV_HOTBACKUP) && !defined(__NETWARE__)
/*************************************************************************
Creates a temporary file that will be deleted on close.
This function is defined in ha_innodb.cc. */
int
innobase_mysql_tmpfile(void);
/*========================*/
/* out: temporary file descriptor, or < 0 on error */
#endif /* !UNIV_HOTBACKUP && !__NETWARE__ */
#endif #endif

View File

@@ -93,6 +93,13 @@ row_ins_step(
/*=========*/ /*=========*/
/* out: query thread to run next or NULL */ /* out: query thread to run next or NULL */
que_thr_t* thr); /* in: query thread */ que_thr_t* thr); /* in: query thread */
/***************************************************************
Creates an entry template for each index of a table. */
void
ins_node_create_entry_list(
/*=======================*/
ins_node_t* node); /* in: row insert node */
/* Insert node structure */ /* Insert node structure */
@@ -112,6 +119,11 @@ struct ins_node_struct{
this should be reset to NULL */ this should be reset to NULL */
UT_LIST_BASE_NODE_T(dtuple_t) UT_LIST_BASE_NODE_T(dtuple_t)
entry_list;/* list of entries, one for each index */ entry_list;/* list of entries, one for each index */
ulint table_version_number;
/* entry_list is created for this version
of the table. If this version is not same
as table->version_number, entry_list must
be re-created. */
byte* row_id_buf;/* buffer for the row id sys field in row */ byte* row_id_buf;/* buffer for the row id sys field in row */
dulint trx_id; /* trx id or the last trx which executed the dulint trx_id; /* trx id or the last trx which executed the
node */ node */

305
include/row0merge.h Normal file
View File

@@ -0,0 +1,305 @@
/******************************************************
Index build routines using a merge sort
(c) 2005 Innobase Oy
Created 13/06/2005 Jan Lindstrom
*******************************************************/
#ifndef row0merge_h
#define row0merge_h
#include "univ.i"
#include "data0data.h"
#include "dict0types.h"
#include "trx0types.h"
#include "que0types.h"
#include "mtr0mtr.h"
#include "rem0types.h"
#include "rem0rec.h"
#include "read0types.h"
#include "btr0types.h"
#include "row0mysql.h"
/* Block size for I/O operations in merge sort */
#define MERGE_BLOCK_SIZE 1048576 /* 1M */
/* Intentional free space on every block */
#define MERGE_BLOCK_SAFETY_MARGIN 128
/* Enable faster index creation debug code */
/* #define UNIV_DEBUG_INDEX_CREATE 1 */
/* This block header structure is used to create linked list of the
blocks to the disk. Every block contains one header.*/
struct merge_block_header_struct {
ulint n_records; /* Number of records in the block. */
dulint offset; /* Offset of this block in the disk. */
dulint next; /* Offset to next block in the disk. */
};
typedef struct merge_block_header_struct merge_block_header_t;
/* This block structure is used to hold index records in the disk
and the memory */
struct merge_block_struct {
merge_block_header_t header; /* Block header information */
char data[MERGE_BLOCK_SIZE - sizeof(merge_block_header_t)];/* Data area i.e. heap */
};
typedef struct merge_block_struct merge_block_t;
/* Records are stored in the memory for main memory linked list
to this structure */
struct merge_rec_struct {
struct merge_rec_struct *next; /* Pointer to next record
in the list */
rec_t* rec; /* Record */
};
typedef struct merge_rec_struct merge_rec_t;
/* This structure is head element for main memory linked list
used for main memory linked list merge sort */
struct merge_rec_list_struct {
merge_rec_t* head; /* Pointer to head of the
list */
merge_rec_t* tail; /* Pointer to tail of the
list */
ulint n_records; /* Number of records in
the list */
ulint total_size; /* Total size of all records in
the list */
mem_heap_t* heap; /* Heap where memory for this
list is allocated */
};
typedef struct merge_rec_list_struct merge_rec_list_t;
/* Information about temporary files used in merge sort are stored
to this structure */
struct merge_file_struct {
os_file_t file; /* File descriptor */
dulint offset; /* File offset */
ulint num_of_blocks; /* Number of blocks */
};
typedef struct merge_file_struct merge_file_t;
/* This structure holds parameters to thread which does a
disk based merge sort and inserts index records */
struct merge_thread_struct {
dict_index_t* index; /* in: Index to be created */
row_prebuilt_t* prebuilt; /* in: Prebuilt */
trx_t* trx; /* in: trx */
os_file_t file; /* in: File handle */
int error; /* out: error code or 0 */
};
typedef struct merge_thread_struct merge_thread_t;
/* This structure holds index field definitions */
struct merge_index_field_struct {
ulint col_type; /* Column type */
ulint prefix_len; /* Prefix len */
char* field_name; /* Field name */
};
typedef struct merge_index_field_struct merge_index_field_t;
/* This structure holds index definitions */
struct merge_index_def_struct {
ulint n_fields; /* Number of fields in index */
ulint ind_type; /* 0, DICT_UNIQUE or DICT_CLUSTERED */
char* name; /* Index name */
merge_index_field_t** fields; /* Field definitions */
};
typedef struct merge_index_def_struct merge_index_def_t;
/************************************************************************
Reads clustered index of the table and create temporary files
containing index entries for indexes to be built. */
ulint
row_merge_read_clustered_index(
/*===========================*/
/* out: DB_SUCCESS if successfull,
or ERROR code */
trx_t* trx, /* in: transaction */
dict_table_t* table, /* in: table where index is created */
dict_index_t** index, /* in: indexes to be created */
merge_file_t** files, /* in: Files where to write index
entries */
ulint num_of_idx); /* in: number of indexes to be
created */
/************************************************************************
Read sorted file containing index data tuples and insert these data
data tuples to the index */
ulint
row_merge_insert_index_tuples(
/*==========================*/
/* out: 0 or error number */
trx_t* trx, /* in: transaction */
dict_index_t* index, /* in: index */
dict_table_t* table, /* in: table */
os_file_t file, /* in: file handle */
dulint offset); /* in: offset where to start
reading */
/*****************************************************************
Merge sort for linked list in the disk. */
dulint
row_merge_sort_linked_list_in_disk(
/*===============================*/
/* out: offset to first block in
the list or ut_dulint_max in
case of error */
dict_index_t* index, /* in: index to be created */
os_file_t file, /* in: File handle */
int* error); /* out: 0 or error */
/*************************************************************************
Allocate and initialize memory for a merge file structure */
merge_file_t*
row_merge_create_file_structure(
/*============================*/
/* out: pointer to merge file
structure */
mem_heap_t* heap); /* in: heap where merge file structure
is allocated */
/*************************************************************************
A thread which merge sorts given file and inserts sorted records to
the index. */
#ifndef __WIN__
void *
#else
ulint
#endif
row_merge_sort_and_insert_thread(
/*=============================*/
/* out: a dummy parameter */
void* arg); /* in: parameters */
/*************************************************************************
Remove a index from system tables */
ulint
row_merge_remove_index(
/*===================*/
/* out: error code or DB_SUCCESS */
dict_index_t* index, /* in: index to be removed */
dict_table_t* table, /* in: table */
trx_t* trx); /* in: transaction handle */
/*************************************************************************
Print definition of a table in the dictionary */
void
row_merge_print_table(
/*==================*/
dict_table_t* table); /* in: table */
/*************************************************************************
Mark all prebuilts using the table obsolete. These prebuilts are
rebuilded later. */
void
row_merge_mark_prebuilt_obsolete(
/*=============================*/
trx_t* trx, /* in: trx */
dict_table_t* table); /* in: table */
/*************************************************************************
Create a temporary table using a definition of the old table. You must
lock data dictionary before calling this function. */
dict_table_t*
row_merge_create_temporary_table(
/*=============================*/
/* out: new temporary table
definition */
const char* table_name, /* in: new table name */
dict_table_t* table, /* in: old table definition */
trx_t* trx, /* in: trx */
ulint* error); /* in:out/ error code or DB_SUCCESS */
/*************************************************************************
Update all prebuilts for this table */
void
row_merge_prebuilts_update(
/*=======================*/
trx_t* trx, /* in: trx */
dict_table_t* old_table); /* in: old table */
/*************************************************************************
Create a temporary table using a definition of the old table. You must
lock data dictionary before calling this function. */
dict_table_t*
row_merge_create_temporary_table(
/*=============================*/
/* out: new temporary table
definition */
const char* table_name, /* in: new table name */
dict_table_t* table, /* in: old table definition */
trx_t* trx, /* in: trx */
ulint* error); /* in:out/ error code or DB_SUCCESS */
/*************************************************************************
Rename the indexes in the dicitionary. */
ulint
row_merge_rename_index(
/*===================*/
/* 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 */
/*************************************************************************
Create the index and load in to the dicitionary. */
ulint
row_merge_create_index(
/*===================*/
/* out: DB_SUCCESS if all OK */
trx_t* trx, /* in: transaction */
dict_index_t** index, /* out: the instance of the index */
dict_table_t* table, /* in: the index is on this table */
const merge_index_def_t* /* in: the index definition */
index_def);
/*************************************************************************
Check if a transaction can use an index.*/
ibool
row_merge_is_index_usable(
/*======================*/
/* out: TRUE if index can be used by
the transaction else FALSE*/
const trx_t* trx, /* in: transaction */
const dict_index_t* /* in: index to check */
index);
/*************************************************************************
If there are views that refer to the old table name then we "attach" to
the new instance of the table else we drop it immediately.*/
ulint
row_merge_drop_table(
/*=================*/
/* out: DB_SUCCESS if all OK else
error code.*/
trx_t* trx, /* in: transaction */
dict_table_t* table); /* in: table instance to drop */
#endif /* row0merge.h */

View File

@@ -21,7 +21,7 @@ Created 9/17/2000 Heikki Tuuri
extern ibool row_rollback_on_timeout; extern ibool row_rollback_on_timeout;
typedef struct row_prebuilt_struct row_prebuilt_t; /* typedef struct row_prebuilt_struct row_prebuilt_t; */
/*********************************************************************** /***********************************************************************
Frees the blob heap in prebuilt when no longer needed. */ Frees the blob heap in prebuilt when no longer needed. */
@@ -153,6 +153,14 @@ row_update_prebuilt_trx(
row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL
handle */ handle */
trx_t* trx); /* in: transaction handle */ trx_t* trx); /* in: transaction handle */
/************************************************************************
Update a prebuilt struct for a MySQL table handle. */
void
row_update_prebuilt(
/*================*/
row_prebuilt_t* prebuilt, /* in: Innobase table handle */
dict_table_t* table); /* in: table */
/************************************************************************* /*************************************************************************
Unlocks an AUTO_INC type lock possibly reserved by trx. */ Unlocks an AUTO_INC type lock possibly reserved by trx. */
@@ -188,6 +196,16 @@ row_lock_table_for_mysql(
prebuilt->select_lock_type */ prebuilt->select_lock_type */
ulint mode); /* in: lock mode of table ulint mode); /* in: lock mode of table
(ignored if table==NULL) */ (ignored if table==NULL) */
/*************************************************************************
Sets a table lock on the table. */
int
row_lock_table_for_merge(
/*=====================*/
/* out: error code or DB_SUCCESS */
trx_t* trx, /* in: lock table for this trx */
dict_table_t* table, /* in: table to lock */
ulint mode); /* in: lock mode of table */
/************************************************************************* /*************************************************************************
Does an insert for MySQL. */ Does an insert for MySQL. */
@@ -413,6 +431,19 @@ row_drop_table_for_mysql(
ibool drop_db);/* in: TRUE=dropping whole database */ 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.*/
int
row_drop_table_for_mysql_no_commit(
/*===============================*/
/* 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 */
/*************************************************************************
Discards the tablespace of a table which stored in an .ibd file. Discarding 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 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. */ the table. Also the flag table->ibd_file_missing is set TRUE. */
@@ -451,7 +482,8 @@ row_rename_table_for_mysql(
/* out: error code or DB_SUCCESS */ /* out: error code or DB_SUCCESS */
const char* old_name, /* in: old table name */ const char* old_name, /* in: old table name */
const char* new_name, /* in: new table name */ const char* new_name, /* in: new table name */
trx_t* trx); /* in: transaction handle */ trx_t* trx, /* in: transaction handle */
ibool commit); /* in: if TRUE then commit trx */
/************************************************************************* /*************************************************************************
Checks a table for corruption. */ Checks a table for corruption. */
@@ -462,7 +494,93 @@ row_check_table_for_mysql(
row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL
handle */ handle */
#endif /* !UNIV_HOTBACKUP */ #endif /* !UNIV_HOTBACKUP */
/*************************************************************************
Build new indexes to a table by reading a cluster index,
creating a temporary file containing index entries, merge sorting
these index entries and inserting sorted index entries to indexes. */
ulint
row_build_index_for_mysql(
/*====================*/
/* out: 0 or error code */
trx_t* trx, /* in: transaction */
dict_table_t* old_table, /* in: Table where rows are
read from */
dict_table_t* new_table, /* in: Table where indexes are
created. Note that old_table ==
new_table if we are creating a
secondary keys. */
dict_index_t** index, /* in: Indexes to be created */
ibool new_primary, /* in: new primary key
i.e. clustered index will be build
for this table */
ulint num_of_keys); /* in: Number of indexes to be
created */
/*************************************************************************
Create query graph for a index creation */
ulint
row_create_index_graph_for_mysql(
/*=============================*/
/* out: DB_SUCCESS or error code */
trx_t* trx, /* in: trx */
dict_table_t* table, /* in: table */
dict_index_t* index); /* in: index */
/*************************************************************************
Remove those indexes which were created before a error happened in
the index build */
ulint
row_remove_indexes_for_mysql(
/*=========================*/
/* out: 0 or error code */
trx_t* trx, /* in: transaction */
dict_table_t* table, /* in: Table where index is created */
dict_index_t** index, /* in: Indexes to be created */
ulint num_created); /* in: Number of indexes created
before error and now must be removed */
/***************************************************************************
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 /* A struct describing a place for an individual column in the MySQL
row format which is presented to the table handler in ha_innobase. row format which is presented to the table handler in ha_innobase.
This template struct is used to speed up row transformations between This template struct is used to speed up row transformations between
@@ -512,16 +630,18 @@ struct mysql_row_templ_struct {
#define ROW_PREBUILT_ALLOCATED 78540783 #define ROW_PREBUILT_ALLOCATED 78540783
#define ROW_PREBUILT_FREED 26423527 #define ROW_PREBUILT_FREED 26423527
#define ROW_PREBUILT_OBSOLETE 12367541
/* A struct for (sometimes lazily) prebuilt structures in an Innobase table /* A struct for (sometimes lazily) prebuilt structures in an Innobase table
handle used within MySQL; these are used to save CPU time. */ handle used within MySQL; these are used to save CPU time. */
struct row_prebuilt_struct { struct row_prebuilt_struct {
ulint magic_n; /* this magic number is set to ulint magic_n; /* this magic number is set to
ROW_PREBUILT_ALLOCATED when created ROW_PREBUILT_ALLOCATED when created,
and to ROW_PREBUILT_FREED when the or ROW_PREBUILT_FREED when the
struct has been freed; used in struct has been freed or
debugging */ ROW_PREBUILT_OBSOLETE when struct
needs a rebuilt */
dict_table_t* table; /* Innobase table handle */ dict_table_t* table; /* Innobase table handle */
trx_t* trx; /* current transaction handle */ trx_t* trx; /* current transaction handle */
ibool sql_stat_start; /* TRUE when we start processing of ibool sql_stat_start; /* TRUE when we start processing of
@@ -668,10 +788,12 @@ struct row_prebuilt_struct {
fetched row in fetch_cache */ fetched row in fetch_cache */
ulint n_fetch_cached; /* number of not yet fetched rows ulint n_fetch_cached; /* number of not yet fetched rows
in fetch_cache */ in fetch_cache */
mem_heap_t* blob_heap; /* in SELECTS BLOB fie lds are copied mem_heap_t* blob_heap; /* in SELECTS BLOB fields are copied
to this heap */ to this heap */
mem_heap_t* old_vers_heap; /* memory heap where a previous mem_heap_t* old_vers_heap; /* memory heap where a previous
version is built in consistent read */ version is built in consistent read */
UT_LIST_NODE_T(row_prebuilt_t) prebuilts;
/* list node of table->prebuilts */
ulint magic_n2; /* this should be the same as ulint magic_n2; /* this should be the same as
magic_n */ magic_n */
}; };

View File

@@ -52,15 +52,16 @@ row_get_rec_roll_ptr(
dict_index_t* index, /* in: clustered index */ dict_index_t* index, /* in: clustered index */
const ulint* offsets);/* in: rec_get_offsets(rec, index) */ const ulint* offsets);/* in: rec_get_offsets(rec, index) */
/********************************************************************* /*********************************************************************
When an insert to a table is performed, this function builds the entry which When an insert or purge to a table is performed, this function builds
has to be inserted to an index on the table. */ the entry to be inserted into or purged from an index on the table. */
dtuple_t* dtuple_t*
row_build_index_entry( row_build_index_entry(
/*==================*/ /*==================*/
/* out: index entry which should be inserted */ /* out: index entry which should be
const dtuple_t* row, /* in: row which should be inserted to the inserted or purged */
table */ const dtuple_t* row, /* in: row which should be
inserted or purged */
row_ext_t* ext, /* in: externally stored column prefixes, row_ext_t* ext, /* in: externally stored column prefixes,
or NULL */ or NULL */
dict_index_t* index, /* in: index on the table */ dict_index_t* index, /* in: index on the table */

View File

@@ -36,4 +36,6 @@ typedef struct purge_node_struct purge_node_t;
typedef struct row_ext_struct row_ext_t; typedef struct row_ext_struct row_ext_t;
typedef struct row_prebuilt_struct row_prebuilt_t;
#endif #endif

View File

@@ -28,6 +28,15 @@ row_undo_ins(
/* out: DB_SUCCESS */ /* out: DB_SUCCESS */
undo_node_t* node); /* in: row undo node */ 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 #ifndef UNIV_NONINL
#include "row0uins.ic" #include "row0uins.ic"

View File

@@ -51,6 +51,24 @@ row_undo_step(
/*==========*/ /*==========*/
/* out: query thread to run next or NULL */ /* out: query thread to run next or NULL */
que_thr_t* thr); /* in: query thread */ 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 */
ulint
row_undo_dictionary(
/*================*/
/* out: DB_SUCCESS or error code */
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 /* 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 versions of a clustered index record, if the transaction has modified it
@@ -78,6 +96,20 @@ struct undo_node_struct{
dulint undo_no;/* undo number of the record */ dulint undo_no;/* undo number of the record */
ulint rec_type;/* undo log record type: TRX_UNDO_INSERT_REC, 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 dulint new_roll_ptr; /* roll ptr to restore to clustered index
record */ record */
dulint new_trx_id; /* trx id to restore to clustered index dulint new_trx_id; /* trx id to restore to clustered index

View File

@@ -60,6 +60,17 @@ trx_undo_rec_get_undo_no(
/*=====================*/ /*=====================*/
/* out: undo no */ /* out: undo no */
trx_undo_rec_t* undo_rec); /* in: undo log record */ trx_undo_rec_t* undo_rec); /* in: undo log record */
/**************************************************************************
* Returns the start of the undo record data area. */
UNIV_INLINE
byte*
trx_undo_rec_get_ptr(
/*==================*/
/* out: compiler info */
trx_undo_rec_t* undo_rec, /* in: undo log record */
dulint undo_no); /* in: undo no read from node */
/************************************************************************** /**************************************************************************
Reads from an undo log record the general parameters. */ Reads from an undo log record the general parameters. */
@@ -201,6 +212,31 @@ trx_undo_report_row_operation(
inserted undo log record, inserted undo log record,
ut_dulint_zero if BTR_NO_UNDO_LOG ut_dulint_zero if BTR_NO_UNDO_LOG
flag was specified */ 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 Copies an undo record to heap. This function can be called if we know that
the undo log record exists. */ the undo log record exists. */
@@ -279,24 +315,40 @@ trx_undo_parse_erase_page_end(
/* Types of an undo log record: these have to be smaller than 16, as the /* Types of an undo log record: these have to be smaller than 16, as the
compilation info multiplied by 16 is ORed to this value in an undo log compilation info multiplied by 16 is ORed to this value in an undo log
record */ record */
#define TRX_UNDO_INSERT_REC 11 /* fresh insert into clustered index */
#define TRX_UNDO_UPD_EXIST_REC 12 /* update of a non-delete-marked #define TRX_UNDO_INSERT_REC 11 /* fresh insert into clustered index */
#define TRX_UNDO_UPD_EXIST_REC 12 /* update of a non-delete-marked
record */ record */
#define TRX_UNDO_UPD_DEL_REC 13 /* update of a delete marked record to #define TRX_UNDO_UPD_DEL_REC 13 /* update of a delete marked record to
a not delete marked record; also the a not delete marked record; also the
fields of the record can change */ fields of the record can change */
#define TRX_UNDO_DEL_MARK_REC 14 /* delete marking of a record; fields #define TRX_UNDO_DEL_MARK_REC 14 /* delete marking of a record; fields
do not change */ 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 #define TRX_UNDO_CMPL_INFO_MULT 16 /* compilation info is multiplied by
this and ORed to the type above */ this and ORed to the type above */
#define TRX_UNDO_UPD_EXTERN 128 /* This bit can be ORed to type_cmpl #define TRX_UNDO_UPD_EXTERN 128 /* This bit can be ORed to type_cmpl
to denote that we updated external to denote that we updated external
storage fields: used by purge to storage fields: used by purge to
free the external storage */ free the external storage */
/* Operation type flags used in trx_undo_report_row_operation */ /* Operation type flags used in trx_undo_report_row_operation
#define TRX_UNDO_INSERT_OP 1 and trx_undo_report_dict_operation */
#define TRX_UNDO_MODIFY_OP 2 #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 #ifndef UNIV_NONINL
#include "trx0rec.ic" #include "trx0rec.ic"

View File

@@ -63,6 +63,20 @@ trx_undo_rec_get_undo_no(
return(mach_dulint_read_much_compressed(ptr)); return(mach_dulint_read_much_compressed(ptr));
} }
/**************************************************************************
Returns the start of the undo record data area. */
UNIV_INLINE
byte*
trx_undo_rec_get_ptr(
/*=================*/
/* out: compiler info */
trx_undo_rec_t* undo_rec, /* in: undo log record */
dulint undo_no) /* in: undo no read from node */
{
return (((byte*) undo_rec) + 3
+ mach_dulint_get_much_compressed_size(undo_no));
}
/*************************************************************************** /***************************************************************************
Copies the undo record to the heap. */ Copies the undo record to the heap. */
UNIV_INLINE UNIV_INLINE

View File

@@ -450,8 +450,19 @@ struct trx_struct{
table. This is a hint that the table table. This is a hint that the table
may need to be dropped in crash may need to be dropped in crash
recovery. */ recovery. */
dulint table_id; /* table id if the preceding field is dict_undo_list_t*
TRUE */ dict_undo_list; /* List of undo records are created
during recovery.*/
dict_redo_list_t*
dict_redo_list; /* List of indexes created by this
transaction.*/
ulint (*sync_cb)(trx_t*, ibool);
/* Transaction synchronization
callback, if ibool parameter is TRUE
then callback invoked for commit else
rollback.*/
dulint table_id; /* Table to drop iff dict_operation
is TRUE.*/
/*------------------------------*/ /*------------------------------*/
int active_trans; /* 1 - if a transaction in MySQL int active_trans; /* 1 - if a transaction in MySQL
is active. 2 - if prepare_commit_mutex is active. 2 - if prepare_commit_mutex
@@ -567,6 +578,9 @@ struct trx_struct{
void* error_info; /* if the error number indicates a void* error_info; /* if the error number indicates a
duplicate key error, a pointer to duplicate key error, a pointer to
the problematic index is stored here */ the problematic index is stored here */
ulint error_key_num; /* if the index creation fails to a
duplicate key error, a mysql key
number of that index is stored here */
sess_t* sess; /* session of the trx, NULL if none */ sess_t* sess; /* session of the trx, NULL if none */
ulint que_state; /* TRX_QUE_RUNNING, TRX_QUE_LOCK_WAIT, ulint que_state; /* TRX_QUE_RUNNING, TRX_QUE_LOCK_WAIT,
... */ ... */

View File

@@ -3678,7 +3678,9 @@ lock_table_enqueue_waiting(
trx = thr_get_trx(thr); trx = thr_get_trx(thr);
if (trx->dict_operation) { /* We have little choice here during index merge operations, and so
we suppress the printing of the message.*/
if (trx->dict_operation && *table->name != TEMP_TABLE_PREFIX) {
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fputs(" InnoDB: Error: a table lock wait happens" fputs(" InnoDB: Error: a table lock wait happens"
" in a dictionary operation!\n" " in a dictionary operation!\n"

View File

@@ -101,6 +101,21 @@ mem_alloc_func_noninline(
return(mem_alloc_func(n, file_name, line)); return(mem_alloc_func(n, file_name, line));
} }
/*******************************************************************
NOTE: Use the corresponding macro instead of this function.
Frees a single buffer of storage from
the dynamic memory of C compiler. Similar to free of C. */
void
mem_free_func_noninline(
/*====================*/
void* ptr, /* in, own: buffer to be freed */
const char* file_name, /* in: file name where created */
ulint line) /* in: line where created */
{
return(mem_free_func(ptr, file_name, line));
}
/************************************************************************** /**************************************************************************
Duplicates a NUL-terminated string, allocated from a memory heap. */ Duplicates a NUL-terminated string, allocated from a memory heap. */
@@ -566,3 +581,60 @@ mem_validate_all_blocks(void)
mem_pool_mutex_exit(); mem_pool_mutex_exit();
} }
#endif #endif
/*******************************************************************
Allocates n bytes of memory from a memory heap. */
void*
mem_heap_alloc_noninline(
/*=====================*/
/* out: allocated storage, NULL if did not
succeed (only possible for
MEM_HEAP_BTR_SEARCH type heaps) */
mem_heap_t* heap, /* in: memory heap */
ulint n) /* in: number of bytes; if the heap is allowed
to grow into the buffer pool, this must be
<= MEM_MAX_ALLOC_IN_BUF */
{
return (mem_heap_alloc(heap, n));
}
/*********************************************************************
NOTE: Use the corresponding macros instead of this function. Creates a
memory heap. For debugging purposes, takes also the file name and line as
argument. */
mem_heap_t*
mem_heap_create_func_noninline(
/*===========================*/
/* out, own: memory heap, NULL if
did not succeed (only possible for
MEM_HEAP_BTR_SEARCH type heaps)*/
ulint n, /* in: desired start block size,
this means that a single user buffer
of size n will fit in the block,
0 creates a default size block;
if init_block is not NULL, n tells
its size in bytes */
ulint type, /* in: heap type */
const char* file_name, /* in: file name where created */
ulint line) /* in: line where created */
{
return(mem_heap_create_func(n, type, file_name, line));
}
/*********************************************************************
NOTE: Use the corresponding macro instead of this function. Frees the space
occupied by a memory heap. In the debug version erases the heap memory
blocks. */
void
mem_heap_free_func_noninline(
/*=========================*/
mem_heap_t* heap, /* in, own: heap to be freed */
const char* file_name __attribute__((unused)),
/* in: file name where freed */
ulint line __attribute__((unused)))
{
mem_heap_free_func(heap, file_name, line);
}

View File

@@ -0,0 +1,974 @@
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a)) engine = innodb;
insert into t1 values (5,5,'oo','oo'),(4,4,'tr','tr'),(3,4,'ad','ad'),(2,3,'ak','ak');
commit;
alter table t1 add index b (b), add index b (b);
ERROR 42000: Duplicate key name 'b'
alter table t1 add index (b,b);
ERROR 42S21: Duplicate column name 'b'
alter table t1 add index d2 (d);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `d2` (`d`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL d2 23 NULL 4
select * from t1 order by d;
a b c d
3 4 ad ad
2 3 ak ak
5 5 oo oo
4 4 tr tr
alter table t1 add unique index (b);
ERROR 23000: Duplicate entry '0' for key 'b'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `d2` (`d`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
alter table t1 add index (b);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `d2` (`d`),
KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
alter table t1 add unique index (c), add index (d);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `c` (`c`),
KEY `d2` (`d`),
KEY `b` (`b`),
KEY `d` (`d`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL c 11 NULL 4
select * from t1 order by c;
a b c d
3 4 ad ad
2 3 ak ak
5 5 oo oo
4 4 tr tr
alter table t1 drop index b, add index (b);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `c` (`c`),
KEY `d2` (`d`),
KEY `d` (`d`),
KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
insert into t1 values(6,1,'ggg','ggg');
select * from t1;
a b c d
2 3 ak ak
3 4 ad ad
4 4 tr tr
5 5 oo oo
6 1 ggg ggg
select * from t1 order by b;
a b c d
6 1 ggg ggg
2 3 ak ak
3 4 ad ad
4 4 tr tr
5 5 oo oo
select * from t1 order by c;
a b c d
3 4 ad ad
2 3 ak ak
6 1 ggg ggg
5 5 oo oo
4 4 tr tr
select * from t1 order by d;
a b c d
3 4 ad ad
2 3 ak ak
6 1 ggg ggg
5 5 oo oo
4 4 tr tr
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL b 5 NULL 5
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL c 11 NULL 5
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL d2 23 NULL 5
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `c` (`c`),
KEY `d2` (`d`),
KEY `d` (`d`),
KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop table t1;
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a)) engine = innodb;
insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,3,'ad','ad'),(4,4,'afe','afe');
commit;
alter table t1 add index (c(2));
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `c` (`c`(2))
) ENGINE=InnoDB DEFAULT CHARSET=latin1
alter table t1 add unique index (d(10));
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `d` (`d`(10)),
KEY `c` (`c`(2))
) ENGINE=InnoDB DEFAULT CHARSET=latin1
insert into t1 values(5,1,'ggg','ggg');
select * from t1;
a b c d
1 1 ab ab
2 2 ac ac
3 3 ad ad
4 4 afe afe
5 1 ggg ggg
select * from t1 order by b;
a b c d
1 1 ab ab
5 1 ggg ggg
2 2 ac ac
3 3 ad ad
4 4 afe afe
select * from t1 order by c;
a b c d
1 1 ab ab
2 2 ac ac
3 3 ad ad
4 4 afe afe
5 1 ggg ggg
select * from t1 order by d;
a b c d
1 1 ab ab
2 2 ac ac
3 3 ad ad
4 4 afe afe
5 1 ggg ggg
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using filesort
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using filesort
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using filesort
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `d` (`d`(10)),
KEY `c` (`c`(2))
) ENGINE=InnoDB DEFAULT CHARSET=latin1
alter table t1 drop index d;
insert into t1 values(8,9,'fff','fff');
select * from t1;
a b c d
1 1 ab ab
2 2 ac ac
3 3 ad ad
4 4 afe afe
5 1 ggg ggg
8 9 fff fff
select * from t1 order by b;
a b c d
1 1 ab ab
5 1 ggg ggg
2 2 ac ac
3 3 ad ad
4 4 afe afe
8 9 fff fff
select * from t1 order by c;
a b c d
1 1 ab ab
2 2 ac ac
3 3 ad ad
4 4 afe afe
8 9 fff fff
5 1 ggg ggg
select * from t1 order by d;
a b c d
1 1 ab ab
2 2 ac ac
3 3 ad ad
4 4 afe afe
8 9 fff fff
5 1 ggg ggg
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 6 Using filesort
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 6 Using filesort
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 6 Using filesort
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `c` (`c`(2))
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop table t1;
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a)) engine = innodb;
insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,2,'ad','ad'),(4,4,'afe','afe');
commit;
alter table t1 add unique index (b,c);
insert into t1 values(8,9,'fff','fff');
select * from t1;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
select * from t1 order by b;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
select * from t1 order by c;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
select * from t1 order by d;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL b 16 NULL 5
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using filesort
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using filesort
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `b` (`b`,`c`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
alter table t1 add index (b,c);
insert into t1 values(11,11,'kkk','kkk');
select * from t1;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
11 11 kkk kkk
select * from t1 order by b;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
11 11 kkk kkk
select * from t1 order by c;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
11 11 kkk kkk
select * from t1 order by d;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
11 11 kkk kkk
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL b 16 NULL 6
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 6 Using filesort
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 6 Using filesort
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `b` (`b`,`c`),
KEY `b_2` (`b`,`c`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
alter table t1 add unique index (c,d);
insert into t1 values(13,13,'yyy','aaa');
select * from t1;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
11 11 kkk kkk
13 13 yyy aaa
select * from t1 order by b;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
11 11 kkk kkk
13 13 yyy aaa
select * from t1 order by c;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
11 11 kkk kkk
13 13 yyy aaa
select * from t1 order by d;
a b c d
13 13 yyy aaa
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
11 11 kkk kkk
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL b 16 NULL 7
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL c 34 NULL 7
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 7 Using filesort
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `b` (`b`,`c`),
UNIQUE KEY `c` (`c`,`d`),
KEY `b_2` (`b`,`c`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop table t1;
create table t1(a int not null, b int not null, c int, primary key (a), key (b)) engine = innodb;
create table t3(a int not null, c int not null, d int, primary key (a), key (c)) engine = innodb;
create table t4(a int not null, d int not null, e int, primary key (a), key (d)) engine = innodb;
create table t2(a int not null, b int not null, c int not null, d int not null, e int,
primary key (a), foreign key (b) references t1(b), foreign key (c) references t3(c),
foreign key (d) references t4(d)) engine = innodb;
alter table t1 drop index b;
ERROR HY000: Cannot drop index 'b': needed in a foreign key constraint
alter table t3 drop index c;
ERROR HY000: Cannot drop index 'c': needed in a foreign key constraint
alter table t4 drop index d;
ERROR HY000: Cannot drop index 'd': needed in a foreign key constraint
alter table t2 drop index b;
ERROR HY000: Cannot drop index 'b': needed in a foreign key constraint
alter table t2 drop index b, drop index c, drop index d;
ERROR HY000: Cannot drop index 'b': needed in a foreign key constraint
set foreign_key_checks=0;
insert into t1 values (1,1,1);
insert into t3 values (1,1,1);
insert into t4 values (1,1,1);
insert into t2 values (1,1,1,1,1);
commit;
alter table t2 drop index b, add index (b);
show create table t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) NOT NULL,
`b` int(11) NOT NULL,
`c` int(11) NOT NULL,
`d` int(11) NOT NULL,
`e` int(11) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `c` (`c`),
KEY `d` (`d`),
KEY `b` (`b`),
CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`b`) REFERENCES `t1` (`b`),
CONSTRAINT `t2_ibfk_2` FOREIGN KEY (`c`) REFERENCES `t3` (`c`),
CONSTRAINT `t2_ibfk_3` FOREIGN KEY (`d`) REFERENCES `t4` (`d`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
set foreign_key_checks=1;
set foreign_key_checks=0;
drop table if exists t1,t2,t3,t4;
set foreign_key_checks=1;
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a))
engine = innodb default charset=utf8;
insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,2,'ad','ad'),(4,4,'afe','afe');
commit;
alter table t1 add unique index (b);
ERROR 23000: Duplicate entry '0' for key 'b'
insert into t1 values(8,9,'fff','fff');
select * from t1;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
select * from t1 order by b;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
select * from t1 order by c;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
select * from t1 order by d;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using filesort
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using filesort
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using filesort
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
alter table t1 add index (b);
insert into t1 values(10,10,'kkk','iii');
select * from t1;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
10 10 kkk iii
select * from t1 order by b;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
10 10 kkk iii
select * from t1 order by c;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
10 10 kkk iii
select * from t1 order by d;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
10 10 kkk iii
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL b 5 NULL 6
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 6 Using filesort
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 6 Using filesort
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
alter table t1 add unique index (c), add index (d);
insert into t1 values(11,11,'aaa','mmm');
select * from t1;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
10 10 kkk iii
11 11 aaa mmm
select * from t1 order by b;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
10 10 kkk iii
11 11 aaa mmm
select * from t1 order by c;
a b c d
11 11 aaa mmm
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
10 10 kkk iii
select * from t1 order by d;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
10 10 kkk iii
11 11 aaa mmm
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL b 5 NULL 7
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL c 31 NULL 7
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL d 63 NULL 7
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `c` (`c`),
KEY `b` (`b`),
KEY `d` (`d`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
check table t1;
Table Op Msg_type Msg_text
test.t1 check status OK
drop table t1;
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a))
engine = innodb default charset=ucs2;
insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,2,'ad','ad'),(4,4,'afe','afe');
commit;
alter table t1 add unique index (b);
ERROR 23000: Duplicate entry '0' for key 'b'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=ucs2
alter table t1 add index (b);
insert into t1 values(8,9,'fff','fff');
select * from t1;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
select * from t1 order by b;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
select * from t1 order by c;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
select * from t1 order by d;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL b 5 NULL 5
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using filesort
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using filesort
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=ucs2
alter table t1 add unique index (c), add index (d);
insert into t1 values(10,10,'aaa','kkk');
select * from t1;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
10 10 aaa kkk
select * from t1 order by b;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
10 10 aaa kkk
select * from t1 order by c;
a b c d
10 10 aaa kkk
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
select * from t1 order by d;
a b c d
1 1 ab ab
2 2 ac ac
3 2 ad ad
4 4 afe afe
8 9 fff fff
10 10 aaa kkk
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL b 5 NULL 6
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL c 21 NULL 6
explain select * from t1 order by d;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL d 43 NULL 6
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `c` (`c`),
KEY `b` (`b`),
KEY `d` (`d`)
) ENGINE=InnoDB DEFAULT CHARSET=ucs2
check table t1;
Table Op Msg_type Msg_text
test.t1 check status OK
drop table t1;
create table t1(a int not null, b int) engine = innodb;
insert into t1 values (1,1),(1,1),(1,1),(1,1);
alter table t1 add unique index (a);
ERROR 23000: Duplicate entry '0' for key 'a'
alter table t1 add unique index (b);
ERROR 23000: Duplicate entry '0' for key 'b'
alter table t1 add unique index (a), add unique index(b);
ERROR 23000: Duplicate entry '0' for key 'a'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop table t1;
create table t1(a int not null, c int not null,b int, primary key(a), unique key(c), key(b)) engine = innodb;
alter table t1 drop index c, drop index b;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`c` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop table t1;
create table t1(a int not null, b int, primary key(a)) engine = innodb;
alter table t1 add index (b);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop table t1;
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a)) engine = innodb;
insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,3,'ac','ac'),(4,4,'afe','afe');
alter table t1 add unique index (b), add unique index (c), add unique index (d);
ERROR 23000: Duplicate entry '' for key 'c'
alter table t1 add unique index (b), add index (d), add unique index (c);
ERROR 23000: Duplicate entry '' for key 'c'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` char(10) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop table t1;
create table t1(a int not null, b int not null, c int, primary key (a), key(c)) engine=innodb;
insert into t1 values (5,1,5),(4,2,4),(3,3,3),(2,4,2),(1,5,1);
alter table t1 add unique index (b);
insert into t1 values (10,20,20),(11,19,19),(12,18,18),(13,17,17);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `b` (`b`),
KEY `c` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
check table t1;
Table Op Msg_type Msg_text
test.t1 check status OK
explain select * from t1 order by c;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL c 5 NULL 9
explain select * from t1 order by a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL PRIMARY 4 NULL 9
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL b 4 NULL 9
select * from t1 order by a;
a b c
1 5 1
2 4 2
3 3 3
4 2 4
5 1 5
10 20 20
11 19 19
12 18 18
13 17 17
select * from t1 order by b;
a b c
5 1 5
4 2 4
3 3 3
2 4 2
1 5 1
13 17 17
12 18 18
11 19 19
10 20 20
select * from t1 order by c;
a b c
1 5 1
2 4 2
3 3 3
4 2 4
5 1 5
13 17 17
12 18 18
11 19 19
10 20 20
drop table t1;
create table t1(a int not null, b int not null) engine=innodb;
insert into t1 values (1,1);
alter table t1 add primary key(b);
insert into t1 values (2,2);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) NOT NULL,
PRIMARY KEY (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
check table t1;
Table Op Msg_type Msg_text
test.t1 check status OK
select * from t1;
a b
1 1
2 2
explain select * from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 2
explain select * from t1 order by a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using filesort
explain select * from t1 order by b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL PRIMARY 4 NULL 2
checksum table t1;
Table Checksum
test.t1 582702641
drop table t1;
create table t1(a int not null) engine=innodb;
insert into t1 values (1);
alter table t1 add primary key(a);
insert into t1 values (2);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
check table t1;
Table Op Msg_type Msg_text
test.t1 check status OK
commit;
select * from t1;
a
1
2
explain select * from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL PRIMARY 4 NULL 2 Using index
explain select * from t1 order by a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL PRIMARY 4 NULL 2 Using index
checksum table t1;
Table Checksum
test.t1 1531596814
drop table t1;
create table t1(a int, b blob,c text) engine=innodb default charset = utf8;
insert into t1 values (1,repeat('jejdkrun87',220),repeat('jejdkrun87',440));
insert into t1 values (2,repeat('adfd72nh9k',440),repeat('adfd72nh9k',1100));
checksum table t1;
Table Checksum
test.t1 1121933170
alter table t1 add primary key (a), add key (b(20));
checksum table t1;
Table Checksum
test.t1 335046842
insert into t1 values (3,repeat('adfdpplkeock',440),repeat('adfdpplkeock',1100));
insert into t1 values (4,repeat('adfdijnmnb78k',440),repeat('adfdijnmnb78k',1100));
insert into t1 values (5,repeat('adfdijn0loKNHJik',440),repeat('adfdijn0loKNHJik',1100));
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL DEFAULT '0',
`b` blob,
`c` text,
PRIMARY KEY (`a`),
KEY `b` (`b`(20))
) ENGINE=InnoDB DEFAULT CHARSET=utf8
check table t1;
Table Op Msg_type Msg_text
test.t1 check status OK
explain select * from t1 where b like 'adfd%';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range b b 23 NULL 2 Using where
checksum table t1;
Table Checksum
test.t1 1008226368
drop table t1;

View File

@@ -0,0 +1,282 @@
-- source include/have_innodb.inc
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a)) engine = innodb;
insert into t1 values (5,5,'oo','oo'),(4,4,'tr','tr'),(3,4,'ad','ad'),(2,3,'ak','ak');
commit;
--error 1061
alter table t1 add index b (b), add index b (b);
--error 1060
alter table t1 add index (b,b);
alter table t1 add index d2 (d);
show create table t1;
explain select * from t1 order by d;
select * from t1 order by d;
--error 1582
alter table t1 add unique index (b);
show create table t1;
alter table t1 add index (b);
show create table t1;
alter table t1 add unique index (c), add index (d);
show create table t1;
explain select * from t1 order by c;
select * from t1 order by c;
alter table t1 drop index b, add index (b);
show create table t1;
insert into t1 values(6,1,'ggg','ggg');
select * from t1;
select * from t1 order by b;
select * from t1 order by c;
select * from t1 order by d;
explain select * from t1 order by b;
explain select * from t1 order by c;
explain select * from t1 order by d;
show create table t1;
drop table t1;
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a)) engine = innodb;
insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,3,'ad','ad'),(4,4,'afe','afe');
commit;
alter table t1 add index (c(2));
show create table t1;
alter table t1 add unique index (d(10));
show create table t1;
insert into t1 values(5,1,'ggg','ggg');
select * from t1;
select * from t1 order by b;
select * from t1 order by c;
select * from t1 order by d;
explain select * from t1 order by b;
explain select * from t1 order by c;
explain select * from t1 order by d;
show create table t1;
alter table t1 drop index d;
insert into t1 values(8,9,'fff','fff');
select * from t1;
select * from t1 order by b;
select * from t1 order by c;
select * from t1 order by d;
explain select * from t1 order by b;
explain select * from t1 order by c;
explain select * from t1 order by d;
show create table t1;
drop table t1;
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a)) engine = innodb;
insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,2,'ad','ad'),(4,4,'afe','afe');
commit;
alter table t1 add unique index (b,c);
insert into t1 values(8,9,'fff','fff');
select * from t1;
select * from t1 order by b;
select * from t1 order by c;
select * from t1 order by d;
explain select * from t1 order by b;
explain select * from t1 order by c;
explain select * from t1 order by d;
show create table t1;
alter table t1 add index (b,c);
insert into t1 values(11,11,'kkk','kkk');
select * from t1;
select * from t1 order by b;
select * from t1 order by c;
select * from t1 order by d;
explain select * from t1 order by b;
explain select * from t1 order by c;
explain select * from t1 order by d;
show create table t1;
alter table t1 add unique index (c,d);
insert into t1 values(13,13,'yyy','aaa');
select * from t1;
select * from t1 order by b;
select * from t1 order by c;
select * from t1 order by d;
explain select * from t1 order by b;
explain select * from t1 order by c;
explain select * from t1 order by d;
show create table t1;
drop table t1;
create table t1(a int not null, b int not null, c int, primary key (a), key (b)) engine = innodb;
create table t3(a int not null, c int not null, d int, primary key (a), key (c)) engine = innodb;
create table t4(a int not null, d int not null, e int, primary key (a), key (d)) engine = innodb;
create table t2(a int not null, b int not null, c int not null, d int not null, e int,
primary key (a), foreign key (b) references t1(b), foreign key (c) references t3(c),
foreign key (d) references t4(d)) engine = innodb;
--error 1542
alter table t1 drop index b;
--error 1542
alter table t3 drop index c;
--error 1542
alter table t4 drop index d;
--error 1542
alter table t2 drop index b;
--error 1542
alter table t2 drop index b, drop index c, drop index d;
set foreign_key_checks=0;
insert into t1 values (1,1,1);
insert into t3 values (1,1,1);
insert into t4 values (1,1,1);
insert into t2 values (1,1,1,1,1);
commit;
alter table t2 drop index b, add index (b);
show create table t2;
set foreign_key_checks=1;
set foreign_key_checks=0;
--disable_warnings
drop table if exists t1,t2,t3,t4;
--enable_warnings
set foreign_key_checks=1;
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a))
engine = innodb default charset=utf8;
insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,2,'ad','ad'),(4,4,'afe','afe');
commit;
--error 1582
alter table t1 add unique index (b);
insert into t1 values(8,9,'fff','fff');
select * from t1;
select * from t1 order by b;
select * from t1 order by c;
select * from t1 order by d;
explain select * from t1 order by b;
explain select * from t1 order by c;
explain select * from t1 order by d;
show create table t1;
alter table t1 add index (b);
insert into t1 values(10,10,'kkk','iii');
select * from t1;
select * from t1 order by b;
select * from t1 order by c;
select * from t1 order by d;
explain select * from t1 order by b;
explain select * from t1 order by c;
explain select * from t1 order by d;
show create table t1;
alter table t1 add unique index (c), add index (d);
insert into t1 values(11,11,'aaa','mmm');
select * from t1;
select * from t1 order by b;
select * from t1 order by c;
select * from t1 order by d;
explain select * from t1 order by b;
explain select * from t1 order by c;
explain select * from t1 order by d;
show create table t1;
check table t1;
drop table t1;
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a))
engine = innodb default charset=ucs2;
insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,2,'ad','ad'),(4,4,'afe','afe');
commit;
--error 1582
alter table t1 add unique index (b);
show create table t1;
alter table t1 add index (b);
insert into t1 values(8,9,'fff','fff');
select * from t1;
select * from t1 order by b;
select * from t1 order by c;
select * from t1 order by d;
explain select * from t1 order by b;
explain select * from t1 order by c;
explain select * from t1 order by d;
show create table t1;
alter table t1 add unique index (c), add index (d);
insert into t1 values(10,10,'aaa','kkk');
select * from t1;
select * from t1 order by b;
select * from t1 order by c;
select * from t1 order by d;
explain select * from t1 order by b;
explain select * from t1 order by c;
explain select * from t1 order by d;
show create table t1;
check table t1;
drop table t1;
create table t1(a int not null, b int) engine = innodb;
insert into t1 values (1,1),(1,1),(1,1),(1,1);
--error 1582
alter table t1 add unique index (a);
--error 1582
alter table t1 add unique index (b);
--error 1582
alter table t1 add unique index (a), add unique index(b);
show create table t1;
drop table t1;
create table t1(a int not null, c int not null,b int, primary key(a), unique key(c), key(b)) engine = innodb;
alter table t1 drop index c, drop index b;
show create table t1;
drop table t1;
create table t1(a int not null, b int, primary key(a)) engine = innodb;
alter table t1 add index (b);
show create table t1;
drop table t1;
create table t1(a int not null, b int, c char(10), d varchar(20), primary key (a)) engine = innodb;
insert into t1 values (1,1,'ab','ab'),(2,2,'ac','ac'),(3,3,'ac','ac'),(4,4,'afe','afe');
--error 1582
alter table t1 add unique index (b), add unique index (c), add unique index (d);
--error 1582
alter table t1 add unique index (b), add index (d), add unique index (c);
show create table t1;
drop table t1;
create table t1(a int not null, b int not null, c int, primary key (a), key(c)) engine=innodb;
insert into t1 values (5,1,5),(4,2,4),(3,3,3),(2,4,2),(1,5,1);
alter table t1 add unique index (b);
insert into t1 values (10,20,20),(11,19,19),(12,18,18),(13,17,17);
show create table t1;
check table t1;
explain select * from t1 order by c;
explain select * from t1 order by a;
explain select * from t1 order by b;
select * from t1 order by a;
select * from t1 order by b;
select * from t1 order by c;
drop table t1;
create table t1(a int not null, b int not null) engine=innodb;
insert into t1 values (1,1);
alter table t1 add primary key(b);
insert into t1 values (2,2);
show create table t1;
check table t1;
select * from t1;
explain select * from t1;
explain select * from t1 order by a;
explain select * from t1 order by b;
checksum table t1;
drop table t1;
create table t1(a int not null) engine=innodb;
insert into t1 values (1);
alter table t1 add primary key(a);
insert into t1 values (2);
show create table t1;
check table t1;
commit;
select * from t1;
explain select * from t1;
explain select * from t1 order by a;
checksum table t1;
drop table t1;
create table t1(a int, b blob,c text) engine=innodb default charset = utf8;
insert into t1 values (1,repeat('jejdkrun87',220),repeat('jejdkrun87',440));
insert into t1 values (2,repeat('adfd72nh9k',440),repeat('adfd72nh9k',1100));
checksum table t1;
alter table t1 add primary key (a), add key (b(20));
checksum table t1;
insert into t1 values (3,repeat('adfdpplkeock',440),repeat('adfdpplkeock',1100));
insert into t1 values (4,repeat('adfdijnmnb78k',440),repeat('adfdijnmnb78k',1100));
insert into t1 values (5,repeat('adfdijn0loKNHJik',440),repeat('adfdijn0loKNHJik',1100));
show create table t1;
check table t1;
explain select * from t1 where b like 'adfd%';
checksum table t1;
drop table t1;

View File

@@ -1095,7 +1095,7 @@ show create table t2;
create index id2 on t2 (id); create index id2 on t2 (id);
show create table t2; show create table t2;
drop index id2 on t2; drop index id2 on t2;
--error 1025,1025 --error 1540,1540
drop index id on t2; drop index id on t2;
show create table t2; show create table t2;
drop table t2; drop table t2;

View File

@@ -497,17 +497,6 @@ os_io_init_simple(void)
} }
} }
#if !defined(UNIV_HOTBACKUP) && !defined(__NETWARE__)
/*************************************************************************
Creates a temporary file that will be deleted on close.
This function is defined in ha_innodb.cc. */
int
innobase_mysql_tmpfile(void);
/*========================*/
/* out: temporary file descriptor, or < 0 on error */
#endif /* !UNIV_HOTBACKUP && !__NETWARE__ */
/*************************************************************************** /***************************************************************************
Creates a temporary file. This function is like tmpfile(3), but Creates a temporary file. This function is like tmpfile(3), but
the temporary file is created in the MySQL temporary directory. the temporary file is created in the MySQL temporary directory.

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* A Bison parser, made by GNU Bison 1.875d. */ /* A Bison parser, made by GNU Bison 2.0. */
/* Skeleton parser for Yacc-like parsing with Bison, /* Skeleton parser for Yacc-like parsing with Bison,
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.

View File

@@ -336,6 +336,9 @@ que_fork_start_command(
fork->last_sel_node = NULL; fork->last_sel_node = NULL;
suspended_thr = NULL;
completed_thr = NULL;
/* Choose the query thread to run: usually there is just one thread, /* Choose the query thread to run: usually there is just one thread,
but in a parallelized select, which necessarily is non-scrollable, but in a parallelized select, which necessarily is non-scrollable,
there may be several to choose from */ there may be several to choose from */

View File

@@ -17,7 +17,7 @@ include ../include/Makefile.i
noinst_LIBRARIES = librow.a noinst_LIBRARIES = librow.a
librow_a_SOURCES = row0ext.c\ librow_a_SOURCES = row0ext.c row0merge.c\
row0ins.c row0mysql.c row0purge.c row0row.c row0sel.c\ row0ins.c row0mysql.c row0purge.c row0row.c row0sel.c\
row0uins.c row0umod.c row0undo.c row0upd.c row0vers.c row0uins.c row0umod.c row0undo.c row0upd.c row0vers.c

View File

@@ -103,7 +103,7 @@ ins_node_create(
/*************************************************************** /***************************************************************
Creates an entry template for each index of a table. */ Creates an entry template for each index of a table. */
static
void void
ins_node_create_entry_list( ins_node_create_entry_list(
/*=======================*/ /*=======================*/

2231
row/row0merge.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -25,12 +25,15 @@ Created 9/17/2000 Heikki Tuuri
#include "dict0boot.h" #include "dict0boot.h"
#include "trx0roll.h" #include "trx0roll.h"
#include "trx0purge.h" #include "trx0purge.h"
#include "trx0rec.h"
#include "trx0undo.h"
#include "lock0lock.h" #include "lock0lock.h"
#include "rem0cmp.h" #include "rem0cmp.h"
#include "log0log.h" #include "log0log.h"
#include "btr0sea.h" #include "btr0sea.h"
#include "fil0fil.h" #include "fil0fil.h"
#include "ibuf0ibuf.h" #include "ibuf0ibuf.h"
#include "row0merge.h"
/* A dummy variable used to fool the compiler */ /* A dummy variable used to fool the compiler */
ibool row_mysql_identically_false = FALSE; ibool row_mysql_identically_false = FALSE;
@@ -79,6 +82,9 @@ row_mysql_is_system_table(
} }
#endif /* !UNIV_HOTBACKUP */ #endif /* !UNIV_HOTBACKUP */
static ibool row_add_table_to_background_drop_list(dict_table_t* table);
/*====================================================================*/
/*********************************************************************** /***********************************************************************
Delays an INSERT, DELETE or UPDATE operation if the purge is lagging. */ Delays an INSERT, DELETE or UPDATE operation if the purge is lagging. */
static static
@@ -548,7 +554,6 @@ handle_new_error:
"InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
"forcing-recovery.html" "forcing-recovery.html"
" for help.\n", stderr); " for help.\n", stderr);
} else { } else {
fprintf(stderr, "InnoDB: unknown error code %lu\n", fprintf(stderr, "InnoDB: unknown error code %lu\n",
(ulong) err); (ulong) err);
@@ -656,9 +661,39 @@ row_create_prebuilt(
prebuilt->old_vers_heap = NULL; prebuilt->old_vers_heap = NULL;
UT_LIST_ADD_LAST(prebuilts, table->prebuilts, prebuilt);
return(prebuilt); return(prebuilt);
} }
/************************************************************************
Update a prebuilt struct for a MySQL table handle. */
void
row_update_prebuilt(
/*================*/
row_prebuilt_t* prebuilt, /* in: Innobase table handle */
dict_table_t* table) /* in: table */
{
dict_index_t* clust_index;
ut_ad(prebuilt && prebuilt->heap && table);
ut_ad(prebuilt->magic_n == ROW_PREBUILT_OBSOLETE);
prebuilt->magic_n = ROW_PREBUILT_ALLOCATED;
prebuilt->magic_n2 = ROW_PREBUILT_ALLOCATED;
clust_index = dict_table_get_first_index(table);
if (!prebuilt->index) {
prebuilt->index = clust_index;
}
if (prebuilt->ins_node) {
ins_node_create_entry_list(prebuilt->ins_node);
}
}
/************************************************************************ /************************************************************************
Free a prebuilt struct for a MySQL table handle. */ Free a prebuilt struct for a MySQL table handle. */
@@ -669,12 +704,15 @@ row_prebuilt_free(
{ {
ulint i; ulint i;
if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED if ((prebuilt->magic_n != ROW_PREBUILT_ALLOCATED
|| prebuilt->magic_n2 != ROW_PREBUILT_ALLOCATED) { && prebuilt->magic_n != ROW_PREBUILT_OBSOLETE)
|| (prebuilt->magic_n2 != ROW_PREBUILT_ALLOCATED
&& prebuilt->magic_n != ROW_PREBUILT_OBSOLETE)) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n" "InnoDB: Error: trying to free a corrupt\n"
"InnoDB: table handle. Magic n %lu," "InnoDB: table handle. Magic n %lu,"
" magic n2 %lu, table name", " magic n2 %lu, table name ",
(ulong) prebuilt->magic_n, (ulong) prebuilt->magic_n,
(ulong) prebuilt->magic_n2); (ulong) prebuilt->magic_n2);
ut_print_name(stderr, NULL, TRUE, prebuilt->table->name); ut_print_name(stderr, NULL, TRUE, prebuilt->table->name);
@@ -738,6 +776,35 @@ row_prebuilt_free(
dict_table_decrement_handle_count(prebuilt->table); dict_table_decrement_handle_count(prebuilt->table);
/* If there were references to this table when a primary index on
this table was created then we drop it here since there are no
references to it now.*/
if (prebuilt->table->to_be_dropped
&& prebuilt->table->n_mysql_handles_opened == 0) {
ibool added;
added = row_add_table_to_background_drop_list(prebuilt->table);
assert(*prebuilt->table->name == TEMP_TABLE_PREFIX);
ut_print_timestamp(stderr);
if (added) {
fputs(" InnoDB: Dropping table ", stderr);
ut_print_name(stderr, NULL, TRUE,
prebuilt->table->name);
putc('\n', stderr);
} else {
fputs(" InnoDB: Error: failed trying to add ",
stderr);
ut_print_name(stderr, NULL, TRUE,
prebuilt->table->name);
fputs(" to the background drop list.\n", stderr);
}
}
UT_LIST_REMOVE(prebuilts, prebuilt->table->prebuilts, prebuilt);
mem_heap_free(prebuilt->heap); mem_heap_free(prebuilt->heap);
} }
@@ -767,9 +834,9 @@ row_update_prebuilt_trx(
if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: trying to use a corrupt\n" "InnoDB: Error: trying to use a corrupt\n"
"InnoDB: table handle. Magic n %lu, table name", "InnoDB: table handle. Magic n %lu, table name ",
(ulong) prebuilt->magic_n); (ulong) prebuilt->magic_n);
ut_print_name(stderr, NULL, TRUE, prebuilt->table->name); ut_print_name(stderr, trx, TRUE, prebuilt->table->name);
putc('\n', stderr); putc('\n', stderr);
mem_analyze_corruption(prebuilt); mem_analyze_corruption(prebuilt);
@@ -1048,6 +1115,88 @@ run_again:
return((int) err); return((int) err);
} }
/*************************************************************************
Sets a table lock on the table */
int
row_lock_table_for_merge(
/*=====================*/
/* out: error code or DB_SUCCESS */
trx_t* trx, /* in: lock table for this trx */
dict_table_t* table, /* in: table to lock */
ulint mode) /* in: lock mode of table */
{
mem_heap_t* heap; /* Memory heap */
que_thr_t* thr;
ulint err;
sel_node_t* node;
ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
heap = mem_heap_create(512);
trx->op_info = "setting table lock for index merge";
node = sel_node_create(heap);
thr = pars_complete_graph_for_exec(node, trx, heap);
/* SB: Not sure about this - Ask Heikki */
thr->graph->state = QUE_FORK_ACTIVE;
/* We use the select query graph as the dummy graph needed
in the lock module call */
thr = que_fork_get_first_thr(que_node_get_parent(thr));
que_thr_move_to_run_state_for_mysql(thr, trx);
run_again:
thr->run_node = thr;
thr->prev_node = thr->common.parent;
err = lock_table(0, table, mode, thr);
trx->error_state = err;
if (err != DB_SUCCESS) {
que_thr_stop_for_mysql(thr);
if (err != DB_QUE_THR_SUSPENDED) {
ibool was_lock_wait;
was_lock_wait = row_mysql_handle_errors(
&err, trx, thr, NULL);
if (was_lock_wait) {
goto run_again;
}
} else {
que_thr_t* run_thr;
que_node_t* parent;
parent = que_node_get_parent(thr);
run_thr = que_fork_start_command(parent);
ut_a(run_thr == thr);
/* There was a lock wait but the thread was not
in a ready to run or running state.*/
trx->error_state = DB_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. */ Does an insert for MySQL. */
@@ -1065,6 +1214,7 @@ row_insert_for_mysql(
ibool was_lock_wait; ibool was_lock_wait;
trx_t* trx = prebuilt->trx; trx_t* trx = prebuilt->trx;
ins_node_t* node = prebuilt->ins_node; ins_node_t* node = prebuilt->ins_node;
dict_table_t* table;
ut_ad(trx); ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
@@ -1087,13 +1237,13 @@ row_insert_for_mysql(
return(DB_ERROR); return(DB_ERROR);
} }
if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED
&& prebuilt->magic_n != ROW_PREBUILT_OBSOLETE) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n" "InnoDB: Error: trying to free a corrupt\n"
"InnoDB: table handle. Magic n %lu, table name", "InnoDB: table handle. Magic n %lu, table name ",
(ulong) prebuilt->magic_n); (ulong) prebuilt->magic_n);
ut_print_name(stderr, prebuilt->trx, TRUE, ut_print_name(stderr, trx, TRUE, prebuilt->table->name);
prebuilt->table->name);
putc('\n', stderr); putc('\n', stderr);
mem_analyze_corruption(prebuilt); mem_analyze_corruption(prebuilt);
@@ -1101,6 +1251,10 @@ row_insert_for_mysql(
ut_error; ut_error;
} }
if (prebuilt->magic_n == ROW_PREBUILT_OBSOLETE) {
row_update_prebuilt(prebuilt, prebuilt->table);
}
if (srv_created_new_raw || srv_force_recovery) { if (srv_created_new_raw || srv_force_recovery) {
fputs("InnoDB: A new raw disk partition was initialized or\n" fputs("InnoDB: A new raw disk partition was initialized or\n"
"InnoDB: innodb_force_recovery is on: we do not allow\n" "InnoDB: innodb_force_recovery is on: we do not allow\n"
@@ -1122,6 +1276,14 @@ row_insert_for_mysql(
if (node == NULL) { if (node == NULL) {
row_get_prebuilt_insert_row(prebuilt); row_get_prebuilt_insert_row(prebuilt);
node = prebuilt->ins_node; node = prebuilt->ins_node;
} else {
table = dict_table_get(prebuilt->table->name, FALSE);
if (prebuilt->ins_node->table_version_number !=
table->version_number) {
row_get_prebuilt_insert_row(prebuilt);
node = prebuilt->ins_node;
}
} }
row_mysql_convert_row_to_innobase(node->row, prebuilt, mysql_rec); row_mysql_convert_row_to_innobase(node->row, prebuilt, mysql_rec);
@@ -1143,6 +1305,10 @@ run_again:
thr->run_node = node; thr->run_node = node;
thr->prev_node = node; thr->prev_node = node;
if (prebuilt->magic_n == ROW_PREBUILT_OBSOLETE) {
row_update_prebuilt(prebuilt, prebuilt->table);
}
row_ins_step(thr); row_ins_step(thr);
err = trx->error_state; err = trx->error_state;
@@ -1325,13 +1491,13 @@ row_update_for_mysql(
return(DB_ERROR); return(DB_ERROR);
} }
if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED
&& prebuilt->magic_n != ROW_PREBUILT_OBSOLETE) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n" "InnoDB: Error: trying to free a corrupt\n"
"InnoDB: table handle. Magic n %lu, table name", "InnoDB: table handle. Magic n %lu, table name ",
(ulong) prebuilt->magic_n); (ulong) prebuilt->magic_n);
ut_print_name(stderr, prebuilt->trx, TRUE, ut_print_name(stderr, trx, TRUE, prebuilt->table->name);
prebuilt->table->name);
putc('\n', stderr); putc('\n', stderr);
mem_analyze_corruption(prebuilt); mem_analyze_corruption(prebuilt);
@@ -1339,6 +1505,10 @@ row_update_for_mysql(
ut_error; ut_error;
} }
if (prebuilt->magic_n == ROW_PREBUILT_OBSOLETE) {
row_update_prebuilt(prebuilt, prebuilt->table);
}
if (srv_created_new_raw || srv_force_recovery) { if (srv_created_new_raw || srv_force_recovery) {
fputs("InnoDB: A new raw disk partition was initialized or\n" fputs("InnoDB: A new raw disk partition was initialized or\n"
"InnoDB: innodb_force_recovery is on: we do not allow\n" "InnoDB: innodb_force_recovery is on: we do not allow\n"
@@ -1391,6 +1561,10 @@ run_again:
thr->run_node = node; thr->run_node = node;
thr->prev_node = node; thr->prev_node = node;
if (prebuilt->magic_n == ROW_PREBUILT_OBSOLETE) {
row_update_prebuilt(prebuilt, prebuilt->table);
}
row_upd_step(thr); row_upd_step(thr);
err = trx->error_state; err = trx->error_state;
@@ -1749,6 +1923,9 @@ row_create_table_for_mysql(
ulint table_name_len; ulint table_name_len;
ulint err; ulint err;
ulint i; ulint i;
ibool retry;
retry = FALSE;
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
#ifdef UNIV_SYNC_DEBUG #ifdef UNIV_SYNC_DEBUG
@@ -1868,6 +2045,7 @@ row_create_table_for_mysql(
heap = mem_heap_create(512); heap = mem_heap_create(512);
retry_create:
trx->dict_operation = TRUE; trx->dict_operation = TRUE;
node = tab_create_graph_create(table, heap); node = tab_create_graph_create(table, heap);
@@ -1884,9 +2062,10 @@ row_create_table_for_mysql(
trx->error_state = DB_SUCCESS; trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE, NULL);
if (err == DB_OUT_OF_FILE_SPACE) { if (err == DB_OUT_OF_FILE_SPACE) {
trx_general_rollback_for_mysql(trx, FALSE, NULL);
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fputs(" InnoDB: Warning: cannot create table ", fputs(" InnoDB: Warning: cannot create table ",
@@ -1903,30 +2082,51 @@ row_create_table_for_mysql(
} else if (err == DB_DUPLICATE_KEY) { } else if (err == DB_DUPLICATE_KEY) {
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fputs(" InnoDB: Error: table ", stderr); if (*table->name != TEMP_TABLE_PREFIX) {
ut_print_name(stderr, trx, TRUE, table->name); trx_general_rollback_for_mysql(
fputs(" already exists in InnoDB internal\n" trx, FALSE, NULL);
"InnoDB: data dictionary. Have you deleted"
" the .frm file\n" fputs(" InnoDB: Error: table ", stderr);
"InnoDB: and not used DROP TABLE?" ut_print_name(stderr, trx, TRUE, table->name);
" Have you used DROP DATABASE\n" fputs(" already exists in InnoDB internal\n"
"InnoDB: for InnoDB tables in" "InnoDB: data dictionary. Have you deleted"
" MySQL version <= 3.23.43?\n" " the .frm file\n"
"InnoDB: See the Restrictions section" "InnoDB: and not used DROP TABLE?"
" of the InnoDB manual.\n" " Have you used DROP DATABASE\n"
"InnoDB: You can drop the orphaned table" "InnoDB: for InnoDB tables in"
" inside InnoDB by\n" " MySQL version <= 3.23.43?\n"
"InnoDB: creating an InnoDB table with" "InnoDB: See the Restrictions section"
" the same name in another\n" " of the InnoDB manual.\n"
"InnoDB: database and copying the .frm file" "InnoDB: You can drop the orphaned table"
" to the current database.\n" " inside InnoDB by\n"
"InnoDB: Then MySQL thinks the table exists," "InnoDB: creating an InnoDB table with"
" and DROP TABLE will\n" " the same name in another\n"
"InnoDB: succeed.\n" "InnoDB: database and copying the .frm file"
"InnoDB: You can look for further help from\n" " to the current database.\n"
"InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" "InnoDB: Then MySQL thinks the table exists,"
"innodb-troubleshooting.html\n", " and DROP TABLE will\n"
stderr); "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);
} else if (!retry) {
fputs(" InnoDB: Warning: table ", stderr);
ut_print_name(stderr, trx, TRUE, table->name);
fputs(" already exists in InnoDB internal\n"
"InnoDB: dropping old temporary table\n",
stderr);
row_drop_table_for_mysql(table->name, trx,
FALSE);
retry = TRUE;
goto retry_create;
} else {
trx_general_rollback_for_mysql(
trx, FALSE, NULL);
}
} }
/* We may also get err == DB_ERROR if the .ibd file for the /* We may also get err == DB_ERROR if the .ibd file for the
@@ -1964,7 +2164,7 @@ row_create_index_for_mysql(
mem_heap_t* heap; mem_heap_t* heap;
que_thr_t* thr; que_thr_t* thr;
ulint err; ulint err;
ulint i, j; ulint i;
ulint len; ulint len;
#ifdef UNIV_SYNC_DEBUG #ifdef UNIV_SYNC_DEBUG
@@ -1982,11 +2182,12 @@ row_create_index_for_mysql(
safer not to allow them. */ safer not to allow them. */
for (i = 0; i < dict_index_get_n_fields(index); i++) { for (i = 0; i < dict_index_get_n_fields(index); i++) {
ulint j;
for (j = 0; j < i; j++) { for (j = 0; j < i; j++) {
if (0 == ut_strcmp( if (0 == ut_strcmp(
dict_index_get_nth_field(index, j)->name, dict_index_get_nth_field(index, j)->name,
dict_index_get_nth_field(index, i)->name)) { dict_index_get_nth_field(index, i)->name)) {
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
fputs(" InnoDB: Error: column ", stderr); fputs(" InnoDB: Error: column ", stderr);
@@ -2433,10 +2634,10 @@ row_discard_tablespace_for_mysql(
ut_print_timestamp(ef); ut_print_timestamp(ef);
fputs(" Cannot DISCARD table ", ef); fputs(" Cannot DISCARD table ", ef);
ut_print_name(ef, trx, TRUE, name); ut_print_name(stderr, trx, TRUE, name);
fputs("\n" fputs("\n"
"because it is referenced by ", ef); "because it is referenced by ", ef);
ut_print_name(ef, trx, TRUE, foreign->foreign_table_name); ut_print_name(stderr, trx, TRUE, foreign->foreign_table_name);
putc('\n', ef); putc('\n', ef);
mutex_exit(&dict_foreign_err_mutex); mutex_exit(&dict_foreign_err_mutex);
@@ -2761,10 +2962,10 @@ row_truncate_table_for_mysql(
ut_print_timestamp(ef); ut_print_timestamp(ef);
fputs(" Cannot truncate table ", ef); fputs(" Cannot truncate table ", ef);
ut_print_name(ef, trx, TRUE, table->name); ut_print_name(stderr, trx, TRUE, table->name);
fputs(" by DROP+CREATE\n" fputs(" by DROP+CREATE\n"
"InnoDB: because it is referenced by ", ef); "InnoDB: because it is referenced by ", ef);
ut_print_name(ef, trx, TRUE, foreign->foreign_table_name); ut_print_name(stderr, trx, TRUE, foreign->foreign_table_name);
putc('\n', ef); putc('\n', ef);
mutex_exit(&dict_foreign_err_mutex); mutex_exit(&dict_foreign_err_mutex);
@@ -2982,6 +3183,30 @@ row_drop_table_for_mysql(
const char* name, /* in: table name */ const char* name, /* in: table name */
trx_t* trx, /* in: transaction handle */ trx_t* trx, /* in: transaction handle */
ibool drop_db)/* in: TRUE=dropping whole database */ ibool drop_db)/* in: TRUE=dropping whole database */
{
ulint err;
err = row_drop_table_for_mysql_no_commit(name, trx, drop_db);
if (!srv_created_new_raw) {
trx_commit_for_mysql(trx);
}
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. */
int
row_drop_table_for_mysql_no_commit(
/*===============================*/
/* 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_foreign_t* foreign;
dict_table_t* table; dict_table_t* table;
@@ -3111,10 +3336,10 @@ check_next_foreign:
ut_print_timestamp(ef); ut_print_timestamp(ef);
fputs(" Cannot drop table ", ef); fputs(" Cannot drop table ", ef);
ut_print_name(ef, trx, TRUE, name); ut_print_name(stderr, trx, TRUE, name);
fputs("\n" fputs("\n"
"because it is referenced by ", ef); "because it is referenced by ", ef);
ut_print_name(ef, trx, TRUE, foreign->foreign_table_name); ut_print_name(stderr, trx, TRUE, foreign->foreign_table_name);
putc('\n', ef); putc('\n', ef);
mutex_exit(&dict_foreign_err_mutex); mutex_exit(&dict_foreign_err_mutex);
@@ -3131,20 +3356,25 @@ check_next_foreign:
added = row_add_table_to_background_drop_list(table); added = row_add_table_to_background_drop_list(table);
if (added) { if (added) {
ut_print_timestamp(stderr); /* Temporary tables can have read views and we don't
fputs(" InnoDB: Warning: MySQL is" print any warning. */
" trying to drop table ", stderr); if (*table->name != TEMP_TABLE_PREFIX) {
ut_print_name(stderr, trx, TRUE, table->name); ut_print_timestamp(stderr);
fputs("\n" fputs(" InnoDB: Warning: MySQL is"
"InnoDB: though there are still" " trying to drop table ", stderr);
" open handles to it.\n" ut_print_name(stderr, trx, TRUE, table->name);
"InnoDB: Adding the table to the" fputs("\n"
" background drop queue.\n", "InnoDB: though there are still"
stderr); " open handles to it.\n"
"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 /* We return DB_SUCCESS to MySQL though the drop will
happen lazily later */ happen lazily later */
err = DB_SUCCESS; err = DB_SUCCESS;
} else { } else {
/* The table is already in the background drop list */ /* The table is already in the background drop list */
@@ -3219,7 +3449,6 @@ check_next_foreign:
"WHERE NAME = :table_name\n" "WHERE NAME = :table_name\n"
"LOCK IN SHARE MODE;\n" "LOCK IN SHARE MODE;\n"
"IF (SQL % NOTFOUND) THEN\n" "IF (SQL % NOTFOUND) THEN\n"
" COMMIT WORK;\n"
" RETURN;\n" " RETURN;\n"
"END IF;\n" "END IF;\n"
"found := 1;\n" "found := 1;\n"
@@ -3272,7 +3501,6 @@ check_next_foreign:
"WHERE TABLE_ID = table_id;\n" "WHERE TABLE_ID = table_id;\n"
"DELETE FROM SYS_TABLES\n" "DELETE FROM SYS_TABLES\n"
"WHERE ID = table_id;\n" "WHERE ID = table_id;\n"
"COMMIT WORK;\n"
"END;\n" "END;\n"
, FALSE, trx); , FALSE, trx);
@@ -3354,8 +3582,6 @@ check_next_foreign:
} }
funct_exit: funct_exit:
trx_commit_for_mysql(trx);
if (locked_dictionary) { if (locked_dictionary) {
row_mysql_unlock_data_dictionary(trx); row_mysql_unlock_data_dictionary(trx);
} }
@@ -3532,7 +3758,8 @@ row_rename_table_for_mysql(
/* out: error code or DB_SUCCESS */ /* out: error code or DB_SUCCESS */
const char* old_name, /* in: old table name */ const char* old_name, /* in: old table name */
const char* new_name, /* in: new table name */ const char* new_name, /* in: new table name */
trx_t* trx) /* in: transaction handle */ trx_t* trx, /* in: transaction handle */
ibool commit) /* in: if TRUE then commit trx */
{ {
dict_table_t* table; dict_table_t* table;
ulint err; ulint err;
@@ -3557,9 +3784,7 @@ row_rename_table_for_mysql(
trx_commit_for_mysql(trx); trx_commit_for_mysql(trx);
return(DB_ERROR); return(DB_ERROR);
} } else if (row_mysql_is_system_table(new_name)) {
if (row_mysql_is_system_table(new_name)) {
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: trying to create a MySQL" "InnoDB: Error: trying to create a MySQL"
@@ -3578,11 +3803,6 @@ row_rename_table_for_mysql(
old_is_tmp = row_is_mysql_tmp_table_name(old_name); old_is_tmp = row_is_mysql_tmp_table_name(old_name);
new_is_tmp = row_is_mysql_tmp_table_name(new_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); table = dict_table_get_low(old_name);
if (!table) { if (!table) {
@@ -3603,9 +3823,7 @@ row_rename_table_for_mysql(
"innodb-troubleshooting.html\n", "innodb-troubleshooting.html\n",
stderr); stderr);
goto funct_exit; goto funct_exit;
} } else if (table->ibd_file_missing) {
if (table->ibd_file_missing) {
err = DB_TABLE_NOT_FOUND; err = DB_TABLE_NOT_FOUND;
ut_print_timestamp(stderr); ut_print_timestamp(stderr);
@@ -3618,9 +3836,7 @@ row_rename_table_for_mysql(
"innodb-troubleshooting.html\n", "innodb-troubleshooting.html\n",
stderr); stderr);
goto funct_exit; goto funct_exit;
} } else if (new_is_tmp) {
if (new_is_tmp) {
/* MySQL is doing an ALTER TABLE command and it renames the /* MySQL is doing an ALTER TABLE command and it renames the
original table to a temporary table name. We want to preserve original table to a temporary table name. We want to preserve
the original foreign key constraint definitions despite the the original foreign key constraint definitions despite the
@@ -3660,9 +3876,7 @@ row_rename_table_for_mysql(
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
goto end; goto end;
} } else if (!new_is_tmp) {
if (!new_is_tmp) {
/* Rename all constraints. */ /* Rename all constraints. */
info = pars_info_create(); info = pars_info_create();
@@ -3860,8 +4074,10 @@ end:
} }
funct_exit: funct_exit:
trx_commit_for_mysql(trx);
row_mysql_unlock_data_dictionary(trx); if (commit) {
trx_commit_for_mysql(trx);
}
if (UNIV_LIKELY_NULL(heap)) { if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap); mem_heap_free(heap);
@@ -4066,8 +4282,7 @@ row_check_table_for_mysql(
if (!btr_validate_index(index, prebuilt->trx)) { if (!btr_validate_index(index, prebuilt->trx)) {
ret = DB_ERROR; ret = DB_ERROR;
} else { } else {
if (!row_scan_and_check_index(prebuilt, if (!row_scan_and_check_index(prebuilt,index, &n_rows)){
index, &n_rows)) {
ret = DB_ERROR; ret = DB_ERROR;
} }
@@ -4118,4 +4333,273 @@ row_check_table_for_mysql(
return(ret); 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 */
ulint
row_create_index_graph_for_mysql(
/*=============================*/
/* out: DB_SUCCESS or error code */
trx_t* trx, /* in: trx */
dict_table_t* table, /* in: table */
dict_index_t* index) /* in: index */
{
ind_node_t* node; /* Index creation node */
mem_heap_t* heap; /* Memory heap */
que_thr_t* thr; /* Query thread */
ulint err = DB_SUCCESS;
ut_ad(trx && index);
heap = mem_heap_create(512);
index->table = table;
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));
return(err);
}
/*************************************************************************
Build new indexes to a table by reading a cluster index,
creating a temporary file containing index entries, merge sorting
these index entries and inserting sorted index entries to indexes. */
ulint
row_build_index_for_mysql(
/*======================*/
/* out: 0 or error code */
trx_t* trx, /* in: transaction */
dict_table_t* old_table, /* in: Table where rows are
read from */
dict_table_t* new_table, /* in: Table where indexes are
created. Note that old_table ==
new_table if we are creating a
secondary keys. */
dict_index_t** index, /* in: Indexes to be created */
ibool new_primary, /* in: new primary key
i.e. clustered index will be build
for this table */
ulint num_of_keys) /* in: Number of indexes to be
created */
{
merge_file_t** merge_files;
mem_heap_t* file_heap;
ulint index_num;
ulint error = DB_SUCCESS;
ut_ad(trx && old_table && new_table && index && num_of_keys);
trx_start_if_not_started(trx);
/* Allocate memory for merge file data structure and initialize
fields */
file_heap = mem_heap_create(
num_of_keys * (sizeof(merge_file_t) + sizeof(merge_file_t*)));
merge_files = mem_heap_alloc(
file_heap, num_of_keys * sizeof(merge_file_t*));
for (index_num = 0; index_num < num_of_keys; index_num++) {
merge_files[index_num] =
row_merge_create_file_structure(file_heap);
}
/* Read clustered index of the table and create files for
secondary index entries for merge sort */
error = row_merge_read_clustered_index(
trx, old_table, index, merge_files, num_of_keys);
if (error != DB_SUCCESS) {
return(error);
}
trx_start_if_not_started(trx);
/* Now we have files containing index entries ready for
sorting and inserting. */
for (index_num = 0; index_num < num_of_keys; index_num++) {
/* Do a merge sort and insert from those files
which we have written at least one block */
if (merge_files[index_num]->num_of_blocks > 0) {
dulint offset = ut_dulint_create(0, 0);
/* Merge sort file using linked list merge
sort for files. */
offset = row_merge_sort_linked_list_in_disk(
index[index_num],
merge_files[index_num]->file,
(int *)&error);
if (error != DB_SUCCESS) {
trx->error_key_num = index_num;
goto func_exit;
}
/* Insert sorted index entries to the table. */
error = row_merge_insert_index_tuples(
trx,
index[index_num],
new_table,
merge_files[index_num]->file,
ut_dulint_create(0,0));
if (error != DB_SUCCESS) {
trx->error_key_num = index_num;
goto func_exit;
}
}
}
func_exit:
if (error == DB_SUCCESS && new_primary) {
row_merge_mark_prebuilt_obsolete(trx, old_table);
}
mem_heap_free(file_heap);
return(error);
}
/*************************************************************************
Remove those indexes which were created before a error happened in
the index build */
ulint
row_remove_indexes_for_mysql(
/*=========================*/
/* out: 0 or error code */
trx_t* trx, /* in: transaction */
dict_table_t* table, /* in: Table where index is created */
dict_index_t** index, /* in: Indexes to be created */
ulint num_created) /* in: Number of indexes created
before error and now must be removed */
{
ulint key_num;
ulint error = DB_SUCCESS;
ut_ad(trx && table && index);
for (key_num = 0; key_num < num_created; key_num++) {
error = row_merge_remove_index(index[key_num], table, trx);
if (error != DB_SUCCESS) {
break;
}
}
return(error);
}
#endif /* !UNIV_HOTBACKUP */ #endif /* !UNIV_HOTBACKUP */

View File

@@ -227,7 +227,7 @@ row_purge_remove_sec_if_poss_low(
/* Not found */ /* Not found */
/* fputs("PURGE:........sec entry not found\n", stderr); */ /* fputs("PURGE:........sec entry not found\n", stderr); */
/* dtuple_print(entry); */ /* dtuple_print(stderr, entry); */
btr_pcur_close(&pcur); btr_pcur_close(&pcur);
mtr_commit(&mtr); mtr_commit(&mtr);

View File

@@ -57,15 +57,16 @@ row_get_trx_id_offset(
} }
/********************************************************************* /*********************************************************************
When an insert to a table is performed, this function builds the entry which When an insert or purge to a table is performed, this function builds
has to be inserted to an index on the table. */ the entry to be inserted into or purged from an index on the table. */
dtuple_t* dtuple_t*
row_build_index_entry( row_build_index_entry(
/*==================*/ /*==================*/
/* out: index entry which should be inserted */ /* out: index entry which should be
const dtuple_t* row, /* in: row which should be inserted to the inserted or purged */
table */ const dtuple_t* row, /* in: row which should be
inserted or purged */
row_ext_t* ext, /* in: externally stored column prefixes, row_ext_t* ext, /* in: externally stored column prefixes,
or NULL */ or NULL */
dict_index_t* index, /* in: index on the table */ dict_index_t* index, /* in: index on the table */

View File

@@ -28,6 +28,7 @@ Created 2/25/1997 Heikki Tuuri
#include "que0que.h" #include "que0que.h"
#include "ibuf0ibuf.h" #include "ibuf0ibuf.h"
#include "log0log.h" #include "log0log.h"
#include "row0merge.h"
/******************************************************************* /*******************************************************************
Removes a clustered index record. The pcur in node was positioned on the Removes a clustered index record. The pcur in node was positioned on the
@@ -52,6 +53,25 @@ row_undo_ins_remove_clust_rec(
ut_a(success); ut_a(success);
if (ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) { if (ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) {
trx_t* trx;
ibool thawed_dictionary = FALSE;
ibool locked_dictionary = FALSE;
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;
}
/* Drop the index tree associated with the row in /* Drop the index tree associated with the row in
SYS_INDEXES table: */ SYS_INDEXES table: */
@@ -65,6 +85,14 @@ row_undo_ins_remove_clust_rec(
success = btr_pcur_restore_position(BTR_MODIFY_LEAF, success = btr_pcur_restore_position(BTR_MODIFY_LEAF,
&(node->pcur), &mtr); &(node->pcur), &mtr);
ut_a(success); ut_a(success);
if (locked_dictionary) {
row_mysql_unlock_data_dictionary(trx);
}
if (thawed_dictionary) {
row_mysql_freeze_data_dictionary(trx);
}
} }
btr_cur = btr_pcur_get_btr_cur(&(node->pcur)); btr_cur = btr_pcur_get_btr_cur(&(node->pcur));
@@ -211,6 +239,36 @@ retry:
return(err); 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. */ Parses the row reference and other info in a fresh insert undo record. */
static static
@@ -219,39 +277,67 @@ row_undo_ins_parse_undo_rec(
/*========================*/ /*========================*/
undo_node_t* node) /* in: row undo node */ undo_node_t* node) /* in: row undo node */
{ {
dict_index_t* clust_index;
byte* ptr; byte* ptr;
dulint undo_no;
dulint table_id; dulint table_id;
ulint type;
ulint dummy;
ibool dummy_extern;
ut_ad(node); ut_ad(node);
ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &dummy, ptr = row_undo_ins_parse_rec_type_and_table_id(node, &table_id);
&dummy_extern, &undo_no, &table_id);
ut_ad(type == TRX_UNDO_INSERT_REC);
node->rec_type = type;
node->table = dict_table_get_on_id(table_id, node->trx); ut_ad(node->rec_type == TRX_UNDO_INSERT_REC
|| node->rec_type == TRX_UNDO_DICTIONARY_REC);
if (node->table == NULL) { if (node->rec_type == TRX_UNDO_INSERT_REC) {
return; trx_t* trx;
ibool thawed_dictionary = FALSE;
ibool locked_dictionary = FALSE;
trx = node->trx;
/* If it's sytem table then we have to acquire the
dictionary lock in X mode.*/
if (ut_dulint_cmp(table_id, DICT_FIELDS_ID) <= 0) {
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;
}
}
node->table = dict_table_get_on_id(table_id, trx);
/* 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 = NULL;
} else {
dict_index_t* clust_index;
clust_index = dict_table_get_first_index(node->table);
ptr = trx_undo_rec_get_row_ref(
ptr, clust_index, &node->ref, node->heap);
}
if (locked_dictionary) {
row_mysql_unlock_data_dictionary(trx);
}
if (thawed_dictionary) {
row_mysql_freeze_data_dictionary(trx);
}
} }
if (node->table->ibd_file_missing) {
/* We skip undo operations to missing .ibd files */
node->table = NULL;
return;
}
clust_index = dict_table_get_first_index(node->table);
ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
node->heap);
} }
/*************************************************************** /***************************************************************
@@ -265,44 +351,49 @@ row_undo_ins(
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ /* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
undo_node_t* node) /* in: row undo node */ undo_node_t* node) /* in: row undo node */
{ {
dtuple_t* entry; ulint err = DB_SUCCESS;
ibool found;
ulint err;
ut_ad(node); ut_ad(node);
ut_ad(node->state == UNDO_NODE_INSERT); ut_ad(node->state == UNDO_NODE_INSERT);
row_undo_ins_parse_undo_rec(node); row_undo_ins_parse_undo_rec(node);
if (node->table == NULL) { /* Dictionary records are undone in a separate function */
found = FALSE;
} else { if (node->rec_type == TRX_UNDO_DICTIONARY_REC) {
found = row_undo_search_clust_to_pcur(node);
} err = row_undo_build_dict_undo_list(node);
} else if (!node->table || !row_undo_search_clust_to_pcur(node)) {
if (!found) {
trx_undo_rec_release(node->trx, node->undo_no); trx_undo_rec_release(node->trx, node->undo_no);
return(DB_SUCCESS); } else {
}
node->index = dict_table_get_next_index( /* Iterate over all the indexes and undo the insert.*/
dict_table_get_first_index(node->table));
while (node->index != NULL) { /* Skip the clustered index (the first index) */
entry = row_build_index_entry(node->row, node->ext, node->index = dict_table_get_next_index(
node->index, node->heap); dict_table_get_first_index(node->table));
err = row_undo_ins_remove_sec(node->index, entry);
if (err != DB_SUCCESS) { while (node->index != NULL) {
dtuple_t* entry;
return(err); entry = row_build_index_entry(node->row, node->ext,
node->index, node->heap);
err = row_undo_ins_remove_sec(node->index, entry);
if (err != DB_SUCCESS) {
return(err);
}
node->index = dict_table_get_next_index(node->index);
} }
node->index = dict_table_get_next_index(node->index); err = row_undo_ins_remove_clust_rec(node);
} }
err = row_undo_ins_remove_clust_rec(node);
return(err); return(err);
} }

View File

@@ -426,7 +426,15 @@ row_undo_mod_del_unmark_sec_and_undo_update(
log_free_check(); log_free_check();
mtr_start(&mtr); mtr_start(&mtr);
found = row_search_index_entry(index, entry, mode, &pcur, &mtr); /* Check if the index was created after this transaction was
started because then this index will not have the changes made
by this transaction.*/
if (*index->name != TEMP_TABLE_PREFIX) {
found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
} else {
return(err);
}
if (!found) { if (!found) {
fputs("InnoDB: error in sec index entry del undo in\n" fputs("InnoDB: error in sec index entry del undo in\n"

View File

@@ -26,6 +26,7 @@ Created 1/8/1997 Heikki Tuuri
#include "row0umod.h" #include "row0umod.h"
#include "row0mysql.h" #include "row0mysql.h"
#include "srv0srv.h" #include "srv0srv.h"
#include "row0merge.h"
/* How to undo row operations? /* How to undo row operations?
(1) For an insert, we have stored a prefix of the clustered index record (1) For an insert, we have stored a prefix of the clustered index record
@@ -124,6 +125,7 @@ row_undo_node_create(
undo->state = UNDO_NODE_FETCH_NEXT; undo->state = UNDO_NODE_FETCH_NEXT;
undo->trx = trx; undo->trx = trx;
undo->rec_sub_type = TRX_UNDO_NULL_REC;
btr_pcur_init(&(undo->pcur)); btr_pcur_init(&(undo->pcur));
@@ -350,3 +352,352 @@ row_undo_step(
return(thr); 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_create(0, 0);
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);
}
ulint
row_undo_dictionary(
/*================*/
/* out: DB_SUCCESS or error code */
trx_t* trx, /* in: transaction */
dict_undo_t* dict_undo) /* in: dict undo info */
{
ulint err = DB_SUCCESS;
switch (dict_undo->op_type) {
case TRX_UNDO_INDEX_CREATE_REC:
err = row_merge_remove_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);
}
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);
if (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);
}
if (err == DB_SUCCESS) {
err = row_drop_table_for_mysql_no_commit(
dict_undo->data.table.new_table->name,
trx, FALSE);
}
} else if (dict_undo->data.table.old_table) {
/* Rename to tmp failed.*/
ut_ad(!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);
}
} 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);
if (dict_undo->data.table.new_table) {
err = row_drop_table_for_mysql_no_commit(
dict_undo->data.table.new_table->name,
trx, FALSE);
}
} else {
/* Shouldn't happen */
ut_error;
}
default:
ut_error;
}
return(err);
}

View File

@@ -130,6 +130,105 @@ trx_undo_left(
return(UNIV_PAGE_SIZE - (ptr - page) - 10 - FIL_PAGE_DATA_END); return(UNIV_PAGE_SIZE - (ptr - page) - 10 - FIL_PAGE_DATA_END);
} }
/**************************************************************************
Get the pointer to where the data for the undo log record will be written.*/
static
byte*
trx_undo_page_get_ptr(
/*==================*/
/* out: ptr to where the undo log
record data will be written,
0 if not enough space.*/
page_t* undo_page, /* in: undo log page */
ulint need) /* in: need these man bytes */
{
byte* ptr; /* pointer within undo_page */
ulint first_free; /* offset within undo page */
ut_ad(undo_page);
ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
+ TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT);
first_free = mach_read_from_2(
undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE);
/* Start writing the undo information from the first free
bytes in the undo page */
ptr = undo_page + first_free;
ut_ad(first_free <= UNIV_PAGE_SIZE);
/* NOTE: the value need must be big enough such that the
general fields written below fit on the undo log page */
if (UNIV_UNLIKELY(trx_undo_left(undo_page, ptr) < need)) {
/* Error, not enough space */
ptr = 0;
} else {
/* Reserve 2 bytes for the pointer to the next undo log
record */
ptr += 2;
}
return(ptr);
}
/**************************************************************************
Set the next and previous pointers in the undo page for the undo record
that was written to ptr. Update the first free value by the number of bytes
written for this undo record.*/
static
ulint
trx_undo_page_set_next_prev_and_add(
/*================================*/
/* out: offset of the inserted entry
on the page if succeeded, 0 if fail */
page_t* undo_page, /* in/out: undo log page */
byte* ptr, /* in: ptr up to where data has been
written on this undo page. */
mtr_t* mtr) /* in: mtr */
{
ulint first_free; /* offset within undo_page */
ulint end_of_rec; /* offset within undo_page */
byte* ptr_to_first_free;
/* pointer within undo_page
that points to the next free
offset value within undo_page.*/
ut_ad(ptr > undo_page);
ut_ad(ptr < undo_page + UNIV_PAGE_SIZE);
if (UNIV_UNLIKELY(trx_undo_left(undo_page, ptr) < 2)) {
return(0);
}
ptr_to_first_free = undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE;
first_free = mach_read_from_2(ptr_to_first_free);
/* Write offset of the previous undo log record */
mach_write_to_2(ptr, first_free);
ptr += 2;
end_of_rec = ptr - undo_page;
/* Write offset of the next undo log record */
mach_write_to_2(undo_page + first_free, end_of_rec);
/* Update the offset to first free undo record */
mach_write_to_2(ptr_to_first_free, end_of_rec);
/* Write this log entry to the UNDO log */
trx_undof_page_add_undo_rec_log(undo_page, first_free,
end_of_rec, mtr);
return(first_free);
}
/************************************************************************** /**************************************************************************
Reports in the undo log of an insert of a clustered index record. */ Reports in the undo log of an insert of a clustered index record. */
static static
@@ -147,9 +246,6 @@ trx_undo_page_report_insert(
{ {
ulint first_free; ulint first_free;
byte* ptr; byte* ptr;
ulint len;
const dfield_t* field;
ulint flen;
ulint i; ulint i;
ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
@@ -172,31 +268,24 @@ trx_undo_page_report_insert(
ptr += 2; ptr += 2;
/* Store first some general parameters to the undo log */ /* Store first some general parameters to the undo log */
mach_write_to_1(ptr, TRX_UNDO_INSERT_REC); *ptr++ = TRX_UNDO_INSERT_REC;
ptr++; ptr += mach_dulint_write_much_compressed(ptr, trx->undo_no);
ptr += mach_dulint_write_much_compressed(ptr, index->table->id);
len = mach_dulint_write_much_compressed(ptr, trx->undo_no);
ptr += len;
len = mach_dulint_write_much_compressed(ptr, (index->table)->id);
ptr += len;
/*----------------------------------------*/ /*----------------------------------------*/
/* Store then the fields required to uniquely determine the record /* Store then the fields required to uniquely determine the record
to be inserted in the clustered index */ to be inserted in the clustered index */
for (i = 0; i < dict_index_get_n_unique(index); i++) { for (i = 0; i < dict_index_get_n_unique(index); i++) {
field = dtuple_get_nth_field(clust_entry, i); const dfield_t* field = dtuple_get_nth_field(clust_entry, i);
ulint flen = dfield_get_len(field);
flen = dfield_get_len(field);
if (trx_undo_left(undo_page, ptr) < 5) { if (trx_undo_left(undo_page, ptr) < 5) {
return(0); return(0);
} }
len = mach_write_compressed(ptr, flen); ptr += mach_write_compressed(ptr, flen);
ptr += len;
if (flen != UNIV_SQL_NULL) { if (flen != UNIV_SQL_NULL) {
if (trx_undo_left(undo_page, ptr) < flen) { if (trx_undo_left(undo_page, ptr) < flen) {
@@ -209,27 +298,192 @@ trx_undo_page_report_insert(
} }
} }
/*----------------------------------------*/ return(trx_undo_page_set_next_prev_and_add(undo_page, ptr, mtr));
/* Write pointers to the previous and the next undo log records */ }
if (trx_undo_left(undo_page, ptr) < 2) { /**************************************************************************
Reports in the undo log of an index create */
static
ulint
trx_undo_page_report_index_create(
/*==============================*/
/* out: offset of the inserted entry
on the page if succeed, 0 if fail */
page_t* undo_page, /* in: undo log page */
trx_t* trx, /* in: transaction */
dict_index_t* index, /* in: index */
mtr_t* mtr) /* in: mtr */
{
byte* ptr;
ut_ad(undo_page && trx && index && mtr);
/* Get the pointer to where we will write our undo data. */
ptr = trx_undo_page_get_ptr(undo_page, 1 + 11 + 1 + 11 + 11);
if (UNIV_UNLIKELY(!ptr)) {
return(0); return(0);
} }
mach_write_to_2(ptr, first_free); /* This is our internal dictionary undo log record. */
ptr += 2; *ptr++ = TRX_UNDO_DICTIONARY_REC;
mach_write_to_2(undo_page + first_free, ptr - undo_page); ptr += mach_dulint_write_much_compressed(ptr, trx->undo_no);
mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE, /* The sub type (discriminator) of this undo dictionary record */
ptr - undo_page); *ptr++ = TRX_UNDO_INDEX_CREATE_REC;
/* Write the log entry to the REDO log of this change in the UNDO /* For index create, we need both the table id and the index id
log */ to be stored in the undo log record.*/
trx_undof_page_add_undo_rec_log(undo_page, first_free,
ptr - undo_page, mtr); ptr += mach_dulint_write_much_compressed(ptr, index->table->id);
return(first_free); ptr += mach_dulint_write_much_compressed(ptr, index->id);
return(trx_undo_page_set_next_prev_and_add(undo_page, ptr, mtr));
}
/**************************************************************************
Reports in the undo log of a table create */
static
ulint
trx_undo_page_report_table_create(
/*==============================*/
/* out: offset of the inserted entry
on the page if succeed, 0 if fail */
page_t* undo_page, /* in: undo log page */
trx_t* trx, /* in: transaction */
const char* table_name, /* in: table name */
mtr_t* mtr) /* in: mtr */
{
byte* ptr;
ulint name_len;
ut_ad(undo_page && trx && table_name && mtr);
name_len = strlen(table_name) + 1;
/* Get the pointer to where we will write our undo data */
ptr = trx_undo_page_get_ptr(undo_page, 1 + 11 + 1 + name_len);
if (UNIV_UNLIKELY(!ptr)) {
return(0);
}
/* The type (discriminator) of this undo log */
*ptr++ = TRX_UNDO_DICTIONARY_REC;
ptr += mach_dulint_write_much_compressed(ptr, trx->undo_no);
/* The sub type (discriminator) of this dictionary undo log */
*ptr++ = TRX_UNDO_TABLE_CREATE_REC;
/* For table create we need to store table name */
memcpy(ptr, table_name, name_len);
ptr += name_len;
return(trx_undo_page_set_next_prev_and_add(undo_page, ptr, mtr));
}
/**************************************************************************
Reports in the undo log of a table drop */
static
ulint
trx_undo_page_report_table_drop(
/*============================*/
/* out: offset of the inserted entry
on the page if succeed, 0 if fail */
page_t* undo_page, /* in: undo log page */
trx_t* trx, /* in: transaction */
const char* table_name, /* in: table name */
mtr_t* mtr) /* in: mtr */
{
byte* ptr;
ulint name_len;
ut_ad(undo_page && trx && table_name && mtr);
name_len = strlen(table_name) + 1;
/* Get the pointer to where we will write our undo data */
ptr = trx_undo_page_get_ptr(undo_page, 1 + 11 + 1 + name_len);
if (UNIV_UNLIKELY(!ptr)) {
return(0);
}
*ptr++ = TRX_UNDO_DICTIONARY_REC;
ptr += mach_dulint_write_much_compressed(ptr, trx->undo_no);
/* The sub type (discriminator) of this dictionary undo log */
*ptr++ = TRX_UNDO_TABLE_DROP_REC;
/* For table drop we need to store a table name */
memcpy(ptr, table_name, name_len);
ptr += name_len;
return(trx_undo_page_set_next_prev_and_add(undo_page, ptr, mtr));
}
/**************************************************************************
Reports in the undo log of a table rename */
static
ulint
trx_undo_page_report_table_rename(
/*==============================*/
/* out: offset of the inserted entry
on the page if succeed, 0 if fail */
page_t* undo_page, /* in: undo log page */
trx_t* trx, /* in: transaction */
const char* new_table_name, /* in: new table name */
const char* old_table_name, /* in: old table name */
const char* tmp_table_name, /* in: the temp name */
mtr_t* mtr) /* in: mtr */
{
byte* ptr;
ulint new_name_len;
ulint old_name_len;
ulint tmp_name_len;
ut_ad(undo_page && trx && new_table_name && old_table_name && mtr);
new_name_len = strlen(new_table_name) + 1;
old_name_len = strlen(old_table_name) + 1;
tmp_name_len = strlen(tmp_table_name) + 1;
/* Get the pointer to where we will write our undo data. */
ptr = trx_undo_page_get_ptr(undo_page, 1 + 11 + 1
+ new_name_len + old_name_len
+ tmp_name_len);
if (UNIV_UNLIKELY(!ptr)) {
return(0);
}
/* The type (discriminator) of this undo log */
*ptr++ = TRX_UNDO_DICTIONARY_REC;
ptr += mach_dulint_write_much_compressed(ptr, trx->undo_no);
/* The sub type (discriminator) of this dictionary undo log */
*ptr++ = TRX_UNDO_TABLE_RENAME_REC;
/* For table rename we need to store the new table name and
the old table name */
memcpy(ptr, new_table_name, new_name_len);
ptr += new_name_len;
memcpy(ptr, old_table_name, old_name_len);
ptr += old_name_len;
memcpy(ptr, tmp_table_name, tmp_name_len);
ptr += tmp_name_len;
return(trx_undo_page_set_next_prev_and_add(undo_page, ptr, mtr));
} }
/************************************************************************** /**************************************************************************
@@ -251,7 +505,6 @@ trx_undo_rec_get_pars(
dulint* table_id) /* out: table id */ dulint* table_id) /* out: table id */
{ {
byte* ptr; byte* ptr;
ulint len;
ulint type_cmpl; ulint type_cmpl;
ptr = undo_rec + 2; ptr = undo_rec + 2;
@@ -270,12 +523,10 @@ trx_undo_rec_get_pars(
*cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT; *cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT;
*undo_no = mach_dulint_read_much_compressed(ptr); *undo_no = mach_dulint_read_much_compressed(ptr);
len = mach_dulint_get_much_compressed_size(*undo_no); ptr += mach_dulint_get_much_compressed_size(*undo_no);
ptr += len;
*table_id = mach_dulint_read_much_compressed(ptr); *table_id = mach_dulint_read_much_compressed(ptr);
len = mach_dulint_get_much_compressed_size(*table_id); ptr += mach_dulint_get_much_compressed_size(*table_id);
ptr += len;
return(ptr); return(ptr);
} }
@@ -671,8 +922,6 @@ trx_undo_update_rec_get_sys_cols(
dulint* roll_ptr, /* out: roll ptr */ dulint* roll_ptr, /* out: roll ptr */
ulint* info_bits) /* out: info bits state */ ulint* info_bits) /* out: info bits state */
{ {
ulint len;
/* Read the state of the info bits */ /* Read the state of the info bits */
*info_bits = mach_read_from_1(ptr); *info_bits = mach_read_from_1(ptr);
ptr += 1; ptr += 1;
@@ -680,12 +929,10 @@ trx_undo_update_rec_get_sys_cols(
/* Read the values of the system columns */ /* Read the values of the system columns */
*trx_id = mach_dulint_read_compressed(ptr); *trx_id = mach_dulint_read_compressed(ptr);
len = mach_dulint_get_compressed_size(*trx_id); ptr += mach_dulint_get_compressed_size(*trx_id);
ptr += len;
*roll_ptr = mach_dulint_read_compressed(ptr); *roll_ptr = mach_dulint_read_compressed(ptr);
len = mach_dulint_get_compressed_size(*roll_ptr); ptr += mach_dulint_get_compressed_size(*roll_ptr);
ptr += len;
return(ptr); return(ptr);
} }
@@ -1142,6 +1389,189 @@ trx_undo_report_row_operation(
return(DB_SUCCESS); return(DB_SUCCESS);
} }
/***************************************************************************
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: trx */
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
for renaming */
dulint* roll_ptr) /* out: rollback pointer to the
inserted undo log record */
{
trx_undo_t* undo;
buf_block_t* undo_block;
ulint offset;
ulint page_no;
trx_rseg_t* rseg;
mtr_t mtr;
ut_ad(trx);
#ifdef UNIV_DEBUG
switch (op_type) {
case TRX_UNDO_TABLE_RENAME_OP:
ut_ad(old_table_name);
case TRX_UNDO_TABLE_DROP_OP:
case TRX_UNDO_TABLE_CREATE_OP:
ut_ad(table_name);
break;
case TRX_UNDO_INDEX_CREATE_OP:
ut_ad(index);
break;
default:
ut_error;
}
#endif
rseg = trx->rseg;
mutex_enter(&(trx->undo_mutex));
/* If the undo log is not assigned yet, assign one */
if (trx->insert_undo == NULL) {
trx_undo_assign_undo(trx, TRX_UNDO_INSERT);
}
undo = trx->insert_undo;
if (undo == NULL) {
/* Did not succeed: out of space */
mutex_exit(&(trx->undo_mutex));
return(DB_OUT_OF_FILE_SPACE);
}
page_no = undo->last_page_no;
mtr_start(&mtr);
for (;;) {
undo_block = buf_page_get_gen(undo->space, undo->zip_size,
page_no, RW_X_LATCH,
undo->guess_block, BUF_GET,
__FILE__, __LINE__, &mtr);
#ifdef UNIV_SYNC_DEBUG
buf_block_dbg_add_level(undo_block, SYNC_TRX_UNDO_PAGE);
#endif /* UNIV_SYNC_DEBUG */
switch (op_type) {
case TRX_UNDO_TABLE_CREATE_OP:
offset = trx_undo_page_report_table_create(
undo_block->frame, trx, table_name, &mtr);
break;
case TRX_UNDO_INDEX_CREATE_OP:
offset = trx_undo_page_report_index_create(
undo_block->frame, trx, index, &mtr);
break;
case TRX_UNDO_TABLE_RENAME_OP:
offset = trx_undo_page_report_table_rename(
undo_block->frame, trx, table_name,
old_table_name, tmp_table_name, &mtr);
break;
case TRX_UNDO_TABLE_DROP_OP:
offset = trx_undo_page_report_table_drop(
undo_block->frame, trx, table_name, &mtr);
break;
default:
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: [Error]: Undefined op_type = %lu "
"at trx_undo_report_dict_operation\n",
(ulong) op_type);
mutex_enter(&kernel_mutex);
trx_print(stderr, trx, 1024);
mutex_exit(&kernel_mutex);
return(DB_ERROR);
}
if (offset == 0) {
/* The record did not fit on the page. We erase the
end segment of the undo log page and write a log
record of it: this is to ensure that in the debug
version the replicate page constructed using the log
records stays identical to the original page */
trx_undo_erase_page_end(undo_block->frame, &mtr);
}
mtr_commit(&mtr);
if (offset != 0) {
/* Success */
break;
}
ut_ad(page_no == undo->last_page_no);
/* We have to extend the undo log by one page */
mtr_start(&mtr);
/* When we add a page to an undo log, this is analogous to
a pessimistic insert in a B-tree, and we must reserve the
counterpart of the tree latch, which is the rseg mutex. */
mutex_enter(&(rseg->mutex));
page_no = trx_undo_add_page(trx, undo, &mtr);
mutex_exit(&(rseg->mutex));
if (page_no == FIL_NULL) {
/* Did not succeed: out of space */
mutex_exit(&(trx->undo_mutex));
mtr_commit(&mtr);
return(DB_OUT_OF_FILE_SPACE);
}
}
undo->empty = FALSE;
undo->top_page_no = page_no;
undo->top_offset = offset;
undo->top_undo_no = trx->undo_no;
undo->guess_block = undo_block;
UT_DULINT_INC(trx->undo_no);
mutex_exit(&(trx->undo_mutex));
*roll_ptr = trx_undo_build_roll_ptr(TRUE, rseg->id, page_no, offset);
return(DB_SUCCESS);
}
/*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/ /*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
/********************************************************************** /**********************************************************************

View File

@@ -425,7 +425,8 @@ trx_rollback_or_clean_all_without_sess(
dict_table_t* table; dict_table_t* table;
ib_longlong rows_to_undo; ib_longlong rows_to_undo;
const char* unit = ""; const char* unit = "";
int err; int err = DB_SUCCESS;
ibool dictionary_locked = FALSE;
mutex_enter(&kernel_mutex); mutex_enter(&kernel_mutex);
@@ -530,8 +531,10 @@ loop:
trx->mysql_process_no = os_proc_get_number(); trx->mysql_process_no = os_proc_get_number();
/* TODO: Doesn't seem right */
if (trx->dict_operation) { if (trx->dict_operation) {
row_mysql_lock_data_dictionary(trx); row_mysql_lock_data_dictionary(trx);
dictionary_locked = TRUE;
} }
que_run_threads(thr); que_run_threads(thr);
@@ -552,7 +555,8 @@ loop:
mutex_exit(&kernel_mutex); mutex_exit(&kernel_mutex);
if (trx->dict_operation) { if (trx->dict_operation && !ut_dulint_is_zero(trx->table_id)) {
/* If the transaction was for a dictionary operation, we /* If the transaction was for a dictionary operation, we
drop the relevant table, if it still exists */ drop the relevant table, if it still exists */
@@ -573,9 +577,35 @@ loop:
ut_a(err == (int) DB_SUCCESS); ut_a(err == (int) DB_SUCCESS);
} }
} else if (trx->dict_undo_list) {
dict_undo_t* dict_undo;
ut_a(trx->dict_undo_list);
dict_undo = UT_LIST_GET_FIRST(*trx->dict_undo_list);
fputs("InnoDB: UNDO dict entries\n", stderr);
while (dict_undo && err == DB_SUCCESS) {
dict_undo = UT_LIST_GET_NEXT(node, dict_undo);
if (dict_undo) {
err = row_undo_dictionary(trx, dict_undo);
}
}
ut_a(err == (int) DB_SUCCESS);
dict_undo_free_list(trx);
mutex_enter(&kernel_mutex);
trx_commit_off_kernel(trx);
mutex_exit(&kernel_mutex);
} }
if (trx->dict_operation) { if (dictionary_locked) {
row_mysql_unlock_data_dictionary(trx); row_mysql_unlock_data_dictionary(trx);
} }
@@ -1245,7 +1275,11 @@ trx_finish_rollback_off_kernel(
} }
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
trx_commit_off_kernel(trx); /* 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);
}
/* Remove all TRX_SIG_TOTAL_ROLLBACK signals from the signal queue and /* Remove all TRX_SIG_TOTAL_ROLLBACK signals from the signal queue and
send reply messages to them */ send reply messages to them */

View File

@@ -127,6 +127,10 @@ trx_create(
trx->must_flush_log_later = FALSE; trx->must_flush_log_later = FALSE;
trx->dict_operation = FALSE; trx->dict_operation = FALSE;
trx->table_id = ut_dulint_create(0, 0);
trx->dict_undo_list = NULL;
trx->dict_redo_list = NULL;
trx->sync_cb = NULL;
trx->mysql_thd = NULL; trx->mysql_thd = NULL;
trx->mysql_query_str = NULL; trx->mysql_query_str = NULL;
@@ -153,6 +157,7 @@ trx_create(
trx->undo_no_arr = NULL; trx->undo_no_arr = NULL;
trx->error_state = DB_SUCCESS; trx->error_state = DB_SUCCESS;
trx->error_key_num = 0;
trx->detailed_error[0] = '\0'; trx->detailed_error[0] = '\0';
trx->sess = sess; trx->sess = sess;
@@ -349,6 +354,8 @@ trx_free(
trx->global_read_view = NULL; trx->global_read_view = NULL;
ut_a(trx->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); mem_free(trx);
} }
@@ -740,6 +747,10 @@ trx_commit_off_kernel(
ut_ad(mutex_own(&kernel_mutex)); 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; trx->must_flush_log_later = FALSE;
rseg = trx->rseg; rseg = trx->rseg;
@@ -1558,6 +1569,14 @@ trx_commit_for_mysql(
ut_a(trx); ut_a(trx);
if (trx->sync_cb) {
ulint err;
err = trx->sync_cb(trx, TRUE);
ut_a(err);
trx->sync_cb = NULL;
}
trx->op_info = "committing"; trx->op_info = "committing";
/* If we are doing the XA recovery of prepared transactions, then /* If we are doing the XA recovery of prepared transactions, then
@@ -1575,7 +1594,7 @@ trx_commit_for_mysql(
trx->sess = trx_dummy_sess; trx->sess = trx_dummy_sess;
} }
mutex_exit(&kernel_mutex); mutex_exit(&kernel_mutex);
trx_start_if_not_started(trx); trx_start_if_not_started(trx);