Skip to content
Permalink
Browse files
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.
  • Loading branch information
FooBarrior committed Sep 14, 2020
1 parent bc2dbdb commit ae8ff3a
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 23 deletions.
@@ -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;

0 comments on commit ae8ff3a

Please sign in to comment.