Commit ae8ff3a0 authored by Nikita Malyavin's avatar Nikita Malyavin

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.
parent bc2dbdb6
......@@ -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;
......@@ -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;
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment