summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThirunarayanan Balathandayuthapani <thiru@mariadb.com>2020-09-02 16:15:02 +0530
committerThirunarayanan Balathandayuthapani <thiru@mariadb.com>2020-09-02 16:16:45 +0530
commit837bbbafc535d14ea102ced983349fd29b84f782 (patch)
treebaa6bdabac006dd30eab0535852fcb2667f609ef
parent054f96ecff111c0925edba5657a69611cb243643 (diff)
downloadmariadb-git-837bbbafc535d14ea102ced983349fd29b84f782.tar.gz
MDEV-23470 InnoDB: Failing assertion: cmp < 0 in row_ins_check_foreign_constraint
During insertion of clustered index, InnoDB does the check for foreign key constraints. Problem is that it uses the clustered index entry to search indexes of referenced tables and it could lead to unexpected result when there is no foreign index. Solution: ======== Rebuild the tuple based on foreign column names before searching it on reference index when there is no foreign index.
-rw-r--r--mysql-test/suite/innodb/r/foreign-keys.result16
-rw-r--r--mysql-test/suite/innodb/t/foreign-keys.test19
-rw-r--r--storage/innobase/row/row0ins.cc74
3 files changed, 100 insertions, 9 deletions
diff --git a/mysql-test/suite/innodb/r/foreign-keys.result b/mysql-test/suite/innodb/r/foreign-keys.result
index 5cbbb5298de..05e68b0c3db 100644
--- a/mysql-test/suite/innodb/r/foreign-keys.result
+++ b/mysql-test/suite/innodb/r/foreign-keys.result
@@ -220,3 +220,19 @@ drop table t1,t2;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
drop table t1,t2;
ERROR 42S02: Unknown table 'test.t2'
+#
+# MDEV-23470 InnoDB: Failing assertion: cmp < 0 in
+# row_ins_check_foreign_constraint
+#
+CREATE TABLE t1(f1 INT NOT NULL PRIMARY KEY, f2 INT NOT NULL)ENGINE=InnoDB;
+CREATE TABLE t2(f1 VARCHAR(100), f2 INT NOT NULL,
+INDEX(f2))ENGINE=InnoDB;
+INSERT INTO t1 VALUES(99, 2);
+ALTER TABLE t2 ADD FOREIGN KEY(f2) REFERENCES t1(f1);
+SET FOREIGN_KEY_CHECKS=0;
+DROP INDEX f2 ON t2;
+SET FOREIGN_KEY_CHECKS=1;
+INSERT INTO t2 VALUES('G', 3);
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`f2`) REFERENCES `t1` (`f1`))
+DROP TABLE t2, t1;
+SET FOREIGN_KEY_CHECKS=DEFAULT;
diff --git a/mysql-test/suite/innodb/t/foreign-keys.test b/mysql-test/suite/innodb/t/foreign-keys.test
index 58a71d11a6a..53fbe8d00dc 100644
--- a/mysql-test/suite/innodb/t/foreign-keys.test
+++ b/mysql-test/suite/innodb/t/foreign-keys.test
@@ -250,3 +250,22 @@ show create table t2;
drop table t1,t2;
--error ER_BAD_TABLE_ERROR
drop table t1,t2;
+
+--echo #
+--echo # MDEV-23470 InnoDB: Failing assertion: cmp < 0 in
+--echo # row_ins_check_foreign_constraint
+--echo #
+CREATE TABLE t1(f1 INT NOT NULL PRIMARY KEY, f2 INT NOT NULL)ENGINE=InnoDB;
+CREATE TABLE t2(f1 VARCHAR(100), f2 INT NOT NULL,
+ INDEX(f2))ENGINE=InnoDB;
+
+INSERT INTO t1 VALUES(99, 2);
+ALTER TABLE t2 ADD FOREIGN KEY(f2) REFERENCES t1(f1);
+
+SET FOREIGN_KEY_CHECKS=0;
+DROP INDEX f2 ON t2;
+SET FOREIGN_KEY_CHECKS=1;
+--error ER_NO_REFERENCED_ROW_2
+INSERT INTO t2 VALUES('G', 3);
+DROP TABLE t2, t1;
+SET FOREIGN_KEY_CHECKS=DEFAULT;
diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
index b8c8a73842f..af6d2f5fa83 100644
--- a/storage/innobase/row/row0ins.cc
+++ b/storage/innobase/row/row0ins.cc
@@ -1905,6 +1905,39 @@ exit_func:
DBUG_RETURN(err);
}
+/** Sets the values of the dtuple fields in ref_entry from the values of
+foreign columns in entry.
+@param[in] foreign foreign key constraint
+@param[in] index clustered index
+@param[in] entry tuple of clustered index
+@param[in] ref_entry tuple of foreign columns
+@return true if all foreign key fields present in clustered index */
+static
+bool row_ins_foreign_index_entry(dict_foreign_t *foreign,
+ const dict_index_t *index,
+ const dtuple_t *entry,
+ dtuple_t *ref_entry)
+{
+ for (ulint i= 0; i < foreign->n_fields; i++)
+ {
+ for (ulint j= 0; j < index->n_fields; j++)
+ {
+ const char *col_name= dict_table_get_col_name(
+ index->table, dict_index_get_nth_col_no(index, j));
+ if (0 == innobase_strcasecmp(col_name, foreign->foreign_col_names[i]))
+ {
+ dfield_copy(&ref_entry->fields[i], &entry->fields[j]);
+ goto got_match;
+ }
+ }
+ return false;
+got_match:
+ continue;
+ }
+
+ return true;
+}
+
/***************************************************************//**
Checks if foreign key constraints fail for an index entry. If index
is not mentioned in any constraint, this function does nothing,
@@ -1923,9 +1956,10 @@ row_ins_check_foreign_constraints(
que_thr_t* thr) /*!< in: query thread */
{
dict_foreign_t* foreign;
- dberr_t err;
+ dberr_t err = DB_SUCCESS;
trx_t* trx;
ibool got_s_lock = FALSE;
+ mem_heap_t* heap = NULL;
DBUG_ASSERT(index->is_primary() == pk);
@@ -1935,13 +1969,36 @@ row_ins_check_foreign_constraints(
"foreign_constraint_check_for_ins");
for (dict_foreign_set::iterator it = table->foreign_set.begin();
- it != table->foreign_set.end();
+ err == DB_SUCCESS && it != table->foreign_set.end();
++it) {
foreign = *it;
if (foreign->foreign_index == index
|| (pk && !foreign->foreign_index)) {
+
+ dtuple_t* ref_tuple = entry;
+ if (UNIV_UNLIKELY(!foreign->foreign_index)) {
+ /* Change primary key entry to
+ foreign key index entry */
+ if (!heap) {
+ heap = mem_heap_create(1000);
+ } else {
+ mem_heap_empty(heap);
+ }
+
+ ref_tuple = dtuple_create(
+ heap, foreign->n_fields);
+ dtuple_set_n_fields_cmp(
+ ref_tuple, foreign->n_fields);
+ if (!row_ins_foreign_index_entry(
+ foreign, index, entry, ref_tuple)) {
+ err = DB_NO_REFERENCED_ROW;
+ break;
+ }
+
+ }
+
dict_table_t* ref_table = NULL;
dict_table_t* referenced_table
= foreign->referenced_table;
@@ -1971,7 +2028,7 @@ row_ins_check_foreign_constraints(
table from being dropped while the check is running. */
err = row_ins_check_foreign_constraint(
- TRUE, foreign, table, entry, thr);
+ TRUE, foreign, table, ref_tuple, thr);
if (referenced_table) {
my_atomic_addlint(
@@ -1986,15 +2043,14 @@ row_ins_check_foreign_constraints(
if (ref_table != NULL) {
dict_table_close(ref_table, FALSE, FALSE);
}
-
- if (err != DB_SUCCESS) {
-
- return(err);
- }
}
}
- return(DB_SUCCESS);
+ if (UNIV_LIKELY_NULL(heap)) {
+ mem_heap_free(heap);
+ }
+
+ return err;
}
/***************************************************************//**