diff options
author | Nikita Malyavin <nikitamalyavin@gmail.com> | 2020-09-09 11:41:06 +1000 |
---|---|---|
committer | Nikita Malyavin <nikitamalyavin@gmail.com> | 2020-09-14 18:08:56 +1000 |
commit | ae8ff3a067c046bf7df1cce175078914c6879d81 (patch) | |
tree | 706739d10cf0dd39ce079e520ade58bedb9db0de | |
parent | bc2dbdb601e0c55b61c2ecbe2e64c119e41fec70 (diff) | |
download | mariadb-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.result | 29 | ||||
-rw-r--r-- | mysql-test/suite/gcol/t/innodb_virtual_fk.test | 32 | ||||
-rw-r--r-- | storage/innobase/row/row0ins.cc | 41 |
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; |