summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Lindström <jan.lindstrom@mariadb.com>2019-06-04 13:19:40 +0300
committerJan Lindström <jan.lindstrom@mariadb.com>2019-06-18 11:24:26 +0300
commit859052dfcc3da3d61e3023c7401ef009cea50bcd (patch)
tree805e62bfdfde8fe76df0057efb2b601897dd0782
parentb003b0c934cf6b59358e31144c4f69cf34622fb8 (diff)
downloadmariadb-git-bb-10.1-MDEV-19660.tar.gz
MDEV-19660: wsrep_rec_get_foreign_key() is dereferencing a stale pointer to a page that was previously latchedbb-10.1-MDEV-19660
In row_ins_foreign_check_on_constraint(), clustered index record is being passed to wsrep_append_foreign_key() after releasing the latch. If a record has been changed by other thread in the meantime then it could lead to a crash when wsrep_rec_get_foreign_key () tries to access the record. wsrep_append_foreign_key Added call to error reporting function and debug error injection. row_ins_foreign_report_err_low Moved actual error reporting to a file to this low level function. row_ins_foreign_report_err Use new row_ins_foreign_report_err_low function for error reporting to a file. wsrep_report_foreign_key_error New function to report foreign key errors when appending a key for Galera processing. row_ins_foreign_check_on_constraint Use cascade->pcur->old_rec instead of clust_rec.
-rw-r--r--mysql-test/suite/galera/r/galera_fk_force_append_error.result162
-rw-r--r--mysql-test/suite/galera/t/galera_fk_force_append_error.test161
-rw-r--r--storage/innobase/handler/ha_innodb.cc110
-rw-r--r--storage/innobase/include/row0ins.h16
-rw-r--r--storage/innobase/row/row0ins.cc113
-rw-r--r--storage/xtradb/handler/ha_innodb.cc110
-rw-r--r--storage/xtradb/include/row0ins.h15
-rw-r--r--storage/xtradb/row/row0ins.cc100
8 files changed, 625 insertions, 162 deletions
diff --git a/mysql-test/suite/galera/r/galera_fk_force_append_error.result b/mysql-test/suite/galera/r/galera_fk_force_append_error.result
new file mode 100644
index 00000000000..48abf468a4d
--- /dev/null
+++ b/mysql-test/suite/galera/r/galera_fk_force_append_error.result
@@ -0,0 +1,162 @@
+call mtr.add_suppression("WSREP: Appending cascaded fk row key failed:.*");
+CREATE TABLE grandparent (
+id INT NOT NULL PRIMARY KEY
+) ENGINE=InnoDB;
+CREATE TABLE parent (
+id INT NOT NULL PRIMARY KEY,
+grandparent_id INT,
+FOREIGN KEY (grandparent_id)
+REFERENCES grandparent(id)
+ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+CREATE TABLE child (
+id INT NOT NULL PRIMARY KEY,
+parent_id INT,
+FOREIGN KEY (parent_id)
+REFERENCES parent(id)
+ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+INSERT INTO grandparent VALUES (1),(2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+INSERT INTO parent VALUES (1,1), (2,2);
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails
+SET GLOBAL debug_dbug = "";
+INSERT INTO parent VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+INSERT INTO child VALUES (1,1), (2,2);
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails
+SET GLOBAL debug_dbug = "";
+INSERT INTO child VALUES (1,1), (2,2);
+DELETE FROM grandparent WHERE id = 1;
+UPDATE grandparent SET id = 5 where id = 2;
+select * from child;
+id parent_id
+2 2
+select * from parent;
+id grandparent_id
+2 5
+select * from grandparent;
+id
+5
+select * from child;
+id parent_id
+2 2
+select * from parent;
+id grandparent_id
+2 5
+select * from grandparent;
+id
+5
+drop table child, parent, grandparent;
+CREATE TABLE grandparent (
+id INT NOT NULL PRIMARY KEY
+) ENGINE=InnoDB;
+CREATE TABLE parent (
+id INT NOT NULL,
+id2 INT NOT NULL,
+grandparent_id INT,
+id3 INT not null,
+id4 INT,
+id5 INT,
+PRIMARY KEY(id,id2),
+unique key(id3),
+key(id4,id5),
+key(id4,id),
+FOREIGN KEY (grandparent_id)
+REFERENCES grandparent(id)
+ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+CREATE TABLE child (
+id INT NOT NULL PRIMARY KEY,
+parent_id INT,
+FOREIGN KEY (parent_id)
+REFERENCES parent(id)
+ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+CREATE TABLE child2 (
+id INT NOT NULL PRIMARY KEY,
+parent_id INT,
+FOREIGN KEY (parent_id)
+REFERENCES parent(id3)
+ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+CREATE TABLE child3 (
+id INT NOT NULL PRIMARY KEY,
+parent_id INT,
+FOREIGN KEY (parent_id)
+REFERENCES parent(id4)
+ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+CREATE TABLE child4 (
+id INT NOT NULL PRIMARY KEY,
+pid1 INT,
+pid2 INT,
+FOREIGN KEY (pid1,pid2)
+REFERENCES parent(id4,id)
+ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+INSERT INTO grandparent VALUES (1),(2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+INSERT INTO parent VALUES (1,1,1,1,1,1), (2,2,2,2,2,2);
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails
+SET GLOBAL debug_dbug = "";
+INSERT INTO parent VALUES (1,1,1,1,1,1), (2,2,2,2,2,2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+INSERT INTO child VALUES (1,1), (2,2);
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails
+SET GLOBAL debug_dbug = "";
+INSERT INTO child VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+INSERT INTO child2 VALUES (1,1), (2,2);
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails
+SET GLOBAL debug_dbug = "";
+INSERT INTO child2 VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+INSERT INTO child3 VALUES (1,1), (2,2);
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails
+SET GLOBAL debug_dbug = "";
+INSERT INTO child3 VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+INSERT INTO child4 VALUES (1,1,1), (2,2,2);
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails
+SET GLOBAL debug_dbug = "";
+INSERT INTO child4 VALUES (1,1,1), (2,2,2);
+DELETE FROM grandparent WHERE id = 1;
+UPDATE grandparent SET id = 5 where id = 2;
+select * from child;
+id parent_id
+2 2
+select * from child2;
+id parent_id
+2 2
+select * from child3;
+id parent_id
+2 2
+select * from child4;
+id pid1 pid2
+2 2 2
+select * from parent;
+id id2 grandparent_id id3 id4 id5
+2 2 5 2 2 2
+select * from grandparent;
+id
+5
+select * from child;
+id parent_id
+2 2
+select * from child2;
+id parent_id
+2 2
+select * from child3;
+id parent_id
+2 2
+select * from child4;
+id pid1 pid2
+2 2 2
+select * from parent;
+id id2 grandparent_id id3 id4 id5
+2 2 5 2 2 2
+select * from grandparent;
+id
+5
+drop table child, child2, child3, child4, parent, grandparent;
diff --git a/mysql-test/suite/galera/t/galera_fk_force_append_error.test b/mysql-test/suite/galera/t/galera_fk_force_append_error.test
new file mode 100644
index 00000000000..ad3f6ebf951
--- /dev/null
+++ b/mysql-test/suite/galera/t/galera_fk_force_append_error.test
@@ -0,0 +1,161 @@
+# Test different foreign keys and force galera foreign key
+# append error.
+
+--source include/galera_cluster.inc
+--source include/have_debug.inc
+
+--connection node_1
+
+call mtr.add_suppression("WSREP: Appending cascaded fk row key failed:.*");
+
+CREATE TABLE grandparent (
+ id INT NOT NULL PRIMARY KEY
+) ENGINE=InnoDB;
+
+CREATE TABLE parent (
+ id INT NOT NULL PRIMARY KEY,
+ grandparent_id INT,
+ FOREIGN KEY (grandparent_id)
+ REFERENCES grandparent(id)
+ ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+
+CREATE TABLE child (
+ id INT NOT NULL PRIMARY KEY,
+ parent_id INT,
+ FOREIGN KEY (parent_id)
+ REFERENCES parent(id)
+ ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+
+INSERT INTO grandparent VALUES (1),(2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+--error ER_NO_REFERENCED_ROW_2
+INSERT INTO parent VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "";
+INSERT INTO parent VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+--error ER_NO_REFERENCED_ROW_2
+INSERT INTO child VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "";
+INSERT INTO child VALUES (1,1), (2,2);
+
+--connection node_2
+DELETE FROM grandparent WHERE id = 1;
+UPDATE grandparent SET id = 5 where id = 2;
+
+--connection node_1
+select * from child;
+select * from parent;
+select * from grandparent;
+
+--connection node_2
+select * from child;
+select * from parent;
+select * from grandparent;
+
+drop table child, parent, grandparent;
+
+--connection node_1
+
+CREATE TABLE grandparent (
+ id INT NOT NULL PRIMARY KEY
+) ENGINE=InnoDB;
+
+CREATE TABLE parent (
+ id INT NOT NULL,
+ id2 INT NOT NULL,
+ grandparent_id INT,
+ id3 INT not null,
+ id4 INT,
+ id5 INT,
+ PRIMARY KEY(id,id2),
+ unique key(id3),
+ key(id4,id5),
+ key(id4,id),
+ FOREIGN KEY (grandparent_id)
+ REFERENCES grandparent(id)
+ ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+
+CREATE TABLE child (
+ id INT NOT NULL PRIMARY KEY,
+ parent_id INT,
+ FOREIGN KEY (parent_id)
+ REFERENCES parent(id)
+ ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+
+CREATE TABLE child2 (
+ id INT NOT NULL PRIMARY KEY,
+ parent_id INT,
+ FOREIGN KEY (parent_id)
+ REFERENCES parent(id3)
+ ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+
+CREATE TABLE child3 (
+ id INT NOT NULL PRIMARY KEY,
+ parent_id INT,
+ FOREIGN KEY (parent_id)
+ REFERENCES parent(id4)
+ ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+
+CREATE TABLE child4 (
+ id INT NOT NULL PRIMARY KEY,
+ pid1 INT,
+ pid2 INT,
+ FOREIGN KEY (pid1,pid2)
+ REFERENCES parent(id4,id)
+ ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+
+INSERT INTO grandparent VALUES (1),(2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+--error ER_NO_REFERENCED_ROW_2
+INSERT INTO parent VALUES (1,1,1,1,1,1), (2,2,2,2,2,2);
+SET GLOBAL debug_dbug = "";
+INSERT INTO parent VALUES (1,1,1,1,1,1), (2,2,2,2,2,2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+--error ER_NO_REFERENCED_ROW_2
+INSERT INTO child VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "";
+INSERT INTO child VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+--error ER_NO_REFERENCED_ROW_2
+INSERT INTO child2 VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "";
+INSERT INTO child2 VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+--error ER_NO_REFERENCED_ROW_2
+INSERT INTO child3 VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "";
+INSERT INTO child3 VALUES (1,1), (2,2);
+SET GLOBAL debug_dbug = "+d,wsrep_append_foreign_key_error";
+--error ER_NO_REFERENCED_ROW_2
+INSERT INTO child4 VALUES (1,1,1), (2,2,2);
+SET GLOBAL debug_dbug = "";
+INSERT INTO child4 VALUES (1,1,1), (2,2,2);
+
+--connection node_2
+DELETE FROM grandparent WHERE id = 1;
+UPDATE grandparent SET id = 5 where id = 2;
+
+--connection node_1
+select * from child;
+select * from child2;
+select * from child3;
+select * from child4;
+select * from parent;
+select * from grandparent;
+
+--connection node_2
+select * from child;
+select * from child2;
+select * from child3;
+select * from child4;
+select * from parent;
+select * from grandparent;
+
+drop table child, child2, child3, child4, parent, grandparent;
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index e04d431174a..de9d4e41158 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -10353,9 +10353,17 @@ wsrep_key_type_to_str(wsrep_key_type type)
return "unknown";
}
+/** Append foreign key information for Galera processing. This is
+local operation and no cluster communication done.
+@param[in] trx Transaction
+@param[in] foreign Foreign key constraint
+@param[in] rec Index record in the child table
+@param[in] index Clustered index
+@param[in] key_type Access type of this key
+ (shared, exclusive, semi...) */
+UNIV_INTERN
ulint
wsrep_append_foreign_key(
-/*===========================*/
trx_t* trx, /*!< in: trx */
dict_foreign_t* foreign, /*!< in: foreign key constraint */
const rec_t* rec, /*!<in: clustered index record */
@@ -10369,15 +10377,25 @@ wsrep_append_foreign_key(
ulint rcode = DB_SUCCESS;
char cache_key[513] = {'\0'};
int cache_key_len;
- bool const copy = true;
+ bool const copy = true;
+ byte key[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
+ ulint len = WSREP_MAX_SUPPORTED_KEY_LENGTH;
+ int i = 0;
+ dict_index_t* idx_target = NULL;
+ dict_index_t* idx = NULL;
+ char *p = NULL;
+ wsrep_buf_t wkey_part[3];
+ wsrep_key_t wkey = {wkey_part, 3};
+ wsrep_t *wsrep = NULL;
- if (!wsrep_on(trx->mysql_thd) ||
- wsrep_thd_exec_mode(thd) != LOCAL_STATE)
+ if (!wsrep_on(trx->mysql_thd)
+ || wsrep_thd_exec_mode(thd) != LOCAL_STATE) {
return DB_SUCCESS;
+ }
- if (!thd || !foreign ||
- (!foreign->referenced_table && !foreign->foreign_table))
- {
+ if (!thd
+ || !foreign
+ || (!foreign->referenced_table && !foreign->foreign_table)) {
WSREP_INFO("FK: %s missing in: %s",
(!thd) ? "thread" :
((!foreign) ? "constraint" :
@@ -10385,44 +10403,38 @@ wsrep_append_foreign_key(
"referenced table" : "foreign table")),
(thd && wsrep_thd_query(thd)) ?
wsrep_thd_query(thd) : "void");
- return DB_ERROR;
+ goto error_handling;
}
- if ( !((referenced) ?
- foreign->referenced_table : foreign->foreign_table))
- {
+ if (!((referenced) ?
+ foreign->referenced_table : foreign->foreign_table)) {
WSREP_DEBUG("pulling %s table into cache",
(referenced) ? "referenced" : "foreign");
mutex_enter(&(dict_sys->mutex));
- if (referenced)
- {
+ if (referenced) {
foreign->referenced_table =
dict_table_get_low(
foreign->referenced_table_name_lookup);
- if (foreign->referenced_table)
- {
+ if (foreign->referenced_table) {
foreign->referenced_index =
wsrep_dict_foreign_find_index(
foreign->referenced_table, NULL,
foreign->referenced_col_names,
- foreign->n_fields,
+ foreign->n_fields,
foreign->foreign_index,
TRUE, FALSE);
}
- }
- else
- {
+ } else {
foreign->foreign_table =
dict_table_get_low(
foreign->foreign_table_name_lookup);
- if (foreign->foreign_table)
- {
+ if (foreign->foreign_table) {
foreign->foreign_index =
wsrep_dict_foreign_find_index(
foreign->foreign_table, NULL,
foreign->foreign_col_names,
foreign->n_fields,
- foreign->referenced_index,
+ foreign->referenced_index,
TRUE, FALSE);
}
}
@@ -10430,30 +10442,28 @@ wsrep_append_foreign_key(
}
if ( !((referenced) ?
- foreign->referenced_table : foreign->foreign_table))
- {
+ foreign->referenced_table : foreign->foreign_table)) {
WSREP_WARN("FK: %s missing in query: %s",
(!foreign->referenced_table) ?
"referenced table" : "foreign table",
(wsrep_thd_query(thd)) ?
wsrep_thd_query(thd) : "void");
- return DB_ERROR;
+ goto error_handling;
}
- byte key[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
- ulint len = WSREP_MAX_SUPPORTED_KEY_LENGTH;
- dict_index_t *idx_target = (referenced) ?
- foreign->referenced_index : index;
- dict_index_t *idx = (referenced) ?
+
+ idx_target = (referenced) ? foreign->referenced_index : index;
+ idx = (referenced) ?
UT_LIST_GET_FIRST(foreign->referenced_table->indexes) :
UT_LIST_GET_FIRST(foreign->foreign_table->indexes);
- int i = 0;
+
while (idx != NULL && idx != idx_target) {
if (innobase_strcasecmp (idx->name, innobase_index_reserve_name) != 0) {
i++;
}
idx = UT_LIST_GET_NEXT(indexes, idx);
}
+
ut_a(idx);
key[0] = (char)i;
@@ -10470,7 +10480,7 @@ wsrep_append_foreign_key(
(index && index->table_name) ? index->table_name :
"void table",
wsrep_thd_query(thd));
- return DB_ERROR;
+ goto error_handling;
}
strncpy(cache_key,
@@ -10479,17 +10489,11 @@ wsrep_append_foreign_key(
foreign->referenced_table->name :
foreign->foreign_table->name) :
foreign->foreign_table->name, sizeof(cache_key) - 1);
+
cache_key_len = strlen(cache_key);
-#ifdef WSREP_DEBUG_PRINT
- ulint j;
- fprintf(stderr, "FK parent key, table: %s %s len: %lu ",
- cache_key, (shared) ? "shared" : "exclusive", len+1);
- for (j=0; j<len+1; j++) {
- fprintf(stderr, " %hhX, ", key[j]);
- }
- fprintf(stderr, "\n");
-#endif
- char *p = strchr(cache_key, '/');
+
+ p = strchr(cache_key, '/');
+
if (p) {
*p = '\0';
} else {
@@ -10498,8 +10502,6 @@ wsrep_append_foreign_key(
foreign->foreign_table->name);
}
- wsrep_buf_t wkey_part[3];
- wsrep_key_t wkey = {wkey_part, 3};
if (!wsrep_prepare_key(
(const uchar*)cache_key,
cache_key_len + 1,
@@ -10509,9 +10511,11 @@ wsrep_append_foreign_key(
WSREP_WARN("key prepare failed for cascaded FK: %s",
(wsrep_thd_query(thd)) ?
wsrep_thd_query(thd) : "void");
- return DB_ERROR;
+ goto error_handling;
}
- wsrep_t *wsrep= get_wsrep();
+
+ wsrep= get_wsrep();
+
rcode = (int)wsrep->append_key(
wsrep,
wsrep_ws_handle(thd, trx),
@@ -10519,15 +10523,27 @@ wsrep_append_foreign_key(
1,
key_type,
copy);
+
+ /* Force error injection for testing error reporting. */
+ DBUG_EXECUTE_IF("wsrep_append_foreign_key_error",
+ rcode = 1;
+ );
+
if (rcode) {
DBUG_PRINT("wsrep", ("row key failed: %lu", rcode));
WSREP_ERROR("Appending cascaded fk row key failed: %s, %lu",
(wsrep_thd_query(thd)) ?
wsrep_thd_query(thd) : "void", rcode);
- return DB_ERROR;
+ goto error_handling;
}
return DB_SUCCESS;
+
+error_handling:
+
+ wsrep_report_foreign_key_error(trx, foreign, rec, NULL);
+
+ return DB_NO_REFERENCED_ROW;
}
static int
diff --git a/storage/innobase/include/row0ins.h b/storage/innobase/include/row0ins.h
index 54e7fa6d1fb..d1a0575cdb1 100644
--- a/storage/innobase/include/row0ins.h
+++ b/storage/innobase/include/row0ins.h
@@ -32,6 +32,7 @@ Created 4/20/1996 Heikki Tuuri
#include "dict0types.h"
#include "trx0types.h"
#include "row0types.h"
+#include "rem0types.h"
/***************************************************************//**
Checks if foreign key constraint fails for an index entry. Sets shared locks
@@ -189,6 +190,21 @@ row_ins_step(
/*=========*/
que_thr_t* thr); /*!< in: query thread */
+#ifdef WITH_WSREP
+/* Report foreign key error from Galera append key.
+@param[in] trx Transaction
+@param[in] foreign Foreign key constraint
+@param[in] rec Index record in the child table
+@param[in] entry Index entry in the parent table */
+UNIV_INTERN
+void
+wsrep_report_foreign_key_error(
+ trx_t* trx,
+ dict_foreign_t* foreign,
+ const rec_t* rec,
+ const dtuple_t* entry);
+#endif /* WITH_WSREP */
+
/* Insert node structure */
struct ins_node_t{
diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
index d60665f52ad..82c9c632a37 100644
--- a/storage/innobase/row/row0ins.cc
+++ b/storage/innobase/row/row0ins.cc
@@ -790,36 +790,26 @@ row_ins_foreign_trx_print(
ut_ad(mutex_own(&dict_foreign_err_mutex));
}
-/*********************************************************************//**
-Reports a foreign key error associated with an update or a delete of a
-parent table index entry. */
+/** Report a foreign key error to a provided file handle
+@param[in] ef file handle
+@param[in] errstr error string from the viewpoint
+ of the parent table
+@param[in] trx Transaction
+@param[in] foreign Foreign key constraint
+@param[in] rec Index record in the child table
+@param[in] entry Index entry in the parent table */
static
void
-row_ins_foreign_report_err(
-/*=======================*/
- const 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 */
- const rec_t* rec, /*!< in: a matching index record in the
- child table */
- const dtuple_t* entry) /*!< in: index entry in the parent
- table */
+row_ins_foreign_report_err_low(
+ FILE* ef,
+ const char* errstr,
+ trx_t* trx,
+ dict_foreign_t* foreign,
+ const rec_t* rec,
+ const dtuple_t* entry)
{
std::string fk_str;
- if (srv_read_only_mode) {
- return;
- }
-
- FILE* ef = dict_foreign_err_file;
- trx_t* trx = thr_get_trx(thr);
-
- row_ins_set_detailed(trx, foreign);
-
- row_ins_foreign_trx_print(trx);
-
fputs("Foreign key constraint fails for table ", ef);
ut_print_name(ef, trx, TRUE, foreign->foreign_table_name);
fputs(":\n", ef);
@@ -845,6 +835,37 @@ row_ins_foreign_report_err(
fputs(", the record is not available\n", ef);
}
putc('\n', ef);
+}
+
+/*********************************************************************//**
+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(
+/*=======================*/
+ const 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 */
+ const rec_t* rec, /*!< in: a matching index record in the
+ child table */
+ const dtuple_t* entry) /*!< in: index entry in the parent
+ table */
+{
+ std::string fk_str;
+
+ if (srv_read_only_mode) {
+ return;
+ }
+
+ FILE* ef = dict_foreign_err_file;
+ trx_t* trx = thr_get_trx(thr);
+
+ row_ins_set_detailed(trx, foreign);
+ row_ins_foreign_trx_print(trx);
+ row_ins_foreign_report_err_low(ef, errstr, trx, foreign, rec, entry);
mutex_exit(&dict_foreign_err_mutex);
}
@@ -935,6 +956,7 @@ row_ins_invalidate_query_cache(
innobase_invalidate_query_cache(thr_get_trx(thr), buf, len);
mem_free(buf);
}
+
#ifdef WITH_WSREP
dberr_t wsrep_append_foreign_key(trx_t *trx,
dict_foreign_t* foreign,
@@ -942,6 +964,25 @@ dberr_t wsrep_append_foreign_key(trx_t *trx,
dict_index_t* clust_index,
ibool referenced,
enum wsrep_key_type key_type);
+
+/* Report foreign key error from Galera append key.
+@param[in] trx Transaction
+@param[in] foreign Foreign key constraint
+@param[in] rec Index record in the child table
+@param[in] entry Index entry in the parent table */
+UNIV_INTERN
+void
+wsrep_report_foreign_key_error(
+ trx_t* trx,
+ dict_foreign_t* foreign,
+ const rec_t* rec,
+ const dtuple_t* entry)
+{
+ mutex_enter(&dict_foreign_err_mutex);
+ row_ins_foreign_report_err_low(stderr, "WSREP: append foreign key failed ",
+ trx, foreign, rec, entry);
+ mutex_exit(&dict_foreign_err_mutex);
+}
#endif /* WITH_WSREP */
/*********************************************************************//**
@@ -1272,6 +1313,15 @@ row_ins_foreign_check_on_constraint(
}
}
+#ifdef WITH_WSREP
+ err = wsrep_append_foreign_key(
+ thr_get_trx(thr),
+ foreign,
+ clust_rec,
+ clust_index,
+ FALSE, WSREP_KEY_EXCLUSIVE);
+#endif /* WITH_WSREP */
+
/* Store pcur position and initialize or store the cascade node
pcur stored position */
@@ -1290,17 +1340,8 @@ row_ins_foreign_check_on_constraint(
cascade->state = UPD_NODE_UPDATE_CLUSTERED;
#ifdef WITH_WSREP
- err = wsrep_append_foreign_key(
- thr_get_trx(thr),
- foreign,
- clust_rec,
- clust_index,
- FALSE, WSREP_KEY_EXCLUSIVE);
- if (err != DB_SUCCESS) {
- fprintf(stderr,
- "WSREP: foreign key append failed: %d\n", err);
- } else
-#endif /* WITH_WSREP */
+ if (err == DB_SUCCESS)
+#endif
err = row_update_cascade_for_mysql(thr, cascade,
foreign->foreign_table);
diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc
index 0cdd5ac2452..0a82a4b91a9 100644
--- a/storage/xtradb/handler/ha_innodb.cc
+++ b/storage/xtradb/handler/ha_innodb.cc
@@ -10907,9 +10907,17 @@ wsrep_key_type_to_str(wsrep_key_type type)
return "unknown";
}
+/** Append foreign key information for Galera processing. This is
+local operation and no cluster communication done.
+@param[in] trx Transaction
+@param[in] foreign Foreign key constraint
+@param[in] rec Index record in the child table
+@param[in] index Clustered index
+@param[in] key_type Access type of this key
+ (shared, exclusive, semi...) */
+UNIV_INTERN
ulint
wsrep_append_foreign_key(
-/*===========================*/
trx_t* trx, /*!< in: trx */
dict_foreign_t* foreign, /*!< in: foreign key constraint */
const rec_t* rec, /*!<in: clustered index record */
@@ -10923,15 +10931,25 @@ wsrep_append_foreign_key(
ulint rcode = DB_SUCCESS;
char cache_key[513] = {'\0'};
int cache_key_len;
- bool const copy = true;
+ bool const copy = true;
+ byte key[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
+ ulint len = WSREP_MAX_SUPPORTED_KEY_LENGTH;
+ int i = 0;
+ dict_index_t* idx_target = NULL;
+ dict_index_t* idx = NULL;
+ char *p = NULL;
+ wsrep_buf_t wkey_part[3];
+ wsrep_key_t wkey = {wkey_part, 3};
+ wsrep_t *wsrep = NULL;
- if (!wsrep_on(trx->mysql_thd) ||
- wsrep_thd_exec_mode(thd) != LOCAL_STATE)
+ if (!wsrep_on(trx->mysql_thd)
+ || wsrep_thd_exec_mode(thd) != LOCAL_STATE) {
return DB_SUCCESS;
+ }
- if (!thd || !foreign ||
- (!foreign->referenced_table && !foreign->foreign_table))
- {
+ if (!thd
+ || !foreign
+ || (!foreign->referenced_table && !foreign->foreign_table)) {
WSREP_INFO("FK: %s missing in: %s",
(!thd) ? "thread" :
((!foreign) ? "constraint" :
@@ -10939,44 +10957,38 @@ wsrep_append_foreign_key(
"referenced table" : "foreign table")),
(thd && wsrep_thd_query(thd)) ?
wsrep_thd_query(thd) : "void");
- return DB_ERROR;
+ goto error_handling;
}
- if ( !((referenced) ?
- foreign->referenced_table : foreign->foreign_table))
- {
+ if (!((referenced) ?
+ foreign->referenced_table : foreign->foreign_table)) {
WSREP_DEBUG("pulling %s table into cache",
(referenced) ? "referenced" : "foreign");
mutex_enter(&(dict_sys->mutex));
- if (referenced)
- {
+ if (referenced) {
foreign->referenced_table =
dict_table_get_low(
foreign->referenced_table_name_lookup);
- if (foreign->referenced_table)
- {
+ if (foreign->referenced_table) {
foreign->referenced_index =
wsrep_dict_foreign_find_index(
foreign->referenced_table, NULL,
foreign->referenced_col_names,
- foreign->n_fields,
+ foreign->n_fields,
foreign->foreign_index,
TRUE, FALSE);
}
- }
- else
- {
+ } else {
foreign->foreign_table =
dict_table_get_low(
foreign->foreign_table_name_lookup);
- if (foreign->foreign_table)
- {
+ if (foreign->foreign_table) {
foreign->foreign_index =
wsrep_dict_foreign_find_index(
foreign->foreign_table, NULL,
foreign->foreign_col_names,
foreign->n_fields,
- foreign->referenced_index,
+ foreign->referenced_index,
TRUE, FALSE);
}
}
@@ -10984,30 +10996,28 @@ wsrep_append_foreign_key(
}
if ( !((referenced) ?
- foreign->referenced_table : foreign->foreign_table))
- {
+ foreign->referenced_table : foreign->foreign_table)) {
WSREP_WARN("FK: %s missing in query: %s",
(!foreign->referenced_table) ?
"referenced table" : "foreign table",
(wsrep_thd_query(thd)) ?
wsrep_thd_query(thd) : "void");
- return DB_ERROR;
+ goto error_handling;
}
- byte key[WSREP_MAX_SUPPORTED_KEY_LENGTH+1] = {'\0'};
- ulint len = WSREP_MAX_SUPPORTED_KEY_LENGTH;
- dict_index_t *idx_target = (referenced) ?
- foreign->referenced_index : index;
- dict_index_t *idx = (referenced) ?
+
+ idx_target = (referenced) ? foreign->referenced_index : index;
+ idx = (referenced) ?
UT_LIST_GET_FIRST(foreign->referenced_table->indexes) :
UT_LIST_GET_FIRST(foreign->foreign_table->indexes);
- int i = 0;
+
while (idx != NULL && idx != idx_target) {
if (innobase_strcasecmp (idx->name, innobase_index_reserve_name) != 0) {
i++;
}
idx = UT_LIST_GET_NEXT(indexes, idx);
}
+
ut_a(idx);
key[0] = (char)i;
@@ -11024,7 +11034,7 @@ wsrep_append_foreign_key(
(index && index->table_name) ? index->table_name :
"void table",
wsrep_thd_query(thd));
- return DB_ERROR;
+ goto error_handling;
}
strncpy(cache_key,
@@ -11033,17 +11043,11 @@ wsrep_append_foreign_key(
foreign->referenced_table->name :
foreign->foreign_table->name) :
foreign->foreign_table->name, sizeof(cache_key) - 1);
+
cache_key_len = strlen(cache_key);
-#ifdef WSREP_DEBUG_PRINT
- ulint j;
- fprintf(stderr, "FK parent key, table: %s %s len: %lu ",
- cache_key, (shared) ? "shared" : "exclusive", len+1);
- for (j=0; j<len+1; j++) {
- fprintf(stderr, " %hhX, ", key[j]);
- }
- fprintf(stderr, "\n");
-#endif
- char *p = strchr(cache_key, '/');
+
+ p = strchr(cache_key, '/');
+
if (p) {
*p = '\0';
} else {
@@ -11052,8 +11056,6 @@ wsrep_append_foreign_key(
foreign->foreign_table->name);
}
- wsrep_buf_t wkey_part[3];
- wsrep_key_t wkey = {wkey_part, 3};
if (!wsrep_prepare_key(
(const uchar*)cache_key,
cache_key_len + 1,
@@ -11063,9 +11065,11 @@ wsrep_append_foreign_key(
WSREP_WARN("key prepare failed for cascaded FK: %s",
(wsrep_thd_query(thd)) ?
wsrep_thd_query(thd) : "void");
- return DB_ERROR;
+ goto error_handling;
}
- wsrep_t *wsrep= get_wsrep();
+
+ wsrep= get_wsrep();
+
rcode = (int)wsrep->append_key(
wsrep,
wsrep_ws_handle(thd, trx),
@@ -11073,15 +11077,27 @@ wsrep_append_foreign_key(
1,
key_type,
copy);
+
+ /* Force error injection for testing error reporting. */
+ DBUG_EXECUTE_IF("wsrep_append_foreign_key_error",
+ rcode = 1;
+ );
+
if (rcode) {
DBUG_PRINT("wsrep", ("row key failed: %lu", rcode));
WSREP_ERROR("Appending cascaded fk row key failed: %s, %lu",
(wsrep_thd_query(thd)) ?
wsrep_thd_query(thd) : "void", rcode);
- return DB_ERROR;
+ goto error_handling;
}
return DB_SUCCESS;
+
+error_handling:
+
+ wsrep_report_foreign_key_error(trx, foreign, rec, NULL);
+
+ return DB_NO_REFERENCED_ROW;
}
static int
diff --git a/storage/xtradb/include/row0ins.h b/storage/xtradb/include/row0ins.h
index 54e7fa6d1fb..d5aaec9f961 100644
--- a/storage/xtradb/include/row0ins.h
+++ b/storage/xtradb/include/row0ins.h
@@ -189,6 +189,21 @@ row_ins_step(
/*=========*/
que_thr_t* thr); /*!< in: query thread */
+#ifdef WITH_WSREP
+/* Report foreign key error from Galera append key.
+@param[in] trx Transaction
+@param[in] foreign Foreign key constraint
+@param[in] rec Index record in the child table
+@param[in] entry Index entry in the parent table */
+UNIV_INTERN
+void
+wsrep_report_foreign_key_error(
+ trx_t* trx,
+ dict_foreign_t* foreign,
+ const rec_t* rec,
+ const dtuple_t* entry);
+#endif /* WITH_WSREP */
+
/* Insert node structure */
struct ins_node_t{
diff --git a/storage/xtradb/row/row0ins.cc b/storage/xtradb/row/row0ins.cc
index 0eefdb17f39..aa2bec1ce13 100644
--- a/storage/xtradb/row/row0ins.cc
+++ b/storage/xtradb/row/row0ins.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, 2018, MariaDB Corporation.
+Copyright (c) 2017, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -796,36 +796,26 @@ row_ins_foreign_trx_print(
ut_ad(mutex_own(&dict_foreign_err_mutex));
}
-/*********************************************************************//**
-Reports a foreign key error associated with an update or a delete of a
-parent table index entry. */
+/** Report a foreign key error to a provided file handle
+@param[in] ef file handle
+@param[in] errstr error string from the viewpoint
+ of the parent table
+@param[in] trx Transaction
+@param[in] foreign Foreign key constraint
+@param[in] rec Index record in the child table
+@param[in] entry Index entry in the parent table */
static
void
-row_ins_foreign_report_err(
-/*=======================*/
- const 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 */
- const rec_t* rec, /*!< in: a matching index record in the
- child table */
- const dtuple_t* entry) /*!< in: index entry in the parent
- table */
+row_ins_foreign_report_err_low(
+ FILE* ef,
+ const char* errstr,
+ trx_t* trx,
+ dict_foreign_t* foreign,
+ const rec_t* rec,
+ const dtuple_t* entry)
{
std::string fk_str;
- if (srv_read_only_mode) {
- return;
- }
-
- FILE* ef = dict_foreign_err_file;
- trx_t* trx = thr_get_trx(thr);
-
- row_ins_set_detailed(trx, foreign);
-
- row_ins_foreign_trx_print(trx);
-
fputs("Foreign key constraint fails for table ", ef);
ut_print_name(ef, trx, TRUE, foreign->foreign_table_name);
fputs(":\n", ef);
@@ -851,6 +841,35 @@ row_ins_foreign_report_err(
fputs(", the record is not available\n", ef);
}
putc('\n', ef);
+}
+
+/*********************************************************************//**
+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(
+/*=======================*/
+ const 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 */
+ const rec_t* rec, /*!< in: a matching index record in the
+ child table */
+ const dtuple_t* entry) /*!< in: index entry in the parent
+ table */
+{
+ if (srv_read_only_mode) {
+ return;
+ }
+
+ FILE* ef = dict_foreign_err_file;
+ trx_t* trx = thr_get_trx(thr);
+
+ row_ins_set_detailed(trx, foreign);
+ row_ins_foreign_trx_print(trx);
+ row_ins_foreign_report_err_low(ef, errstr, trx, foreign, rec, entry);
mutex_exit(&dict_foreign_err_mutex);
}
@@ -941,6 +960,7 @@ row_ins_invalidate_query_cache(
innobase_invalidate_query_cache(thr_get_trx(thr), buf, len);
mem_free(buf);
}
+
#ifdef WITH_WSREP
dberr_t wsrep_append_foreign_key(trx_t *trx,
dict_foreign_t* foreign,
@@ -948,6 +968,25 @@ dberr_t wsrep_append_foreign_key(trx_t *trx,
dict_index_t* clust_index,
ibool referenced,
enum wsrep_key_type key_type);
+
+/* Report foreign key error from Galera append key.
+@param[in] trx Transaction
+@param[in] foreign Foreign key constraint
+@param[in] rec Index record in the child table
+@param[in] entry Index entry in the parent table */
+UNIV_INTERN
+void
+wsrep_report_foreign_key_error(
+ trx_t* trx,
+ dict_foreign_t* foreign,
+ const rec_t* rec,
+ const dtuple_t* entry)
+{
+ mutex_enter(&dict_foreign_err_mutex);
+ row_ins_foreign_report_err_low(stderr, "WSREP: append foreign key failed ",
+ trx, foreign, rec, entry);
+ mutex_exit(&dict_foreign_err_mutex);
+}
#endif /* WITH_WSREP */
/*********************************************************************//**
@@ -1299,14 +1338,11 @@ row_ins_foreign_check_on_constraint(
err = wsrep_append_foreign_key(
thr_get_trx(thr),
foreign,
- clust_rec,
+ cascade->pcur->old_rec,
clust_index,
FALSE, WSREP_KEY_EXCLUSIVE);
- if (err != DB_SUCCESS) {
- fprintf(stderr,
- "WSREP: foreign key append failed: %d\n", err);
- } else
-#endif /* WITH_WSREP */
+ if (err == DB_SUCCESS)
+#endif
err = row_update_cascade_for_mysql(thr, cascade,
foreign->foreign_table);