summaryrefslogtreecommitdiff
path: root/innobase
diff options
context:
space:
mode:
Diffstat (limited to 'innobase')
-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.h8
-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/include/univ.i12
-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.c280
-rw-r--r--innobase/row/row0mysql.c219
-rw-r--r--innobase/row/row0sel.c49
-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
31 files changed, 855 insertions, 196 deletions
diff --git a/innobase/btr/btr0cur.c b/innobase/btr/btr0cur.c
index 10813bbe1f7..1274719cf7d 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 = 0; /* remove warning */
#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 69b1e7c61fd..0da59d39646 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 43589eb03fe..5f6d947bb92 100644
--- a/innobase/dict/dict0dict.c
+++ b/innobase/dict/dict0dict.c
@@ -1652,7 +1652,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;
@@ -1857,8 +1857,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;
@@ -1886,20 +1887,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;
+ }
}
}
@@ -1918,14 +1927,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)) {
@@ -1951,7 +1964,7 @@ dict_scan_table_name(
ptr++;
}
- if (ptr - old_ptr > 9000) {
+ if (ptr - old_ptr > 2000) {
return(old_ptr);
}
@@ -1982,6 +1995,8 @@ dict_scan_table_name(
second_table_name[ptr - old_ptr] = '\0';
}
+ *success = TRUE;
+
*table = dict_table_get_low(second_table_name);
if (*ptr == '`') {
@@ -2047,9 +2062,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);
@@ -2094,7 +2112,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);
}
@@ -2145,9 +2163,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);
@@ -2165,7 +2187,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) {
@@ -2187,43 +2209,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;
}
@@ -3039,6 +3122,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 53ba09616f7..221a6c7dabb 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 67d76814936..01fa12955ff 100644
--- a/innobase/include/os0file.h
+++ b/innobase/include/os0file.h
@@ -25,12 +25,6 @@ extern ibool os_has_said_disk_full;
#define UNIV_NON_BUFFERED_IO
-#else
-
-#if defined(HAVE_AIO_H) && defined(HAVE_LIBRT)
-#define POSIX_ASYNC_IO
-#endif
-
#endif
#ifdef __WIN__
@@ -347,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/include/univ.i b/innobase/include/univ.i
index 8870d80f611..160a435319a 100644
--- a/innobase/include/univ.i
+++ b/innobase/include/univ.i
@@ -14,6 +14,13 @@ Created 1/20/1994 Heikki Tuuri
#include <windows.h>
+/* When compiling for Itanium IA64, undefine the flag below to prevent use
+of 32-bit assembler */
+
+#ifndef WIN64
+#define UNIV_CAN_USE_X86_ASSEMBLER
+#endif
+
/* If you want to check for errors with compiler level -W4,
comment out the above include of windows.h and let the following defines
be defined:
@@ -71,13 +78,14 @@ memory is read outside the allocated blocks. */
*/
/* Make a non-inline debug version */
+
/*
#define UNIV_DEBUG
#define UNIV_MEM_DEBUG
-#define UNIV_SEARCH_DEBUG
+#define UNIV_SYNC_DEBUG
#define UNIV_IBUF_DEBUG
-#define UNIV_SYNC_DEBUG
+#define UNIV_SEARCH_DEBUG
#define UNIV_SYNC_PERF_STAT
#define UNIV_SEARCH_PERF_STAT
*/
diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c
index f2309a5c562..b168ba41771 100644
--- a/innobase/lock/lock0lock.c
+++ b/innobase/lock/lock0lock.c
@@ -1535,6 +1535,8 @@ lock_rec_enqueue_waiting(
if (que_thr_stop(thr)) {
+ ut_a(0);
+
return(DB_QUE_THR_SUSPENDED);
}
@@ -2919,6 +2921,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 35d39caad00..dd0718ce946 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 = 0; /* remove warning */
ulint mix_id_pos;
diff --git a/innobase/que/que0que.c b/innobase/que/que0que.c
index 1cee316f32c..7fa444f6741 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 eb07291e709..ed4df08fcf3 100644
--- a/innobase/row/row0ins.c
+++ b/innobase/row/row0ins.c
@@ -356,6 +356,227 @@ 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 can happen if there is a circular reference of
+ rows such that cascading delete comes to delete a row
+ already in the process of being delete marked */
+/*
+ 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);
+*/
+ err = DB_SUCCESS;
+
+ 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 +637,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 +706,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 +729,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 +772,8 @@ next_rec:
}
}
+ btr_pcur_close(&pcur);
+
mtr_commit(&mtr);
/* Restore old value */
@@ -561,6 +801,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 +813,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 +1122,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 = 0; /* remove warning */
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 +1142,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 78b32a5642b..d6c8d7ab412 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -28,6 +28,9 @@ Created 9/17/2000 Heikki Tuuri
#include "rem0cmp.h"
#include "log0log.h"
+/* A dummy variable used to fool the compiler */
+ibool row_mysql_identically_false = FALSE;
+
/* List of tables we should drop in background. ALTER TABLE in MySQL requires
that the table handler can drop the table in background when there are no
queries to it any more. Protected by the kernel mutex. */
@@ -63,15 +66,48 @@ row_mysql_store_blob_ref(
byte* dest, /* in: where to store */
ulint col_len, /* in: dest buffer size: determines into
how many bytes the BLOB length is stored,
- this may vary from 1 to 4 bytes */
- byte* data, /* in: BLOB data */
- ulint len) /* in: BLOB length */
+ the space for the length may vary from 1
+ to 4 bytes */
+ byte* data, /* in: BLOB data; if the value to store
+ is SQL NULL this should be NULL pointer */
+ ulint len) /* in: BLOB length; if the value to store
+ is SQL NULL this should be 0; remember
+ also to set the NULL bit in the MySQL record
+ header! */
{
+ ulint sum = 0;
+ ulint i;
+
+ /* MySQL might assume the field is set to zero except the length and
+ the pointer fields */
+
+ memset(dest, '\0', col_len);
+
/* In dest there are 1 - 4 bytes reserved for the BLOB length,
and after that 8 bytes reserved for the pointer to the data.
In 32-bit architectures we only use the first 4 bytes of the pointer
slot. */
+ ut_a(col_len - 8 > 1 || len < 256);
+ ut_a(col_len - 8 > 2 || len < 256 * 256);
+ ut_a(col_len - 8 > 3 || len < 256 * 256 * 256);
+
+ /* We try to track an elusive bug which probably was fixed
+ May 9, 2002, but better be sure: we probe the data buffer
+ to make sure it is in valid allocated memory */
+
+ for (i = 0; i < len; i++) {
+
+ sum += (ulint)(data + i);
+ }
+
+ /* The variable below is identically false, we just fool the
+ compiler to not optimize away our loop */
+ if (row_mysql_identically_false) {
+
+ printf("Sum %lu\n", sum);
+ }
+
mach_write_to_n_little_endian(dest, col_len - 8, len);
ut_memcpy(dest + col_len - 8, (byte*)&data, sizeof(byte*));
@@ -499,29 +535,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 = table->stat_modified_counter;
- 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
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 +743,7 @@ run_again:
prebuilt->table->stat_n_rows--;
}
- row_update_statistics_if_needed(prebuilt);
+ row_update_statistics_if_needed(prebuilt->table);
trx->op_info = (char *) "";
return((int) err);
@@ -746,6 +777,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 +835,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 +965,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 +975,66 @@ run_again:
srv_n_rows_updated++;
}
- row_update_statistics_if_needed(prebuilt);
+ row_update_statistics_if_needed(prebuilt->table);
trx->op_info = (char *) "";
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). */
@@ -1171,6 +1275,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);
@@ -1223,6 +1328,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 = (char *) "";
@@ -1270,6 +1377,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);
@@ -1300,6 +1408,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));
@@ -1355,6 +1464,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;
@@ -1379,6 +1489,7 @@ row_table_add_foreign_constraints(
}
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
return((int) err);
}
@@ -1473,7 +1584,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);
}
@@ -1722,6 +1834,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));
}
@@ -1734,9 +1849,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));
@@ -1771,6 +1883,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);
@@ -1798,10 +1926,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);
@@ -1837,6 +1964,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))) {
@@ -1878,6 +2006,7 @@ loop:
}
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
trx_commit_for_mysql(trx);
@@ -2014,6 +2143,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);
@@ -2095,6 +2225,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 a7462babb73..f3dced15fdf 100644
--- a/innobase/row/row0sel.c
+++ b/innobase/row/row0sel.c
@@ -2071,13 +2071,11 @@ row_sel_store_mysql_rec(
data = rec_get_nth_field(rec, templ->rec_field_no, &len);
if (rec_get_nth_field_extern_bit(rec, templ->rec_field_no)) {
+
/* Copy an externally stored field to the temporary
heap */
- if (prebuilt->trx->has_search_latch) {
- rw_lock_s_unlock(&btr_search_latch);
- prebuilt->trx->has_search_latch = FALSE;
- }
+ ut_a(!prebuilt->trx->has_search_latch);
extern_field_heap = mem_heap_create(UNIV_PAGE_SIZE);
@@ -2091,6 +2089,8 @@ row_sel_store_mysql_rec(
if (len != UNIV_SQL_NULL) {
if (templ->type == DATA_BLOB) {
+ ut_a(prebuilt->templ_contains_blob);
+
/* Copy the BLOB data to the BLOB
heap of prebuilt */
@@ -2116,8 +2116,28 @@ row_sel_store_mysql_rec(
extern_field_heap = NULL;
}
} else {
- mysql_rec[templ->mysql_null_byte_offset] |=
+ /* MySQL sometimes seems to copy the 'data'
+ pointed to by a BLOB field even if the field
+ has been marked to contain the SQL NULL value.
+ This caused seg faults reported by two users.
+ Set the BLOB length to 0 and the data pointer
+ to NULL to avoid a seg fault. */
+
+ if (templ->type == DATA_BLOB) {
+ row_sel_field_store_in_mysql_format(
+ mysql_rec + templ->mysql_col_offset,
+ templ->mysql_col_len, NULL,
+ 0, templ->type, templ->is_unsigned);
+ }
+
+ 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);
+ }
}
}
}
@@ -2363,6 +2383,7 @@ row_sel_push_cache_row_for_mysql(
ulint i;
ut_ad(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE);
+ ut_a(!prebuilt->templ_contains_blob);
if (prebuilt->fetch_cache[0] == NULL) {
/* Allocate memory for the fetch cache */
@@ -2403,6 +2424,7 @@ row_sel_try_search_shortcut_for_mysql(
rec_t* rec;
ut_ad(index->type & DICT_CLUSTERED);
+ ut_ad(!prebuilt->templ_contains_blob);
btr_pcur_open_with_no_init(index, search_tuple, PAGE_CUR_GE,
BTR_SEARCH_LEAF, pcur,
@@ -2585,8 +2607,16 @@ row_search_for_mysql(
mtr_start(&mtr);
- if (match_mode == ROW_SEL_EXACT && index->type & DICT_UNIQUE
+ /* Since we must release the search system latch when we retrieve an
+ externally stored field, we cannot use the adaptive hash index in a
+ search in the case the row may be long and there may be externally
+ stored fields */
+
+ if (match_mode == ROW_SEL_EXACT
+ && index->type & DICT_UNIQUE
&& index->type & DICT_CLUSTERED
+ && !prebuilt->templ_contains_blob
+ && (prebuilt->mysql_row_len < UNIV_PAGE_SIZE / 8)
&& dtuple_get_n_fields(search_tuple)
== dict_index_get_n_unique(index)) {
@@ -2939,15 +2969,18 @@ rec_loop:
/* We found a qualifying row */
if (prebuilt->n_rows_fetched >= MYSQL_FETCH_CACHE_THRESHOLD
- && !prebuilt->templ_contains_blob
&& prebuilt->select_lock_type == LOCK_NONE
+ && !prebuilt->templ_contains_blob
&& !prebuilt->clust_index_was_generated
&& prebuilt->template_type
!= ROW_MYSQL_DUMMY_TEMPLATE) {
/* Inside an update, for example, we do not cache rows,
since we may use the cursor position to do the actual
- update, that is why we require ...lock_type == LOCK_NONE */
+ update, that is why we require ...lock_type == LOCK_NONE.
+ Since we keep space in prebuilt only for the BLOBs of
+ a single row, we cannot cache rows in the case there
+ are BLOBs in the fields to be fetched. */
row_sel_push_cache_row_for_mysql(prebuilt, rec);
diff --git a/innobase/row/row0upd.c b/innobase/row/row0upd.c
index 710f650c0bd..1d5319a182b 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 a17646a8c0f..f366ce0d160 100644
--- a/innobase/srv/srv0srv.c
+++ b/innobase/srv/srv0srv.c
@@ -2026,15 +2026,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);
@@ -2070,10 +2073,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 3f40aa166ee..144ed263db9 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 607c80edd09..87b82cbee3a 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) {