summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <heikki@hundin.mysql.fi>2002-04-18 10:40:32 +0300
committerunknown <heikki@hundin.mysql.fi>2002-04-18 10:40:32 +0300
commit209a500df9f97dcbc910a4c66f82efe2b10867c5 (patch)
tree4e69f6dc9cf132e85fb626a6120e6576baf6c127
parent9c2c5a3636eb730fedbbeb613ee946ef15735be0 (diff)
downloadmariadb-git-209a500df9f97dcbc910a4c66f82efe2b10867c5.tar.gz
Many files:
Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/btr/btr0cur.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/dict/dict0crea.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/dict/dict0dict.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/dict/dict0load.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/dict/dict0mem.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/btr0btr.h: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/dict0mem.h: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/ibuf0ibuf.h: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/os0file.h: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/os0sync.h: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/row0mysql.h: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/row0upd.h: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/sync0sync.h: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/trx0sys.h: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/trx0trx.h: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/ibuf0ibuf.ic: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/os0sync.ic: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/sync0sync.ic: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/include/trx0sys.ic: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/lock/lock0lock.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/pars/pars0opt.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/que/que0que.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/row/row0ins.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/row/row0mysql.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/row/row0sel.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/row/row0upd.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/srv/srv0srv.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/sync/sync0sync.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/trx/trx0sys.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys innobase/trx/trx0trx.c: Implement ON DELETE CASCADE and facilitate switching off of UNIQUE constraints and foreign keys
-rw-r--r--innobase/btr/btr0cur.c11
-rw-r--r--innobase/dict/dict0crea.c3
-rw-r--r--innobase/dict/dict0dict.c161
-rw-r--r--innobase/dict/dict0load.c5
-rw-r--r--innobase/dict/dict0mem.c4
-rw-r--r--innobase/include/btr0btr.h6
-rw-r--r--innobase/include/dict0mem.h32
-rw-r--r--innobase/include/ibuf0ibuf.h6
-rw-r--r--innobase/include/ibuf0ibuf.ic12
-rw-r--r--innobase/include/os0file.h2
-rw-r--r--innobase/include/os0sync.h5
-rw-r--r--innobase/include/os0sync.ic17
-rw-r--r--innobase/include/row0mysql.h21
-rw-r--r--innobase/include/row0upd.h10
-rw-r--r--innobase/include/sync0sync.h9
-rw-r--r--innobase/include/sync0sync.ic4
-rw-r--r--innobase/include/trx0sys.h2
-rw-r--r--innobase/include/trx0sys.ic1
-rw-r--r--innobase/include/trx0trx.h14
-rw-r--r--innobase/lock/lock0lock.c3
-rw-r--r--innobase/pars/pars0opt.c3
-rw-r--r--innobase/que/que0que.c9
-rw-r--r--innobase/row/row0ins.c276
-rw-r--r--innobase/row/row0mysql.c177
-rw-r--r--innobase/row/row0sel.c8
-rw-r--r--innobase/row/row0upd.c109
-rw-r--r--innobase/srv/srv0srv.c20
-rw-r--r--innobase/sync/sync0sync.c6
-rw-r--r--innobase/trx/trx0sys.c3
-rw-r--r--innobase/trx/trx0trx.c7
30 files changed, 768 insertions, 178 deletions
diff --git a/innobase/btr/btr0cur.c b/innobase/btr/btr0cur.c
index ddcf5e8faf2..daebb8dbbaf 100644
--- a/innobase/btr/btr0cur.c
+++ b/innobase/btr/btr0cur.c
@@ -228,6 +228,7 @@ btr_cur_search_to_nth_level(
ulint insert_planned;
ulint buf_mode;
ulint estimate;
+ ulint ignore_sec_unique;
ulint root_height;
#ifdef BTR_CUR_ADAPT
btr_search_t* info;
@@ -246,7 +247,9 @@ btr_cur_search_to_nth_level(
#endif
insert_planned = latch_mode & BTR_INSERT;
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));
@@ -343,7 +346,8 @@ btr_cur_search_to_nth_level(
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
page is not in the buffer pool */
@@ -356,7 +360,6 @@ retry_page_get:
buf_mode,
IB__FILE__, __LINE__,
mtr);
-
if (page == NULL) {
/* This must be a search to perform an insert;
try insert to the insert buffer */
@@ -365,7 +368,7 @@ retry_page_get:
ut_ad(insert_planned);
ut_ad(cursor->thr);
- if (ibuf_should_try(index) &&
+ if (ibuf_should_try(index, ignore_sec_unique) &&
ibuf_insert(tuple, index, space, page_no,
cursor->thr)) {
/* Insertion to the insert buffer succeeded */
diff --git a/innobase/dict/dict0crea.c b/innobase/dict/dict0crea.c
index f15d36251e4..38c09c1011d 100644
--- a/innobase/dict/dict0crea.c
+++ b/innobase/dict/dict0crea.c
@@ -1201,7 +1201,8 @@ loop:
ut_dulint_get_low(id),
table->name,
foreign->referenced_table_name,
- foreign->n_fields);
+ foreign->n_fields
+ + (foreign->type << 24));
for (i = 0; i < foreign->n_fields; i++) {
diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c
index d52483074cd..a6f268c2153 100644
--- a/innobase/dict/dict0dict.c
+++ b/innobase/dict/dict0dict.c
@@ -1648,7 +1648,7 @@ dict_foreign_find_index(
->col->name;
if (ut_strlen(columns[i]) !=
ut_strlen(col_name)
- || 0 != ut_memcmp(columns[i],
+ || 0 != ut_cmp_in_lower_case(columns[i],
col_name,
ut_strlen(col_name))) {
break;
@@ -1853,8 +1853,9 @@ dict_scan_col(
ibool* success,/* out: TRUE if success */
dict_table_t* table, /* in: table in which the column is */
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 */
+ ulint* column_name_len)/* out: column name length */
{
dict_col_t* col;
char* old_ptr;
@@ -1882,20 +1883,28 @@ dict_scan_col(
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)
- && 0 == ut_cmp_in_lower_case(col->name, old_ptr,
+ if (ut_strlen(col->name) == (ulint)(ptr - old_ptr)
+ && 0 == ut_cmp_in_lower_case(col->name, old_ptr,
(ulint)(ptr - old_ptr))) {
- /* Found */
+ /* Found */
- *success = TRUE;
- *column = col;
- *column_name = col->name;
+ *success = TRUE;
+ *column = col;
+ *column_name = col->name;
- break;
+ break;
+ }
}
}
@@ -1914,14 +1923,18 @@ dict_scan_table_name(
/*=================*/
/* out: scanned to */
char* ptr, /* in: scanned to */
- dict_table_t** table, /* out: table object or NULL if error */
- char* name) /* in: foreign key table name */
+ dict_table_t** table, /* out: table object or NULL */
+ 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* old_ptr;
ulint i;
- char second_table_name[10000];
+ *success = FALSE;
*table = NULL;
while (isspace(*ptr)) {
@@ -1947,7 +1960,7 @@ dict_scan_table_name(
ptr++;
}
- if (ptr - old_ptr > 9000) {
+ if (ptr - old_ptr > 2000) {
return(old_ptr);
}
@@ -1978,6 +1991,8 @@ dict_scan_table_name(
second_table_name[ptr - old_ptr] = '\0';
}
+ *success = TRUE;
+
*table = dict_table_get_low(second_table_name);
if (*ptr == '`') {
@@ -2043,9 +2058,12 @@ dict_create_foreign_constraints(
ibool success;
ulint error;
ulint i;
- dict_col_t* columns[1000];
- char* column_names[1000];
-
+ ulint j;
+ 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)));
table = dict_table_get_low(name);
@@ -2090,7 +2108,7 @@ loop:
/* Scan the columns in the first list */
col_loop1:
ptr = dict_scan_col(ptr, &success, table, columns + i,
- column_names + i);
+ column_names + i, column_name_lens + i);
if (!success) {
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2141,9 +2159,13 @@ col_loop1:
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);
return(DB_CANNOT_ADD_CONSTRAINT);
@@ -2161,7 +2183,7 @@ col_loop1:
col_loop2:
ptr = dict_scan_col(ptr, &success, referenced_table, columns + i,
- column_names + i);
+ column_names + i, column_name_lens + i);
i++;
if (!success) {
@@ -2183,43 +2205,104 @@ col_loop2:
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
and in the right order, and the types are the same as in
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);
-
- if (!index) {
- dict_foreign_free(foreign);
- return(DB_CANNOT_ADD_CONSTRAINT);
+ if (!index) {
+ dict_foreign_free(foreign);
+ return(DB_CANNOT_ADD_CONSTRAINT);
+ }
+ } else {
+ ut_a(trx->check_foreigns == FALSE);
+ index = NULL;
}
foreign->referenced_index = index;
foreign->referenced_table = referenced_table;
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,
- 1 + ut_strlen(referenced_table->name));
+ ut_memcpy(foreign->referenced_table_name, referenced_table_name,
+ 1 + ut_strlen(referenced_table_name));
foreign->referenced_col_names = mem_heap_alloc(foreign->heap,
i * sizeof(void*));
for (i = 0; i < foreign->n_fields; i++) {
foreign->referenced_col_names[i]
= mem_heap_alloc(foreign->heap,
- 1 + ut_strlen(columns[i]->name));
- ut_memcpy(
- foreign->referenced_col_names[i], columns[i]->name,
- 1 + ut_strlen(columns[i]->name));
+ 1 + column_name_lens[i]);
+ ut_memcpy(foreign->referenced_col_names[i], column_names[i],
+ column_name_lens[i]);
+ (foreign->referenced_col_names[i])[column_name_lens[i]] = '\0';
}
/* We found an ok constraint definition: add to the lists */
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);
+ }
goto loop;
}
@@ -3034,6 +3117,14 @@ dict_print_info_on_foreign_keys_in_create_format(
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);
}
diff --git a/innobase/dict/dict0load.c b/innobase/dict/dict0load.c
index 29c98db4a53..4917359c748 100644
--- a/innobase/dict/dict0load.c
+++ b/innobase/dict/dict0load.c
@@ -946,6 +946,11 @@ dict_load_foreign(
foreign->n_fields = mach_read_from_4(rec_get_nth_field(rec, 5, &len));
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);
diff --git a/innobase/dict/dict0mem.c b/innobase/dict/dict0mem.c
index 1f9a44aca35..52f46062065 100644
--- a/innobase/dict/dict0mem.c
+++ b/innobase/dict/dict0mem.c
@@ -61,7 +61,8 @@ dict_mem_table_create(
table->mem_fix = 0;
table->n_mysql_handles_opened = 0;
-
+ table->n_foreign_key_checks_running = 0;
+
table->cached = FALSE;
table->cols = mem_heap_alloc(heap, (n_cols + DATA_N_SYS_COLS)
@@ -235,6 +236,7 @@ dict_mem_foreign_create(void)
foreign->id = NULL;
+ foreign->type = 0;
foreign->foreign_table_name = NULL;
foreign->foreign_table = NULL;
foreign->foreign_col_names = NULL;
diff --git a/innobase/include/btr0btr.h b/innobase/include/btr0btr.h
index bea85565125..d22f9d79c1c 100644
--- a/innobase/include/btr0btr.h
+++ b/innobase/include/btr0btr.h
@@ -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
optimization */
#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. */
UNIV_INLINE
diff --git a/innobase/include/dict0mem.h b/innobase/include/dict0mem.h
index 5ef0103087a..ef15c99fdba 100644
--- a/innobase/include/dict0mem.h
+++ b/innobase/include/dict0mem.h
@@ -249,6 +249,8 @@ struct dict_foreign_struct{
this memory heap */
char* id; /* id of the constraint as a
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 */
dict_table_t* foreign_table; /* table where the foreign key is */
char** foreign_col_names;/* names of the columns in the
@@ -278,6 +280,9 @@ struct dict_foreign_struct{
table */
};
+#define DICT_FOREIGN_ON_DELETE_CASCADE 1
+#define DICT_FOREIGN_ON_DELETE_SET_NULL 2
+
#define DICT_INDEX_MAGIC_N 76789786
/* Data structure for a database table */
@@ -313,6 +318,12 @@ struct dict_table_struct{
NOT allowed until this count gets to zero;
MySQL does NOT itself check the number of
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
to the dictionary cache */
lock_t* auto_inc_lock;/* a buffer for an auto-inc lock
@@ -359,17 +370,16 @@ struct dict_table_struct{
after database startup or table creation */
ulint stat_modified_counter;
/* when a row is inserted, updated, or deleted,
- we add the row length to this number; we
- calculate new estimates for the stat_...
- values for the table and the indexes at an
- interval of 2 GB or when about 1 / 16 of table
- has been modified; also
- when the estimate operation is called
- for MySQL SHOW TABLE STATUS; the counter is
- reset to zero at statistics calculation;
- this counter
- is not protected by any latch, because this
- is only used for heuristics */
+ we add 1 to this number; we calculate new
+ estimates for the stat_... values for the
+ table and the indexes at an interval of 2 GB
+ or when about 1 / 16 of table has been
+ modified; also when the estimate operation is
+ called for MySQL SHOW TABLE STATUS; the
+ counter is reset to zero at statistics
+ calculation; this counter is not protected by
+ any latch, because this is only used for
+ heuristics */
/*----------------------*/
mutex_t autoinc_mutex;
/* mutex protecting the autoincrement
diff --git a/innobase/include/ibuf0ibuf.h b/innobase/include/ibuf0ibuf.h
index 99fb1595f49..fac28461be4 100644
--- a/innobase/include/ibuf0ibuf.h
+++ b/innobase/include/ibuf0ibuf.h
@@ -127,7 +127,11 @@ UNIV_INLINE
ibool
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
routine. */
diff --git a/innobase/include/ibuf0ibuf.ic b/innobase/include/ibuf0ibuf.ic
index e969a0550da..0886c8c02cc 100644
--- a/innobase/include/ibuf0ibuf.ic
+++ b/innobase/include/ibuf0ibuf.ic
@@ -81,10 +81,16 @@ UNIV_INLINE
ibool
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))
- && ibuf->meter > IBUF_THRESHOLD) {
+ if (!(index->type & DICT_CLUSTERED)
+ && (ignore_sec_unique || !(index->type & DICT_UNIQUE))
+ && ibuf->meter > IBUF_THRESHOLD) {
+
ibuf_flush_count++;
if (ibuf_flush_count % 8 == 0) {
diff --git a/innobase/include/os0file.h b/innobase/include/os0file.h
index 3dba8ba3158..01fa12955ff 100644
--- a/innobase/include/os0file.h
+++ b/innobase/include/os0file.h
@@ -341,6 +341,8 @@ os_aio_windows_handle(
void** message2,
ulint* type); /* out: OS_FILE_WRITE or ..._READ */
#endif
+
+/* Currently we do not use Posix async i/o */
#ifdef POSIX_ASYNC_IO
/**************************************************************************
This function is only used in Posix asynchronous i/o. Waits for an aio
diff --git a/innobase/include/os0sync.h b/innobase/include/os0sync.h
index 617f6b036fe..b2d613c4619 100644
--- a/innobase/include/os0sync.h
+++ b/innobase/include/os0sync.h
@@ -149,9 +149,9 @@ void
os_mutex_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
ulint
os_fast_mutex_trylock(
@@ -160,7 +160,6 @@ os_fast_mutex_trylock(
was reserved by another
thread */
os_fast_mutex_t* fast_mutex); /* in: mutex to acquire */
-#endif
/**************************************************************
Releases ownership of a fast mutex. */
diff --git a/innobase/include/os0sync.ic b/innobase/include/os0sync.ic
index aa00300dec5..6bff75d8ec6 100644
--- a/innobase/include/os0sync.ic
+++ b/innobase/include/os0sync.ic
@@ -10,9 +10,9 @@ Created 9/6/1995 Heikki Tuuri
#include <winbase.h>
#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
ulint
os_fast_mutex_trylock(
@@ -23,20 +23,11 @@ os_fast_mutex_trylock(
os_fast_mutex_t* fast_mutex) /* in: mutex to acquire */
{
#ifdef __WIN__
- int ret;
+ EnterCriticalSection(fast_mutex);
- /* TODO: TryEnterCriticalSection is probably not found from
- NT versions < 4! */
- ret = TryEnterCriticalSection(fast_mutex);
-
- if (ret) {
- return(0);
- }
-
- return(1);
+ return(0);
#else
return((ulint) pthread_mutex_trylock(fast_mutex));
#endif
}
-#endif
diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h
index 92693319175..13b3dffd874 100644
--- a/innobase/include/row0mysql.h
+++ b/innobase/include/row0mysql.h
@@ -209,6 +209,27 @@ row_update_for_mysql(
row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL
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
table ends to characters INNODB_MONITOR, then this also starts
printing of monitor output by the master thread. */
diff --git a/innobase/include/row0upd.h b/innobase/include/row0upd.h
index 106d3866b25..9a3e2463267 100644
--- a/innobase/include/row0upd.h
+++ b/innobase/include/row0upd.h
@@ -312,6 +312,11 @@ struct upd_node_struct{
ibool in_mysql_interface;
/* TRUE if the update node was created
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
table cursor: the rows returned will be
updated */
@@ -322,6 +327,11 @@ struct upd_node_struct{
of the MySQL interface */
dict_table_t* table; /* table where updated */
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
to retrieve from the table */
ibool has_clust_rec_x_lock;
diff --git a/innobase/include/sync0sync.h b/innobase/include/sync0sync.h
index 6c3bff66e27..4f55709a5d7 100644
--- a/innobase/include/sync0sync.h
+++ b/innobase/include/sync0sync.h
@@ -359,12 +359,17 @@ V
Memory pool mutex */
/* 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
latching order checking */
#define SYNC_LEVEL_NONE 2000 /* default: level not defined */
+#define SYNC_FOREIGN_KEY_CHECK 1001
#define SYNC_DICT 1000
#define SYNC_DICT_AUTOINC_MUTEX 999
-#define SYNC_FOREIGN_KEY_CHECK 998
#define SYNC_PURGE_IS_RUNNING 997
#define SYNC_DICT_HEADER 995
#define SYNC_IBUF_HEADER 914
@@ -429,7 +434,7 @@ implementation of a mutual exclusion semaphore. */
struct mutex_struct {
ulint lock_word; /* This ulint is the target of the atomic
test-and-set instruction in Win32 */
-#ifndef _WIN32
+#if !defined(_WIN32) || !defined(UNIV_CAN_USE_X86_ASSEMBLER)
os_fast_mutex_t
os_fast_mutex; /* In other systems we use this OS mutex
in place of lock_word */
diff --git a/innobase/include/sync0sync.ic b/innobase/include/sync0sync.ic
index 9531377ce0b..9014eb5fb54 100644
--- a/innobase/include/sync0sync.ic
+++ b/innobase/include/sync0sync.ic
@@ -53,7 +53,7 @@ mutex_test_and_set(
1 */
mutex_t* mutex) /* in: mutex */
{
-#ifdef _WIN32
+#if defined(_WIN32) && defined(UNIV_CAN_USE_X86_ASSEMBLER)
ulint res;
ulint* lw; /* assembler code is used to ensure that
lock_word is loaded from memory */
@@ -120,7 +120,7 @@ mutex_reset_lock_word(
/*==================*/
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
lock_word is loaded from memory */
ut_ad(mutex);
diff --git a/innobase/include/trx0sys.h b/innobase/include/trx0sys.h
index 98c726fb118..60d5adb72d1 100644
--- a/innobase/include/trx0sys.h
+++ b/innobase/include/trx0sys.h
@@ -259,7 +259,7 @@ therefore 256 */
/* The offset of the transaction system header on the page */
#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
modulo TRX_SYS_TRX_ID_UPDATE_MARGIN
diff --git a/innobase/include/trx0sys.ic b/innobase/include/trx0sys.ic
index 786e7905933..ada2d8cb19c 100644
--- a/innobase/include/trx0sys.ic
+++ b/innobase/include/trx0sys.ic
@@ -93,7 +93,6 @@ trx_sysf_get(
{
trx_sysf_t* header;
- ut_ad(mutex_own(&(kernel_mutex)));
ut_ad(mtr);
header = TRX_SYS + buf_page_get(TRX_SYS_SPACE, TRX_SYS_PAGE_NO,
diff --git a/innobase/include/trx0trx.h b/innobase/include/trx0trx.h
index 26c9ace08b6..261f33d3dc3 100644
--- a/innobase/include/trx0trx.h
+++ b/innobase/include/trx0trx.h
@@ -298,6 +298,17 @@ struct trx_struct{
of view of concurrency control:
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 no; /* transaction serialization number ==
max trx id when the transaction is
@@ -328,6 +339,9 @@ struct trx_struct{
/* how many tables the current SQL
statement uses, except those
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;
/* TRUE if this trx has latched the
search system latch in S-mode */
diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c
index 0b2b85d0337..9fd6811d639 100644
--- a/innobase/lock/lock0lock.c
+++ b/innobase/lock/lock0lock.c
@@ -1534,6 +1534,8 @@ lock_rec_enqueue_waiting(
if (que_thr_stop(thr)) {
+ ut_a(0);
+
return(DB_QUE_THR_SUSPENDED);
}
@@ -2918,6 +2920,7 @@ lock_table_enqueue_waiting(
stopped anyway */
if (que_thr_stop(thr)) {
+ ut_a(0);
return(DB_QUE_THR_SUSPENDED);
}
diff --git a/innobase/pars/pars0opt.c b/innobase/pars/pars0opt.c
index 6f4957f96ee..9814c4b21cc 100644
--- a/innobase/pars/pars0opt.c
+++ b/innobase/pars/pars0opt.c
@@ -526,7 +526,8 @@ opt_search_plan_for_table(
dict_index_t* best_index;
ulint n_fields;
ulint goodness;
- ulint last_op;
+ ulint last_op = 75946965; /* Eliminate a Purify
+ warning */
ulint best_goodness;
ulint best_last_op;
ulint mix_id_pos;
diff --git a/innobase/que/que0que.c b/innobase/que/que0que.c
index 97843311d21..41a90a7fb42 100644
--- a/innobase/que/que0que.c
+++ b/innobase/que/que0que.c
@@ -555,6 +555,12 @@ que_graph_free_recursive(
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);
mem_heap_free(upd->heap);
@@ -1110,9 +1116,6 @@ que_thr_move_to_run_state_for_mysql(
trx->n_active_thrs++;
thr->is_active = TRUE;
-
- ut_ad((thr->graph)->n_active_thrs == 1);
- ut_ad(trx->n_active_thrs == 1);
}
thr->state = QUE_THR_RUNNING;
diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c
index 04df66c3bbd..0ba6db39e2a 100644
--- a/innobase/row/row0ins.c
+++ b/innobase/row/row0ins.c
@@ -356,6 +356,223 @@ row_ins_dupl_error_with_rec(
}
/*************************************************************************
+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
records. */
static
@@ -416,6 +633,13 @@ row_ins_check_foreign_constraint(
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
suppress the foreign key check: this is compatible with Oracle,
for example */
@@ -478,8 +702,8 @@ row_ins_check_foreign_constraint(
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);
@@ -501,11 +725,21 @@ row_ins_check_foreign_constraint(
if (check_ref) {
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 {
err = DB_ROW_IS_REFERENCED;
+ break;
}
-
- break;
}
}
@@ -534,6 +768,8 @@ next_rec:
}
}
+ btr_pcur_close(&pcur);
+
mtr_commit(&mtr);
/* Restore old value */
@@ -561,6 +797,10 @@ row_ins_check_foreign_constraints(
{
dict_foreign_t* foreign;
ulint err;
+ trx_t* trx;
+ ibool got_s_lock = FALSE;
+
+ trx = thr_get_trx(thr);
foreign = UT_LIST_GET_FIRST(table->foreign_list);
@@ -569,16 +809,26 @@ row_ins_check_foreign_constraints(
if (foreign->referenced_table == NULL) {
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,
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) {
return(err);
}
@@ -868,13 +1118,14 @@ row_ins_index_entry_low(
ulint n_ext_vec,/* in: number of fields in ext_vec */
que_thr_t* thr) /* in: query thread */
{
- btr_cur_t cursor;
+ btr_cur_t cursor;
+ ulint ignore_sec_unique = 0;
ulint modify;
rec_t* insert_rec;
rec_t* rec;
ulint err;
ulint n_unique;
- big_rec_t* big_rec = NULL;
+ big_rec_t* big_rec = NULL;
mtr_t mtr;
log_free_check();
@@ -887,8 +1138,13 @@ row_ins_index_entry_low(
the function will return in both low_match and up_match of the
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,
- mode | BTR_INSERT, &cursor, 0, &mtr);
+ mode | BTR_INSERT | ignore_sec_unique,
+ &cursor, 0, &mtr);
if (cursor.flag == BTR_CUR_INSERT_TO_IBUF) {
/* The insertion was made to the insert buffer already during
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 2aca698eebd..7969394f5e4 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -499,29 +499,24 @@ UNIV_INLINE
void
row_update_statistics_if_needed(
/*============================*/
- row_prebuilt_t* prebuilt) /* in: prebuilt struct */
+ dict_table_t* table) /* in: table */
{
ulint counter;
- counter = prebuilt->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;
+ counter = table->stat_modified_counter;
- prebuilt->table->stat_modified_counter = counter;
+ table->stat_modified_counter = counter + 1;
/* Calculate new statistics if 1 / 16 of table has been modified
since the last time a statistics batch was run, or if
- stat_modified_counter > 2 000 000 000 (to avoid wrap-around) */
+ stat_modified_counter > 2 000 000 000 (to avoid wrap-around).
+ We calculate statistics at most every 16th round, since we may have
+ a counter table which is very small and updated very often. */
if (counter > 2000000000
- || ((ib_longlong)counter >
- (UNIV_PAGE_SIZE * prebuilt->table->stat_clustered_index_size)
- / 16)) {
+ || ((ib_longlong)counter > 16 + table->stat_n_rows / 16)) {
- dict_update_statistics(prebuilt->table);
+ dict_update_statistics(table);
}
}
@@ -712,7 +707,7 @@ run_again:
prebuilt->table->stat_n_rows--;
}
- row_update_statistics_if_needed(prebuilt);
+ row_update_statistics_if_needed(prebuilt->table);
trx->op_info = "";
return((int) err);
@@ -746,6 +741,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
graph has not yet been built in the prebuilt struct, then this function
first builds it. */
@@ -767,26 +799,9 @@ row_get_prebuilt_update_vector(
/* Not called before for this handle: create an update node
and query graph to the prebuilt struct */
- node = upd_node_create(prebuilt->heap);
-
- prebuilt->upd_node = node;
+ node = row_create_update_node_for_mysql(table, prebuilt->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),
- 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_node = node;
prebuilt->upd_graph =
que_node_get_parent(
@@ -914,7 +929,7 @@ run_again:
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) {
prebuilt->table->stat_n_rows--;
}
@@ -924,13 +939,66 @@ run_again:
srv_n_rows_updated++;
}
- row_update_statistics_if_needed(prebuilt);
+ row_update_statistics_if_needed(prebuilt->table);
trx->op_info = "";
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
index on it (on row id). */
@@ -1169,6 +1237,7 @@ row_create_table_for_mysql(
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
+ rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex));
heap = mem_heap_create(512);
@@ -1221,6 +1290,8 @@ row_create_table_for_mysql(
}
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
+
que_graph_free((que_t*) que_node_get_parent(thr));
trx->op_info = "";
@@ -1268,6 +1339,7 @@ row_create_index_for_mysql(
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
+ rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex));
heap = mem_heap_create(512);
@@ -1298,6 +1370,7 @@ row_create_index_for_mysql(
}
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
que_graph_free((que_t*) que_node_get_parent(thr));
@@ -1353,6 +1426,7 @@ row_table_add_foreign_constraints(
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
+ rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex));
trx->dict_operation = TRUE;
@@ -1377,6 +1451,7 @@ row_table_add_foreign_constraints(
}
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
return((int) err);
}
@@ -1471,7 +1546,8 @@ loop:
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);
}
@@ -1717,6 +1793,9 @@ row_drop_table_for_mysql(
no deadlocks can occur then in these operations */
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));
}
@@ -1729,9 +1808,6 @@ row_drop_table_for_mysql(
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 */
rw_lock_s_lock(&(purge_sys->purge_is_running));
@@ -1766,6 +1842,22 @@ row_drop_table_for_mysql(
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 */
lock_reset_all_on_table(table);
@@ -1793,10 +1885,9 @@ row_drop_table_for_mysql(
funct_exit:
rw_lock_s_unlock(&(purge_sys->purge_is_running));
- rw_lock_x_unlock(&(dict_foreign_key_check_lock));
-
if (!has_dict_mutex) {
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
}
que_graph_free(graph);
@@ -1832,6 +1923,7 @@ row_drop_database_for_mysql(
trx_start_if_not_started(trx);
loop:
+ rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex));
while (table_name = dict_get_first_table_name_in_db(name)) {
@@ -1873,6 +1965,7 @@ loop:
}
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
trx_commit_for_mysql(trx);
@@ -2009,6 +2102,7 @@ row_rename_table_for_mysql(
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
+ rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex));
table = dict_table_get_low(old_name);
@@ -2090,6 +2184,7 @@ row_rename_table_for_mysql(
}
funct_exit:
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
que_graph_free(graph);
diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c
index 9ebd47c25bf..a516699edf9 100644
--- a/innobase/row/row0sel.c
+++ b/innobase/row/row0sel.c
@@ -2115,8 +2115,14 @@ row_sel_store_mysql_rec(
extern_field_heap = NULL;
}
} 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);
+ }
}
}
}
diff --git a/innobase/row/row0upd.c b/innobase/row/row0upd.c
index a566e29f2c3..457cb72aaad 100644
--- a/innobase/row/row0upd.c
+++ b/innobase/row/row0upd.c
@@ -73,8 +73,7 @@ steps of query graph execution. */
/*************************************************************************
Checks if index currently is mentioned as a referenced index in a foreign
-key constraint. This function also loads into the dictionary cache the
-possible referencing table. */
+key constraint. */
static
ibool
row_upd_index_is_referenced(
@@ -85,44 +84,28 @@ row_upd_index_is_referenced(
the referencing table has been dropped when
we leave this function: this function is only
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;
- ulint phase = 1;
-try_again:
if (!UT_LIST_GET_FIRST(table->referenced_list)) {
return(FALSE);
}
- if (phase == 2) {
- mutex_enter(&(dict_sys->mutex));
+ if (!trx->has_dict_foreign_key_check_lock) {
+ 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);
while (foreign) {
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) {
- mutex_exit(&(dict_sys->mutex));
+ if (!trx->has_dict_foreign_key_check_lock) {
+ rw_lock_s_unlock(&dict_foreign_key_check_lock);
}
return(TRUE);
@@ -131,10 +114,8 @@ try_again:
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
}
- rw_lock_s_unlock(&dict_foreign_key_check_lock);
-
- if (phase == 2) {
- mutex_exit(&(dict_sys->mutex));
+ if (!trx->has_dict_foreign_key_check_lock) {
+ rw_lock_s_unlock(&dict_foreign_key_check_lock);
}
return(FALSE);
@@ -160,8 +141,17 @@ row_upd_check_references_constraints(
dict_foreign_t* foreign;
mem_heap_t* heap;
dtuple_t* entry;
+ trx_t* trx;
rec_t* rec;
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);
@@ -173,17 +163,61 @@ row_upd_check_references_constraints(
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);
while (foreign) {
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,
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) {
- 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);
return(err);
@@ -193,7 +227,11 @@ row_upd_check_references_constraints(
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);
return(DB_SUCCESS);
@@ -222,6 +260,9 @@ upd_node_create(
node->index = NULL;
node->update = NULL;
+ node->cascade_heap = NULL;
+ node->cascade_node = NULL;
+
node->select = NULL;
node->heap = mem_heap_create(128);
@@ -1027,7 +1068,7 @@ row_upd_sec_index_entry(
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);
@@ -1391,7 +1432,7 @@ row_upd_clust_step(
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;
diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c
index 8afe1396f1b..d4aa085479b 100644
--- a/innobase/srv/srv0srv.c
+++ b/innobase/srv/srv0srv.c
@@ -2017,15 +2017,18 @@ srv_suspend_mysql_thread(
/*=====================*/
/* out: TRUE if the lock wait timeout was
exceeded */
- que_thr_t* thr) /* in: query thread associated with
- the MySQL OS thread */
+ que_thr_t* thr) /* in: query thread associated with the MySQL
+ OS thread */
{
srv_slot_t* slot;
os_event_t event;
double wait_time;
-
+ trx_t* trx;
+
ut_ad(!mutex_own(&kernel_mutex));
+ trx = thr_get_trx(thr);
+
os_event_set(srv_lock_timeout_thread_event);
mutex_enter(&kernel_mutex);
@@ -2061,10 +2064,21 @@ srv_suspend_mysql_thread(
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 */
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 */
srv_conc_force_enter_innodb(thr_get_trx(thr));
diff --git a/innobase/sync/sync0sync.c b/innobase/sync/sync0sync.c
index 8b2a39e15eb..f5b4e35e8d2 100644
--- a/innobase/sync/sync0sync.c
+++ b/innobase/sync/sync0sync.c
@@ -220,7 +220,7 @@ mutex_create_func(
char* cfile_name, /* in: file name 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);
#else
os_fast_mutex_init(&(mutex->os_fast_mutex));
@@ -273,7 +273,7 @@ mutex_free(
mutex_exit(&mutex_list_mutex);
-#ifndef _WIN32
+#if !defined(_WIN32) || !defined(UNIV_CAN_USE_X86_ASSEMBLER)
os_fast_mutex_free(&(mutex->os_fast_mutex));
#endif
/* If we free the mutex protecting the mutex list (freeing is
@@ -1009,7 +1009,7 @@ sync_thread_add_level(
} else if (level == SYNC_ANY_LATCH) {
ut_a(sync_thread_levels_g(array, SYNC_ANY_LATCH));
} 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) {
ut_a(sync_thread_levels_g(array, SYNC_DOUBLEWRITE));
} else if (level == SYNC_BUF_BLOCK) {
diff --git a/innobase/trx/trx0sys.c b/innobase/trx/trx0sys.c
index 6c9776560bd..32a1db48488 100644
--- a/innobase/trx/trx0sys.c
+++ b/innobase/trx/trx0sys.c
@@ -438,7 +438,6 @@ trx_sys_update_mysql_binlog_offset(
trx_sysf_t* sys_header;
char namebuf[TRX_SYS_MYSQL_LOG_NAME_LEN];
- ut_ad(mutex_own(&kernel_mutex));
ut_ad(trx->mysql_log_file_name);
memset(namebuf, ' ', TRX_SYS_MYSQL_LOG_NAME_LEN - 1);
@@ -524,7 +523,7 @@ trx_sys_print_mysql_binlog_offset(void)
}
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
+ TRX_SYS_MYSQL_LOG_OFFSET_HIGH),
mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c
index 6cbf97104b3..ae607c33fbe 100644
--- a/innobase/trx/trx0trx.c
+++ b/innobase/trx/trx0trx.c
@@ -71,6 +71,9 @@ trx_create(
trx->type = TRX_USER;
trx->conc_state = TRX_NOT_STARTED;
+ trx->check_foreigns = TRUE;
+ trx->check_unique_secondary = TRUE;
+
trx->dict_operation = FALSE;
trx->mysql_thd = NULL;
@@ -113,6 +116,7 @@ trx_create(
trx->lock_heap = mem_heap_create_in_buffer(256);
UT_LIST_INIT(trx->trx_locks);
+ trx->has_dict_foreign_key_check_lock = FALSE;
trx->has_search_latch = FALSE;
trx->search_latch_timeout = BTR_SEA_TIMEOUT;
@@ -703,8 +707,7 @@ trx_commit_off_kernel(
/*-------------------------------------*/
- /* Only in some performance tests the variable srv_flush..
- will be set to FALSE: */
+ /* Most MySQL users run with srv_flush.. set to FALSE: */
if (srv_flush_log_at_trx_commit) {