summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Malyavin <nikitamalyavin@gmail.com>2020-09-09 11:41:06 +1000
committerNikita Malyavin <nikitamalyavin@gmail.com>2020-09-14 18:08:56 +1000
commitae8ff3a067c046bf7df1cce175078914c6879d81 (patch)
tree706739d10cf0dd39ce079e520ade58bedb9db0de
parentbc2dbdb601e0c55b61c2ecbe2e64c119e41fec70 (diff)
downloadmariadb-git-ae8ff3a067c046bf7df1cce175078914c6879d81.tar.gz
MDEV-20396 Server crashes after DELETE with SEL NULL Foreign key and a
virtual column in index Problem: row_ins_foreign_fill_virtual was unconditionally set virtual fields to NULL even though the field is not a part of a foreign key (but a part of an index) Solution: The new virtual value should be computed with regard to cascade updates.
-rw-r--r--mysql-test/suite/gcol/r/innodb_virtual_fk.result29
-rw-r--r--mysql-test/suite/gcol/t/innodb_virtual_fk.test32
-rw-r--r--storage/innobase/row/row0ins.cc41
3 files changed, 79 insertions, 23 deletions
diff --git a/mysql-test/suite/gcol/r/innodb_virtual_fk.result b/mysql-test/suite/gcol/r/innodb_virtual_fk.result
index d5b4755e3c5..a3cdacb67a2 100644
--- a/mysql-test/suite/gcol/r/innodb_virtual_fk.result
+++ b/mysql-test/suite/gcol/r/innodb_virtual_fk.result
@@ -740,3 +740,32 @@ t1 CREATE TABLE `t1` (
KEY `v4` (`v4`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
DROP TABLE t1;
+#
+# MDEV-20396 Server crashes after DELETE with SEL NULL Foreign key and a
+# virtual column in index
+#
+CREATE TABLE parent
+(
+ID int unsigned NOT NULL,
+PRIMARY KEY (ID)
+);
+CREATE TABLE child
+(
+ID int unsigned NOT NULL,
+ParentID int unsigned NULL,
+Value int unsigned NOT NULL DEFAULT 0,
+Flag int unsigned AS (Value) VIRTUAL,
+PRIMARY KEY (ID),
+KEY (ParentID, Flag),
+FOREIGN KEY (ParentID) REFERENCES parent (ID) ON DELETE SET NULL
+ON UPDATE CASCADE
+);
+INSERT INTO parent (ID) VALUES (100);
+INSERT INTO child (ID,ParentID,Value) VALUES (123123,100,1);
+DELETE FROM parent WHERE ID=100;
+select * from child;
+ID ParentID Value Flag
+123123 NULL 1 1
+INSERT INTO parent (ID) VALUES (100);
+UPDATE child SET ParentID=100 WHERE ID=123123;
+DROP TABLE child, parent;
diff --git a/mysql-test/suite/gcol/t/innodb_virtual_fk.test b/mysql-test/suite/gcol/t/innodb_virtual_fk.test
index c484bb5dc0c..226bacabaef 100644
--- a/mysql-test/suite/gcol/t/innodb_virtual_fk.test
+++ b/mysql-test/suite/gcol/t/innodb_virtual_fk.test
@@ -605,3 +605,35 @@ ALTER TABLE t1 ADD CONSTRAINT fk FOREIGN KEY (v4) REFERENCES nosuch(col);
SHOW CREATE TABLE t1;
# Cleanup
DROP TABLE t1;
+
+--echo #
+--echo # MDEV-20396 Server crashes after DELETE with SEL NULL Foreign key and a
+--echo # virtual column in index
+--echo #
+CREATE TABLE parent
+(
+ ID int unsigned NOT NULL,
+ PRIMARY KEY (ID)
+);
+
+CREATE TABLE child
+(
+ ID int unsigned NOT NULL,
+ ParentID int unsigned NULL,
+ Value int unsigned NOT NULL DEFAULT 0,
+ Flag int unsigned AS (Value) VIRTUAL,
+ PRIMARY KEY (ID),
+ KEY (ParentID, Flag),
+ FOREIGN KEY (ParentID) REFERENCES parent (ID) ON DELETE SET NULL
+ ON UPDATE CASCADE
+);
+
+INSERT INTO parent (ID) VALUES (100);
+INSERT INTO child (ID,ParentID,Value) VALUES (123123,100,1);
+DELETE FROM parent WHERE ID=100;
+select * from child;
+INSERT INTO parent (ID) VALUES (100);
+UPDATE child SET ParentID=100 WHERE ID=123123;
+
+# Cleanup
+DROP TABLE child, parent;
diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
index af6d2f5fa83..87f62125c03 100644
--- a/storage/innobase/row/row0ins.cc
+++ b/storage/innobase/row/row0ins.cc
@@ -945,8 +945,6 @@ row_ins_foreign_fill_virtual(
&ext, cascade->heap);
n_diff = update->n_fields;
- update->n_fields += n_v_fld;
-
if (index->table->vc_templ == NULL) {
/** This can occur when there is a cascading
delete or update after restart. */
@@ -979,7 +977,7 @@ row_ins_foreign_fill_virtual(
return DB_COMPUTE_VALUE_FAILED;
}
- upd_field = upd_get_nth_field(update, n_diff);
+ upd_field = update->fields + n_diff;
upd_field->old_v_val = static_cast<dfield_t*>(
mem_heap_alloc(cascade->heap,
@@ -989,30 +987,27 @@ row_ins_foreign_fill_virtual(
upd_field_set_v_field_no(upd_field, i, index);
- if (node->is_delete
- ? (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)
- : (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL)) {
-
- dfield_set_null(&upd_field->new_val);
- }
-
- if (!node->is_delete
- && (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)) {
-
- dfield_t* new_vfield = innobase_get_computed_value(
- update->old_vrow, col, index,
- &vc.heap, update->heap, NULL, thd,
- mysql_table, record, NULL,
- node->update, foreign);
+ bool set_null =
+ node->is_delete
+ ? (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)
+ : (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL);
- if (new_vfield == NULL) {
- return DB_COMPUTE_VALUE_FAILED;
- }
+ dfield_t* new_vfield = innobase_get_computed_value(
+ update->old_vrow, col, index,
+ &vc.heap, update->heap, NULL, thd,
+ mysql_table, record, NULL,
+ set_null ? update : node->update, foreign);
- dfield_copy(&(upd_field->new_val), new_vfield);
+ if (new_vfield == NULL) {
+ return DB_COMPUTE_VALUE_FAILED;
}
- n_diff++;
+ dfield_copy(&upd_field->new_val, new_vfield);
+
+ if (!dfield_datas_are_binary_equal(
+ upd_field->old_v_val,
+ &upd_field->new_val, 0))
+ n_diff++;
}
update->n_fields = n_diff;