Many files:

  Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys
parent 6519ed11
...@@ -228,6 +228,7 @@ btr_cur_search_to_nth_level( ...@@ -228,6 +228,7 @@ btr_cur_search_to_nth_level(
ulint insert_planned; ulint insert_planned;
ulint buf_mode; ulint buf_mode;
ulint estimate; ulint estimate;
ulint ignore_sec_unique;
ulint root_height; ulint root_height;
#ifdef BTR_CUR_ADAPT #ifdef BTR_CUR_ADAPT
btr_search_t* info; btr_search_t* info;
...@@ -246,7 +247,9 @@ btr_cur_search_to_nth_level( ...@@ -246,7 +247,9 @@ btr_cur_search_to_nth_level(
#endif #endif
insert_planned = latch_mode & BTR_INSERT; insert_planned = latch_mode & BTR_INSERT;
estimate = latch_mode & BTR_ESTIMATE; estimate = latch_mode & BTR_ESTIMATE;
latch_mode = latch_mode & ~(BTR_INSERT | BTR_ESTIMATE); ignore_sec_unique = latch_mode & BTR_IGNORE_SEC_UNIQUE;
latch_mode = latch_mode & ~(BTR_INSERT | BTR_ESTIMATE
| BTR_IGNORE_SEC_UNIQUE);
ut_ad(!insert_planned || (mode == PAGE_CUR_LE)); ut_ad(!insert_planned || (mode == PAGE_CUR_LE));
...@@ -343,7 +346,8 @@ btr_cur_search_to_nth_level( ...@@ -343,7 +346,8 @@ btr_cur_search_to_nth_level(
rw_latch = latch_mode; rw_latch = latch_mode;
if (insert_planned && ibuf_should_try(index)) { if (insert_planned && ibuf_should_try(index,
ignore_sec_unique)) {
/* Try insert to the insert buffer if the /* Try insert to the insert buffer if the
page is not in the buffer pool */ page is not in the buffer pool */
...@@ -356,7 +360,6 @@ retry_page_get: ...@@ -356,7 +360,6 @@ retry_page_get:
buf_mode, buf_mode,
IB__FILE__, __LINE__, IB__FILE__, __LINE__,
mtr); mtr);
if (page == NULL) { if (page == NULL) {
/* This must be a search to perform an insert; /* This must be a search to perform an insert;
try insert to the insert buffer */ try insert to the insert buffer */
...@@ -365,7 +368,7 @@ retry_page_get: ...@@ -365,7 +368,7 @@ retry_page_get:
ut_ad(insert_planned); ut_ad(insert_planned);
ut_ad(cursor->thr); ut_ad(cursor->thr);
if (ibuf_should_try(index) && if (ibuf_should_try(index, ignore_sec_unique) &&
ibuf_insert(tuple, index, space, page_no, ibuf_insert(tuple, index, space, page_no,
cursor->thr)) { cursor->thr)) {
/* Insertion to the insert buffer succeeded */ /* Insertion to the insert buffer succeeded */
......
...@@ -1201,7 +1201,8 @@ loop: ...@@ -1201,7 +1201,8 @@ loop:
ut_dulint_get_low(id), ut_dulint_get_low(id),
table->name, table->name,
foreign->referenced_table_name, foreign->referenced_table_name,
foreign->n_fields); foreign->n_fields
+ (foreign->type << 24));
for (i = 0; i < foreign->n_fields; i++) { for (i = 0; i < foreign->n_fields; i++) {
......
...@@ -1648,7 +1648,7 @@ dict_foreign_find_index( ...@@ -1648,7 +1648,7 @@ dict_foreign_find_index(
->col->name; ->col->name;
if (ut_strlen(columns[i]) != if (ut_strlen(columns[i]) !=
ut_strlen(col_name) ut_strlen(col_name)
|| 0 != ut_memcmp(columns[i], || 0 != ut_cmp_in_lower_case(columns[i],
col_name, col_name,
ut_strlen(col_name))) { ut_strlen(col_name))) {
break; break;
...@@ -1853,8 +1853,9 @@ dict_scan_col( ...@@ -1853,8 +1853,9 @@ dict_scan_col(
ibool* success,/* out: TRUE if success */ ibool* success,/* out: TRUE if success */
dict_table_t* table, /* in: table in which the column is */ dict_table_t* table, /* in: table in which the column is */
dict_col_t** column, /* out: pointer to column if success */ dict_col_t** column, /* out: pointer to column if success */
char** column_name)/* out: pointer to column->name if char** column_name,/* out: pointer to column->name if
success */ success */
ulint* column_name_len)/* out: column name length */
{ {
dict_col_t* col; dict_col_t* col;
char* old_ptr; char* old_ptr;
...@@ -1882,20 +1883,28 @@ dict_scan_col( ...@@ -1882,20 +1883,28 @@ dict_scan_col(
ptr++; ptr++;
} }
for (i = 0; i < dict_table_get_n_cols(table); i++) { *column_name_len = (ulint)(ptr - old_ptr);
if (table == NULL) {
*success = TRUE;
*column = NULL;
*column_name = old_ptr;
} else {
for (i = 0; i < dict_table_get_n_cols(table); i++) {
col = dict_table_get_nth_col(table, i); col = dict_table_get_nth_col(table, i);
if (ut_strlen(col->name) == (ulint)(ptr - old_ptr) if (ut_strlen(col->name) == (ulint)(ptr - old_ptr)
&& 0 == ut_cmp_in_lower_case(col->name, old_ptr, && 0 == ut_cmp_in_lower_case(col->name, old_ptr,
(ulint)(ptr - old_ptr))) { (ulint)(ptr - old_ptr))) {
/* Found */ /* Found */
*success = TRUE; *success = TRUE;
*column = col; *column = col;
*column_name = col->name; *column_name = col->name;
break; break;
}
} }
} }
...@@ -1914,14 +1923,18 @@ dict_scan_table_name( ...@@ -1914,14 +1923,18 @@ dict_scan_table_name(
/*=================*/ /*=================*/
/* out: scanned to */ /* out: scanned to */
char* ptr, /* in: scanned to */ char* ptr, /* in: scanned to */
dict_table_t** table, /* out: table object or NULL if error */ dict_table_t** table, /* out: table object or NULL */
char* name) /* in: foreign key table name */ char* name, /* in: foreign key table name */
ibool* success,/* out: TRUE if ok name found */
char* second_table_name)/* in/out: buffer where to store
the referenced table name; must be at least
2500 bytes */
{ {
char* dot_ptr = NULL; char* dot_ptr = NULL;
char* old_ptr; char* old_ptr;
ulint i; ulint i;
char second_table_name[10000];
*success = FALSE;
*table = NULL; *table = NULL;
while (isspace(*ptr)) { while (isspace(*ptr)) {
...@@ -1947,7 +1960,7 @@ dict_scan_table_name( ...@@ -1947,7 +1960,7 @@ dict_scan_table_name(
ptr++; ptr++;
} }
if (ptr - old_ptr > 9000) { if (ptr - old_ptr > 2000) {
return(old_ptr); return(old_ptr);
} }
...@@ -1978,6 +1991,8 @@ dict_scan_table_name( ...@@ -1978,6 +1991,8 @@ dict_scan_table_name(
second_table_name[ptr - old_ptr] = '\0'; second_table_name[ptr - old_ptr] = '\0';
} }
*success = TRUE;
*table = dict_table_get_low(second_table_name); *table = dict_table_get_low(second_table_name);
if (*ptr == '`') { if (*ptr == '`') {
...@@ -2043,9 +2058,12 @@ dict_create_foreign_constraints( ...@@ -2043,9 +2058,12 @@ dict_create_foreign_constraints(
ibool success; ibool success;
ulint error; ulint error;
ulint i; ulint i;
dict_col_t* columns[1000]; ulint j;
char* column_names[1000]; dict_col_t* columns[500];
char* column_names[500];
ulint column_name_lens[500];
char referenced_table_name[2500];
ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(mutex_own(&(dict_sys->mutex)));
table = dict_table_get_low(name); table = dict_table_get_low(name);
...@@ -2090,7 +2108,7 @@ loop: ...@@ -2090,7 +2108,7 @@ loop:
/* Scan the columns in the first list */ /* Scan the columns in the first list */
col_loop1: col_loop1:
ptr = dict_scan_col(ptr, &success, table, columns + i, ptr = dict_scan_col(ptr, &success, table, columns + i,
column_names + i); column_names + i, column_name_lens + i);
if (!success) { if (!success) {
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
...@@ -2141,9 +2159,13 @@ col_loop1: ...@@ -2141,9 +2159,13 @@ col_loop1:
1 + ut_strlen(columns[i]->name)); 1 + ut_strlen(columns[i]->name));
} }
ptr = dict_scan_table_name(ptr, &referenced_table, name); ptr = dict_scan_table_name(ptr, &referenced_table, name,
&success, referenced_table_name);
if (!referenced_table) { /* Note that referenced_table can be NULL if the user has suppressed
checking of foreign key constraints! */
if (!success || (!referenced_table && trx->check_foreigns)) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
...@@ -2161,7 +2183,7 @@ col_loop1: ...@@ -2161,7 +2183,7 @@ col_loop1:
col_loop2: col_loop2:
ptr = dict_scan_col(ptr, &success, referenced_table, columns + i, ptr = dict_scan_col(ptr, &success, referenced_table, columns + i,
column_names + i); column_names + i, column_name_lens + i);
i++; i++;
if (!success) { if (!success) {
...@@ -2183,43 +2205,104 @@ col_loop2: ...@@ -2183,43 +2205,104 @@ col_loop2:
return(DB_CANNOT_ADD_CONSTRAINT); return(DB_CANNOT_ADD_CONSTRAINT);
} }
ptr = dict_accept(ptr, "ON", &success);
if (!success) {
goto try_find_index;
}
ptr = dict_accept(ptr, "DELETE", &success);
if (!success) {
goto try_find_index;
}
ptr = dict_accept(ptr, "CASCADE", &success);
if (success) {
foreign->type = DICT_FOREIGN_ON_DELETE_CASCADE;
goto try_find_index;
}
ptr = dict_accept(ptr, "SET", &success);
if (!success) {
goto try_find_index;
}
ptr = dict_accept(ptr, "NULL", &success);
if (success) {
for (j = 0; j < foreign->n_fields; j++) {
if ((dict_index_get_nth_type(
foreign->foreign_index, j)->prtype)
& DATA_NOT_NULL) {
/* It is not sensible to define SET NULL
if the column is not allowed to be NULL! */
dict_foreign_free(foreign);
return(DB_CANNOT_ADD_CONSTRAINT);
}
}
foreign->type = DICT_FOREIGN_ON_DELETE_SET_NULL;
goto try_find_index;
}
try_find_index:
/* Try to find an index which contains the columns as the first fields /* Try to find an index which contains the columns as the first fields
and in the right order, and the types are the same as in and in the right order, and the types are the same as in
foreign->foreign_index */ foreign->foreign_index */
index = dict_foreign_find_index(referenced_table, column_names, i, if (referenced_table) {
index = dict_foreign_find_index(referenced_table,
column_names, i,
foreign->foreign_index); foreign->foreign_index);
if (!index) {
if (!index) { dict_foreign_free(foreign);
dict_foreign_free(foreign); return(DB_CANNOT_ADD_CONSTRAINT);
return(DB_CANNOT_ADD_CONSTRAINT); }
} else {
ut_a(trx->check_foreigns == FALSE);
index = NULL;
} }
foreign->referenced_index = index; foreign->referenced_index = index;
foreign->referenced_table = referenced_table; foreign->referenced_table = referenced_table;
foreign->referenced_table_name = mem_heap_alloc(foreign->heap, foreign->referenced_table_name = mem_heap_alloc(foreign->heap,
1 + ut_strlen(referenced_table->name)); 1 + ut_strlen(referenced_table_name));
ut_memcpy(foreign->referenced_table_name, referenced_table->name, ut_memcpy(foreign->referenced_table_name, referenced_table_name,
1 + ut_strlen(referenced_table->name)); 1 + ut_strlen(referenced_table_name));
foreign->referenced_col_names = mem_heap_alloc(foreign->heap, foreign->referenced_col_names = mem_heap_alloc(foreign->heap,
i * sizeof(void*)); i * sizeof(void*));
for (i = 0; i < foreign->n_fields; i++) { for (i = 0; i < foreign->n_fields; i++) {
foreign->referenced_col_names[i] foreign->referenced_col_names[i]
= mem_heap_alloc(foreign->heap, = mem_heap_alloc(foreign->heap,
1 + ut_strlen(columns[i]->name)); 1 + column_name_lens[i]);
ut_memcpy( ut_memcpy(foreign->referenced_col_names[i], column_names[i],
foreign->referenced_col_names[i], columns[i]->name, column_name_lens[i]);
1 + ut_strlen(columns[i]->name)); (foreign->referenced_col_names[i])[column_name_lens[i]] = '\0';
} }
/* We found an ok constraint definition: add to the lists */ /* We found an ok constraint definition: add to the lists */
UT_LIST_ADD_LAST(foreign_list, table->foreign_list, foreign); UT_LIST_ADD_LAST(foreign_list, table->foreign_list, foreign);
UT_LIST_ADD_LAST(referenced_list, referenced_table->referenced_list,
if (referenced_table) {
UT_LIST_ADD_LAST(referenced_list,
referenced_table->referenced_list,
foreign); foreign);
}
goto loop; goto loop;
} }
...@@ -3034,6 +3117,14 @@ dict_print_info_on_foreign_keys_in_create_format( ...@@ -3034,6 +3117,14 @@ dict_print_info_on_foreign_keys_in_create_format(
buf2 += sprintf(buf2, ")"); buf2 += sprintf(buf2, ")");
if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE) {
buf2 += sprintf(buf2, " ON DELETE CASCADE");
}
if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) {
buf2 += sprintf(buf2, " ON DELETE SET NULL");
}
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
} }
......
...@@ -946,6 +946,11 @@ dict_load_foreign( ...@@ -946,6 +946,11 @@ dict_load_foreign(
foreign->n_fields = mach_read_from_4(rec_get_nth_field(rec, 5, &len)); foreign->n_fields = mach_read_from_4(rec_get_nth_field(rec, 5, &len));
ut_a(len == 4); ut_a(len == 4);
/* We store the type to the bits 24-31 of n_fields */
foreign->type = foreign->n_fields >> 24;
foreign->n_fields = foreign->n_fields & 0xFFFFFF;
foreign->id = mem_heap_alloc(foreign->heap, ut_strlen(id) + 1); foreign->id = mem_heap_alloc(foreign->heap, ut_strlen(id) + 1);
......
...@@ -61,7 +61,8 @@ dict_mem_table_create( ...@@ -61,7 +61,8 @@ dict_mem_table_create(
table->mem_fix = 0; table->mem_fix = 0;
table->n_mysql_handles_opened = 0; table->n_mysql_handles_opened = 0;
table->n_foreign_key_checks_running = 0;
table->cached = FALSE; table->cached = FALSE;
table->cols = mem_heap_alloc(heap, (n_cols + DATA_N_SYS_COLS) table->cols = mem_heap_alloc(heap, (n_cols + DATA_N_SYS_COLS)
...@@ -235,6 +236,7 @@ dict_mem_foreign_create(void) ...@@ -235,6 +236,7 @@ dict_mem_foreign_create(void)
foreign->id = NULL; foreign->id = NULL;
foreign->type = 0;
foreign->foreign_table_name = NULL; foreign->foreign_table_name = NULL;
foreign->foreign_table = NULL; foreign->foreign_table = NULL;
foreign->foreign_col_names = NULL; foreign->foreign_col_names = NULL;
......
...@@ -49,6 +49,12 @@ inserted to the index, at the searched position */ ...@@ -49,6 +49,12 @@ inserted to the index, at the searched position */
/* This flag ORed to latch mode says that we do the search in query /* This flag ORed to latch mode says that we do the search in query
optimization */ optimization */
#define BTR_ESTIMATE 1024 #define BTR_ESTIMATE 1024
/* This flag ORed to latch mode says that we can ignore possible
UNIQUE definition on secondary indexes when we decide if we can use the
insert buffer to speed up inserts */
#define BTR_IGNORE_SEC_UNIQUE 2048
/****************************************************************** /******************************************************************
Gets a buffer page and declares its latching order level. */ Gets a buffer page and declares its latching order level. */
UNIV_INLINE UNIV_INLINE
......
...@@ -249,6 +249,8 @@ struct dict_foreign_struct{ ...@@ -249,6 +249,8 @@ struct dict_foreign_struct{
this memory heap */ this memory heap */
char* id; /* id of the constraint as a char* id; /* id of the constraint as a
null-terminated string */ null-terminated string */
ulint type; /* 0 or DICT_FOREIGN_ON_DELETE_CASCADE
or DICT_FOREIGN_ON_DELETE_SET_NULL */
char* foreign_table_name;/* foreign table name */ char* foreign_table_name;/* foreign table name */
dict_table_t* foreign_table; /* table where the foreign key is */ dict_table_t* foreign_table; /* table where the foreign key is */
char** foreign_col_names;/* names of the columns in the char** foreign_col_names;/* names of the columns in the
...@@ -278,6 +280,9 @@ struct dict_foreign_struct{ ...@@ -278,6 +280,9 @@ struct dict_foreign_struct{
table */ table */
}; };
#define DICT_FOREIGN_ON_DELETE_CASCADE 1
#define DICT_FOREIGN_ON_DELETE_SET_NULL 2
#define DICT_INDEX_MAGIC_N 76789786 #define DICT_INDEX_MAGIC_N 76789786
/* Data structure for a database table */ /* Data structure for a database table */
...@@ -313,6 +318,12 @@ struct dict_table_struct{ ...@@ -313,6 +318,12 @@ struct dict_table_struct{
NOT allowed until this count gets to zero; NOT allowed until this count gets to zero;
MySQL does NOT itself check the number of MySQL does NOT itself check the number of
open handles at drop */ open handles at drop */
ulint n_foreign_key_checks_running;
/* count of how many foreign key check
operations are currently being performed
on the table: we cannot drop the table while
there are foreign key checks running on
it! */
ibool cached; /* TRUE if the table object has been added ibool cached; /* TRUE if the table object has been added
to the dictionary cache */ to the dictionary cache */
lock_t* auto_inc_lock;/* a buffer for an auto-inc lock lock_t* auto_inc_lock;/* a buffer for an auto-inc lock
...@@ -359,17 +370,16 @@ struct dict_table_struct{ ...@@ -359,17 +370,16 @@ struct dict_table_struct{
after database startup or table creation */ after database startup or table creation */
ulint stat_modified_counter; ulint stat_modified_counter;
/* when a row is inserted, updated, or deleted, /* when a row is inserted, updated, or deleted,
we add the row length to this number; we we add 1 to this number; we calculate new
calculate new estimates for the stat_... estimates for the stat_... values for the
values for the table and the indexes at an table and the indexes at an interval of 2 GB
interval of 2 GB or when about 1 / 16 of table or when about 1 / 16 of table has been
has been modified; also modified; also when the estimate operation is
when the estimate operation is called called for MySQL SHOW TABLE STATUS; the
for MySQL SHOW TABLE STATUS; the counter is counter is reset to zero at statistics
reset to zero at statistics calculation; calculation; this counter is not protected by
this counter any latch, because this is only used for
is not protected by any latch, because this heuristics */
is only used for heuristics */
/*----------------------*/ /*----------------------*/
mutex_t autoinc_mutex; mutex_t autoinc_mutex;
/* mutex protecting the autoincrement /* mutex protecting the autoincrement
......
...@@ -127,7 +127,11 @@ UNIV_INLINE ...@@ -127,7 +127,11 @@ UNIV_INLINE
ibool ibool
ibuf_should_try( ibuf_should_try(
/*============*/ /*============*/
dict_index_t* index); /* in: index where to insert */ dict_index_t* index, /* in: index where to insert */
ulint ignore_sec_unique); /* in: if != 0, we should
ignore UNIQUE constraint on
a secondary index when we
decide */
/********************************************************************** /**********************************************************************
Returns TRUE if the current OS thread is performing an insert buffer Returns TRUE if the current OS thread is performing an insert buffer
routine. */ routine. */
......
...@@ -81,10 +81,16 @@ UNIV_INLINE ...@@ -81,10 +81,16 @@ UNIV_INLINE
ibool ibool
ibuf_should_try( ibuf_should_try(
/*============*/ /*============*/
dict_index_t* index) /* in: index where to insert */ dict_index_t* index, /* in: index where to insert */
ulint ignore_sec_unique) /* in: if != 0, we should
ignore UNIQUE constraint on
a secondary index when we
decide */
{ {
if (!(index->type & (DICT_CLUSTERED | DICT_UNIQUE)) if (!(index->type & DICT_CLUSTERED)
&& ibuf->meter > IBUF_THRESHOLD) { && (ignore_sec_unique || !(index->type & DICT_UNIQUE))
&& ibuf->meter > IBUF_THRESHOLD) {
ibuf_flush_count++; ibuf_flush_count++;
if (ibuf_flush_count % 8 == 0) { if (ibuf_flush_count % 8 == 0) {
......
...@@ -341,6 +341,8 @@ os_aio_windows_handle( ...@@ -341,6 +341,8 @@ os_aio_windows_handle(
void** message2, void** message2,
ulint* type); /* out: OS_FILE_WRITE or ..._READ */ ulint* type); /* out: OS_FILE_WRITE or ..._READ */
#endif #endif
/* Currently we do not use Posix async i/o */
#ifdef POSIX_ASYNC_IO #ifdef POSIX_ASYNC_IO
/************************************************************************** /**************************************************************************
This function is only used in Posix asynchronous i/o. Waits for an aio This function is only used in Posix asynchronous i/o. Waits for an aio
......
...@@ -149,9 +149,9 @@ void ...@@ -149,9 +149,9 @@ void
os_mutex_free( os_mutex_free(
/*==========*/ /*==========*/
os_mutex_t mutex); /* in: mutex to free */ os_mutex_t mutex); /* in: mutex to free */
#ifndef _WIN32
/************************************************************** /**************************************************************
Acquires ownership of a fast mutex. */ Acquires ownership of a fast mutex. Currently in Windows this is the same
as os_fast_mutex_lock! */
UNIV_INLINE UNIV_INLINE
ulint ulint
os_fast_mutex_trylock( os_fast_mutex_trylock(
...@@ -160,7 +160,6 @@ os_fast_mutex_trylock( ...@@ -160,7 +160,6 @@ os_fast_mutex_trylock(
was reserved by another was reserved by another
thread */ thread */
os_fast_mutex_t* fast_mutex); /* in: mutex to acquire */ os_fast_mutex_t* fast_mutex); /* in: mutex to acquire */
#endif
/************************************************************** /**************************************************************
Releases ownership of a fast mutex. */ Releases ownership of a fast mutex. */
......
...@@ -10,9 +10,9 @@ Created 9/6/1995 Heikki Tuuri ...@@ -10,9 +10,9 @@ Created 9/6/1995 Heikki Tuuri
#include <winbase.h> #include <winbase.h>
#endif #endif
#ifndef _WIN32
/************************************************************** /**************************************************************
Acquires ownership of a fast mutex. */ Acquires ownership of a fast mutex. Currently in Windows this is the same
as os_fast_mutex_lock! */
UNIV_INLINE UNIV_INLINE
ulint ulint
os_fast_mutex_trylock( os_fast_mutex_trylock(
...@@ -23,20 +23,11 @@ os_fast_mutex_trylock( ...@@ -23,20 +23,11 @@ os_fast_mutex_trylock(
os_fast_mutex_t* fast_mutex) /* in: mutex to acquire */ os_fast_mutex_t* fast_mutex) /* in: mutex to acquire */
{ {
#ifdef __WIN__ #ifdef __WIN__
int ret; EnterCriticalSection(fast_mutex);
/* TODO: TryEnterCriticalSection is probably not found from return(0);
NT versions < 4! */
ret = TryEnterCriticalSection(fast_mutex);
if (ret) {
return(0);
}
return(1);
#else #else
return((ulint) pthread_mutex_trylock(fast_mutex)); return((ulint) pthread_mutex_trylock(fast_mutex));
#endif #endif
} }
#endif
...@@ -209,6 +209,27 @@ row_update_for_mysql( ...@@ -209,6 +209,27 @@ row_update_for_mysql(
row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL
handle */ handle */
/************************************************************************* /*************************************************************************
Creates an query graph node of 'update' type to be used in the MySQL
interface. */
upd_node_t*
row_create_update_node_for_mysql(
/*=============================*/
/* out, own: update node */
dict_table_t* table, /* in: table to update */
mem_heap_t* heap); /* in: mem heap from which allocated */
/**************************************************************************
Does a cascaded delete or set null in a foreign key operation. */
ulint
row_update_cascade_for_mysql(
/*=========================*/
/* out: error code or DB_SUCCESS */
que_thr_t* thr, /* in: query thread */
upd_node_t* node, /* in: update node used in the cascade
or set null operation */
dict_table_t* table); /* in: table where we do the operation */
/*************************************************************************
Does a table creation operation for MySQL. If the name of the created Does a table creation operation for MySQL. If the name of the created
table ends to characters INNODB_MONITOR, then this also starts table ends to characters INNODB_MONITOR, then this also starts
printing of monitor output by the master thread. */ printing of monitor output by the master thread. */
......
...@@ -312,6 +312,11 @@ struct upd_node_struct{ ...@@ -312,6 +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 */
upd_node_t* cascade_node;/* NULL or an update node template which
is used to implement ON DELETE CASCADE
or ... SET NULL for foreign keys */
mem_heap_t* cascade_heap;/* NULL or a mem heap where the cascade
node is created */
sel_node_t* select; /* query graph subtree implementing a base sel_node_t* select; /* query graph subtree implementing a base
table cursor: the rows returned will be table cursor: the rows returned will be
updated */ updated */
...@@ -322,6 +327,11 @@ struct upd_node_struct{ ...@@ -322,6 +327,11 @@ struct upd_node_struct{
of the MySQL interface */ of the MySQL interface */
dict_table_t* table; /* table where updated */ dict_table_t* table; /* table where updated */
upd_t* update; /* update vector for the row */ upd_t* update; /* update vector for the row */
ulint update_n_fields;
/* when this struct is used to implement
a cascade operation for foreign keys, we store
here the size of the buffer allocated for use
as the update vector */
sym_node_list_t columns;/* symbol table nodes for the columns sym_node_list_t columns;/* symbol table nodes for the columns
to retrieve from the table */ to retrieve from the table */
ibool has_clust_rec_x_lock; ibool has_clust_rec_x_lock;
......
...@@ -359,12 +359,17 @@ V ...@@ -359,12 +359,17 @@ V
Memory pool mutex */ Memory pool mutex */
/* Latching order levels */ /* Latching order levels */
/* User transaction locks are higher than any of the latch levels below:
no latches are allowed when a thread goes to wait for a normal table
or row lock! */
#define SYNC_USER_TRX_LOCK 9999
#define SYNC_NO_ORDER_CHECK 3000 /* this can be used to suppress #define SYNC_NO_ORDER_CHECK 3000 /* this can be used to suppress
latching order checking */ latching order checking */
#define SYNC_LEVEL_NONE 2000 /* default: level not defined */ #define SYNC_LEVEL_NONE 2000 /* default: level not defined */
#define SYNC_FOREIGN_KEY_CHECK 1001
#define SYNC_DICT 1000 #define SYNC_DICT 1000
#define SYNC_DICT_AUTOINC_MUTEX 999 #define SYNC_DICT_AUTOINC_MUTEX 999
#define SYNC_FOREIGN_KEY_CHECK 998
#define SYNC_PURGE_IS_RUNNING 997 #define SYNC_PURGE_IS_RUNNING 997
#define SYNC_DICT_HEADER 995 #define SYNC_DICT_HEADER 995
#define SYNC_IBUF_HEADER 914 #define SYNC_IBUF_HEADER 914
...@@ -429,7 +434,7 @@ implementation of a mutual exclusion semaphore. */ ...@@ -429,7 +434,7 @@ implementation of a mutual exclusion semaphore. */
struct mutex_struct { struct mutex_struct {
ulint lock_word; /* This ulint is the target of the atomic ulint lock_word; /* This ulint is the target of the atomic
test-and-set instruction in Win32 */ test-and-set instruction in Win32 */
#ifndef _WIN32 #if !defined(_WIN32) || !defined(UNIV_CAN_USE_X86_ASSEMBLER)
os_fast_mutex_t os_fast_mutex_t
os_fast_mutex; /* In other systems we use this OS mutex os_fast_mutex; /* In other systems we use this OS mutex
in place of lock_word */ in place of lock_word */
......
...@@ -53,7 +53,7 @@ mutex_test_and_set( ...@@ -53,7 +53,7 @@ mutex_test_and_set(
1 */ 1 */
mutex_t* mutex) /* in: mutex */ mutex_t* mutex) /* in: mutex */
{ {
#ifdef _WIN32 #if defined(_WIN32) && defined(UNIV_CAN_USE_X86_ASSEMBLER)
ulint res; ulint res;
ulint* lw; /* assembler code is used to ensure that ulint* lw; /* assembler code is used to ensure that
lock_word is loaded from memory */ lock_word is loaded from memory */
...@@ -120,7 +120,7 @@ mutex_reset_lock_word( ...@@ -120,7 +120,7 @@ mutex_reset_lock_word(
/*==================*/ /*==================*/
mutex_t* mutex) /* in: mutex */ mutex_t* mutex) /* in: mutex */
{ {
#ifdef _WIN32 #if defined(_WIN32) && defined(UNIV_CAN_USE_X86_ASSEMBLER)
ulint* lw; /* assembler code is used to ensure that ulint* lw; /* assembler code is used to ensure that
lock_word is loaded from memory */ lock_word is loaded from memory */
ut_ad(mutex); ut_ad(mutex);
......
...@@ -259,7 +259,7 @@ therefore 256 */ ...@@ -259,7 +259,7 @@ therefore 256 */
/* The offset of the transaction system header on the page */ /* The offset of the transaction system header on the page */
#define TRX_SYS FSEG_PAGE_DATA #define TRX_SYS FSEG_PAGE_DATA
/* Transaction system header; protected by trx_sys->mutex */ /* Transaction system header */
/*-------------------------------------------------------------*/ /*-------------------------------------------------------------*/
#define TRX_SYS_TRX_ID_STORE 0 /* the maximum trx id or trx number #define TRX_SYS_TRX_ID_STORE 0 /* the maximum trx id or trx number
modulo TRX_SYS_TRX_ID_UPDATE_MARGIN modulo TRX_SYS_TRX_ID_UPDATE_MARGIN
......
...@@ -93,7 +93,6 @@ trx_sysf_get( ...@@ -93,7 +93,6 @@ trx_sysf_get(
{ {
trx_sysf_t* header; trx_sysf_t* header;
ut_ad(mutex_own(&(kernel_mutex)));
ut_ad(mtr); ut_ad(mtr);
header = TRX_SYS + buf_page_get(TRX_SYS_SPACE, TRX_SYS_PAGE_NO, header = TRX_SYS + buf_page_get(TRX_SYS_SPACE, TRX_SYS_PAGE_NO,
......
...@@ -298,6 +298,17 @@ struct trx_struct{ ...@@ -298,6 +298,17 @@ struct trx_struct{
of view of concurrency control: of view of concurrency control:
TRX_ACTIVE, TRX_COMMITTED_IN_MEMORY, TRX_ACTIVE, TRX_COMMITTED_IN_MEMORY,
... */ ... */
ibool check_foreigns; /* normally TRUE, but if the user
wants to suppress foreign key checks,
(in table imports, for example) we
set this FALSE */
ibool check_unique_secondary;
/* normally TRUE, but if the user
wants to speed up inserts by
suppressing unique key checks
for secondary indexes when we decide
if we can use the insert buffer for
them, we set this FALSE */
dulint id; /* transaction id */ dulint id; /* transaction id */
dulint no; /* transaction serialization number == dulint no; /* transaction serialization number ==
max trx id when the transaction is max trx id when the transaction is
...@@ -328,6 +339,9 @@ struct trx_struct{ ...@@ -328,6 +339,9 @@ struct trx_struct{
/* how many tables the current SQL /* how many tables the current SQL
statement uses, except those statement uses, except those
in consistent read */ in consistent read */
ibool has_dict_foreign_key_check_lock;
/* TRUE if the trx currently holds
an s-lock on dict_foreign_... */
ibool has_search_latch; ibool has_search_latch;
/* TRUE if this trx has latched the /* TRUE if this trx has latched the
search system latch in S-mode */ search system latch in S-mode */
......
...@@ -1534,6 +1534,8 @@ lock_rec_enqueue_waiting( ...@@ -1534,6 +1534,8 @@ lock_rec_enqueue_waiting(
if (que_thr_stop(thr)) { if (que_thr_stop(thr)) {
ut_a(0);
return(DB_QUE_THR_SUSPENDED); return(DB_QUE_THR_SUSPENDED);
} }
...@@ -2918,6 +2920,7 @@ lock_table_enqueue_waiting( ...@@ -2918,6 +2920,7 @@ lock_table_enqueue_waiting(
stopped anyway */ stopped anyway */
if (que_thr_stop(thr)) { if (que_thr_stop(thr)) {
ut_a(0);
return(DB_QUE_THR_SUSPENDED); return(DB_QUE_THR_SUSPENDED);
} }
......
...@@ -526,7 +526,8 @@ opt_search_plan_for_table( ...@@ -526,7 +526,8 @@ opt_search_plan_for_table(
dict_index_t* best_index; dict_index_t* best_index;
ulint n_fields; ulint n_fields;
ulint goodness; ulint goodness;
ulint last_op; ulint last_op = 75946965; /* Eliminate a Purify
warning */
ulint best_goodness; ulint best_goodness;
ulint best_last_op; ulint best_last_op;
ulint mix_id_pos; ulint mix_id_pos;
......
...@@ -555,6 +555,12 @@ que_graph_free_recursive( ...@@ -555,6 +555,12 @@ que_graph_free_recursive(
btr_pcur_free_for_mysql(upd->pcur); btr_pcur_free_for_mysql(upd->pcur);
} }
que_graph_free_recursive(upd->cascade_node);
if (upd->cascade_heap) {
mem_heap_free(upd->cascade_heap);
}
que_graph_free_recursive(upd->select); que_graph_free_recursive(upd->select);
mem_heap_free(upd->heap); mem_heap_free(upd->heap);
...@@ -1110,9 +1116,6 @@ que_thr_move_to_run_state_for_mysql( ...@@ -1110,9 +1116,6 @@ que_thr_move_to_run_state_for_mysql(
trx->n_active_thrs++; trx->n_active_thrs++;
thr->is_active = TRUE; thr->is_active = TRUE;
ut_ad((thr->graph)->n_active_thrs == 1);
ut_ad(trx->n_active_thrs == 1);
} }
thr->state = QUE_THR_RUNNING; thr->state = QUE_THR_RUNNING;
......
...@@ -355,6 +355,223 @@ row_ins_dupl_error_with_rec( ...@@ -355,6 +355,223 @@ row_ins_dupl_error_with_rec(
return(FALSE); return(FALSE);
} }
/*************************************************************************
Either deletes or sets the referencing columns SQL NULL in a child row.
Used in ON DELETE ... clause for foreign keys when a parent row is
deleted. */
static
ulint
row_ins_foreign_delete_or_set_null(
/*===============================*/
/* out: DB_SUCCESS, DB_LOCK_WAIT,
or error code */
que_thr_t* thr, /* in: query thread whose run_node
is an update node */
dict_foreign_t* foreign, /* in: foreign key constraint whose
type is != 0 */
btr_pcur_t* pcur, /* in: cursor placed on a matching
index record in the child table */
mtr_t* mtr) /* in: mtr holding the latch of pcur
page */
{
upd_node_t* node;
upd_node_t* cascade;
dict_table_t* table = foreign->foreign_table;
dict_index_t* index;
dict_index_t* clust_index;
dtuple_t* ref;
mem_heap_t* tmp_heap;
rec_t* rec;
rec_t* clust_rec;
upd_t* update;
ulint err;
ulint i;
char err_buf[1000];
ut_a(thr && foreign && pcur && mtr);
node = thr->run_node;
if (node->cascade_node == NULL) {
/* Extend our query graph by creating a child to current
update node. The child is used in the cascade or set null
operation. */
node->cascade_heap = mem_heap_create(128);
node->cascade_node = row_create_update_node_for_mysql(
table, node->cascade_heap);
que_node_set_parent(node->cascade_node, node);
}
/* Initialize cascade_node to do the operation we want. Note that we
use the SAME cascade node to do all foreign key operations of the
SQL DELETE: the table of the cascade node may change if there are
several child tables to the table where the delete is done! */
cascade = node->cascade_node;
cascade->table = table;
if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE ) {
cascade->is_delete = TRUE;
} else {
cascade->is_delete = FALSE;
if (foreign->n_fields > cascade->update_n_fields) {
/* We have to make the update vector longer */
cascade->update = upd_create(foreign->n_fields,
node->cascade_heap);
cascade->update_n_fields = foreign->n_fields;
}
}
index = btr_pcur_get_btr_cur(pcur)->index;
rec = btr_pcur_get_rec(pcur);
if (index->type & DICT_CLUSTERED) {
/* pcur is already positioned in the clustered index of
the child table */
clust_index = index;
clust_rec = rec;
} else {
/* We have to look for the record in the clustered index
in the child table */
clust_index = dict_table_get_first_index(table);
tmp_heap = mem_heap_create(256);
ref = row_build_row_ref(ROW_COPY_POINTERS, index, rec,
tmp_heap);
btr_pcur_open_with_no_init(clust_index, ref,
PAGE_CUR_LE, BTR_SEARCH_LEAF,
cascade->pcur, 0, mtr);
mem_heap_free(tmp_heap);
clust_rec = btr_pcur_get_rec(cascade->pcur);
}
if (!page_rec_is_user_rec(clust_rec)) {
fprintf(stderr, "InnoDB: error in cascade of a foreign key op\n"
"InnoDB: index %s table %s\n", index->name,
index->table->name);
rec_sprintf(err_buf, 900, rec);
fprintf(stderr, "InnoDB: record %s\n", err_buf);
rec_sprintf(err_buf, 900, clust_rec);
fprintf(stderr, "InnoDB: clustered record %s\n", err_buf);
fprintf(stderr,
"InnoDB: Make a detailed bug report and send it\n");
fprintf(stderr, "InnoDB: to mysql@lists.mysql.com\n");
err = DB_SUCCESS;
goto nonstandard_exit_func;
}
/* Set an X-lock on the row to delete or update in the child table */
err = lock_table(0, table, LOCK_IX, thr);
if (err == DB_SUCCESS) {
err = lock_clust_rec_read_check_and_lock(0, clust_rec,
clust_index, LOCK_X, thr);
}
if (err != DB_SUCCESS) {
goto nonstandard_exit_func;
}
if (rec_get_deleted_flag(clust_rec)) {
/* This should never happen since we already have an S-lock
on non-delete-marked clust_rec or secondary index record! */
fprintf(stderr,
"InnoDB: error 2 in cascade of a foreign key op\n"
"InnoDB: index %s table %s\n", index->name,
index->table->name);
rec_sprintf(err_buf, 900, rec);
fprintf(stderr, "InnoDB: record %s\n", err_buf);
rec_sprintf(err_buf, 900, clust_rec);
fprintf(stderr, "InnoDB: clustered record %s\n", err_buf);
fprintf(stderr,
"InnoDB: Make a detailed bug report and send it\n");
fprintf(stderr, "InnoDB: to mysql@lists.mysql.com\n");
ut_a(0);
goto nonstandard_exit_func;
}
if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) {
/* Build the appropriate update vector which sets
foreign->n_fields first fields in rec to SQL NULL */
update = cascade->update;
update->info_bits = 0;
update->n_fields = foreign->n_fields;
for (i = 0; i < foreign->n_fields; i++) {
(update->fields + i)->field_no
= dict_table_get_nth_col_pos(table,
dict_index_get_nth_col_no(index, i));
(update->fields + i)->exp = NULL;
(update->fields + i)->new_val.len = UNIV_SQL_NULL;
(update->fields + i)->new_val.data = NULL;
(update->fields + i)->extern_storage = FALSE;
}
}
/* Store pcur position and initialize or store the cascade node
pcur stored position */
btr_pcur_store_position(pcur, mtr);
if (index == clust_index) {
btr_pcur_copy_stored_position(cascade->pcur, pcur);
} else {
btr_pcur_store_position(cascade->pcur, mtr);
}
mtr_commit(mtr);
ut_a(cascade->pcur->rel_pos == BTR_PCUR_ON);
cascade->state = UPD_NODE_UPDATE_CLUSTERED;
err = row_update_cascade_for_mysql(thr, cascade,
foreign->foreign_table);
mtr_start(mtr);
/* Restore pcur position */
btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, mtr);
return(err);
nonstandard_exit_func:
btr_pcur_store_position(pcur, mtr);
mtr_commit(mtr);
mtr_start(mtr);
btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, mtr);
return(err);
}
/************************************************************************* /*************************************************************************
Sets a shared lock on a record. Used in locking possible duplicate key Sets a shared lock on a record. Used in locking possible duplicate key
records. */ records. */
...@@ -416,6 +633,13 @@ row_ins_check_foreign_constraint( ...@@ -416,6 +633,13 @@ row_ins_check_foreign_constraint(
ut_ad(rw_lock_own(&dict_foreign_key_check_lock, RW_LOCK_SHARED)); ut_ad(rw_lock_own(&dict_foreign_key_check_lock, RW_LOCK_SHARED));
if (thr_get_trx(thr)->check_foreigns == FALSE) {
/* The user has suppressed foreign key checks currently for
this session */
return(DB_SUCCESS);
}
/* If any of the foreign key fields in entry is SQL NULL, we /* If any of the foreign key fields in entry is SQL NULL, we
suppress the foreign key check: this is compatible with Oracle, suppress the foreign key check: this is compatible with Oracle,
for example */ for example */
...@@ -478,8 +702,8 @@ row_ins_check_foreign_constraint( ...@@ -478,8 +702,8 @@ row_ins_check_foreign_constraint(
goto next_rec; goto next_rec;
} }
/* Try to place a lock on the index record */ /* Try to place a lock on the index record */
err = row_ins_set_shared_rec_lock(rec, check_index, thr); err = row_ins_set_shared_rec_lock(rec, check_index, thr);
...@@ -501,11 +725,21 @@ row_ins_check_foreign_constraint( ...@@ -501,11 +725,21 @@ row_ins_check_foreign_constraint(
if (check_ref) { if (check_ref) {
err = DB_SUCCESS; err = DB_SUCCESS;
break;
} else if (foreign->type != 0) {
err =
row_ins_foreign_delete_or_set_null(
thr, foreign, &pcur, &mtr);
if (err != DB_SUCCESS) {
break;
}
} else { } else {
err = DB_ROW_IS_REFERENCED; err = DB_ROW_IS_REFERENCED;
break;
} }
break;
} }
} }
...@@ -534,6 +768,8 @@ next_rec: ...@@ -534,6 +768,8 @@ next_rec:
} }
} }
btr_pcur_close(&pcur);
mtr_commit(&mtr); mtr_commit(&mtr);
/* Restore old value */ /* Restore old value */
...@@ -561,6 +797,10 @@ row_ins_check_foreign_constraints( ...@@ -561,6 +797,10 @@ row_ins_check_foreign_constraints(
{ {
dict_foreign_t* foreign; dict_foreign_t* foreign;
ulint err; ulint err;
trx_t* trx;
ibool got_s_lock = FALSE;
trx = thr_get_trx(thr);
foreign = UT_LIST_GET_FIRST(table->foreign_list); foreign = UT_LIST_GET_FIRST(table->foreign_list);
...@@ -569,16 +809,26 @@ row_ins_check_foreign_constraints( ...@@ -569,16 +809,26 @@ row_ins_check_foreign_constraints(
if (foreign->referenced_table == NULL) { if (foreign->referenced_table == NULL) {
dict_table_get(foreign->referenced_table_name, dict_table_get(foreign->referenced_table_name,
thr_get_trx(thr)); trx);
} }
rw_lock_s_lock(&dict_foreign_key_check_lock); if (!trx->has_dict_foreign_key_check_lock) {
got_s_lock = TRUE;
rw_lock_s_lock(&dict_foreign_key_check_lock);
trx->has_dict_foreign_key_check_lock = TRUE;
}
err = row_ins_check_foreign_constraint(TRUE, foreign, err = row_ins_check_foreign_constraint(TRUE, foreign,
table, index, entry, thr); table, index, entry, thr);
if (got_s_lock) {
rw_lock_s_unlock(&dict_foreign_key_check_lock); rw_lock_s_unlock(&dict_foreign_key_check_lock);
trx->has_dict_foreign_key_check_lock = FALSE;
}
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
return(err); return(err);
} }
...@@ -868,13 +1118,14 @@ row_ins_index_entry_low( ...@@ -868,13 +1118,14 @@ row_ins_index_entry_low(
ulint n_ext_vec,/* in: number of fields in ext_vec */ ulint n_ext_vec,/* in: number of fields in ext_vec */
que_thr_t* thr) /* in: query thread */ que_thr_t* thr) /* in: query thread */
{ {
btr_cur_t cursor; btr_cur_t cursor;
ulint ignore_sec_unique = 0;
ulint modify; ulint modify;
rec_t* insert_rec; rec_t* insert_rec;
rec_t* rec; rec_t* rec;
ulint err; ulint err;
ulint n_unique; ulint n_unique;
big_rec_t* big_rec = NULL; big_rec_t* big_rec = NULL;
mtr_t mtr; mtr_t mtr;
log_free_check(); log_free_check();
...@@ -887,8 +1138,13 @@ row_ins_index_entry_low( ...@@ -887,8 +1138,13 @@ row_ins_index_entry_low(
the function will return in both low_match and up_match of the the function will return in both low_match and up_match of the
cursor sensible values */ cursor sensible values */
if (!(thr_get_trx(thr)->check_unique_secondary)) {
ignore_sec_unique = BTR_IGNORE_SEC_UNIQUE;
}
btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE,
mode | BTR_INSERT, &cursor, 0, &mtr); mode | BTR_INSERT | ignore_sec_unique,
&cursor, 0, &mtr);
if (cursor.flag == BTR_CUR_INSERT_TO_IBUF) { if (cursor.flag == BTR_CUR_INSERT_TO_IBUF) {
/* The insertion was made to the insert buffer already during /* The insertion was made to the insert buffer already during
......
...@@ -499,29 +499,24 @@ UNIV_INLINE ...@@ -499,29 +499,24 @@ UNIV_INLINE
void void
row_update_statistics_if_needed( row_update_statistics_if_needed(
/*============================*/ /*============================*/
row_prebuilt_t* prebuilt) /* in: prebuilt struct */ dict_table_t* table) /* in: table */
{ {
ulint counter; ulint counter;
counter = prebuilt->table->stat_modified_counter; counter = table->stat_modified_counter;
/* Since the physical size of an InnoDB row is bigger than the
MySQL row len, we put a safety factor 2 below */
counter += 2 * prebuilt->mysql_row_len;
prebuilt->table->stat_modified_counter = counter; table->stat_modified_counter = counter + 1;
/* Calculate new statistics if 1 / 16 of table has been modified /* Calculate new statistics if 1 / 16 of table has been modified
since the last time a statistics batch was run, or if since the last time a statistics batch was run, or if
stat_modified_counter > 2 000 000 000 (to avoid wrap-around) */ stat_modified_counter > 2 000 000 000 (to avoid wrap-around).
We calculate statistics at most every 16th round, since we may have
a counter table which is very small and updated very often. */
if (counter > 2000000000 if (counter > 2000000000
|| ((ib_longlong)counter > || ((ib_longlong)counter > 16 + table->stat_n_rows / 16)) {
(UNIV_PAGE_SIZE * prebuilt->table->stat_clustered_index_size)
/ 16)) {
dict_update_statistics(prebuilt->table); dict_update_statistics(table);
} }
} }
...@@ -712,7 +707,7 @@ run_again: ...@@ -712,7 +707,7 @@ run_again:
prebuilt->table->stat_n_rows--; prebuilt->table->stat_n_rows--;
} }
row_update_statistics_if_needed(prebuilt); row_update_statistics_if_needed(prebuilt->table);
trx->op_info = ""; trx->op_info = "";
return((int) err); return((int) err);
...@@ -745,6 +740,43 @@ row_prebuild_sel_graph( ...@@ -745,6 +740,43 @@ row_prebuild_sel_graph(
} }
} }
/*************************************************************************
Creates an query graph node of 'update' type to be used in the MySQL
interface. */
upd_node_t*
row_create_update_node_for_mysql(
/*=============================*/
/* out, own: update node */
dict_table_t* table, /* in: table to update */
mem_heap_t* heap) /* in: mem heap from which allocated */
{
upd_node_t* node;
node = upd_node_create(heap);
node->in_mysql_interface = TRUE;
node->is_delete = FALSE;
node->searched_update = FALSE;
node->select_will_do_update = FALSE;
node->select = NULL;
node->pcur = btr_pcur_create_for_mysql();
node->table = table;
node->update = upd_create(dict_table_get_n_cols(table), heap);
node->update_n_fields = dict_table_get_n_cols(table);
UT_LIST_INIT(node->columns);
node->has_clust_rec_x_lock = TRUE;
node->cmpl_info = 0;
node->table_sym = NULL;
node->col_assign_list = NULL;
return(node);
}
/************************************************************************* /*************************************************************************
Gets pointer to a prebuilt update vector used in updates. If the update Gets pointer to a prebuilt update vector used in updates. If the update
graph has not yet been built in the prebuilt struct, then this function graph has not yet been built in the prebuilt struct, then this function
...@@ -767,26 +799,9 @@ row_get_prebuilt_update_vector( ...@@ -767,26 +799,9 @@ row_get_prebuilt_update_vector(
/* Not called before for this handle: create an update node /* Not called before for this handle: create an update node
and query graph to the prebuilt struct */ and query graph to the prebuilt struct */
node = upd_node_create(prebuilt->heap); node = row_create_update_node_for_mysql(table, prebuilt->heap);
prebuilt->upd_node = node;
node->in_mysql_interface = TRUE; prebuilt->upd_node = node;
node->is_delete = FALSE;
node->searched_update = FALSE;
node->select_will_do_update = FALSE;
node->select = NULL;
node->pcur = btr_pcur_create_for_mysql();
node->table = table;
node->update = upd_create(dict_table_get_n_cols(table),
prebuilt->heap);
UT_LIST_INIT(node->columns);
node->has_clust_rec_x_lock = TRUE;
node->cmpl_info = 0;
node->table_sym = NULL;
node->col_assign_list = NULL;
prebuilt->upd_graph = prebuilt->upd_graph =
que_node_get_parent( que_node_get_parent(
...@@ -914,7 +929,7 @@ run_again: ...@@ -914,7 +929,7 @@ run_again:
que_thr_stop_for_mysql_no_error(thr, trx); que_thr_stop_for_mysql_no_error(thr, trx);
if (prebuilt->upd_node->is_delete) { if (node->is_delete) {
if (prebuilt->table->stat_n_rows > 0) { if (prebuilt->table->stat_n_rows > 0) {
prebuilt->table->stat_n_rows--; prebuilt->table->stat_n_rows--;
} }
...@@ -924,13 +939,66 @@ run_again: ...@@ -924,13 +939,66 @@ run_again:
srv_n_rows_updated++; srv_n_rows_updated++;
} }
row_update_statistics_if_needed(prebuilt); row_update_statistics_if_needed(prebuilt->table);
trx->op_info = ""; trx->op_info = "";
return((int) err); return((int) err);
} }
/**************************************************************************
Does a cascaded delete or set null in a foreign key operation. */
ulint
row_update_cascade_for_mysql(
/*=========================*/
/* out: error code or DB_SUCCESS */
que_thr_t* thr, /* in: query thread */
upd_node_t* node, /* in: update node used in the cascade
or set null operation */
dict_table_t* table) /* in: table where we do the operation */
{
ulint err;
trx_t* trx;
trx = thr_get_trx(thr);
run_again:
thr->run_node = node;
thr->prev_node = node;
row_upd_step(thr);
err = trx->error_state;
if (err == DB_LOCK_WAIT) {
que_thr_stop_for_mysql(thr);
row_mysql_handle_errors(&err, trx, thr, NULL);
goto run_again;
}
if (err != DB_SUCCESS) {
return(err);
}
if (node->is_delete) {
if (table->stat_n_rows > 0) {
table->stat_n_rows--;
}
srv_n_rows_deleted++;
} else {
srv_n_rows_updated++;
}
row_update_statistics_if_needed(table);
return(err);
}
/************************************************************************* /*************************************************************************
Checks if a table is such that we automatically created a clustered Checks if a table is such that we automatically created a clustered
index on it (on row id). */ index on it (on row id). */
...@@ -1169,6 +1237,7 @@ row_create_table_for_mysql( ...@@ -1169,6 +1237,7 @@ row_create_table_for_mysql(
/* Serialize data dictionary operations with dictionary mutex: /* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */ no deadlocks can occur then in these operations */
rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex)); mutex_enter(&(dict_sys->mutex));
heap = mem_heap_create(512); heap = mem_heap_create(512);
...@@ -1221,6 +1290,8 @@ row_create_table_for_mysql( ...@@ -1221,6 +1290,8 @@ row_create_table_for_mysql(
} }
mutex_exit(&(dict_sys->mutex)); mutex_exit(&(dict_sys->mutex));
rw_lock_x_unlock(&(dict_foreign_key_check_lock));
que_graph_free((que_t*) que_node_get_parent(thr)); que_graph_free((que_t*) que_node_get_parent(thr));
trx->op_info = ""; trx->op_info = "";
...@@ -1268,6 +1339,7 @@ row_create_index_for_mysql( ...@@ -1268,6 +1339,7 @@ row_create_index_for_mysql(
/* Serialize data dictionary operations with dictionary mutex: /* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */ no deadlocks can occur then in these operations */
rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex)); mutex_enter(&(dict_sys->mutex));
heap = mem_heap_create(512); heap = mem_heap_create(512);
...@@ -1298,6 +1370,7 @@ row_create_index_for_mysql( ...@@ -1298,6 +1370,7 @@ row_create_index_for_mysql(
} }
mutex_exit(&(dict_sys->mutex)); mutex_exit(&(dict_sys->mutex));
rw_lock_x_unlock(&(dict_foreign_key_check_lock));
que_graph_free((que_t*) que_node_get_parent(thr)); que_graph_free((que_t*) que_node_get_parent(thr));
...@@ -1353,6 +1426,7 @@ row_table_add_foreign_constraints( ...@@ -1353,6 +1426,7 @@ row_table_add_foreign_constraints(
/* Serialize data dictionary operations with dictionary mutex: /* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */ no deadlocks can occur then in these operations */
rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex)); mutex_enter(&(dict_sys->mutex));
trx->dict_operation = TRUE; trx->dict_operation = TRUE;
...@@ -1377,6 +1451,7 @@ row_table_add_foreign_constraints( ...@@ -1377,6 +1451,7 @@ row_table_add_foreign_constraints(
} }
mutex_exit(&(dict_sys->mutex)); mutex_exit(&(dict_sys->mutex));
rw_lock_x_unlock(&(dict_foreign_key_check_lock));
return((int) err); return((int) err);
} }
...@@ -1471,7 +1546,8 @@ loop: ...@@ -1471,7 +1546,8 @@ loop:
goto already_dropped; goto already_dropped;
} }
if (table->n_mysql_handles_opened > 0) { if (table->n_mysql_handles_opened > 0
|| table->n_foreign_key_checks_running > 0) {
return(n_tables + n_tables_dropped); return(n_tables + n_tables_dropped);
} }
...@@ -1717,6 +1793,9 @@ row_drop_table_for_mysql( ...@@ -1717,6 +1793,9 @@ row_drop_table_for_mysql(
no deadlocks can occur then in these operations */ no deadlocks can occur then in these operations */
if (!has_dict_mutex) { if (!has_dict_mutex) {
/* Prevent foreign key checks while we are dropping the table */
rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex)); mutex_enter(&(dict_sys->mutex));
} }
...@@ -1729,9 +1808,6 @@ row_drop_table_for_mysql( ...@@ -1729,9 +1808,6 @@ row_drop_table_for_mysql(
graph->fork_type = QUE_FORK_MYSQL_INTERFACE; graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
/* Prevent foreign key checks while we are dropping the table */
rw_lock_x_lock(&(dict_foreign_key_check_lock));
/* Prevent purge from running while we are dropping the table */ /* Prevent purge from running while we are dropping the table */
rw_lock_s_lock(&(purge_sys->purge_is_running)); rw_lock_s_lock(&(purge_sys->purge_is_running));
...@@ -1766,6 +1842,22 @@ row_drop_table_for_mysql( ...@@ -1766,6 +1842,22 @@ row_drop_table_for_mysql(
goto funct_exit; goto funct_exit;
} }
if (table->n_foreign_key_checks_running > 0) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: You are trying to drop table %s\n"
"InnoDB: though there are foreign key check running on it.\n"
"InnoDB: Adding the table to the background drop queue.\n",
table->name);
row_add_table_to_background_drop_list(table);
err = DB_SUCCESS;
goto funct_exit;
}
/* Remove any locks there are on the table or its records */ /* Remove any locks there are on the table or its records */
lock_reset_all_on_table(table); lock_reset_all_on_table(table);
...@@ -1793,10 +1885,9 @@ row_drop_table_for_mysql( ...@@ -1793,10 +1885,9 @@ row_drop_table_for_mysql(
funct_exit: funct_exit:
rw_lock_s_unlock(&(purge_sys->purge_is_running)); rw_lock_s_unlock(&(purge_sys->purge_is_running));
rw_lock_x_unlock(&(dict_foreign_key_check_lock));
if (!has_dict_mutex) { if (!has_dict_mutex) {
mutex_exit(&(dict_sys->mutex)); mutex_exit(&(dict_sys->mutex));
rw_lock_x_unlock(&(dict_foreign_key_check_lock));
} }
que_graph_free(graph); que_graph_free(graph);
...@@ -1832,6 +1923,7 @@ row_drop_database_for_mysql( ...@@ -1832,6 +1923,7 @@ row_drop_database_for_mysql(
trx_start_if_not_started(trx); trx_start_if_not_started(trx);
loop: loop:
rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex)); mutex_enter(&(dict_sys->mutex));
while (table_name = dict_get_first_table_name_in_db(name)) { while (table_name = dict_get_first_table_name_in_db(name)) {
...@@ -1873,6 +1965,7 @@ loop: ...@@ -1873,6 +1965,7 @@ loop:
} }
mutex_exit(&(dict_sys->mutex)); mutex_exit(&(dict_sys->mutex));
rw_lock_x_unlock(&(dict_foreign_key_check_lock));
trx_commit_for_mysql(trx); trx_commit_for_mysql(trx);
...@@ -2009,6 +2102,7 @@ row_rename_table_for_mysql( ...@@ -2009,6 +2102,7 @@ row_rename_table_for_mysql(
/* Serialize data dictionary operations with dictionary mutex: /* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */ no deadlocks can occur then in these operations */
rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex)); mutex_enter(&(dict_sys->mutex));
table = dict_table_get_low(old_name); table = dict_table_get_low(old_name);
...@@ -2090,6 +2184,7 @@ row_rename_table_for_mysql( ...@@ -2090,6 +2184,7 @@ row_rename_table_for_mysql(
} }
funct_exit: funct_exit:
mutex_exit(&(dict_sys->mutex)); mutex_exit(&(dict_sys->mutex));
rw_lock_x_unlock(&(dict_foreign_key_check_lock));
que_graph_free(graph); que_graph_free(graph);
......
...@@ -2115,8 +2115,14 @@ row_sel_store_mysql_rec( ...@@ -2115,8 +2115,14 @@ row_sel_store_mysql_rec(
extern_field_heap = NULL; extern_field_heap = NULL;
} }
} else { } else {
mysql_rec[templ->mysql_null_byte_offset] |= if (!templ->mysql_null_bit_mask) {
fprintf(stderr,
"InnoDB: Error: trying to return an SQL NULL field in a non-null\n"
"innoDB: column! Table name %s\n", prebuilt->table->name);
} else {
mysql_rec[templ->mysql_null_byte_offset] |=
(byte) (templ->mysql_null_bit_mask); (byte) (templ->mysql_null_bit_mask);
}
} }
} }
} }
......
...@@ -73,8 +73,7 @@ steps of query graph execution. */ ...@@ -73,8 +73,7 @@ steps of query graph execution. */
/************************************************************************* /*************************************************************************
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. This function also loads into the dictionary cache the key constraint. */
possible referencing table. */
static static
ibool ibool
row_upd_index_is_referenced( row_upd_index_is_referenced(
...@@ -85,44 +84,28 @@ row_upd_index_is_referenced( ...@@ -85,44 +84,28 @@ row_upd_index_is_referenced(
the referencing table has been dropped when the referencing table has been dropped when
we leave this function: this function is only we leave this function: this function is only
for heuristic use! */ for heuristic use! */
dict_index_t* index) /* in: index */ dict_index_t* index, /* in: index */
trx_t* trx) /* in: transaction */
{ {
dict_table_t* table = index->table; dict_table_t* table = index->table;
dict_foreign_t* foreign; dict_foreign_t* foreign;
ulint phase = 1;
try_again:
if (!UT_LIST_GET_FIRST(table->referenced_list)) { if (!UT_LIST_GET_FIRST(table->referenced_list)) {
return(FALSE); return(FALSE);
} }
if (phase == 2) { if (!trx->has_dict_foreign_key_check_lock) {
mutex_enter(&(dict_sys->mutex)); rw_lock_s_lock(&dict_foreign_key_check_lock);
} }
rw_lock_s_lock(&dict_foreign_key_check_lock);
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) { if (foreign->referenced_index == index) {
if (foreign->foreign_table == NULL) {
if (phase == 2) {
dict_table_get_low(foreign->
foreign_table_name);
} else {
phase = 2;
rw_lock_s_unlock(
&dict_foreign_key_check_lock);
goto try_again;
}
}
rw_lock_s_unlock(&dict_foreign_key_check_lock);
if (phase == 2) { if (!trx->has_dict_foreign_key_check_lock) {
mutex_exit(&(dict_sys->mutex)); rw_lock_s_unlock(&dict_foreign_key_check_lock);
} }
return(TRUE); return(TRUE);
...@@ -131,10 +114,8 @@ try_again: ...@@ -131,10 +114,8 @@ try_again:
foreign = UT_LIST_GET_NEXT(referenced_list, foreign); foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
} }
rw_lock_s_unlock(&dict_foreign_key_check_lock); if (!trx->has_dict_foreign_key_check_lock) {
rw_lock_s_unlock(&dict_foreign_key_check_lock);
if (phase == 2) {
mutex_exit(&(dict_sys->mutex));
} }
return(FALSE); return(FALSE);
...@@ -160,8 +141,17 @@ row_upd_check_references_constraints( ...@@ -160,8 +141,17 @@ row_upd_check_references_constraints(
dict_foreign_t* foreign; dict_foreign_t* foreign;
mem_heap_t* heap; mem_heap_t* heap;
dtuple_t* entry; dtuple_t* entry;
trx_t* trx;
rec_t* rec; rec_t* rec;
ulint err; ulint err;
ibool got_s_lock = FALSE;
if (UT_LIST_GET_FIRST(table->referenced_list) == NULL) {
return(DB_SUCCESS);
}
trx = thr_get_trx(thr);
rec = btr_pcur_get_rec(pcur); rec = btr_pcur_get_rec(pcur);
...@@ -173,17 +163,61 @@ row_upd_check_references_constraints( ...@@ -173,17 +163,61 @@ row_upd_check_references_constraints(
mtr_start(mtr); mtr_start(mtr);
rw_lock_s_lock(&dict_foreign_key_check_lock); if (!trx->has_dict_foreign_key_check_lock) {
got_s_lock = TRUE;
rw_lock_s_lock(&dict_foreign_key_check_lock);
trx->has_dict_foreign_key_check_lock = TRUE;
}
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) { if (foreign->referenced_index == index) {
if (foreign->foreign_table == NULL) {
dict_table_get(foreign->foreign_table_name,
trx);
}
if (foreign->foreign_table) {
mutex_enter(&(dict_sys->mutex));
(foreign->foreign_table
->n_foreign_key_checks_running)++;
mutex_exit(&(dict_sys->mutex));
}
/* NOTE that if the thread ends up waiting for a lock
we will release dict_foreign_key_check_lock
temporarily! But the counter on the table
protects 'foreign' from being dropped while the check
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);
if (foreign->foreign_table) {
mutex_enter(&(dict_sys->mutex));
ut_a(foreign->foreign_table
->n_foreign_key_checks_running > 0);
(foreign->foreign_table
->n_foreign_key_checks_running)--;
mutex_exit(&(dict_sys->mutex));
}
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
rw_lock_s_unlock(&dict_foreign_key_check_lock); if (got_s_lock) {
rw_lock_s_unlock(
&dict_foreign_key_check_lock);
trx->has_dict_foreign_key_check_lock
= FALSE;
}
mem_heap_free(heap); mem_heap_free(heap);
return(err); return(err);
...@@ -193,7 +227,11 @@ row_upd_check_references_constraints( ...@@ -193,7 +227,11 @@ row_upd_check_references_constraints(
foreign = UT_LIST_GET_NEXT(referenced_list, foreign); foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
} }
rw_lock_s_unlock(&dict_foreign_key_check_lock); if (got_s_lock) {
rw_lock_s_unlock(&dict_foreign_key_check_lock);
trx->has_dict_foreign_key_check_lock = FALSE;
}
mem_heap_free(heap); mem_heap_free(heap);
return(DB_SUCCESS); return(DB_SUCCESS);
...@@ -222,6 +260,9 @@ upd_node_create( ...@@ -222,6 +260,9 @@ upd_node_create(
node->index = NULL; node->index = NULL;
node->update = NULL; node->update = NULL;
node->cascade_heap = NULL;
node->cascade_node = NULL;
node->select = NULL; node->select = NULL;
node->heap = mem_heap_create(128); node->heap = mem_heap_create(128);
...@@ -1027,7 +1068,7 @@ row_upd_sec_index_entry( ...@@ -1027,7 +1068,7 @@ row_upd_sec_index_entry(
index = node->index; index = node->index;
check_ref = row_upd_index_is_referenced(index); check_ref = row_upd_index_is_referenced(index, thr_get_trx(thr));
heap = mem_heap_create(1024); heap = mem_heap_create(1024);
...@@ -1391,7 +1432,7 @@ row_upd_clust_step( ...@@ -1391,7 +1432,7 @@ row_upd_clust_step(
index = dict_table_get_first_index(node->table); index = dict_table_get_first_index(node->table);
check_ref = row_upd_index_is_referenced(index); check_ref = row_upd_index_is_referenced(index, thr_get_trx(thr));
pcur = node->pcur; pcur = node->pcur;
......
...@@ -2017,15 +2017,18 @@ srv_suspend_mysql_thread( ...@@ -2017,15 +2017,18 @@ srv_suspend_mysql_thread(
/*=====================*/ /*=====================*/
/* out: TRUE if the lock wait timeout was /* out: TRUE if the lock wait timeout was
exceeded */ exceeded */
que_thr_t* thr) /* in: query thread associated with que_thr_t* thr) /* in: query thread associated with the MySQL
the MySQL OS thread */ OS thread */
{ {
srv_slot_t* slot; srv_slot_t* slot;
os_event_t event; os_event_t event;
double wait_time; double wait_time;
trx_t* trx;
ut_ad(!mutex_own(&kernel_mutex)); ut_ad(!mutex_own(&kernel_mutex));
trx = thr_get_trx(thr);
os_event_set(srv_lock_timeout_thread_event); os_event_set(srv_lock_timeout_thread_event);
mutex_enter(&kernel_mutex); mutex_enter(&kernel_mutex);
...@@ -2061,10 +2064,21 @@ srv_suspend_mysql_thread( ...@@ -2061,10 +2064,21 @@ srv_suspend_mysql_thread(
srv_conc_force_exit_innodb(thr_get_trx(thr)); srv_conc_force_exit_innodb(thr_get_trx(thr));
/* Release possible foreign key check latch */
if (trx->has_dict_foreign_key_check_lock) {
rw_lock_s_unlock(&dict_foreign_key_check_lock);
}
/* Wait for the release */ /* Wait for the release */
os_event_wait(event); os_event_wait(event);
if (trx->has_dict_foreign_key_check_lock) {
rw_lock_s_lock(&dict_foreign_key_check_lock);
}
/* Return back inside InnoDB */ /* Return back inside InnoDB */
srv_conc_force_enter_innodb(thr_get_trx(thr)); srv_conc_force_enter_innodb(thr_get_trx(thr));
......
...@@ -220,7 +220,7 @@ mutex_create_func( ...@@ -220,7 +220,7 @@ mutex_create_func(
char* cfile_name, /* in: file name where created */ char* cfile_name, /* in: file name where created */
ulint cline) /* in: file line where created */ ulint cline) /* in: file line where created */
{ {
#ifdef _WIN32 #if defined(_WIN32) && defined(UNIV_CAN_USE_X86_ASSEMBLER)
mutex_reset_lock_word(mutex); mutex_reset_lock_word(mutex);
#else #else
os_fast_mutex_init(&(mutex->os_fast_mutex)); os_fast_mutex_init(&(mutex->os_fast_mutex));
...@@ -273,7 +273,7 @@ mutex_free( ...@@ -273,7 +273,7 @@ mutex_free(
mutex_exit(&mutex_list_mutex); mutex_exit(&mutex_list_mutex);
#ifndef _WIN32 #if !defined(_WIN32) || !defined(UNIV_CAN_USE_X86_ASSEMBLER)
os_fast_mutex_free(&(mutex->os_fast_mutex)); os_fast_mutex_free(&(mutex->os_fast_mutex));
#endif #endif
/* If we free the mutex protecting the mutex list (freeing is /* If we free the mutex protecting the mutex list (freeing is
...@@ -1009,7 +1009,7 @@ sync_thread_add_level( ...@@ -1009,7 +1009,7 @@ sync_thread_add_level(
} else if (level == SYNC_ANY_LATCH) { } else if (level == SYNC_ANY_LATCH) {
ut_a(sync_thread_levels_g(array, SYNC_ANY_LATCH)); ut_a(sync_thread_levels_g(array, SYNC_ANY_LATCH));
} else if (level == SYNC_TRX_SYS_HEADER) { } else if (level == SYNC_TRX_SYS_HEADER) {
ut_a(sync_thread_levels_contain(array, SYNC_KERNEL)); ut_a(sync_thread_levels_g(array, SYNC_TRX_SYS_HEADER));
} else if (level == SYNC_DOUBLEWRITE) { } else if (level == SYNC_DOUBLEWRITE) {
ut_a(sync_thread_levels_g(array, SYNC_DOUBLEWRITE)); ut_a(sync_thread_levels_g(array, SYNC_DOUBLEWRITE));
} else if (level == SYNC_BUF_BLOCK) { } else if (level == SYNC_BUF_BLOCK) {
......
...@@ -438,7 +438,6 @@ trx_sys_update_mysql_binlog_offset( ...@@ -438,7 +438,6 @@ trx_sys_update_mysql_binlog_offset(
trx_sysf_t* sys_header; trx_sysf_t* sys_header;
char namebuf[TRX_SYS_MYSQL_LOG_NAME_LEN]; char namebuf[TRX_SYS_MYSQL_LOG_NAME_LEN];
ut_ad(mutex_own(&kernel_mutex));
ut_ad(trx->mysql_log_file_name); ut_ad(trx->mysql_log_file_name);
memset(namebuf, ' ', TRX_SYS_MYSQL_LOG_NAME_LEN - 1); memset(namebuf, ' ', TRX_SYS_MYSQL_LOG_NAME_LEN - 1);
...@@ -524,7 +523,7 @@ trx_sys_print_mysql_binlog_offset(void) ...@@ -524,7 +523,7 @@ trx_sys_print_mysql_binlog_offset(void)
} }
fprintf(stderr, fprintf(stderr,
"InnoDB: Last MySQL binlog file offset %lu %lu, file name %s\n", "InnoDB: Last MySQL binlog file position %lu %lu, file name %s\n",
mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ TRX_SYS_MYSQL_LOG_OFFSET_HIGH), + TRX_SYS_MYSQL_LOG_OFFSET_HIGH),
mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
......
...@@ -71,6 +71,9 @@ trx_create( ...@@ -71,6 +71,9 @@ trx_create(
trx->type = TRX_USER; trx->type = TRX_USER;
trx->conc_state = TRX_NOT_STARTED; trx->conc_state = TRX_NOT_STARTED;
trx->check_foreigns = TRUE;
trx->check_unique_secondary = TRUE;
trx->dict_operation = FALSE; trx->dict_operation = FALSE;
trx->mysql_thd = NULL; trx->mysql_thd = NULL;
...@@ -113,6 +116,7 @@ trx_create( ...@@ -113,6 +116,7 @@ trx_create(
trx->lock_heap = mem_heap_create_in_buffer(256); trx->lock_heap = mem_heap_create_in_buffer(256);
UT_LIST_INIT(trx->trx_locks); UT_LIST_INIT(trx->trx_locks);
trx->has_dict_foreign_key_check_lock = FALSE;
trx->has_search_latch = FALSE; trx->has_search_latch = FALSE;
trx->search_latch_timeout = BTR_SEA_TIMEOUT; trx->search_latch_timeout = BTR_SEA_TIMEOUT;
...@@ -703,8 +707,7 @@ trx_commit_off_kernel( ...@@ -703,8 +707,7 @@ trx_commit_off_kernel(
/*-------------------------------------*/ /*-------------------------------------*/
/* Only in some performance tests the variable srv_flush.. /* Most MySQL users run with srv_flush.. set to FALSE: */
will be set to FALSE: */
if (srv_flush_log_at_trx_commit) { if (srv_flush_log_at_trx_commit) {
......
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