Many files:

  Merge InnoDB-4.0.7. Support for ON UPDATE CASCADE
sql_select.cc:
  Remove superfluous prints to .err log when a locking SELECT fails to a deadlock or a lock wait timeout
parent f03bbd3f
...@@ -19,6 +19,9 @@ Created 2/17/1996 Heikki Tuuri ...@@ -19,6 +19,9 @@ Created 2/17/1996 Heikki Tuuri
#include "btr0btr.h" #include "btr0btr.h"
#include "ha0ha.h" #include "ha0ha.h"
ulint btr_search_this_is_zero = 0; /* A dummy variable to fool the
compiler */
ulint btr_search_n_succ = 0; ulint btr_search_n_succ = 0;
ulint btr_search_n_hash_fail = 0; ulint btr_search_n_hash_fail = 0;
...@@ -56,14 +59,18 @@ before hash index building is started */ ...@@ -56,14 +59,18 @@ before hash index building is started */
/************************************************************************ /************************************************************************
Builds a hash index on a page with the given parameters. If the page already Builds a hash index on a page with the given parameters. If the page already
has a hash index with different parameters, the old hash index is removed. */ has a hash index with different parameters, the old hash index is removed.
If index is non-NULL, this function checks if n_fields and n_bytes are
sensible values, and does not build a hash index if not. */
static static
void void
btr_search_build_page_hash_index( btr_search_build_page_hash_index(
/*=============================*/ /*=============================*/
dict_index_t* index, /* in: index for which to build, or NULL if
not known */
page_t* page, /* in: index page, s- or x-latched */ page_t* page, /* in: index page, s- or x-latched */
ulint n_fields, /* in: hash this many full fields */ ulint n_fields,/* in: hash this many full fields */
ulint n_bytes, /* in: hash this many bytes from the next ulint n_bytes,/* in: hash this many bytes from the next
field */ field */
ulint side); /* in: hash for searches from this side */ ulint side); /* in: hash for searches from this side */
...@@ -173,7 +180,9 @@ btr_search_info_create( ...@@ -173,7 +180,9 @@ btr_search_info_create(
} }
/************************************************************************* /*************************************************************************
Updates the search info of an index about hash successes. */ Updates the search info of an index about hash successes. NOTE that info
is NOT protected by any semaphore, to save CPU time! Do not assume its fields
are consistent. */
static static
void void
btr_search_info_update_hash( btr_search_info_update_hash(
...@@ -295,7 +304,9 @@ set_new_recomm: ...@@ -295,7 +304,9 @@ set_new_recomm:
} }
/************************************************************************* /*************************************************************************
Updates the block search info on hash successes. */ Updates the block search info on hash successes. NOTE that info and
block->n_hash_helps, n_fields, n_bytes, side are NOT protected by any
semaphore, to save CPU time! Do not assume the fields are consistent. */
static static
ibool ibool
btr_search_update_block_hash_info( btr_search_update_block_hash_info(
...@@ -425,12 +436,19 @@ btr_search_info_update_slow( ...@@ -425,12 +436,19 @@ btr_search_info_update_slow(
{ {
buf_block_t* block; buf_block_t* block;
ibool build_index; ibool build_index;
ulint* params;
ulint* params2;
ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED) ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)
&& !rw_lock_own(&btr_search_latch, RW_LOCK_EX)); && !rw_lock_own(&btr_search_latch, RW_LOCK_EX));
block = buf_block_align(btr_cur_get_rec(cursor)); block = buf_block_align(btr_cur_get_rec(cursor));
/* NOTE that the following two function calls do NOT protect
info or block->n_fields etc. with any semaphore, to save CPU time!
We cannot assume the fields are consistent when we return from
those functions! */
btr_search_info_update_hash(info, cursor); btr_search_info_update_hash(info, cursor);
build_index = btr_search_update_block_hash_info(info, block, cursor); build_index = btr_search_update_block_hash_info(info, block, cursor);
...@@ -453,10 +471,30 @@ btr_search_info_update_slow( ...@@ -453,10 +471,30 @@ btr_search_info_update_slow(
} }
if (build_index) { if (build_index) {
btr_search_build_page_hash_index(block->frame, /* Note that since we did not protect block->n_fields etc.
block->n_fields, with any semaphore, the values can be inconsistent. We have
block->n_bytes, to check inside the function call that they make sense. We
block->side); also malloc an array and store the values there to make sure
the compiler does not let the function call parameters change
inside the called function. It might be that the compiler
would optimize the call just to pass pointers to block. */
params = mem_alloc(3 * sizeof(ulint));
params[0] = block->n_fields;
params[1] = block->n_bytes;
params[2] = block->side;
/* Make sure the compiler cannot deduce the values and do
optimizations */
params2 = params + btr_search_this_is_zero;
btr_search_build_page_hash_index(cursor->index,
block->frame,
params2[0],
params2[1],
params2[2]);
mem_free(params);
} }
} }
...@@ -974,14 +1012,18 @@ btr_search_drop_page_hash_when_freed( ...@@ -974,14 +1012,18 @@ btr_search_drop_page_hash_when_freed(
/************************************************************************ /************************************************************************
Builds a hash index on a page with the given parameters. If the page already Builds a hash index on a page with the given parameters. If the page already
has a hash index with different parameters, the old hash index is removed. */ has a hash index with different parameters, the old hash index is removed.
If index is non-NULL, this function checks if n_fields and n_bytes are
sensible values, and does not build a hash index if not. */
static static
void void
btr_search_build_page_hash_index( btr_search_build_page_hash_index(
/*=============================*/ /*=============================*/
dict_index_t* index, /* in: index for which to build, or NULL if
not known */
page_t* page, /* in: index page, s- or x-latched */ page_t* page, /* in: index page, s- or x-latched */
ulint n_fields, /* in: hash this many full fields */ ulint n_fields,/* in: hash this many full fields */
ulint n_bytes, /* in: hash this many bytes from the next ulint n_bytes,/* in: hash this many bytes from the next
field */ field */
ulint side) /* in: hash for searches from this side */ ulint side) /* in: hash for searches from this side */
{ {
...@@ -1026,11 +1068,19 @@ btr_search_build_page_hash_index( ...@@ -1026,11 +1068,19 @@ btr_search_build_page_hash_index(
return; return;
} }
/* Check that the values for hash index build are sensible */
if (n_fields + n_bytes == 0) { if (n_fields + n_bytes == 0) {
return; return;
} }
if (index && (dict_index_get_n_unique_in_tree(index) < n_fields
|| (dict_index_get_n_unique_in_tree(index) == n_fields
&& n_bytes > 0))) {
return;
}
/* Calculate and cache fold values and corresponding records into /* Calculate and cache fold values and corresponding records into
an array for fast insertion to the hash index */ an array for fast insertion to the hash index */
...@@ -1187,8 +1237,8 @@ btr_search_move_or_delete_hash_entries( ...@@ -1187,8 +1237,8 @@ btr_search_move_or_delete_hash_entries(
ut_a(n_fields + n_bytes > 0); ut_a(n_fields + n_bytes > 0);
btr_search_build_page_hash_index(new_page, n_fields, n_bytes, btr_search_build_page_hash_index(NULL, new_page, n_fields,
side); n_bytes, side);
ut_a(n_fields == block->curr_n_fields); ut_a(n_fields == block->curr_n_fields);
ut_a(n_bytes == block->curr_n_bytes); ut_a(n_bytes == block->curr_n_bytes);
ut_a(side == block->curr_side); ut_a(side == block->curr_side);
......
...@@ -1154,8 +1154,10 @@ dict_index_add_to_cache( ...@@ -1154,8 +1154,10 @@ dict_index_add_to_cache(
if (dict_index_get_nth_field(index, j)->col if (dict_index_get_nth_field(index, j)->col
== dict_index_get_nth_field(index, i)->col) { == dict_index_get_nth_field(index, i)->col) {
ut_print_timestamp(stderr);
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: column %s appears twice in index %s of table %s\n" " InnoDB: Error: column %s appears twice in index %s of table %s\n"
"InnoDB: This is not allowed in InnoDB.\n" "InnoDB: This is not allowed in InnoDB.\n"
"InnoDB: UPDATE can cause such an index to become corrupt in InnoDB.\n", "InnoDB: UPDATE can cause such an index to become corrupt in InnoDB.\n",
dict_index_get_nth_field(index, i)->col->name, dict_index_get_nth_field(index, i)->col->name,
...@@ -2233,6 +2235,9 @@ dict_create_foreign_constraints( ...@@ -2233,6 +2235,9 @@ dict_create_foreign_constraints(
ulint error; ulint error;
ulint i; ulint i;
ulint j; ulint j;
ibool is_on_delete;
ulint n_on_deletes;
ulint n_on_updates;
dict_col_t* columns[500]; dict_col_t* columns[500];
char* column_names[500]; char* column_names[500];
ulint column_name_lens[500]; ulint column_name_lens[500];
...@@ -2392,6 +2397,12 @@ col_loop2: ...@@ -2392,6 +2397,12 @@ col_loop2:
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
n_on_deletes = 0;
n_on_updates = 0;
scan_on_conditions:
/* Loop here as long as we can find ON ... conditions */
ptr = dict_accept(ptr, "ON", &success); ptr = dict_accept(ptr, "ON", &success);
if (!success) { if (!success) {
...@@ -2402,23 +2413,58 @@ col_loop2: ...@@ -2402,23 +2413,58 @@ col_loop2:
ptr = dict_accept(ptr, "DELETE", &success); ptr = dict_accept(ptr, "DELETE", &success);
if (!success) { if (!success) {
ptr = dict_accept(ptr, "UPDATE", &success);
if (!success) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
is_on_delete = FALSE;
n_on_updates++;
} else {
is_on_delete = TRUE;
n_on_deletes++;
}
ptr = dict_accept(ptr, "RESTRICT", &success); ptr = dict_accept(ptr, "RESTRICT", &success);
if (success) { if (success) {
goto try_find_index; goto scan_on_conditions;
} }
ptr = dict_accept(ptr, "CASCADE", &success); ptr = dict_accept(ptr, "CASCADE", &success);
if (success) { if (success) {
foreign->type = DICT_FOREIGN_ON_DELETE_CASCADE; if (is_on_delete) {
foreign->type |= DICT_FOREIGN_ON_DELETE_CASCADE;
} else {
foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE;
}
goto try_find_index; goto scan_on_conditions;
}
ptr = dict_accept(ptr, "NO", &success);
if (success) {
ptr = dict_accept(ptr, "ACTION", &success);
if (!success) {
dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT);
}
if (is_on_delete) {
foreign->type |= DICT_FOREIGN_ON_DELETE_NO_ACTION;
} else {
foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION;
}
goto scan_on_conditions;
} }
ptr = dict_accept(ptr, "SET", &success); ptr = dict_accept(ptr, "SET", &success);
...@@ -2451,15 +2497,18 @@ col_loop2: ...@@ -2451,15 +2497,18 @@ col_loop2:
} }
} }
foreign->type = DICT_FOREIGN_ON_DELETE_SET_NULL; if (is_on_delete) {
foreign->type |= DICT_FOREIGN_ON_DELETE_SET_NULL;
} else {
foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL;
}
try_find_index: goto scan_on_conditions;
/* We check that there are no superfluous words like 'ON UPDATE ...'
which we do not support yet. */
ptr = dict_accept(ptr, (char *) "ON", &success); try_find_index:
if (n_on_deletes > 1 || n_on_updates > 1) {
/* It is an error to define more than 1 action */
if (success) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
...@@ -3286,7 +3335,8 @@ dict_print_info_on_foreign_keys_in_create_format( ...@@ -3286,7 +3335,8 @@ dict_print_info_on_foreign_keys_in_create_format(
/*=============================================*/ /*=============================================*/
char* buf, /* in: auxiliary buffer */ char* buf, /* in: auxiliary buffer */
char* str, /* in/out: pointer to a string */ char* str, /* in/out: pointer to a string */
ulint len, /* in: space in str available for info */ ulint len, /* in: str has to be a buffer at least
len + 5000 bytes */
dict_table_t* table) /* in: table */ dict_table_t* table) /* in: table */
{ {
...@@ -3356,14 +3406,30 @@ dict_print_info_on_foreign_keys_in_create_format( ...@@ -3356,14 +3406,30 @@ dict_print_info_on_foreign_keys_in_create_format(
buf2 += sprintf(buf2, ")"); buf2 += sprintf(buf2, ")");
if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE) { if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) {
buf2 += sprintf(buf2, " ON DELETE CASCADE"); buf2 += sprintf(buf2, " ON DELETE CASCADE");
} }
if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) { if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) {
buf2 += sprintf(buf2, " ON DELETE SET NULL"); buf2 += sprintf(buf2, " ON DELETE SET NULL");
} }
if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
buf2 += sprintf(buf2, " ON DELETE NO ACTION");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) {
buf2 += sprintf(buf2, " ON UPDATE CASCADE");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) {
buf2 += sprintf(buf2, " ON UPDATE SET NULL");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
buf2 += sprintf(buf2, " ON UPDATE NO ACTION");
}
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
} }
no_space: no_space:
...@@ -3455,6 +3521,22 @@ dict_print_info_on_foreign_keys( ...@@ -3455,6 +3521,22 @@ dict_print_info_on_foreign_keys(
buf2 += sprintf(buf2, " ON DELETE SET NULL"); buf2 += sprintf(buf2, " ON DELETE SET NULL");
} }
if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
buf2 += sprintf(buf2, " ON DELETE NO ACTION");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) {
buf2 += sprintf(buf2, " ON UPDATE CASCADE");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) {
buf2 += sprintf(buf2, " ON UPDATE SET NULL");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
buf2 += sprintf(buf2, " ON UPDATE NO ACTION");
}
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
} }
no_space: no_space:
......
...@@ -2479,20 +2479,20 @@ try_again: ...@@ -2479,20 +2479,20 @@ try_again:
n_free = n_free_list_ext + n_free_up; n_free = n_free_list_ext + n_free_up;
if (alloc_type == FSP_NORMAL) { if (alloc_type == FSP_NORMAL) {
/* We reserve 1 extent + 4 % of the space size to undo logs /* We reserve 1 extent + 0.5 % of the space size to undo logs
and 1 extent + 1 % to cleaning operations; NOTE: this source and 1 extent + 0.5 % to cleaning operations; NOTE: this source
code is duplicated in the function below! */ code is duplicated in the function below! */
reserve = 2 + ((size / FSP_EXTENT_SIZE) * 5) / 100; reserve = 2 + ((size / FSP_EXTENT_SIZE) * 2) / 200;
if (n_free <= reserve + n_ext) { if (n_free <= reserve + n_ext) {
goto try_to_extend; goto try_to_extend;
} }
} else if (alloc_type == FSP_UNDO) { } else if (alloc_type == FSP_UNDO) {
/* We reserve 1 % of the space size to cleaning operations */ /* We reserve 0.5 % of the space size to cleaning operations */
reserve = 1 + ((size / FSP_EXTENT_SIZE) * 1) / 100; reserve = 1 + ((size / FSP_EXTENT_SIZE) * 1) / 200;
if (n_free <= reserve + n_ext) { if (n_free <= reserve + n_ext) {
...@@ -2572,11 +2572,11 @@ fsp_get_available_space_in_free_extents( ...@@ -2572,11 +2572,11 @@ fsp_get_available_space_in_free_extents(
n_free = n_free_list_ext + n_free_up; n_free = n_free_list_ext + n_free_up;
/* We reserve 1 extent + 4 % of the space size to undo logs /* We reserve 1 extent + 0.5 % of the space size to undo logs
and 1 extent + 1 % to cleaning operations; NOTE: this source and 1 extent + 0.5 % to cleaning operations; NOTE: this source
code is duplicated in the function above! */ code is duplicated in the function above! */
reserve = 2 + ((size / FSP_EXTENT_SIZE) * 5) / 100; reserve = 2 + ((size / FSP_EXTENT_SIZE) * 2) / 200;
if (reserve > n_free) { if (reserve > n_free) {
return(0); return(0);
......
...@@ -2658,9 +2658,6 @@ reset_bit: ...@@ -2658,9 +2658,6 @@ reset_bit:
} }
} }
ibuf_data->n_merges++;
ibuf_data->n_merged_recs += n_inserts;
#ifdef UNIV_IBUF_DEBUG #ifdef UNIV_IBUF_DEBUG
/* printf("Ibuf merge %lu records volume %lu to page no %lu\n", /* printf("Ibuf merge %lu records volume %lu to page no %lu\n",
n_inserts, volume, page_no); */ n_inserts, volume, page_no); */
...@@ -2670,6 +2667,14 @@ reset_bit: ...@@ -2670,6 +2667,14 @@ reset_bit:
mem_heap_free(heap); mem_heap_free(heap);
/* Protect our statistics keeping from race conditions */
mutex_enter(&ibuf_mutex);
ibuf_data->n_merges++;
ibuf_data->n_merged_recs += n_inserts;
mutex_exit(&ibuf_mutex);
ibuf_exit(); ibuf_exit();
#ifdef UNIV_IBUF_DEBUG #ifdef UNIV_IBUF_DEBUG
ut_a(ibuf_count_get(space, page_no) == 0); ut_a(ibuf_count_get(space, page_no) == 0);
......
...@@ -728,8 +728,8 @@ struct buf_block_struct{ ...@@ -728,8 +728,8 @@ struct buf_block_struct{
bufferfixed, or (2) the thread has an bufferfixed, or (2) the thread has an
x-latch on the block */ x-latch on the block */
/* 5. Hash search fields: NOTE that these fields are protected by /* 5. Hash search fields: NOTE that the first 4 fields are NOT
btr_search_mutex */ protected by any semaphore! */
ulint n_hash_helps; /* counter which controls building ulint n_hash_helps; /* counter which controls building
of a new hash index for the page */ of a new hash index for the page */
...@@ -742,6 +742,9 @@ struct buf_block_struct{ ...@@ -742,6 +742,9 @@ struct buf_block_struct{
whether the leftmost record of several whether the leftmost record of several
records with the same prefix should be records with the same prefix should be
indexed in the hash index */ indexed in the hash index */
/* The following 4 fields are protected by btr_search_latch: */
ibool is_hashed; /* TRUE if hash index has already been ibool is_hashed; /* TRUE if hash index has already been
built on this page; note that it does built on this page; note that it does
not guarantee that the index is not guarantee that the index is
......
...@@ -42,7 +42,8 @@ Created 5/24/1996 Heikki Tuuri ...@@ -42,7 +42,8 @@ Created 5/24/1996 Heikki Tuuri
#define DB_CANNOT_ADD_CONSTRAINT 38 /* adding a foreign key constraint #define DB_CANNOT_ADD_CONSTRAINT 38 /* adding a foreign key constraint
to a table failed */ to a table failed */
#define DB_CORRUPTION 39 /* data structure corruption noticed */ #define DB_CORRUPTION 39 /* data structure corruption noticed */
#define DB_COL_APPEARS_TWICE_IN_INDEX 40 #define DB_COL_APPEARS_TWICE_IN_INDEX 40 /* InnoDB cannot handle an index
where same column appears twice */
/* The following are partial failure codes */ /* The following are partial failure codes */
#define DB_FAIL 1000 #define DB_FAIL 1000
......
...@@ -280,8 +280,15 @@ struct dict_foreign_struct{ ...@@ -280,8 +280,15 @@ struct dict_foreign_struct{
table */ table */
}; };
/* The flags for ON_UPDATE and ON_DELETE can be ORed; the default is that
a foreign key constraint is enforced, therefore RESTRICT just means no flag */
#define DICT_FOREIGN_ON_DELETE_CASCADE 1 #define DICT_FOREIGN_ON_DELETE_CASCADE 1
#define DICT_FOREIGN_ON_DELETE_SET_NULL 2 #define DICT_FOREIGN_ON_DELETE_SET_NULL 2
#define DICT_FOREIGN_ON_UPDATE_CASCADE 4
#define DICT_FOREIGN_ON_UPDATE_SET_NULL 8
#define DICT_FOREIGN_ON_DELETE_NO_ACTION 16
#define DICT_FOREIGN_ON_UPDATE_NO_ACTION 32
#define DICT_INDEX_MAGIC_N 76789786 #define DICT_INDEX_MAGIC_N 76789786
......
...@@ -127,16 +127,18 @@ mem_heap_create_func( ...@@ -127,16 +127,18 @@ mem_heap_create_func(
ulint line /* in: line where created */ ulint line /* in: line where created */
); );
/********************************************************************* /*********************************************************************
NOTE: Use the corresponding macro instead of this function. NOTE: Use the corresponding macro instead of this function. Frees the space
Frees the space occupied by a memory heap. */ occupied by a memory heap. In the debug version erases the heap memory
blocks. */
UNIV_INLINE UNIV_INLINE
void void
mem_heap_free_func( mem_heap_free_func(
/*===============*/ /*===============*/
mem_heap_t* heap, /* in, own: heap to be freed */ mem_heap_t* heap, /* in, own: heap to be freed */
char* file_name, /* in: file name where freed */ char* file_name __attribute__((unused)),
ulint line /* in: line where freed */ /* in: file name where freed */
); ulint line __attribute__((unused)));
/* 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
......
...@@ -440,9 +440,10 @@ void ...@@ -440,9 +440,10 @@ void
mem_heap_free_func( mem_heap_free_func(
/*===============*/ /*===============*/
mem_heap_t* heap, /* in, own: heap to be freed */ mem_heap_t* heap, /* in, own: heap to be freed */
char* file_name, /* in: file name where freed */ char* file_name __attribute__((unused)),
ulint line /* in: line where freed */ /* in: file name where freed */
) ulint line __attribute__((unused)))
/* in: line where freed */
{ {
mem_block_t* block; mem_block_t* block;
mem_block_t* prev_block; mem_block_t* prev_block;
......
...@@ -492,7 +492,11 @@ struct row_prebuilt_struct { ...@@ -492,7 +492,11 @@ struct row_prebuilt_struct {
fetch many rows from the same cursor: fetch many rows from the same cursor:
it saves CPU time to fetch them in a it saves CPU time to fetch them in a
batch; we reserve mysql_row_len batch; we reserve mysql_row_len
bytes for each such row */ bytes for each such row; these
pointers point 4 bytes past the
allocated mem buf start, because
there is a 4 byte magic number at the
start and at the end */
ulint fetch_cache_first;/* position of the first not yet ulint fetch_cache_first;/* position of the first not yet
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
...@@ -501,8 +505,12 @@ struct row_prebuilt_struct { ...@@ -501,8 +505,12 @@ struct row_prebuilt_struct {
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 */
ulint magic_n2; /* this should be the same as
magic_n */
}; };
#define ROW_PREBUILT_FETCH_MAGIC_N 465765687
#define ROW_MYSQL_WHOLE_ROW 0 #define ROW_MYSQL_WHOLE_ROW 0
#define ROW_MYSQL_REC_FIELDS 1 #define ROW_MYSQL_REC_FIELDS 1
#define ROW_MYSQL_NO_TEMPLATE 2 #define ROW_MYSQL_NO_TEMPLATE 2
......
...@@ -312,8 +312,11 @@ struct upd_node_struct{ ...@@ -312,8 +312,11 @@ struct upd_node_struct{
ibool in_mysql_interface; ibool in_mysql_interface;
/* TRUE if the update node was created /* TRUE if the update node was created
for the MySQL interface */ for the MySQL interface */
dict_foreign_t* foreign;/* NULL or pointer to a foreign key
constraint if this update node is used in
doing an ON DELETE or ON UPDATE operation */
upd_node_t* cascade_node;/* NULL or an update node template which upd_node_t* cascade_node;/* NULL or an update node template which
is used to implement ON DELETE CASCADE is used to implement ON DELETE/UPDATE CASCADE
or ... SET NULL for foreign keys */ or ... SET NULL for foreign keys */
mem_heap_t* cascade_heap;/* NULL or a mem heap where the cascade mem_heap_t* cascade_heap;/* NULL or a mem heap where the cascade
node is created */ node is created */
......
...@@ -15,6 +15,7 @@ Created 5/12/1997 Heikki Tuuri ...@@ -15,6 +15,7 @@ Created 5/12/1997 Heikki Tuuri
#include "ut0mem.h" #include "ut0mem.h"
#include "ut0lst.h" #include "ut0lst.h"
#include "ut0byte.h" #include "ut0byte.h"
#include "mem0mem.h"
/* We would like to use also the buffer frames to allocate memory. This /* We would like to use also the buffer frames to allocate memory. This
would be desirable, because then the memory consumption of the database would be desirable, because then the memory consumption of the database
...@@ -251,7 +252,6 @@ mem_pool_fill_free_list( ...@@ -251,7 +252,6 @@ mem_pool_fill_free_list(
mem_area_t* area; mem_area_t* area;
mem_area_t* area2; mem_area_t* area2;
ibool ret; ibool ret;
char err_buf[500];
ut_ad(mutex_own(&(pool->mutex))); ut_ad(mutex_own(&(pool->mutex)));
...@@ -300,11 +300,8 @@ mem_pool_fill_free_list( ...@@ -300,11 +300,8 @@ mem_pool_fill_free_list(
} }
if (UT_LIST_GET_LEN(pool->free_list[i + 1]) == 0) { if (UT_LIST_GET_LEN(pool->free_list[i + 1]) == 0) {
ut_sprintf_buf(err_buf, ((byte*)area) - 50, 100); mem_analyze_corruption((byte*)area);
fprintf(stderr,
"InnoDB: Error: Removing element from mem pool free list %lu\n"
"InnoDB: though the list length is 0! Dump of 100 bytes around element:\n%s\n",
i + 1, err_buf);
ut_a(0); ut_a(0);
} }
...@@ -340,7 +337,6 @@ mem_area_alloc( ...@@ -340,7 +337,6 @@ mem_area_alloc(
mem_area_t* area; mem_area_t* area;
ulint n; ulint n;
ibool ret; ibool ret;
char err_buf[500];
n = ut_2_log(ut_max(size + MEM_AREA_EXTRA_SIZE, MEM_AREA_MIN_SIZE)); n = ut_2_log(ut_max(size + MEM_AREA_EXTRA_SIZE, MEM_AREA_MIN_SIZE));
...@@ -364,20 +360,22 @@ mem_area_alloc( ...@@ -364,20 +360,22 @@ mem_area_alloc(
} }
if (!mem_area_get_free(area)) { if (!mem_area_get_free(area)) {
ut_sprintf_buf(err_buf, ((byte*)area) - 50, 100);
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: Removing element from mem pool free list %lu though the\n" "InnoDB: Error: Removing element from mem pool free list %lu though the\n"
"InnoDB: element is not marked free! Dump of 100 bytes around element:\n%s\n", "InnoDB: element is not marked free!\n",
n, err_buf); n);
mem_analyze_corruption((byte*)area);
ut_a(0); ut_a(0);
} }
if (UT_LIST_GET_LEN(pool->free_list[n]) == 0) { if (UT_LIST_GET_LEN(pool->free_list[n]) == 0) {
ut_sprintf_buf(err_buf, ((byte*)area) - 50, 100);
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: Removing element from mem pool free list %lu\n" "InnoDB: Error: Removing element from mem pool free list %lu\n"
"InnoDB: though the list length is 0! Dump of 100 bytes around element:\n%s\n", "InnoDB: though the list length is 0!\n",
n, err_buf); n);
mem_analyze_corruption((byte*)area);
ut_a(0); ut_a(0);
} }
...@@ -451,7 +449,6 @@ mem_area_free( ...@@ -451,7 +449,6 @@ mem_area_free(
void* new_ptr; void* new_ptr;
ulint size; ulint size;
ulint n; ulint n;
char err_buf[500];
if (mem_out_of_mem_err_msg_count > 0) { if (mem_out_of_mem_err_msg_count > 0) {
/* It may be that the area was really allocated from the /* It may be that the area was really allocated from the
...@@ -469,17 +466,24 @@ mem_area_free( ...@@ -469,17 +466,24 @@ mem_area_free(
area = (mem_area_t*) (((byte*)ptr) - MEM_AREA_EXTRA_SIZE); area = (mem_area_t*) (((byte*)ptr) - MEM_AREA_EXTRA_SIZE);
if (mem_area_get_free(area)) { if (mem_area_get_free(area)) {
ut_sprintf_buf(err_buf, ((byte*)area) - 50, 100);
fprintf(stderr, fprintf(stderr,
"InnoDB: Error: Freeing element to mem pool free list though the\n" "InnoDB: Error: Freeing element to mem pool free list though the\n"
"InnoDB: element is marked free! Dump of 100 bytes around element:\n%s\n", "InnoDB: element is marked free!\n");
err_buf);
mem_analyze_corruption((byte*)area);
ut_a(0); ut_a(0);
} }
size = mem_area_get_size(area); size = mem_area_get_size(area);
ut_ad(size != 0); if (size == 0) {
fprintf(stderr,
"InnoDB: Error: Mem area size is 0. Possibly a memory overrun of the\n"
"InnoDB: previous allocated area!\n");
mem_analyze_corruption((byte*)area);
ut_a(0);
}
#ifdef UNIV_LIGHT_MEM_DEBUG #ifdef UNIV_LIGHT_MEM_DEBUG
if (((byte*)area) + size < pool->buf + pool->size) { if (((byte*)area) + size < pool->buf + pool->size) {
...@@ -488,7 +492,15 @@ mem_area_free( ...@@ -488,7 +492,15 @@ mem_area_free(
next_size = mem_area_get_size( next_size = mem_area_get_size(
(mem_area_t*)(((byte*)area) + size)); (mem_area_t*)(((byte*)area) + size));
ut_a(ut_2_power_up(next_size) == next_size); if (ut_2_power_up(next_size) != next_size) {
fprintf(stderr,
"InnoDB: Error: Memory area size %lu, next area size %lu not a power of 2!\n"
"InnoDB: Possibly a memory overrun of the buffer being freed here.\n",
size, next_size);
mem_analyze_corruption((byte*)area);
ut_a(0);
}
} }
#endif #endif
buddy = mem_area_get_buddy(area, size, pool); buddy = mem_area_get_buddy(area, size, pool);
......
...@@ -322,13 +322,129 @@ row_ins_clust_index_entry_by_modify( ...@@ -322,13 +322,129 @@ row_ins_clust_index_entry_by_modify(
} }
/************************************************************************* /*************************************************************************
Either deletes or sets the referencing columns SQL NULL in a child row. Returns TRUE if in a cascaded update/delete an ancestor node of node
Used in ON DELETE ... clause for foreign keys when a parent row is updates table. */
deleted. */ static
ibool
row_ins_cascade_ancestor_updates_table(
/*===================================*/
/* out: TRUE if an ancestor updates table */
que_node_t* node, /* in: node in a query graph */
dict_table_t* table) /* in: table */
{
que_node_t* parent;
upd_node_t* upd_node;
parent = que_node_get_parent(node);
while (que_node_get_type(parent) == QUE_NODE_UPDATE) {
upd_node = parent;
if (upd_node->table == table) {
return(TRUE);
}
parent = que_node_get_parent(parent);
ut_a(parent);
}
return(FALSE);
}
/**********************************************************************
Calculates the update vector node->cascade->update for a child table in
a cascaded update. */
static static
ulint ulint
row_ins_foreign_delete_or_set_null( row_ins_cascade_calc_update_vec(
/*===============================*/ /*============================*/
/* out: number of fields in the
calculated update vector; the value
can also be 0 if no foreign key
fields changed */
upd_node_t* node, /* in: update node of the parent
table */
dict_foreign_t* foreign) /* in: foreign key constraint whose
type is != 0 */
{
upd_node_t* cascade = node->cascade_node;
dict_table_t* table = foreign->foreign_table;
dict_index_t* index = foreign->foreign_index;
upd_t* update;
upd_field_t* ufield;
dict_table_t* parent_table;
dict_index_t* parent_index;
upd_t* parent_update;
upd_field_t* parent_ufield;
ulint n_fields_updated;
ulint parent_field_no;
ulint i;
ulint j;
ut_a(node && foreign && cascade && table && index);
/* Calculate the appropriate update vector which will set the fields
in the child index record to the same value as the referenced index
record will get in the update. */
parent_table = node->table;
ut_a(parent_table == foreign->referenced_table);
parent_index = foreign->referenced_index;
parent_update = node->update;
update = cascade->update;
update->info_bits = 0;
update->n_fields = foreign->n_fields;
n_fields_updated = 0;
for (i = 0; i < foreign->n_fields; i++) {
parent_field_no = dict_table_get_nth_col_pos(
parent_table,
dict_index_get_nth_col_no(
parent_index, i));
for (j = 0; j < parent_update->n_fields; j++) {
parent_ufield = parent_update->fields + j;
if (parent_ufield->field_no == parent_field_no) {
/* A field in the parent index record is
updated. Let us make the update vector
field for the child table. */
ufield = update->fields + n_fields_updated;
ufield->field_no =
dict_table_get_nth_col_pos(table,
dict_index_get_nth_col_no(index, i));
ufield->exp = NULL;
ufield->new_val = parent_ufield->new_val;
ufield->extern_storage = FALSE;
n_fields_updated++;
}
}
}
update->n_fields = n_fields_updated;
return(n_fields_updated);
}
/*************************************************************************
Perform referential actions or checks when a parent row is deleted or updated
and the constraint had an ON DELETE or ON UPDATE condition which was not
RESTRICT. */
static
ulint
row_ins_foreign_check_on_constraint(
/*================================*/
/* out: DB_SUCCESS, DB_LOCK_WAIT, /* out: DB_SUCCESS, DB_LOCK_WAIT,
or error code */ or error code */
que_thr_t* thr, /* in: query thread whose run_node que_thr_t* thr, /* in: query thread whose run_node
...@@ -378,15 +494,34 @@ row_ins_foreign_delete_or_set_null( ...@@ -378,15 +494,34 @@ row_ins_foreign_delete_or_set_null(
ut_strlen(table->name) + 1); ut_strlen(table->name) + 1);
node = thr->run_node; node = thr->run_node;
ut_a(que_node_get_type(node) == QUE_NODE_UPDATE); if (node->is_delete && 0 == (foreign->type &
(DICT_FOREIGN_ON_DELETE_CASCADE
| DICT_FOREIGN_ON_DELETE_SET_NULL))) {
/* No action is defined: return a foreign key error if
NO ACTION is not specified */
if (!node->is_delete) { if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
/* According to SQL-92 an UPDATE with respect to FOREIGN
KEY constraints is not semantically equivalent to a return(DB_SUCCESS);
DELETE + INSERT. Therefore we do not perform any action }
here and consequently the child rows would be left
orphaned if we would let the UPDATE happen. Thus we return return(DB_ROW_IS_REFERENCED);
an error. */ }
if (!node->is_delete && 0 == (foreign->type &
(DICT_FOREIGN_ON_UPDATE_CASCADE
| DICT_FOREIGN_ON_UPDATE_SET_NULL))) {
/* This is an UPDATE */
/* No action is defined: return a foreign key error if
NO ACTION is not specified */
if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
return(DB_SUCCESS);
}
return(DB_ROW_IS_REFERENCED); return(DB_ROW_IS_REFERENCED);
} }
...@@ -411,7 +546,10 @@ row_ins_foreign_delete_or_set_null( ...@@ -411,7 +546,10 @@ row_ins_foreign_delete_or_set_null(
cascade->table = table; cascade->table = table;
if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE ) { cascade->foreign = foreign;
if (node->is_delete
&& (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE)) {
cascade->is_delete = TRUE; cascade->is_delete = TRUE;
} else { } else {
cascade->is_delete = FALSE; cascade->is_delete = FALSE;
...@@ -425,8 +563,30 @@ row_ins_foreign_delete_or_set_null( ...@@ -425,8 +563,30 @@ row_ins_foreign_delete_or_set_null(
} }
} }
/* We do not allow cyclic cascaded updating of the same
table. Check that we are not updating the same table which
is already being modified in this cascade chain. We have to
check this because the modification of the indexes of a
'parent' table may still be incomplete, and we must avoid
seeing the indexes of the parent table in an inconsistent
state! In this way we also prevent possible infinite
update loops caused by cyclic cascaded updates. */
if (!cascade->is_delete
&& row_ins_cascade_ancestor_updates_table(cascade, table)) {
/* We do not know if this would break foreign key
constraints, but play safe and return an error */
err = DB_ROW_IS_REFERENCED;
goto nonstandard_exit_func;
}
index = btr_pcur_get_btr_cur(pcur)->index; index = btr_pcur_get_btr_cur(pcur)->index;
ut_a(index == foreign->foreign_index);
rec = btr_pcur_get_rec(pcur); rec = btr_pcur_get_rec(pcur);
if (index->type & DICT_CLUSTERED) { if (index->type & DICT_CLUSTERED) {
...@@ -520,7 +680,11 @@ row_ins_foreign_delete_or_set_null( ...@@ -520,7 +680,11 @@ row_ins_foreign_delete_or_set_null(
goto nonstandard_exit_func; goto nonstandard_exit_func;
} }
if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) { if ((node->is_delete
&& (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL))
|| (!node->is_delete
&& (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL))) {
/* Build the appropriate update vector which sets /* Build the appropriate update vector which sets
foreign->n_fields first fields in rec to SQL NULL */ foreign->n_fields first fields in rec to SQL NULL */
...@@ -540,6 +704,26 @@ row_ins_foreign_delete_or_set_null( ...@@ -540,6 +704,26 @@ row_ins_foreign_delete_or_set_null(
} }
} }
if (!node->is_delete
&& (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)) {
/* Build the appropriate update vector which sets changing
foreign->n_fields first fields in rec to new values */
row_ins_cascade_calc_update_vec(node, foreign);
if (cascade->update->n_fields == 0) {
/* The update does not change any columns referred
to in this foreign key constraint: no need to do
anything */
err = DB_SUCCESS;
goto nonstandard_exit_func;
}
}
/* Store pcur position and initialize or store the cascade node /* Store pcur position and initialize or store the cascade node
pcur stored position */ pcur stored position */
...@@ -629,6 +813,7 @@ row_ins_check_foreign_constraint( ...@@ -629,6 +813,7 @@ row_ins_check_foreign_constraint(
dtuple_t* entry, /* in: index entry for index */ dtuple_t* entry, /* in: index entry for index */
que_thr_t* thr) /* in: query thread */ que_thr_t* thr) /* in: query thread */
{ {
upd_node_t* upd_node;
dict_table_t* check_table; dict_table_t* check_table;
dict_index_t* check_index; dict_index_t* check_index;
ulint n_fields_cmp; ulint n_fields_cmp;
...@@ -665,6 +850,30 @@ run_again: ...@@ -665,6 +850,30 @@ run_again:
} }
} }
if (que_node_get_type(thr->run_node) == QUE_NODE_UPDATE) {
upd_node = thr->run_node;
if (!(upd_node->is_delete) && upd_node->foreign == foreign) {
/* If a cascaded update is done as defined by a
foreign key constraint, do not check that
constraint for the child row. In ON UPDATE CASCADE
the update of the parent row is only half done when
we come here: if we would check the constraint here
for the child row it would fail.
A QUESTION remains: if in the child table there are
several constraints which refer to the same parent
table, we should merge all updates to the child as
one update? And the updates can be contradictory!
Currently we just perform the update associated
with each foreign key constraint, one after
another, and the user has problems predicting in
which order they are performed. */
return(DB_SUCCESS);
}
}
if (check_ref) { if (check_ref) {
check_table = foreign->referenced_table; check_table = foreign->referenced_table;
check_index = foreign->referenced_index; check_index = foreign->referenced_index;
...@@ -774,8 +983,12 @@ run_again: ...@@ -774,8 +983,12 @@ run_again:
break; break;
} else if (foreign->type != 0) { } else if (foreign->type != 0) {
/* There is an ON UPDATE or ON DELETE
condition: check them in a separate
function */
err = err =
row_ins_foreign_delete_or_set_null( row_ins_foreign_check_on_constraint(
thr, foreign, &pcur, &mtr); thr, foreign, &pcur, &mtr);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
......
...@@ -313,6 +313,7 @@ row_create_prebuilt( ...@@ -313,6 +313,7 @@ row_create_prebuilt(
prebuilt = mem_heap_alloc(heap, sizeof(row_prebuilt_t)); prebuilt = mem_heap_alloc(heap, sizeof(row_prebuilt_t));
prebuilt->magic_n = ROW_PREBUILT_ALLOCATED; prebuilt->magic_n = ROW_PREBUILT_ALLOCATED;
prebuilt->magic_n2 = ROW_PREBUILT_ALLOCATED;
prebuilt->table = table; prebuilt->table = table;
...@@ -378,11 +379,12 @@ row_prebuilt_free( ...@@ -378,11 +379,12 @@ 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) {
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 %s\n", "InnoDB: table handle. Magic n %lu, magic n2 %lu, table name %s\n",
prebuilt->magic_n, prebuilt->table->name); prebuilt->magic_n, prebuilt->magic_n2, prebuilt->table->name);
mem_analyze_corruption((byte*)prebuilt); mem_analyze_corruption((byte*)prebuilt);
...@@ -390,6 +392,7 @@ row_prebuilt_free( ...@@ -390,6 +392,7 @@ row_prebuilt_free(
} }
prebuilt->magic_n = ROW_PREBUILT_FREED; prebuilt->magic_n = ROW_PREBUILT_FREED;
prebuilt->magic_n2 = ROW_PREBUILT_FREED;
btr_pcur_free_for_mysql(prebuilt->pcur); btr_pcur_free_for_mysql(prebuilt->pcur);
btr_pcur_free_for_mysql(prebuilt->clust_pcur); btr_pcur_free_for_mysql(prebuilt->clust_pcur);
...@@ -420,7 +423,23 @@ row_prebuilt_free( ...@@ -420,7 +423,23 @@ row_prebuilt_free(
for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) { for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) {
if (prebuilt->fetch_cache[i] != NULL) { if (prebuilt->fetch_cache[i] != NULL) {
mem_free(prebuilt->fetch_cache[i]);
if ((ROW_PREBUILT_FETCH_MAGIC_N !=
mach_read_from_4((prebuilt->fetch_cache[i]) - 4))
|| (ROW_PREBUILT_FETCH_MAGIC_N !=
mach_read_from_4((prebuilt->fetch_cache[i])
+ prebuilt->mysql_row_len))) {
fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n"
"InnoDB: fetch buffer.\n");
mem_analyze_corruption(
prebuilt->fetch_cache[i]);
ut_a(0);
}
mem_free((prebuilt->fetch_cache[i]) - 4);
} }
} }
...@@ -1435,7 +1454,7 @@ int ...@@ -1435,7 +1454,7 @@ int
row_create_index_for_mysql( row_create_index_for_mysql(
/*=======================*/ /*=======================*/
/* out: error number or DB_SUCCESS */ /* out: error number or DB_SUCCESS */
dict_index_t* index, /* in: index defintion */ dict_index_t* index, /* in: index definition */
trx_t* trx) /* in: transaction handle */ trx_t* trx) /* in: transaction handle */
{ {
ind_node_t* node; ind_node_t* node;
...@@ -1444,6 +1463,8 @@ row_create_index_for_mysql( ...@@ -1444,6 +1463,8 @@ row_create_index_for_mysql(
ulint namelen; ulint namelen;
ulint keywordlen; ulint keywordlen;
ulint err; ulint err;
ulint i;
ulint j;
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(mutex_own(&(dict_sys->mutex)));
...@@ -1465,6 +1486,31 @@ row_create_index_for_mysql( ...@@ -1465,6 +1486,31 @@ row_create_index_for_mysql(
return(DB_SUCCESS); return(DB_SUCCESS);
} }
/* Check that the same column does not appear twice in the index.
InnoDB assumes this in its algorithms, e.g., update of an index
entry */
for (i = 0; i < dict_index_get_n_fields(index); i++) {
for (j = 0; j < i; j++) {
if (0 == ut_strcmp(
dict_index_get_nth_field(index, j)->name,
dict_index_get_nth_field(index, i)->name)) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: column %s appears twice in index %s of table %s\n"
"InnoDB: This is not allowed in InnoDB.\n",
dict_index_get_nth_field(index, i)->name,
index->name, index->table_name);
err = DB_COL_APPEARS_TWICE_IN_INDEX;
goto error_handling;
}
}
}
heap = mem_heap_create(512); heap = mem_heap_create(512);
trx->dict_operation = TRUE; trx->dict_operation = TRUE;
...@@ -1479,9 +1525,11 @@ row_create_index_for_mysql( ...@@ -1479,9 +1525,11 @@ row_create_index_for_mysql(
err = trx->error_state; err = trx->error_state;
que_graph_free((que_t*) que_node_get_parent(thr));
error_handling:
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
/* We have special error handling here */ /* We have special error handling here */
ut_a(err == DB_OUT_OF_FILE_SPACE);
trx->error_state = DB_SUCCESS; trx->error_state = DB_SUCCESS;
...@@ -1492,8 +1540,6 @@ row_create_index_for_mysql( ...@@ -1492,8 +1540,6 @@ row_create_index_for_mysql(
trx->error_state = DB_SUCCESS; trx->error_state = DB_SUCCESS;
} }
que_graph_free((que_t*) que_node_get_parent(thr));
trx->op_info = (char *) ""; trx->op_info = (char *) "";
return((int) err); return((int) err);
......
...@@ -2415,6 +2415,7 @@ row_sel_push_cache_row_for_mysql( ...@@ -2415,6 +2415,7 @@ row_sel_push_cache_row_for_mysql(
row_prebuilt_t* prebuilt, /* in: prebuilt struct */ row_prebuilt_t* prebuilt, /* in: prebuilt struct */
rec_t* rec) /* in: record to push */ rec_t* rec) /* in: record to push */
{ {
byte* buf;
ulint i; ulint i;
ut_ad(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE); ut_ad(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE);
...@@ -2424,8 +2425,18 @@ row_sel_push_cache_row_for_mysql( ...@@ -2424,8 +2425,18 @@ row_sel_push_cache_row_for_mysql(
/* Allocate memory for the fetch cache */ /* Allocate memory for the fetch cache */
for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) { for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) {
prebuilt->fetch_cache[i] = mem_alloc(
prebuilt->mysql_row_len); /* A user has reported memory corruption in these
buffers in Linux. Put magic numbers there to help
to track a possible bug. */
buf = mem_alloc(prebuilt->mysql_row_len + 8);
prebuilt->fetch_cache[i] = buf + 4;
mach_write_to_4(buf, ROW_PREBUILT_FETCH_MAGIC_N);
mach_write_to_4(buf + 4 + prebuilt->mysql_row_len,
ROW_PREBUILT_FETCH_MAGIC_N);
} }
} }
......
...@@ -71,6 +71,20 @@ the x-latch freed? The most efficient way for performing a ...@@ -71,6 +71,20 @@ the x-latch freed? The most efficient way for performing a
searched delete is obviously to keep the x-latch for several searched delete is obviously to keep the x-latch for several
steps of query graph execution. */ steps of query graph execution. */
/***************************************************************
Checks if an update vector changes some of the first fields of an index
record. */
static
ibool
row_upd_changes_first_fields(
/*=========================*/
/* out: TRUE if changes */
dtuple_t* entry, /* in: old value of index entry */
dict_index_t* index, /* in: index of entry */
upd_t* update, /* in: update vector for the row */
ulint n); /* in: how many first fields to check */
/************************************************************************* /*************************************************************************
Checks if index currently is mentioned as a referenced index in a foreign Checks if index currently is mentioned as a referenced index in a foreign
key constraint. */ key constraint. */
...@@ -132,6 +146,7 @@ ulint ...@@ -132,6 +146,7 @@ ulint
row_upd_check_references_constraints( row_upd_check_references_constraints(
/*=================================*/ /*=================================*/
/* out: DB_SUCCESS or an error code */ /* out: DB_SUCCESS or an error code */
upd_node_t* node, /* in: row update node */
btr_pcur_t* pcur, /* in: cursor positioned on a record; NOTE: the btr_pcur_t* pcur, /* in: cursor positioned on a record; NOTE: the
cursor position is lost in this function! */ cursor position is lost in this function! */
dict_table_t* table, /* in: table in question */ dict_table_t* table, /* in: table in question */
...@@ -173,7 +188,16 @@ row_upd_check_references_constraints( ...@@ -173,7 +188,16 @@ row_upd_check_references_constraints(
foreign = UT_LIST_GET_FIRST(table->referenced_list); foreign = UT_LIST_GET_FIRST(table->referenced_list);
while (foreign) { while (foreign) {
if (foreign->referenced_index == index) { /* Note that we may have an update which updates the index
record, but does NOT update the first fields which are
referenced in a foreign key constraint. Then the update does
NOT break the constraint. */
if (foreign->referenced_index == index
&& (node->is_delete
|| row_upd_changes_first_fields(entry, index,
node->update, foreign->n_fields))) {
if (foreign->foreign_table == NULL) { if (foreign->foreign_table == NULL) {
dict_table_get(foreign->foreign_table_name, dict_table_get(foreign->foreign_table_name,
trx); trx);
...@@ -189,10 +213,9 @@ row_upd_check_references_constraints( ...@@ -189,10 +213,9 @@ row_upd_check_references_constraints(
} }
/* NOTE that if the thread ends up waiting for a lock /* NOTE that if the thread ends up waiting for a lock
we will release dict_operation_lock we will release dict_operation_lock temporarily!
temporarily! But the counter on the table But the counter on the table protects 'foreign' from
protects 'foreign' from being dropped while the check being dropped while the check is running. */
is running. */
err = row_ins_check_foreign_constraint(FALSE, foreign, err = row_ins_check_foreign_constraint(FALSE, foreign,
table, index, entry, thr); table, index, entry, thr);
...@@ -255,6 +278,7 @@ upd_node_create( ...@@ -255,6 +278,7 @@ upd_node_create(
node->index = NULL; node->index = NULL;
node->update = NULL; node->update = NULL;
node->foreign = NULL;
node->cascade_heap = NULL; node->cascade_heap = NULL;
node->cascade_node = NULL; node->cascade_node = NULL;
...@@ -953,6 +977,53 @@ row_upd_changes_some_index_ord_field_binary( ...@@ -953,6 +977,53 @@ row_upd_changes_some_index_ord_field_binary(
return(FALSE); return(FALSE);
} }
/***************************************************************
Checks if an update vector changes some of the first fields of an index
record. */
static
ibool
row_upd_changes_first_fields(
/*=========================*/
/* out: TRUE if changes */
dtuple_t* entry, /* in: index entry */
dict_index_t* index, /* in: index of entry */
upd_t* update, /* in: update vector for the row */
ulint n) /* in: how many first fields to check */
{
upd_field_t* upd_field;
dict_field_t* ind_field;
dict_col_t* col;
ulint n_upd_fields;
ulint col_pos;
ulint i, j;
ut_a(update && index);
ut_a(n <= dict_index_get_n_fields(index));
n_upd_fields = upd_get_n_fields(update);
for (i = 0; i < n; i++) {
ind_field = dict_index_get_nth_field(index, i);
col = dict_field_get_col(ind_field);
col_pos = dict_col_get_clust_pos(col);
for (j = 0; j < n_upd_fields; j++) {
upd_field = upd_get_nth_field(update, j);
if (col_pos == upd_field->field_no
&& cmp_dfield_dfield(
dtuple_get_nth_field(entry, i),
&(upd_field->new_val))) {
return(TRUE);
}
}
}
return(FALSE);
}
/************************************************************************* /*************************************************************************
Copies the column values from a record. */ Copies the column values from a record. */
UNIV_INLINE UNIV_INLINE
...@@ -1106,9 +1177,11 @@ row_upd_sec_index_entry( ...@@ -1106,9 +1177,11 @@ row_upd_sec_index_entry(
err = btr_cur_del_mark_set_sec_rec(0, btr_cur, TRUE, err = btr_cur_del_mark_set_sec_rec(0, btr_cur, TRUE,
thr, &mtr); thr, &mtr);
if (err == DB_SUCCESS && check_ref) { if (err == DB_SUCCESS && check_ref) {
/* NOTE that the following call loses /* NOTE that the following call loses
the position of pcur ! */ the position of pcur ! */
err = row_upd_check_references_constraints( err = row_upd_check_references_constraints(
node,
&pcur, index->table, &pcur, index->table,
index, thr, &mtr); index, thr, &mtr);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
...@@ -1224,7 +1297,7 @@ row_upd_clust_rec_by_insert( ...@@ -1224,7 +1297,7 @@ row_upd_clust_rec_by_insert(
if (check_ref) { if (check_ref) {
/* NOTE that the following call loses /* NOTE that the following call loses
the position of pcur ! */ the position of pcur ! */
err = row_upd_check_references_constraints( err = row_upd_check_references_constraints(node,
pcur, table, pcur, table,
index, thr, mtr); index, thr, mtr);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
...@@ -1392,7 +1465,8 @@ row_upd_del_mark_clust_rec( ...@@ -1392,7 +1465,8 @@ row_upd_del_mark_clust_rec(
if (err == DB_SUCCESS && check_ref) { if (err == DB_SUCCESS && check_ref) {
/* NOTE that the following call loses the position of pcur ! */ /* NOTE that the following call loses the position of pcur ! */
err = row_upd_check_references_constraints(pcur, index->table, err = row_upd_check_references_constraints(node,
pcur, index->table,
index, thr, mtr); index, thr, mtr);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
mtr_commit(mtr); mtr_commit(mtr);
......
...@@ -529,6 +529,9 @@ open_or_create_log_file( ...@@ -529,6 +529,9 @@ open_or_create_log_file(
new database */ new database */
ibool* log_file_created, /* out: TRUE if new log file ibool* log_file_created, /* out: TRUE if new log file
created */ created */
ibool log_file_has_been_opened,/* in: TRUE if a log file has been
opened before: then it is an error
to try to create another log file */
ulint k, /* in: log group number */ ulint k, /* in: log group number */
ulint i) /* in: log file number in group */ ulint i) /* in: log file number in group */
{ {
...@@ -587,6 +590,11 @@ open_or_create_log_file( ...@@ -587,6 +590,11 @@ open_or_create_log_file(
fprintf(stderr, fprintf(stderr,
" InnoDB: Log file %s did not exist: new to be created\n", " InnoDB: Log file %s did not exist: new to be created\n",
name); name);
if (log_file_has_been_opened) {
return(DB_ERROR);
}
fprintf(stderr, "InnoDB: Setting log file %s size to %lu MB\n", fprintf(stderr, "InnoDB: Setting log file %s size to %lu MB\n",
name, srv_log_file_size name, srv_log_file_size
>> (20 - UNIV_PAGE_SIZE_SHIFT)); >> (20 - UNIV_PAGE_SIZE_SHIFT));
...@@ -1160,7 +1168,8 @@ innobase_start_or_create_for_mysql(void) ...@@ -1160,7 +1168,8 @@ innobase_start_or_create_for_mysql(void)
for (i = 0; i < srv_n_log_files; i++) { for (i = 0; i < srv_n_log_files; i++) {
err = open_or_create_log_file(create_new_db, err = open_or_create_log_file(create_new_db,
&log_file_created, k, i); &log_file_created,
log_opened, k, i);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
return((int) err); return((int) err);
......
...@@ -262,7 +262,7 @@ ut_print_buf( ...@@ -262,7 +262,7 @@ ut_print_buf(
data = buf; data = buf;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
if (isprint((char)(*data))) { if (isprint((int)(*data))) {
printf("%c", (char)*data); printf("%c", (char)*data);
} }
data++; data++;
...@@ -302,7 +302,7 @@ ut_sprintf_buf( ...@@ -302,7 +302,7 @@ ut_sprintf_buf(
data = buf; data = buf;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
if (isprint((char)(*data))) { if (isprint((int)(*data))) {
n += sprintf(str + n, "%c", (char)*data); n += sprintf(str + n, "%c", (char)*data);
} else { } else {
n += sprintf(str + n, "."); n += sprintf(str + n, ".");
......
...@@ -4709,6 +4709,9 @@ join_read_const(JOIN_TAB *tab) ...@@ -4709,6 +4709,9 @@ join_read_const(JOIN_TAB *tab)
empty_record(table); empty_record(table);
if (error != HA_ERR_KEY_NOT_FOUND) if (error != HA_ERR_KEY_NOT_FOUND)
{ {
/* Locking reads can legally return also these errors, do not
print them to the .err log */
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error("read_const: Got error %d when reading table %s", sql_print_error("read_const: Got error %d when reading table %s",
error, table->path); error, table->path);
table->file->print_error(error,MYF(0)); table->file->print_error(error,MYF(0));
...@@ -4772,6 +4775,7 @@ join_read_always_key(JOIN_TAB *tab) ...@@ -4772,6 +4775,7 @@ join_read_always_key(JOIN_TAB *tab)
{ {
if (error != HA_ERR_KEY_NOT_FOUND) if (error != HA_ERR_KEY_NOT_FOUND)
{ {
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error("read_const: Got error %d when reading table %s",error, sql_print_error("read_const: Got error %d when reading table %s",error,
table->path); table->path);
table->file->print_error(error,MYF(0)); table->file->print_error(error,MYF(0));
...@@ -4801,6 +4805,7 @@ join_read_last_key(JOIN_TAB *tab) ...@@ -4801,6 +4805,7 @@ join_read_last_key(JOIN_TAB *tab)
{ {
if (error != HA_ERR_KEY_NOT_FOUND) if (error != HA_ERR_KEY_NOT_FOUND)
{ {
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error("read_const: Got error %d when reading table %s",error, sql_print_error("read_const: Got error %d when reading table %s",error,
table->path); table->path);
table->file->print_error(error,MYF(0)); table->file->print_error(error,MYF(0));
...@@ -4833,6 +4838,7 @@ join_read_next_same(READ_RECORD *info) ...@@ -4833,6 +4838,7 @@ join_read_next_same(READ_RECORD *info)
{ {
if (error != HA_ERR_END_OF_FILE) if (error != HA_ERR_END_OF_FILE)
{ {
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error("read_next: Got error %d when reading table %s",error, sql_print_error("read_next: Got error %d when reading table %s",error,
table->path); table->path);
table->file->print_error(error,MYF(0)); table->file->print_error(error,MYF(0));
...@@ -4855,6 +4861,7 @@ join_read_prev_same(READ_RECORD *info) ...@@ -4855,6 +4861,7 @@ join_read_prev_same(READ_RECORD *info)
{ {
if (error != HA_ERR_END_OF_FILE) if (error != HA_ERR_END_OF_FILE)
{ {
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error("read_next: Got error %d when reading table %s",error, sql_print_error("read_next: Got error %d when reading table %s",error,
table->path); table->path);
table->file->print_error(error,MYF(0)); table->file->print_error(error,MYF(0));
...@@ -4926,6 +4933,7 @@ join_read_first(JOIN_TAB *tab) ...@@ -4926,6 +4933,7 @@ join_read_first(JOIN_TAB *tab)
{ {
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
{ {
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error("read_first_with_key: Got error %d when reading table", sql_print_error("read_first_with_key: Got error %d when reading table",
error); error);
table->file->print_error(error,MYF(0)); table->file->print_error(error,MYF(0));
...@@ -4945,7 +4953,9 @@ join_read_next(READ_RECORD *info) ...@@ -4945,7 +4953,9 @@ join_read_next(READ_RECORD *info)
{ {
if (error != HA_ERR_END_OF_FILE) if (error != HA_ERR_END_OF_FILE)
{ {
sql_print_error("read_next_with_key: Got error %d when reading table %s", if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error(
"read_next_with_key: Got error %d when reading table %s",
error, info->table->path); error, info->table->path);
info->file->print_error(error,MYF(0)); info->file->print_error(error,MYF(0));
return 1; return 1;
...@@ -4977,6 +4987,7 @@ join_read_last(JOIN_TAB *tab) ...@@ -4977,6 +4987,7 @@ join_read_last(JOIN_TAB *tab)
{ {
if (error != HA_ERR_END_OF_FILE) if (error != HA_ERR_END_OF_FILE)
{ {
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error("read_last_with_key: Got error %d when reading table", sql_print_error("read_last_with_key: Got error %d when reading table",
error, table->path); error, table->path);
table->file->print_error(error,MYF(0)); table->file->print_error(error,MYF(0));
...@@ -4996,7 +5007,9 @@ join_read_prev(READ_RECORD *info) ...@@ -4996,7 +5007,9 @@ join_read_prev(READ_RECORD *info)
{ {
if (error != HA_ERR_END_OF_FILE) if (error != HA_ERR_END_OF_FILE)
{ {
sql_print_error("read_prev_with_key: Got error %d when reading table: %s", if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error(
"read_prev_with_key: Got error %d when reading table: %s",
error,info->table->path); error,info->table->path);
info->file->print_error(error,MYF(0)); info->file->print_error(error,MYF(0));
return 1; return 1;
...@@ -5024,6 +5037,7 @@ join_ft_read_first(JOIN_TAB *tab) ...@@ -5024,6 +5037,7 @@ join_ft_read_first(JOIN_TAB *tab)
{ {
if (error != HA_ERR_END_OF_FILE) if (error != HA_ERR_END_OF_FILE)
{ {
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error("ft_read_first: Got error %d when reading table %s", sql_print_error("ft_read_first: Got error %d when reading table %s",
error, table->path); error, table->path);
table->file->print_error(error,MYF(0)); table->file->print_error(error,MYF(0));
...@@ -5042,6 +5056,7 @@ join_ft_read_next(READ_RECORD *info) ...@@ -5042,6 +5056,7 @@ join_ft_read_next(READ_RECORD *info)
{ {
if (error != HA_ERR_END_OF_FILE) if (error != HA_ERR_END_OF_FILE)
{ {
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error("ft_read_next: Got error %d when reading table %s", sql_print_error("ft_read_next: Got error %d when reading table %s",
error, info->table->path); error, info->table->path);
info->file->print_error(error,MYF(0)); info->file->print_error(error,MYF(0));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment