summaryrefslogtreecommitdiff
path: root/innobase
diff options
context:
space:
mode:
authorunknown <heikki@hundin.mysql.fi>2003-04-16 16:45:01 +0300
committerunknown <heikki@hundin.mysql.fi>2003-04-16 16:45:01 +0300
commit33ac47279b7367c816c90642f1b4e530c05f248e (patch)
tree7fff68a7504903b0da22aa86bbf09e69351a2184 /innobase
parent07c29cc91aaba8a7982db9b7ba9c95ffc9ab7445 (diff)
downloadmariadb-git-33ac47279b7367c816c90642f1b4e530c05f248e.tar.gz
Many files:
Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/buf/buf0flu.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/dict/dict0crea.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/dict/dict0dict.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/dict/dict0load.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/ibuf/ibuf0ibuf.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/include/db0err.h: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/include/dict0dict.h: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/include/row0ins.h: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/lock/lock0lock.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/os/os0file.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/row/row0ins.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/row/row0mysql.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/row/row0sel.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/row/row0upd.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/srv/srv0srv.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/ut/ut0ut.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works sql/ha_innodb.cc: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works
Diffstat (limited to 'innobase')
-rw-r--r--innobase/buf/buf0flu.c12
-rw-r--r--innobase/dict/dict0crea.c8
-rw-r--r--innobase/dict/dict0dict.c674
-rw-r--r--innobase/dict/dict0load.c39
-rw-r--r--innobase/ibuf/ibuf0ibuf.c13
-rw-r--r--innobase/include/db0err.h2
-rw-r--r--innobase/include/dict0dict.h35
-rw-r--r--innobase/include/row0ins.h1
-rw-r--r--innobase/lock/lock0lock.c42
-rw-r--r--innobase/os/os0file.c22
-rw-r--r--innobase/row/row0ins.c160
-rw-r--r--innobase/row/row0mysql.c121
-rw-r--r--innobase/row/row0sel.c35
-rw-r--r--innobase/row/row0upd.c2
-rw-r--r--innobase/srv/srv0srv.c22
-rw-r--r--innobase/ut/ut0ut.c2
16 files changed, 985 insertions, 205 deletions
diff --git a/innobase/buf/buf0flu.c b/innobase/buf/buf0flu.c
index 78bde60c9b2..516056b5174 100644
--- a/innobase/buf/buf0flu.c
+++ b/innobase/buf/buf0flu.c
@@ -106,7 +106,7 @@ buf_flush_ready_for_replace(
BUF_BLOCK_FILE_PAGE and in the LRU list*/
{
ut_ad(mutex_own(&(buf_pool->mutex)));
- ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
+ ut_a(block->state == BUF_BLOCK_FILE_PAGE);
if ((ut_dulint_cmp(block->oldest_modification, ut_dulint_zero) > 0)
|| (block->buf_fix_count != 0)
@@ -227,7 +227,9 @@ buf_flush_buffered_writes(void)
}
for (i = 0; i < trx_doublewrite->first_free; i++) {
+
block = trx_doublewrite->buf_block_arr[i];
+ ut_a(block->state == BUF_BLOCK_FILE_PAGE);
if (block->check_index_page_at_flush
&& !page_simple_validate(block->frame)) {
@@ -236,10 +238,12 @@ buf_flush_buffered_writes(void)
ut_print_timestamp(stderr);
fprintf(stderr,
- " InnoDB: Apparent corruption of an index page\n"
+ " InnoDB: Apparent corruption of an index page n:o %lu in space %lu\n"
"InnoDB: to be written to data file. We intentionally crash server\n"
"InnoDB: to prevent corrupt data from ending up in data\n"
- "InnoDB: files.\n");
+ "InnoDB: files.\n",
+ block->offset, block->space);
+
ut_a(0);
}
}
@@ -432,6 +436,8 @@ buf_flush_try_page(
block = buf_page_hash_get(space, offset);
+ ut_a(block->state == BUF_BLOCK_FILE_PAGE);
+
if (flush_type == BUF_FLUSH_LIST
&& block && buf_flush_ready_for_flush(block, flush_type)) {
diff --git a/innobase/dict/dict0crea.c b/innobase/dict/dict0crea.c
index b0f84e5663a..3619ac02f4d 100644
--- a/innobase/dict/dict0crea.c
+++ b/innobase/dict/dict0crea.c
@@ -1173,6 +1173,7 @@ dict_create_add_foreigns_to_dictionary(
if (NULL == dict_table_get_low((char *) "SYS_FOREIGN")) {
fprintf(stderr,
"InnoDB: table SYS_FOREIGN not found from internal data dictionary\n");
+
return(DB_ERROR);
}
@@ -1259,6 +1260,13 @@ loop:
"InnoDB: at http://www.innodb.com/ibman.html\n");
}
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Internal error in foreign key constraint creation for table %.500s.\n"
+"See the MySQL .err log in the datadir for more information.\n", table->name);
+ mutex_exit(&dict_foreign_err_mutex);
+
return(error);
}
diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c
index c70e848c5c8..74fe5cd5b70 100644
--- a/innobase/dict/dict0dict.c
+++ b/innobase/dict/dict0dict.c
@@ -185,6 +185,12 @@ dict_foreign_free(
/*==============*/
dict_foreign_t* foreign); /* in, own: foreign key struct */
+/* Buffer for storing detailed information about the latest foreig key
+error */
+char* dict_foreign_err_buf = NULL;
+mutex_t dict_foreign_err_mutex; /* mutex protecting the buffer */
+
+
/************************************************************************
Checks if the database name in two table names is the same. */
static
@@ -573,6 +579,11 @@ dict_init(void)
rw_lock_create(&dict_operation_lock);
rw_lock_set_level(&dict_operation_lock, SYNC_DICT_OPERATION);
+
+ dict_foreign_err_buf = mem_alloc(DICT_FOREIGN_ERR_BUF_LEN);
+ dict_foreign_err_buf[0] = '\0';
+ mutex_create(&dict_foreign_err_mutex);
+ mutex_set_level(&dict_foreign_err_mutex, SYNC_ANY_LATCH);
}
/**************************************************************************
@@ -1818,6 +1829,7 @@ dict_foreign_add_to_cache(
dict_foreign_t* for_in_cache = NULL;
dict_index_t* index;
ibool added_to_referenced_list = FALSE;
+ char* buf = dict_foreign_err_buf;
ut_ad(mutex_own(&(dict_sys->mutex)));
@@ -1850,9 +1862,29 @@ dict_foreign_add_to_cache(
for_in_cache->foreign_index);
if (index == NULL) {
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Error in foreign key constraint of table %.500s:\n"
+"there is no index in referenced table which would contain\n"
+"the columns as the first columns, or the data types in the\n"
+"referenced table do not match to the ones in table. Constraint:\n",
+ for_in_cache->foreign_table_name);
+ dict_print_info_on_foreign_key_in_create_format(
+ for_in_cache, buf + strlen(buf));
+ if (for_in_cache->foreign_index) {
+ sprintf(buf + strlen(buf),
+"\nThe index in the foreign key in table is %.500s\n"
+"See http://www.innodb.com/ibman.html about correct foreign key definition.\n",
+ for_in_cache->foreign_index->name);
+ }
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
if (for_in_cache == foreign) {
mem_heap_free(foreign->heap);
}
+
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -1871,6 +1903,25 @@ dict_foreign_add_to_cache(
for_in_cache->referenced_index);
if (index == NULL) {
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Error in foreign key constraint of table %.500s:\n"
+"there is no index in the table which would contain\n"
+"the columns as the first columns, or the data types in the\n"
+"table do not match to the ones in the referenced table. Constraint:\n",
+ for_in_cache->foreign_table_name);
+ dict_print_info_on_foreign_key_in_create_format(
+ for_in_cache, buf + strlen(buf));
+ if (for_in_cache->foreign_index) {
+ sprintf(buf + strlen(buf),
+"\nIndex of the foreign key in the referenced table is %.500s\n"
+"See http://www.innodb.com/ibman.html about correct foreign key definition.\n",
+ for_in_cache->referenced_index->name);
+ }
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
if (for_in_cache == foreign) {
if (added_to_referenced_list) {
UT_LIST_REMOVE(referenced_list,
@@ -2038,7 +2089,7 @@ dict_scan_col(
if (*ptr == '`') {
ptr++;
}
-
+
return(ptr);
}
@@ -2141,18 +2192,21 @@ dict_scan_table_name(
}
/*************************************************************************
-Skips one 'word', like an id. For the lexical definition of 'word', see the
-code below. */
+Scans an id. For the lexical definition of an 'id', see the code below.
+Strips backquotes from around the id. */
static
char*
-dict_skip_word(
-/*===========*/
+dict_scan_id(
+/*=========*/
/* out: scanned to */
char* ptr, /* in: scanned to */
- ibool* success)/* out: TRUE if success, FALSE if just spaces left in
- string */
+ char** start, /* out: start of the id; NULL if no id was
+ scannable */
+ ulint* len) /* out: length of the id */
{
- *success = FALSE;
+ ibool scanned_backquote = FALSE;
+
+ *start = NULL;
while (isspace(*ptr)) {
ptr++;
@@ -2164,21 +2218,59 @@ dict_skip_word(
}
if (*ptr == '`') {
+ scanned_backquote = TRUE;
ptr++;
}
- while (!isspace(*ptr) && *ptr != ',' && *ptr != '(' && *ptr != '`'
- && *ptr != '\0') {
+ *start = ptr;
+
+ while (!isspace(*ptr) && *ptr != ',' && *ptr != '(' && *ptr != ')'
+ && *ptr != '\0' && *ptr != '`') {
ptr++;
}
- *success = TRUE;
+ *len = (ulint) (ptr - *start);
+
+ if (scanned_backquote) {
+ if (*ptr == '`') {
+ ptr++;
+ } else {
+ /* Syntax error */
+ *start = NULL;
+ }
+ }
return(ptr);
}
/*************************************************************************
+Skips one id. */
+static
+char*
+dict_skip_word(
+/*===========*/
+ /* out: scanned to */
+ char* ptr, /* in: scanned to */
+ ibool* success)/* out: TRUE if success, FALSE if just spaces left in
+ string or a syntax error */
+{
+ char* start;
+ ulint len;
+
+ *success = FALSE;
+
+ ptr = dict_scan_id(ptr, &start, &len);
+
+ if (start) {
+ *success = TRUE;
+ }
+
+ return(ptr);
+}
+
+#ifdef currentlynotused
+/*************************************************************************
Returns the number of opening brackets '(' subtracted by the number
of closing brackets ')' between string and ptr. */
static
@@ -2204,6 +2296,106 @@ dict_bracket_count(
return(count);
}
+#endif
+
+/*************************************************************************
+Removes MySQL comments from an SQL string. A comment is either
+(a) '#' to the end of the line,
+(b) '--<space>' to the end of the line, or
+(c) '<slash><asterisk>' till the next '<asterisk><slash>' (like the familiar
+C comment syntax). */
+static
+char*
+dict_strip_comments(
+/*================*/
+ /* out, own: SQL string stripped from
+ comments; the caller must free this
+ with mem_free()! */
+ char* sql_string) /* in: SQL string */
+{
+ char* str;
+ char* sptr;
+ char* ptr;
+
+ str = mem_alloc(strlen(sql_string) + 1);
+
+ sptr = sql_string;
+ ptr = str;
+
+ for (;;) {
+ if (*sptr == '\0') {
+ *ptr = '\0';
+
+ return(str);
+ }
+
+ if (*sptr == '#'
+ || (strlen(sptr) >= 3 && 0 == memcmp("-- ", sptr, 3))) {
+ for (;;) {
+ /* In Unix a newline is 0x0D while in Windows
+ it is 0x0A followed by 0x0D */
+
+ if (*sptr == (char)0x0A
+ || *sptr == (char)0x0D
+ || *sptr == '\0') {
+
+ break;
+ }
+
+ sptr++;
+ }
+ }
+
+ if (strlen(sptr) >= 2 && *sptr == '/' && *(sptr + 1) == '*') {
+ for (;;) {
+ if (strlen(sptr) >= 2
+ && *sptr == '*' && *(sptr + 1) == '/') {
+
+ sptr += 2;
+
+ break;
+ }
+
+ if (*sptr == '\0') {
+
+ break;
+ }
+
+ sptr++;
+ }
+ }
+
+ *ptr = *sptr;
+
+ ptr++;
+ sptr++;
+ }
+}
+
+/*************************************************************************
+Reports a simple foreign key create clause syntax error. */
+static
+void
+dict_foreign_report_syntax_err(
+/*===========================*/
+ char* name, /* in: table name */
+ char* start_of_latest_foreign,/* in: start of the foreign key clause
+ in the SQL string */
+ char* ptr) /* in: place of the syntax error */
+{
+ char* buf = dict_foreign_err_buf;
+
+ mutex_enter(&dict_foreign_err_mutex);
+
+ ut_sprintf_timestamp(buf);
+
+ sprintf(buf + strlen(buf),
+" Error in foreign key constraint of table %.500s,\n%.500s.\n"
+"Syntax error close to:\n%.500s\n", name, start_of_latest_foreign, ptr);
+
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+}
/*************************************************************************
Scans a table create SQL string and adds to the data dictionary the foreign
@@ -2211,10 +2403,10 @@ key constraints declared in the string. This function should be called after
the indexes for a table have been created. Each foreign key constraint must
be accompanied with indexes in both participating tables. The indexes are
allowed to contain more fields than mentioned in the constraint. */
-
+static
ulint
-dict_create_foreign_constraints(
-/*============================*/
+dict_create_foreign_constraints_low(
+/*================================*/
/* out: error code or DB_SUCCESS */
trx_t* trx, /* in: transaction */
char* sql_string, /* in: table create or ALTER TABLE
@@ -2230,7 +2422,9 @@ dict_create_foreign_constraints(
dict_table_t* referenced_table;
dict_index_t* index;
dict_foreign_t* foreign;
- char* ptr = sql_string;
+ char* ptr = sql_string;
+ char* start_of_latest_foreign = sql_string;
+ char* buf = dict_foreign_err_buf;
ibool success;
ulint error;
ulint i;
@@ -2248,6 +2442,15 @@ dict_create_foreign_constraints(
table = dict_table_get_low(name);
if (table == NULL) {
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Error in foreign key constraint of table %.500s.\n"
+"Cannot find the table from the internal data dictionary of InnoDB.\n"
+"Create table statement:\n%.2000\n", name, sql_string);
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
return(DB_ERROR);
}
loop:
@@ -2263,6 +2466,8 @@ loop:
return(error);
}
+ start_of_latest_foreign = ptr;
+
ptr = dict_accept(ptr, (char *) "FOREIGN", &success);
if (!isspace(*ptr)) {
@@ -2283,13 +2488,19 @@ loop:
ptr = dict_skip_word(ptr, &success);
if (!success) {
+ dict_foreign_report_syntax_err(name,
+ start_of_latest_foreign, ptr);
+
return(DB_CANNOT_ADD_CONSTRAINT);
}
ptr = dict_accept(ptr, (char *) "(", &success);
if (!success) {
- return(DB_CANNOT_ADD_CONSTRAINT);
+ /* We do not flag a syntax error here because in an
+ ALTER TABLE we may also have DROP FOREIGN KEY abc */
+
+ goto loop;
}
}
@@ -2300,6 +2511,15 @@ col_loop1:
ptr = dict_scan_col(ptr, &success, table, columns + i,
column_names + i, column_name_lens + i);
if (!success) {
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Error in foreign key constraint of table %.500s,\n%.500s.\n"
+"Cannot resolve column name close to:\n%.500s\n", name,
+ start_of_latest_foreign, ptr);
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2314,6 +2534,8 @@ col_loop1:
ptr = dict_accept(ptr, (char *) ")", &success);
if (!success) {
+ dict_foreign_report_syntax_err(name, start_of_latest_foreign,
+ ptr);
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2323,12 +2545,24 @@ col_loop1:
index = dict_foreign_find_index(table, column_names, i, NULL);
if (!index) {
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Error in foreign key constraint of table %.500s:\n"
+"There is no index in the table %.500s where the columns appear\n"
+"as the first columns. Constraint:\n%.500s\n"
+"See http://www.innodb.com/ibman.html for correct foreign key definition.\n",
+ name, name, start_of_latest_foreign);
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
return(DB_CANNOT_ADD_CONSTRAINT);
}
-
ptr = dict_accept(ptr, (char *) "REFERENCES", &success);
if (!success || !isspace(*ptr)) {
+ dict_foreign_report_syntax_err(name, start_of_latest_foreign,
+ ptr);
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2358,6 +2592,15 @@ col_loop1:
if (!success || (!referenced_table && trx->check_foreigns)) {
dict_foreign_free(foreign);
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Error in foreign key constraint of table %.500s,\n%.500s.\n"
+"Cannot resolve table name close to:\n"
+"%.500s\n", name, start_of_latest_foreign, ptr);
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2365,7 +2608,8 @@ col_loop1:
if (!success) {
dict_foreign_free(foreign);
-
+ dict_foreign_report_syntax_err(name, start_of_latest_foreign,
+ ptr);
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2380,6 +2624,15 @@ col_loop2:
if (!success) {
dict_foreign_free(foreign);
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Error in foreign key constraint of table %.500s,\n%.500s\n"
+"Cannot resolve column name close to:\n"
+"%.500s\n", name, start_of_latest_foreign, ptr);
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2394,6 +2647,8 @@ col_loop2:
if (!success || foreign->n_fields != i) {
dict_foreign_free(foreign);
+ dict_foreign_report_syntax_err(name, start_of_latest_foreign,
+ ptr);
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2416,9 +2671,10 @@ scan_on_conditions:
ptr = dict_accept(ptr, "UPDATE", &success);
if (!success) {
-
dict_foreign_free(foreign);
+ dict_foreign_report_syntax_err(name,
+ start_of_latest_foreign, ptr);
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2454,6 +2710,8 @@ scan_on_conditions:
if (!success) {
dict_foreign_free(foreign);
+ dict_foreign_report_syntax_err(name,
+ start_of_latest_foreign, ptr);
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2471,7 +2729,8 @@ scan_on_conditions:
if (!success) {
dict_foreign_free(foreign);
-
+ dict_foreign_report_syntax_err(name, start_of_latest_foreign,
+ ptr);
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2479,7 +2738,8 @@ scan_on_conditions:
if (!success) {
dict_foreign_free(foreign);
-
+ dict_foreign_report_syntax_err(name, start_of_latest_foreign,
+ ptr);
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2493,6 +2753,15 @@ scan_on_conditions:
dict_foreign_free(foreign);
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Error in foreign key constraint of table %.500s,\n%.500s.\n"
+"You have defined a SET NULL condition though some of the\n"
+"columns is defined as NOT NULL.\n", name, start_of_latest_foreign);
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
return(DB_CANNOT_ADD_CONSTRAINT);
}
}
@@ -2511,6 +2780,15 @@ try_find_index:
dict_foreign_free(foreign);
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Error in foreign key constraint of table %.500s,\n%.500s.\n"
+"You have twice an ON DELETE clause or twice an ON UPDATE clause.\n",
+ name, start_of_latest_foreign);
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
return(DB_CANNOT_ADD_CONSTRAINT);
}
@@ -2524,6 +2802,18 @@ try_find_index:
foreign->foreign_index);
if (!index) {
dict_foreign_free(foreign);
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Error in foreign key constraint of table %.500s:\n"
+"Cannot find an index in the referenced table where the\n"
+"referenced columns appear as the first columns, or column types\n"
+"in the table and the referenced table do not match for constraint:\n%.500s\n"
+"See http://www.innodb.com/ibman.html for correct foreign key definition.\n",
+ name, start_of_latest_foreign);
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
return(DB_CANNOT_ADD_CONSTRAINT);
}
} else {
@@ -2564,6 +2854,165 @@ try_find_index:
goto loop;
}
+/*************************************************************************
+Scans a table create SQL string and adds to the data dictionary the foreign
+key constraints declared in the string. This function should be called after
+the indexes for a table have been created. Each foreign key constraint must
+be accompanied with indexes in both participating tables. The indexes are
+allowed to contain more fields than mentioned in the constraint. */
+
+ulint
+dict_create_foreign_constraints(
+/*============================*/
+ /* out: error code or DB_SUCCESS */
+ trx_t* trx, /* in: transaction */
+ char* sql_string, /* in: table create or ALTER TABLE
+ statement where foreign keys are declared like:
+ FOREIGN KEY (a, b) REFERENCES table2(c, d),
+ table2 can be written also with the database
+ name before it: test.table2; the default
+ database id the database of parameter name */
+ char* name) /* in: table full name in the normalized form
+ database_name/table_name */
+{
+ char* str;
+ ulint err;
+
+ str = dict_strip_comments(sql_string);
+
+ err = dict_create_foreign_constraints_low(trx, str, name);
+
+ mem_free(str);
+
+ return(err);
+}
+
+/**************************************************************************
+Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. */
+
+ulint
+dict_foreign_parse_drop_constraints(
+/*================================*/
+ /* out: DB_SUCCESS or
+ DB_CANNOT_DROP_CONSTRAINT if
+ syntax error or the constraint
+ id does not match */
+ mem_heap_t* heap, /* in: heap from which we can
+ allocate memory */
+ trx_t* trx, /* in: transaction */
+ dict_table_t* table, /* in: table */
+ ulint* n, /* out: number of constraints
+ to drop */
+ char*** constraints_to_drop) /* out: id's of the
+ constraints to drop */
+{
+ dict_foreign_t* foreign;
+ ibool success;
+ char* str;
+ char* ptr;
+ char* buf = dict_foreign_err_buf;
+ char* start;
+ char* id;
+ ulint len;
+
+ *n = 0;
+
+ *constraints_to_drop = mem_heap_alloc(heap, 1000 * sizeof(char*));
+
+ str = dict_strip_comments(*(trx->mysql_query_str));
+ ptr = str;
+
+ ut_ad(mutex_own(&(dict_sys->mutex)));
+loop:
+ ptr = dict_scan_to(ptr, (char *) "DROP");
+
+ if (*ptr == '\0') {
+ ut_a(*n < 1000);
+
+ mem_free(str);
+
+ return(DB_SUCCESS);
+ }
+
+ ptr = dict_accept(ptr, (char *) "DROP", &success);
+
+ if (!isspace(*ptr)) {
+
+ goto loop;
+ }
+
+ ptr = dict_accept(ptr, (char *) "FOREIGN", &success);
+
+ if (!success) {
+
+ goto loop;
+ }
+
+ ptr = dict_accept(ptr, (char *) "KEY", &success);
+
+ if (!success) {
+
+ goto syntax_error;
+ }
+
+ ptr = dict_scan_id(ptr, &start, &len);
+
+ if (start == NULL) {
+
+ goto syntax_error;
+ }
+
+ id = mem_heap_alloc(heap, len + 1);
+ ut_memcpy(id, start, len);
+ id[len] = '\0';
+ (*constraints_to_drop)[*n] = id;
+ (*n)++;
+
+ /* Look for the given constraint id */
+
+ foreign = UT_LIST_GET_FIRST(table->foreign_list);
+
+ while (foreign != NULL) {
+ if (0 == ut_strcmp(foreign->id, id)) {
+
+ /* Found */
+ break;
+ }
+
+ foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
+ }
+
+ if (foreign == NULL) {
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Error in dropping of a foreign key constraint of table %.500s,\n"
+"just before:\n%s\n in SQL command\n%s\nCannot find a constraint with the\n"
+"given id %s.\n", table->name, ptr, str, id);
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
+ mem_free(str);
+
+ return(DB_CANNOT_DROP_CONSTRAINT);
+ }
+
+ goto loop;
+
+syntax_error:
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf),
+" Syntax error in dropping of a foreign key constraint of table %.500s,\n"
+"close to:\n%s\n in SQL command\n%s\n", table->name, ptr, str);
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
+ mem_free(str);
+
+ return(DB_CANNOT_DROP_CONSTRAINT);
+}
+
/*==================== END OF FOREIGN KEY PROCESSING ====================*/
/**************************************************************************
@@ -3285,7 +3734,6 @@ dict_index_print_low(
n_vals = index->stat_n_diff_key_vals[1];
}
-
printf(
" INDEX: name %s, table name %s, id %lu %lu, fields %lu/%lu, type %lu\n",
index->name, index->table_name,
@@ -3327,6 +3775,99 @@ dict_field_print_low(
}
/**************************************************************************
+Sprintfs to a string info on a foreign key of a table in a format suitable
+for CREATE TABLE. */
+
+char*
+dict_print_info_on_foreign_key_in_create_format(
+/*============================================*/
+ /* out: how far in buf we printed */
+ dict_foreign_t* foreign,/* in: foreign key constraint */
+ char* buf) /* in: buffer of at least 5000 bytes */
+{
+ char* buf2 = buf;
+ ulint i;
+
+ buf2 += sprintf(buf2, ",\n CONSTRAINT `%s` FOREIGN KEY (",
+ foreign->id);
+ for (i = 0; i < foreign->n_fields; i++) {
+ if ((ulint)(buf2 - buf) >= 4000) {
+
+ goto no_space;
+ }
+ buf2 += sprintf(buf2, "`%.250s`",
+ foreign->foreign_col_names[i]);
+
+ if (i + 1 < foreign->n_fields) {
+ buf2 += sprintf(buf2, ", ");
+ }
+ }
+
+ if (dict_tables_have_same_db(foreign->foreign_table_name,
+ foreign->referenced_table_name)) {
+ /* Do not print the database name of the referenced
+ table */
+ buf2 += sprintf(buf2, ") REFERENCES `%.500s` (",
+ dict_remove_db_name(
+ foreign->referenced_table_name));
+ } else {
+ buf2 += sprintf(buf2, ") REFERENCES `%.500s` (",
+ foreign->referenced_table_name);
+ /* Change the '/' in the table name to '.' */
+
+ for (i = ut_strlen(buf); i > 0; i--) {
+ if (buf[i] == '/') {
+
+ buf[i] = '.';
+
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < foreign->n_fields; i++) {
+ if ((ulint)(buf2 - buf) >= 4000) {
+
+ goto no_space;
+ }
+ buf2 += sprintf(buf2, "`%.250s`",
+ foreign->referenced_col_names[i]);
+ if (i + 1 < foreign->n_fields) {
+ buf2 += sprintf(buf2, ", ");
+ }
+ }
+
+ buf2 += sprintf(buf2, ")");
+
+ if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) {
+ buf2 += sprintf(buf2, " ON DELETE CASCADE");
+ }
+
+ if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) {
+ buf2 += sprintf(buf2, " ON DELETE SET NULL");
+ }
+
+ if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
+ buf2 += sprintf(buf2, " ON DELETE NO ACTION");
+ }
+
+ if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) {
+ buf2 += sprintf(buf2, " ON UPDATE CASCADE");
+ }
+
+ if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) {
+ buf2 += sprintf(buf2, " ON UPDATE SET NULL");
+ }
+
+ if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
+ buf2 += sprintf(buf2, " ON UPDATE NO ACTION");
+ }
+
+no_space:
+ return(buf2);
+}
+
+/**************************************************************************
Sprintfs to a string info on foreign keys of a table in a format suitable
for CREATE TABLE. */
static
@@ -3335,13 +3876,12 @@ dict_print_info_on_foreign_keys_in_create_format(
/*=============================================*/
char* buf, /* in: auxiliary buffer */
char* str, /* in/out: pointer to a string */
- ulint len, /* in: str has to be a buffer at least
- len + 5000 bytes */
+ ulint len, /* in: buf has to be a buffer of at least
+ len + 5000 bytes; str must have at least
+ len + 1 bytes */
dict_table_t* table) /* in: table */
{
-
dict_foreign_t* foreign;
- ulint i;
char* buf2;
buf2 = buf;
@@ -3357,78 +3897,12 @@ dict_print_info_on_foreign_keys_in_create_format(
}
while (foreign != NULL) {
- buf2 += sprintf(buf2, ",\n FOREIGN KEY (");
-
- for (i = 0; i < foreign->n_fields; i++) {
- if ((ulint)(buf2 - buf) >= len) {
- goto no_space;
- }
- buf2 += sprintf(buf2, "`%s`",
- foreign->foreign_col_names[i]);
-
- if (i + 1 < foreign->n_fields) {
- buf2 += sprintf(buf2, ", ");
- }
- }
-
- if (dict_tables_have_same_db(table->name,
- foreign->referenced_table_name)) {
- /* Do not print the database name of the referenced
- table */
- buf2 += sprintf(buf2, ") REFERENCES `%s` (",
- dict_remove_db_name(
- foreign->referenced_table_name));
- } else {
- buf2 += sprintf(buf2, ") REFERENCES `%s` (",
- foreign->referenced_table_name);
- /* Change the '/' in the table name to '.' */
-
- for (i = ut_strlen(buf); i > 0; i--) {
- if (buf[i] == '/') {
+ if ((ulint)(buf2 - buf) >= len) {
+ goto no_space;
+ }
- buf[i] = '.';
-
- break;
- }
- }
- }
-
- for (i = 0; i < foreign->n_fields; i++) {
- if ((ulint)(buf2 - buf) >= len) {
- goto no_space;
- }
- buf2 += sprintf(buf2, "`%s`",
- foreign->referenced_col_names[i]);
- if (i + 1 < foreign->n_fields) {
- buf2 += sprintf(buf2, ", ");
- }
- }
-
- buf2 += sprintf(buf2, ")");
-
- if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) {
- buf2 += sprintf(buf2, " ON DELETE CASCADE");
- }
-
- if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) {
- buf2 += sprintf(buf2, " ON DELETE SET NULL");
- }
-
- if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
- buf2 += sprintf(buf2, " ON DELETE NO ACTION");
- }
-
- if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) {
- buf2 += sprintf(buf2, " ON UPDATE CASCADE");
- }
-
- if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) {
- buf2 += sprintf(buf2, " ON UPDATE SET NULL");
- }
-
- if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
- buf2 += sprintf(buf2, " ON UPDATE NO ACTION");
- }
+ buf2 = dict_print_info_on_foreign_key_in_create_format(
+ foreign, buf2);
foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
}
@@ -3489,7 +3963,7 @@ dict_print_info_on_foreign_keys(
goto no_space;
}
- buf2 += sprintf(buf2, "%s",
+ buf2 += sprintf(buf2, "%.500s",
foreign->foreign_col_names[i]);
if (i + 1 < foreign->n_fields) {
@@ -3497,14 +3971,14 @@ dict_print_info_on_foreign_keys(
}
}
- buf2 += sprintf(buf2, ") REFER %s(",
+ buf2 += sprintf(buf2, ") REFER %.500s(",
foreign->referenced_table_name);
for (i = 0; i < foreign->n_fields; i++) {
if ((ulint)(buf2 - buf) >= len) {
goto no_space;
}
- buf2 += sprintf(buf2, "%s",
+ buf2 += sprintf(buf2, "%.500s",
foreign->referenced_col_names[i]);
if (i + 1 < foreign->n_fields) {
buf2 += sprintf(buf2, " ");
diff --git a/innobase/dict/dict0load.c b/innobase/dict/dict0load.c
index d8d426d2036..8f39605e493 100644
--- a/innobase/dict/dict0load.c
+++ b/innobase/dict/dict0load.c
@@ -456,7 +456,7 @@ dict_load_indexes(
ut_ad(len == 8);
id = mach_read_from_8(field);
- ut_a(0 == ut_strcmp("NAME",
+ ut_a(0 == ut_strcmp((char*)"NAME",
dict_field_get_col(
dict_index_get_nth_field(
dict_table_get_first_index(sys_indexes), 4))->name));
@@ -515,7 +515,7 @@ dict_load_indexes(
&& ((type & DICT_CLUSTERED)
|| ((table == dict_sys->sys_tables)
&& (name_len == ut_strlen("ID_IND"))
- && (0 == ut_memcmp(name_buf, "ID_IND",
+ && (0 == ut_memcmp(name_buf, (char*)"ID_IND",
name_len))))) {
/* The index was created in memory already in
@@ -566,6 +566,7 @@ dict_load_table(
char* buf;
ulint space;
ulint n_cols;
+ ulint err;
mtr_t mtr;
ut_ad(mutex_own(&(dict_sys->mutex)));
@@ -674,8 +675,25 @@ dict_load_table(
dict_load_indexes(table, heap);
- ut_a(DB_SUCCESS == dict_load_foreigns(table->name));
+ err = dict_load_foreigns(table->name);
+/*
+ if (err != DB_SUCCESS) {
+
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_print_timestamp(stderr);
+
+ fprintf(stderr,
+" InnoDB: Error: could not make a foreign key definition to match\n"
+"InnoDB: the foreign key table or the referenced table!\n"
+"InnoDB: The data dictionary of InnoDB is corrupt. You may need to drop\n"
+"InnoDB: and recreate the foreign key table or the referenced table.\n"
+"InnoDB: Send a detailed bug report to mysql@lists.mysql.com\n"
+"InnoDB: Latest foreign key error printout:\n%s\n", dict_foreign_err_buf);
+
+ mutex_exit(&dict_foreign_err_mutex);
+ }
+*/
mem_heap_free(heap);
return(table);
@@ -978,8 +996,8 @@ dict_load_foreign(
field = rec_get_nth_field(rec, 4, &len);
- foreign->referenced_table_name = mem_heap_alloc(foreign->heap, 1 + len);
-
+ foreign->referenced_table_name = mem_heap_alloc(foreign->heap,
+ 1 + len);
ut_memcpy(foreign->referenced_table_name, field, len);
foreign->referenced_table_name[len] = '\0';
@@ -988,10 +1006,19 @@ dict_load_foreign(
dict_load_foreign_cols(id, foreign);
+ /* If the foreign table is not yet in the dictionary cache, we
+ have to load it so that we are able to make type comparisons
+ in the next function call. */
+
+ dict_table_get_low(foreign->foreign_table_name);
+
/* Note that there may already be a foreign constraint object in
the dictionary cache for this constraint: then the following
call only sets the pointers in it to point to the appropriate table
- and index objects and frees the newly created object foreign. */
+ and index objects and frees the newly created object foreign.
+ Adding to the cache should always succeed since we are not creating
+ a new foreign key constraint but loading one from the data
+ dictionary. */
err = dict_foreign_add_to_cache(foreign);
diff --git a/innobase/ibuf/ibuf0ibuf.c b/innobase/ibuf/ibuf0ibuf.c
index 5cd066afc27..187afa17047 100644
--- a/innobase/ibuf/ibuf0ibuf.c
+++ b/innobase/ibuf/ibuf0ibuf.c
@@ -2391,7 +2391,7 @@ ibuf_delete_rec(
ut_ad(ibuf_inside());
- success = btr_cur_optimistic_delete(btr_pcur_get_btr_cur(pcur), mtr);
+ success = btr_cur_optimistic_delete(btr_pcur_get_btr_cur(pcur), mtr);
if (success) {
#ifdef UNIV_IBUF_DEBUG
@@ -2401,7 +2401,7 @@ ibuf_delete_rec(
return(FALSE);
}
- /* We have to resort to a pessimistic delete from ibuf */
+ /* We have to resort to a pessimistic delete from ibuf */
btr_pcur_store_position(pcur, mtr);
btr_pcur_commit_specify_mtr(pcur, mtr);
@@ -2420,17 +2420,22 @@ ibuf_delete_rec(
fprintf(stderr, "InnoDB: ibuf cursor restoration fails!\n");
fprintf(stderr, "InnoDB: ibuf record inserted to page %lu\n",
page_no);
+ fflush(stderr);
+
rec_print(btr_pcur_get_rec(pcur));
rec_print(pcur->old_rec);
dtuple_print(search_tuple);
rec_print(page_rec_get_next(btr_pcur_get_rec(pcur)));
+ fflush(stdout);
mtr_commit(mtr);
fprintf(stderr, "InnoDB: Validating insert buffer tree:\n");
- ut_a(btr_validate_tree(ibuf_data->index->tree));
- fprintf(stderr, "InnoDB: Ibuf tree ok\n");
+ ut_a(btr_validate_tree(ibuf_data->index->tree));
+
+ fprintf(stderr, "InnoDB: ibuf tree ok\n");
+ fflush(stderr);
}
ut_a(success);
diff --git a/innobase/include/db0err.h b/innobase/include/db0err.h
index c67c09bad27..ab7d0caa35c 100644
--- a/innobase/include/db0err.h
+++ b/innobase/include/db0err.h
@@ -44,6 +44,8 @@ Created 5/24/1996 Heikki Tuuri
#define DB_CORRUPTION 39 /* data structure corruption noticed */
#define DB_COL_APPEARS_TWICE_IN_INDEX 40 /* InnoDB cannot handle an index
where same column appears twice */
+#define DB_CANNOT_DROP_CONSTRAINT 40 /* dropping a foreign key constraint
+ from a table failed */
/* The following are partial failure codes */
#define DB_FAIL 1000
diff --git a/innobase/include/dict0dict.h b/innobase/include/dict0dict.h
index b5e6e04a1de..600965700ed 100644
--- a/innobase/include/dict0dict.h
+++ b/innobase/include/dict0dict.h
@@ -219,6 +219,24 @@ dict_create_foreign_constraints(
char* name); /* in: table full name in the normalized form
database_name/table_name */
/**************************************************************************
+Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. */
+
+ulint
+dict_foreign_parse_drop_constraints(
+/*================================*/
+ /* out: DB_SUCCESS or
+ DB_CANNOT_DROP_CONSTRAINT if
+ syntax error or the constraint
+ id does not match */
+ mem_heap_t* heap, /* in: heap from which we can
+ allocate memory */
+ trx_t* trx, /* in: transaction */
+ dict_table_t* table, /* in: table */
+ ulint* n, /* out: number of constraints
+ to drop */
+ char*** constraints_to_drop); /* out: id's of the
+ constraints to drop */
+/**************************************************************************
Returns a table object and memoryfixes it. NOTE! This is a high-level
function to be used mainly from outside the 'dict' directory. Inside this
directory dict_table_get_low is usually the appropriate function. */
@@ -333,6 +351,16 @@ dict_print_info_on_foreign_keys(
char* str, /* in/out: pointer to a string */
ulint len, /* in: space in str available for info */
dict_table_t* table); /* in: table */
+/**************************************************************************
+Sprintfs to a string info on a foreign key of a table in a format suitable
+for CREATE TABLE. */
+
+char*
+dict_print_info_on_foreign_key_in_create_format(
+/*============================================*/
+ /* out: how far in buf we printed */
+ dict_foreign_t* foreign,/* in: foreign key constraint */
+ char* buf); /* in: buffer of at least 5000 bytes */
/************************************************************************
Gets the first index on the table (the clustered index). */
UNIV_INLINE
@@ -808,6 +836,13 @@ void
dict_mutex_exit_for_mysql(void);
/*===========================*/
+/* The following len must be at least 10000 bytes! */
+#define DICT_FOREIGN_ERR_BUF_LEN 10000
+
+/* Buffer for storing detailed information about the latest foreig key
+error */
+extern char* dict_foreign_err_buf;
+extern mutex_t dict_foreign_err_mutex; /* mutex protecting the buffer */
extern dict_sys_t* dict_sys; /* the dictionary system */
extern rw_lock_t dict_operation_lock;
diff --git a/innobase/include/row0ins.h b/innobase/include/row0ins.h
index cc3b9fa7e9a..a5b4b74e7fc 100644
--- a/innobase/include/row0ins.h
+++ b/innobase/include/row0ins.h
@@ -35,7 +35,6 @@ row_ins_check_foreign_constraint(
dictionary cache if they exist at all */
dict_table_t* table, /* in: if check_ref is TRUE, then the foreign
table, else the referenced table */
- dict_index_t* index, /* in: index in table */
dtuple_t* entry, /* in: index entry for index */
que_thr_t* thr); /* in: query thread */
/*************************************************************************
diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c
index 7b08d6b89b8..4bb1d243ed4 100644
--- a/innobase/lock/lock0lock.c
+++ b/innobase/lock/lock0lock.c
@@ -3092,8 +3092,7 @@ lock_deadlock_recursive(
err_buf += strlen(err_buf);
err_buf += sprintf(err_buf,
- " LATEST DETECTED DEADLOCK:\n"
- "*** (1) TRANSACTION:\n");
+ "\n*** (1) TRANSACTION:\n");
trx_print(err_buf, wait_lock->trx);
err_buf += strlen(err_buf);
@@ -3934,24 +3933,15 @@ lock_print_info(
return;
}
-
- buf += sprintf(buf, "Trx id counter %lu %lu\n",
- ut_dulint_get_high(trx_sys->max_trx_id),
- ut_dulint_get_low(trx_sys->max_trx_id));
-
- buf += sprintf(buf,
- "Purge done for trx's n:o < %lu %lu undo n:o < %lu %lu\n",
- ut_dulint_get_high(purge_sys->purge_trx_no),
- ut_dulint_get_low(purge_sys->purge_trx_no),
- ut_dulint_get_high(purge_sys->purge_undo_no),
- ut_dulint_get_low(purge_sys->purge_undo_no));
lock_mutex_enter_kernel();
- buf += sprintf(buf,
- "Total number of lock structs in row lock hash table %lu\n",
- lock_get_n_rec_locks());
if (lock_deadlock_found) {
+
+ buf += sprintf(buf,
+"------------------------\n"
+"LATEST DETECTED DEADLOCK\n"
+"------------------------\n");
if ((ulint)(buf_end - buf)
< 100 + strlen(lock_latest_err_buf)) {
@@ -3972,6 +3962,26 @@ lock_print_info(
return;
}
+ buf += sprintf(buf,
+"------------\n"
+"TRANSACTIONS\n"
+"------------\n");
+
+ buf += sprintf(buf, "Trx id counter %lu %lu\n",
+ ut_dulint_get_high(trx_sys->max_trx_id),
+ ut_dulint_get_low(trx_sys->max_trx_id));
+
+ buf += sprintf(buf,
+ "Purge done for trx's n:o < %lu %lu undo n:o < %lu %lu\n",
+ ut_dulint_get_high(purge_sys->purge_trx_no),
+ ut_dulint_get_low(purge_sys->purge_trx_no),
+ ut_dulint_get_high(purge_sys->purge_undo_no),
+ ut_dulint_get_low(purge_sys->purge_undo_no));
+
+ buf += sprintf(buf,
+ "Total number of lock structs in row lock hash table %lu\n",
+ lock_get_n_rec_locks());
+
buf += sprintf(buf, "LIST OF TRANSACTIONS FOR EACH SESSION:\n");
/* First print info on non-active transactions */
diff --git a/innobase/os/os0file.c b/innobase/os/os0file.c
index 6324fcdbef1..1d1d84adda7 100644
--- a/innobase/os/os0file.c
+++ b/innobase/os/os0file.c
@@ -214,9 +214,14 @@ os_file_get_last_error(void)
"InnoDB: the directory. It may also be you have created a subdirectory\n"
"InnoDB: of the same name as a data file.\n");
} else {
- fprintf(stderr,
- "InnoDB: Look from section 13.2 at http://www.innodb.com/ibman.html\n"
- "InnoDB: what the error number means.\n");
+ if (strerror((int)err) != NULL) {
+ fprintf(stderr,
+ "InnoDB: Error number %lu means '%s'.\n", err, strerror((int)err));
+ }
+
+ fprintf(stderr,
+ "InnoDB: See also section 13.2 at http://www.innodb.com/ibman.html\n"
+ "InnoDB: about operating system error numbers.\n");
}
}
@@ -252,9 +257,14 @@ os_file_get_last_error(void)
"InnoDB: The error means mysqld does not have the access rights to\n"
"InnoDB: the directory.\n");
} else {
- fprintf(stderr,
- "InnoDB: Look from section 13.2 at http://www.innodb.com/ibman.html\n"
- "InnoDB: what the error number means or use the perror program of MySQL.\n");
+ if (strerror((int)err) != NULL) {
+ fprintf(stderr,
+ "InnoDB: Error number %lu means '%s'.\n", err, strerror((int)err));
+ }
+
+ fprintf(stderr,
+ "InnoDB: See also section 13.2 at http://www.innodb.com/ibman.html\n"
+ "InnoDB: about operating system error numbers.\n");
}
}
diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c
index 990ef99b2a4..1a616b74756 100644
--- a/innobase/row/row0ins.c
+++ b/innobase/row/row0ins.c
@@ -438,6 +438,111 @@ row_ins_cascade_calc_update_vec(
}
/*************************************************************************
+Reports a foreign key error associated with an update or a delete of a
+parent table index entry. */
+static
+void
+row_ins_foreign_report_err(
+/*=======================*/
+ char* errstr, /* in: error string from the viewpoint
+ of the parent table */
+ que_thr_t* thr, /* in: query thread whose run_node
+ is an update node */
+ dict_foreign_t* foreign, /* in: foreign key constraint */
+ rec_t* rec, /* in: a matching index record in the
+ child table */
+ dtuple_t* entry) /* in: index entry in the parent
+ table */
+{
+ char* buf = dict_foreign_err_buf;
+
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf), " Transaction:\n");
+ trx_print(buf + strlen(buf), thr_get_trx(thr));
+
+ sprintf(buf + strlen(buf),
+"Foreign key constraint fails for table %.500s:\n",
+ foreign->foreign_table_name);
+ dict_print_info_on_foreign_key_in_create_format(
+ foreign, buf + strlen(buf));
+ sprintf(buf + strlen(buf), "\n%s", errstr);
+ sprintf(buf + strlen(buf),
+" in parent table, in index %.500s tuple:\n",
+ foreign->referenced_index->name);
+ if (entry) {
+ dtuple_sprintf(buf + strlen(buf), 1000, entry);
+ }
+ sprintf(buf + strlen(buf),
+"\nBut in child table %.500s, in index %.500s, there is a record:\n",
+ foreign->foreign_table_name, foreign->foreign_index->name);
+ if (rec) {
+ rec_sprintf(buf + strlen(buf), 1000, rec);
+ }
+ sprintf(buf + strlen(buf), "\n");
+
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+
+ mutex_exit(&dict_foreign_err_mutex);
+}
+
+/*************************************************************************
+Reports a foreign key error to dict_foreign_err_buf when we are trying
+to add an index entry to a child table. Note that the adding may be the result
+of an update, too. */
+static
+void
+row_ins_foreign_report_add_err(
+/*===========================*/
+ que_thr_t* thr, /* in: query thread whose run_node
+ is an insert node */
+ dict_foreign_t* foreign, /* in: foreign key constraint */
+ rec_t* rec, /* in: a record in the parent table:
+ it does not match entry because we
+ have an error! */
+ dtuple_t* entry) /* in: index entry to insert in the
+ child table */
+{
+ char* buf = dict_foreign_err_buf;
+
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf), " Transaction:\n");
+ trx_print(buf + strlen(buf), thr_get_trx(thr));
+ sprintf(buf + strlen(buf),
+"Foreign key constraint fails for table %.500s:\n",
+ foreign->foreign_table_name);
+ dict_print_info_on_foreign_key_in_create_format(
+ foreign, buf + strlen(buf));
+ sprintf(buf + strlen(buf),
+"\nTrying to add in child table, in index %.500s tuple:\n",
+ foreign->foreign_index->name);
+ if (entry) {
+ dtuple_sprintf(buf + strlen(buf), 1000, entry);
+ }
+ sprintf(buf + strlen(buf),
+"\nBut in parent table %.500s, in index %.500s,\n"
+"the closest match we can find is record:\n",
+ foreign->referenced_table_name,
+ foreign->referenced_index->name);
+ if (rec && page_rec_is_supremum(rec)) {
+ /* If the cursor ended on a supremum record, it is better
+ to report the previous record in the error message, so that
+ the user gets a more descriptive error message. */
+ rec = page_rec_get_prev(rec);
+ }
+
+ if (rec) {
+ rec_sprintf(buf + strlen(buf), 1000, rec);
+ }
+ sprintf(buf + strlen(buf), "\n");
+
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+
+ mutex_exit(&dict_foreign_err_mutex);
+}
+
+/*************************************************************************
Perform referential actions or checks when a parent row is deleted or updated
and the constraint had an ON DELETE or ON UPDATE condition which was not
RESTRICT. */
@@ -453,6 +558,8 @@ row_ins_foreign_check_on_constraint(
type is != 0 */
btr_pcur_t* pcur, /* in: cursor placed on a matching
index record in the child table */
+ dtuple_t* entry, /* in: index entry in the parent
+ table */
mtr_t* mtr) /* in: mtr holding the latch of pcur
page */
{
@@ -506,6 +613,10 @@ row_ins_foreign_check_on_constraint(
return(DB_SUCCESS);
}
+ row_ins_foreign_report_err((char*)"Trying to delete",
+ thr, foreign,
+ btr_pcur_get_rec(pcur), entry);
+
return(DB_ROW_IS_REFERENCED);
}
@@ -523,6 +634,10 @@ row_ins_foreign_check_on_constraint(
return(DB_SUCCESS);
}
+ row_ins_foreign_report_err((char*)"Trying to update",
+ thr, foreign,
+ btr_pcur_get_rec(pcur), entry);
+
return(DB_ROW_IS_REFERENCED);
}
@@ -580,6 +695,10 @@ row_ins_foreign_check_on_constraint(
err = DB_ROW_IS_REFERENCED;
+ row_ins_foreign_report_err(
+(char*)"Trying an update, possibly causing a cyclic cascaded update\n"
+"in the child table,", thr, foreign, btr_pcur_get_rec(pcur), entry);
+
goto nonstandard_exit_func;
}
@@ -809,11 +928,10 @@ row_ins_check_foreign_constraint(
dictionary cache if they exist at all */
dict_table_t* table, /* in: if check_ref is TRUE, then the foreign
table, else the referenced table */
- dict_index_t* index __attribute__((unused)),/* in: index in table */
dtuple_t* entry, /* in: index entry for index */
que_thr_t* thr) /* in: query thread */
{
- upd_node_t* upd_node;
+ upd_node_t* upd_node;
dict_table_t* check_table;
dict_index_t* check_index;
ulint n_fields_cmp;
@@ -824,6 +942,7 @@ row_ins_check_foreign_constraint(
int cmp;
ulint err;
ulint i;
+ char* buf = dict_foreign_err_buf;
mtr_t mtr;
run_again:
@@ -884,6 +1003,25 @@ run_again:
if (check_table == NULL) {
if (check_ref) {
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf), " Transaction:\n");
+ trx_print(buf + strlen(buf), thr_get_trx(thr));
+ sprintf(buf + strlen(buf),
+"Foreign key constraint fails for table %.500s:\n",
+ foreign->foreign_table_name);
+ dict_print_info_on_foreign_key_in_create_format(
+ foreign, buf + strlen(buf));
+ sprintf(buf + strlen(buf),
+"\nTrying to add to index %.500s tuple:\n", foreign->foreign_index->name);
+ dtuple_sprintf(buf + strlen(buf), 1000, entry);
+ sprintf(buf + strlen(buf),
+"\nBut the parent table %.500s does not currently exist!\n",
+ foreign->referenced_table_name);
+
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
return(DB_NO_REFERENCED_ROW);
}
@@ -949,7 +1087,8 @@ run_again:
if (cmp == 0) {
if (rec_get_deleted_flag(rec)) {
- err = row_ins_set_shared_rec_lock(LOCK_ORDINARY,
+ err = row_ins_set_shared_rec_lock(
+ LOCK_ORDINARY,
rec, check_index, thr);
if (err != DB_SUCCESS) {
@@ -989,13 +1128,17 @@ run_again:
err =
row_ins_foreign_check_on_constraint(
- thr, foreign, &pcur, &mtr);
-
+ thr, foreign, &pcur, entry,
+ &mtr);
if (err != DB_SUCCESS) {
break;
}
} else {
+ row_ins_foreign_report_err(
+ (char*)"Trying to delete or update",
+ thr, foreign, rec, entry);
+
err = DB_ROW_IS_REFERENCED;
break;
}
@@ -1012,6 +1155,8 @@ run_again:
if (check_ref) {
err = DB_NO_REFERENCED_ROW;
+ row_ins_foreign_report_add_err(
+ thr, foreign, rec, entry);
} else {
err = DB_SUCCESS;
}
@@ -1025,6 +1170,9 @@ next_rec:
if (!moved) {
if (check_ref) {
+ rec = btr_pcur_get_rec(&pcur);
+ row_ins_foreign_report_add_err(
+ thr, foreign, rec, entry);
err = DB_NO_REFERENCED_ROW;
} else {
err = DB_SUCCESS;
@@ -1100,7 +1248,7 @@ row_ins_check_foreign_constraints(
}
err = row_ins_check_foreign_constraint(TRUE, foreign,
- table, index, entry, thr);
+ table, entry, thr);
if (got_s_lock) {
row_mysql_unfreeze_data_dictionary(trx);
}
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 0ca1a7516e0..81ed94e2628 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -6,7 +6,7 @@ Contains also create table and other data dictionary operations.
Created 9/17/2000 Heikki Tuuri
*******************************************************/
-
+
#include "row0mysql.h"
#ifdef UNIV_NONINL
@@ -1156,7 +1156,7 @@ row_mysql_recover_tmp_table(
return(DB_ERROR);
}
- if (0 == ut_memcmp(ptr, "/rsql", 5)) {
+ if (0 == ut_memcmp(ptr, (char*)"/rsql", 5)) {
ptr++;
*ptr = '#';
@@ -1280,10 +1280,10 @@ row_create_table_for_mysql(
}
trx->op_info = (char *) "creating table";
-
- if (0 == ut_strcmp(table->name, "mysql/host")
- || 0 == ut_strcmp(table->name, "mysql/user")
- || 0 == ut_strcmp(table->name, "mysql/db")) {
+
+ if (0 == ut_strcmp(table->name, (char*)"mysql/host")
+ || 0 == ut_strcmp(table->name, (char*)"mysql/user")
+ || 0 == ut_strcmp(table->name, (char*)"mysql/db")) {
fprintf(stderr,
"InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n"
@@ -1303,7 +1303,7 @@ row_create_table_for_mysql(
if (namelen >= keywordlen
&& 0 == ut_memcmp(table->name + namelen - keywordlen,
- "_recover_innodb_tmp_table", keywordlen)) {
+ (char*)"_recover_innodb_tmp_table", keywordlen)) {
/* MySQL prevents accessing of tables whose name begins
with #sql, that is temporary tables. If mysqld crashes in
@@ -1371,7 +1371,7 @@ row_create_table_for_mysql(
if (namelen >= keywordlen
&& 0 == ut_memcmp(table->name + namelen - keywordlen,
- "innodb_mem_validate", keywordlen)) {
+ (char*)"innodb_mem_validate", keywordlen)) {
/* We define here a debugging feature intended for
developers */
@@ -1481,7 +1481,7 @@ row_create_index_for_mysql(
if (namelen >= keywordlen
&& 0 == ut_memcmp(
index->table_name + namelen - keywordlen,
- "_recover_innodb_tmp_table", keywordlen)) {
+ (char*)"_recover_innodb_tmp_table", keywordlen)) {
return(DB_SUCCESS);
}
@@ -1586,7 +1586,7 @@ row_table_add_foreign_constraints(
if (namelen >= keywordlen
&& 0 == ut_memcmp(
name + namelen - keywordlen,
- "_recover_innodb_tmp_table", keywordlen)) {
+ (char*)"_recover_innodb_tmp_table", keywordlen)) {
return(DB_SUCCESS);
}
@@ -1808,7 +1808,6 @@ row_drop_table_for_mysql(
ulint len;
ulint namelen;
ulint keywordlen;
- ulint rounds = 0;
ibool locked_dictionary = FALSE;
char buf[10000];
@@ -2155,7 +2154,7 @@ row_is_mysql_tmp_table_name(
ulint i;
for (i = 0; i <= ut_strlen(name) - 5; i++) {
- if (ut_memcmp(name + i, "/#sql", 5) == 0) {
+ if (ut_memcmp(name + i, (char*)"/#sql", 5) == 0) {
return(TRUE);
}
@@ -2177,12 +2176,16 @@ row_rename_table_for_mysql(
{
dict_table_t* table;
que_thr_t* thr;
- que_t* graph;
+ que_t* graph = NULL;
ulint err;
char* str1;
char* str2;
char* str3;
+ mem_heap_t* heap = NULL;
+ char** constraints_to_drop = NULL;
+ ulint n_constraints_to_drop = 0;
ulint len;
+ ulint i;
char buf[10000];
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
@@ -2200,10 +2203,10 @@ row_rename_table_for_mysql(
trx_commit_for_mysql(trx);
return(DB_ERROR);
}
-
- if (0 == ut_strcmp(new_name, "mysql/host")
- || 0 == ut_strcmp(new_name, "mysql/user")
- || 0 == ut_strcmp(new_name, "mysql/db")) {
+
+ if (0 == ut_strcmp(new_name, (char*)"mysql/host")
+ || 0 == ut_strcmp(new_name, (char*)"mysql/user")
+ || 0 == ut_strcmp(new_name, (char*)"mysql/db")) {
fprintf(stderr,
"InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n"
@@ -2217,6 +2220,19 @@ row_rename_table_for_mysql(
trx->op_info = (char *) "renaming table";
trx_start_if_not_started(trx);
+ /* Serialize data dictionary operations with dictionary mutex:
+ no deadlocks can occur then in these operations */
+
+ row_mysql_lock_data_dictionary(trx);
+
+ table = dict_table_get_low(old_name);
+
+ if (!table) {
+ err = DB_TABLE_NOT_FOUND;
+
+ goto funct_exit;
+ }
+
str1 = (char *)
"PROCEDURE RENAME_TABLE_PROC () IS\n"
"new_table_name CHAR;\n"
@@ -2229,14 +2245,43 @@ row_rename_table_for_mysql(
if (row_is_mysql_tmp_table_name(new_name)) {
- /* We want to preserve the original foreign key
- constraint definitions despite the name change */
+ /* MySQL is doing an ALTER TABLE command and it renames the
+ original table to a temporary table name. We want to preserve
+ the original foreign key constraint definitions despite the
+ name change. An exception is those constraints for which
+ the ALTER TABLE contained DROP FOREIGN KEY <foreign key id>.*/
- str3 = (char*)
- "';\n"
- "UPDATE SYS_TABLES SET NAME = new_table_name\n"
- "WHERE NAME = old_table_name;\n"
- "END;\n";
+ heap = mem_heap_create(100);
+
+ err = dict_foreign_parse_drop_constraints(heap, trx,
+ table,
+ &n_constraints_to_drop,
+ &constraints_to_drop);
+ if (err != DB_SUCCESS) {
+
+ goto funct_exit;
+ }
+
+ str3 = mem_heap_alloc(heap,
+ 1000 + 500 * n_constraints_to_drop);
+ *str3 = '\0';
+ sprintf(str3,
+ "';\n"
+ "UPDATE SYS_TABLES SET NAME = new_table_name\n"
+ "WHERE NAME = old_table_name;\n");
+
+ for (i = 0; i < n_constraints_to_drop; i++) {
+ sprintf(str3 + strlen(str3),
+ "DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s';\n"
+ "DELETE FROM SYS_FOREIGN WHERE ID = '%s';\n",
+ constraints_to_drop[i],
+ constraints_to_drop[i]);
+ }
+
+ sprintf(str3 + strlen(str3),
+ "END;\n");
+
+ ut_a(strlen(str3) < 1000 + 500 * n_constraints_to_drop);
} else {
str3 = (char*)
"';\n"
@@ -2267,13 +2312,6 @@ row_rename_table_for_mysql(
ut_memcpy(buf + len, str3, ut_strlen(str3) + 1);
- /* Serialize data dictionary operations with dictionary mutex:
- no deadlocks can occur then in these operations */
-
- row_mysql_lock_data_dictionary(trx);
-
- table = dict_table_get_low(old_name);
-
graph = pars_sql(buf);
ut_a(graph);
@@ -2283,12 +2321,6 @@ row_rename_table_for_mysql(
graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
- if (!table) {
- err = DB_TABLE_NOT_FOUND;
-
- goto funct_exit;
- }
-
ut_a(thr = que_fork_start_command(graph, SESS_COMM_EXECUTE, 0));
que_run_threads(thr);
@@ -2329,6 +2361,13 @@ row_rename_table_for_mysql(
if (row_is_mysql_tmp_table_name(old_name)) {
+ /* MySQL is doing an ALTER TABLE command and it
+ renames the created temporary table to the name
+ of the original table. In the ALTER TABLE we maybe
+ created some FOREIGN KEY constraints for the temporary
+ table. But we want to load also the foreign key
+ constraint definitions for the original table name. */
+
err = dict_load_foreigns(new_name);
if (err != DB_SUCCESS) {
@@ -2354,7 +2393,13 @@ row_rename_table_for_mysql(
funct_exit:
row_mysql_unlock_data_dictionary(trx);
- que_graph_free(graph);
+ if (graph) {
+ que_graph_free(graph);
+ }
+
+ if (heap) {
+ mem_heap_free(heap);
+ }
trx_commit_for_mysql(trx);
diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c
index 4bc81ad8a8e..40994c9962c 100644
--- a/innobase/row/row0sel.c
+++ b/innobase/row/row0sel.c
@@ -2602,6 +2602,24 @@ row_search_for_mysql(
printf("N tables locked %lu\n", trx->mysql_n_tables_locked);
*/
/*-------------------------------------------------------------*/
+ /* PHASE 0: Release a possible s-latch we are holding on the
+ adaptive hash index latch if there is someone waiting behind */
+
+ if (trx->has_search_latch
+ && btr_search_latch.writer != RW_LOCK_NOT_LOCKED) {
+
+ /* There is an x-latch request on the adaptive hash index:
+ release the s-latch to reduce starvation and wait for
+ BTR_SEA_TIMEOUT rounds before trying to keep it again over
+ calls from MySQL */
+
+ rw_lock_s_unlock(&btr_search_latch);
+ trx->has_search_latch = FALSE;
+
+ trx->search_latch_timeout = BTR_SEA_TIMEOUT;
+ }
+
+ /*-------------------------------------------------------------*/
/* PHASE 1: Try to pop the row from the prefetch cache */
if (direction == 0) {
@@ -2736,23 +2754,7 @@ row_search_for_mysql(
NOT prepared to inserts interleaved with the SELECT,
and if we try that, we can deadlock on the adaptive
hash index semaphore! */
-
- if (btr_search_latch.writer != RW_LOCK_NOT_LOCKED) {
- /* There is an x-latch request: release
- a possible s-latch to reduce starvation
- and wait for BTR_SEA_TIMEOUT rounds before
- trying to keep it again over calls from
- MySQL */
-
- if (trx->has_search_latch) {
- rw_lock_s_unlock(&btr_search_latch);
- trx->has_search_latch = FALSE;
- }
- trx->search_latch_timeout = BTR_SEA_TIMEOUT;
-
- goto no_shortcut;
- }
#ifndef UNIV_SEARCH_DEBUG
if (!trx->has_search_latch) {
rw_lock_s_lock(&btr_search_latch);
@@ -2810,7 +2812,6 @@ row_search_for_mysql(
}
}
-no_shortcut:
/*-------------------------------------------------------------*/
/* PHASE 3: Open or restore index cursor position */
diff --git a/innobase/row/row0upd.c b/innobase/row/row0upd.c
index 64569bf3f96..5fce1c1861b 100644
--- a/innobase/row/row0upd.c
+++ b/innobase/row/row0upd.c
@@ -218,7 +218,7 @@ row_upd_check_references_constraints(
being dropped while the check is running. */
err = row_ins_check_foreign_constraint(FALSE, foreign,
- table, index, entry, thr);
+ table, entry, thr);
if (foreign->foreign_table) {
mutex_enter(&(dict_sys->mutex));
diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c
index c6f3bd40dfe..ccb3755b252 100644
--- a/innobase/srv/srv0srv.c
+++ b/innobase/srv/srv0srv.c
@@ -1869,11 +1869,11 @@ retry:
/* Go to wait for the event; when a thread leaves InnoDB it will
release this thread */
- trx->op_info = "waiting in InnoDB queue";
+ trx->op_info = (char*)"waiting in InnoDB queue";
os_event_wait(slot->event);
- trx->op_info = "";
+ trx->op_info = (char*)"";
os_fast_mutex_lock(&srv_conc_mutex);
@@ -2346,12 +2346,22 @@ srv_sprintf_innodb_monitor(
buf = buf + strlen(buf);
ut_a(buf < buf_end + 1500);
- buf += sprintf(buf, "------------\n"
- "TRANSACTIONS\n"
- "------------\n");
+ if (*dict_foreign_err_buf != '\0') {
+ buf += sprintf(buf,
+ "------------------------\n"
+ "LATEST FOREIGN KEY ERROR\n"
+ "------------------------\n");
+
+ if (buf_end - buf > 6000) {
+ buf+= sprintf(buf, "%.4000s", dict_foreign_err_buf);
+ }
+ }
+
+ ut_a(buf < buf_end + 1500);
+
lock_print_info(buf, buf_end);
buf = buf + strlen(buf);
-
+
buf += sprintf(buf, "--------\n"
"FILE I/O\n"
"--------\n");
diff --git a/innobase/ut/ut0ut.c b/innobase/ut/ut0ut.c
index bb5eb662cd7..95037ec3570 100644
--- a/innobase/ut/ut0ut.c
+++ b/innobase/ut/ut0ut.c
@@ -204,7 +204,7 @@ ut_get_year_month_day(
cal_tm_ptr = localtime(&tm);
- *year = (ulint)cal_tm_ptr->tm_year;
+ *year = (ulint)cal_tm_ptr->tm_year + 1900;
*month = (ulint)cal_tm_ptr->tm_mon + 1;
*day = (ulint)cal_tm_ptr->tm_mday;
#endif