1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-41666 Corruption on statement rollback when ON UPDATE SET NULL affects the base column

Problem:
=======
  - When a foreign key column is the base column of a
virtual column that is indexed together with other virtual
columns, a cascaded update (ON UPDATE/DELETE SET NULL)
computed the old value of only the dependent virtual column.
The other virtual key parts of the index stayed NULL in
update->old_vrow. If the statement was rolled back,
row_upd_store_v_row() used those NULLs, so
row_upd_sec_index_entry() searched the secondary
index for a record  that did not exist and hit
the "Record in index was not found on update" assertion.

Solution:
=========
dict_mem_fill_vcol_set_for_base_col():  Add every virtual key part
of each index that contains the dependent virtual column to
dict_foreign_t::v_cols, as dict_mem_fill_vcol_from_v_indexes()
already does. row_ins_foreign_fill_virtual() then computes the
old values of all of them.

Based on mysql/mysql-server@c48fa1e57a
This commit is contained in:
Thirunarayanan Balathandayuthapani
2026-10-21 20:41:36 +05:30
parent 4946e66139
commit f941fc810c
3 changed files with 114 additions and 4 deletions

View File

@@ -84,3 +84,53 @@ select * from b;
cola v_cola p_cola c_cola
10 2 2 12
drop table b, a;
#
# MDEV-41666 Corruption on statement rollback when
# ON UPDATE SET NULL affects the base column
#
CREATE TABLE t18 (
c5 INT NOT NULL,
PRIMARY KEY (c5)
) ENGINE=InnoDB;
CREATE TABLE t21 (
c1 INT NOT NULL,
c2 INT DEFAULT NULL,
c2v INT AS (c2 DIV 1), c1v INT AS (FLOOR(c1)),
KEY i15 (c2v, c1v),
CONSTRAINT t21_ibfk_1 FOREIGN KEY (c2) REFERENCES t18 (c5) ON UPDATE SET NULL
) ENGINE=InnoDB;
INSERT INTO t18 (c5) VALUES (1), (2);
INSERT INTO t21 (c1, c2) VALUES (0, 1);
UPDATE t18 SET c5 = 3;
ERROR 23000: Duplicate entry '3' for key 'PRIMARY'
SELECT c5 FROM t18 ORDER BY c5;
c5
1
2
SELECT c1, c2 FROM t21;
c1 c2
0 1
DROP TABLE t21, t18;
CREATE TABLE parent (
id INT PRIMARY KEY
) ENGINE=InnoDB;
CREATE TABLE child (
a INT DEFAULT NULL,
parent_id INT DEFAULT NULL,
av INT AS (IFNULL(a, 0)), pv INT AS (IFNULL(parent_id, 0)),
KEY idx_functional (av, pv),
CONSTRAINT fk_parent FOREIGN KEY (parent_id) REFERENCES parent (id)
ON DELETE SET NULL
) ENGINE=InnoDB;
INSERT INTO parent VALUES (1);
INSERT INTO child(a, parent_id) VALUES (NULL, 1);
START TRANSACTION;
DELETE FROM parent WHERE id = 1;
ROLLBACK;
SELECT id FROM parent;
id
1
SELECT a, parent_id FROM child;
a parent_id
NULL 1
DROP TABLE child, parent;

View File

@@ -59,3 +59,59 @@ delete from a;
select * from a;
select * from b;
drop table b, a;
--echo #
--echo # MDEV-41666 Corruption on statement rollback when
--echo # ON UPDATE SET NULL affects the base column
--echo #
CREATE TABLE t18 (
c5 INT NOT NULL,
PRIMARY KEY (c5)
) ENGINE=InnoDB;
CREATE TABLE t21 (
c1 INT NOT NULL,
c2 INT DEFAULT NULL,
c2v INT AS (c2 DIV 1), c1v INT AS (FLOOR(c1)),
KEY i15 (c2v, c1v),
CONSTRAINT t21_ibfk_1 FOREIGN KEY (c2) REFERENCES t18 (c5) ON UPDATE SET NULL
) ENGINE=InnoDB;
INSERT INTO t18 (c5) VALUES (1), (2);
INSERT INTO t21 (c1, c2) VALUES (0, 1);
# This first changes 1 to 3 and cascades c2 = 1 to c2 = NULL.
# Updating the second parent row (2 to 3) then hits the duplicate
# primary key. The whole statement must roll back, including the
# functional-index update in t21.
--error ER_DUP_ENTRY
UPDATE t18 SET c5 = 3;
SELECT c5 FROM t18 ORDER BY c5;
SELECT c1, c2 FROM t21;
DROP TABLE t21, t18;
# Bug#39872048: DELETE with ON DELETE SET NULL must preserve all functional
# index key parts when its effects are rolled back.
CREATE TABLE parent (
id INT PRIMARY KEY
) ENGINE=InnoDB;
CREATE TABLE child (
a INT DEFAULT NULL,
parent_id INT DEFAULT NULL,
av INT AS (IFNULL(a, 0)), pv INT AS (IFNULL(parent_id, 0)),
KEY idx_functional (av, pv),
CONSTRAINT fk_parent FOREIGN KEY (parent_id) REFERENCES parent (id)
ON DELETE SET NULL
) ENGINE=InnoDB;
INSERT INTO parent VALUES (1);
INSERT INTO child(a, parent_id) VALUES (NULL, 1);
START TRANSACTION;
DELETE FROM parent WHERE id = 1;
ROLLBACK;
SELECT id FROM parent;
SELECT a, parent_id FROM child;
DROP TABLE child, parent;

View File

@@ -964,11 +964,15 @@ dict_mem_fill_vcol_set_for_base_col(
table,
v_col->base_col[j]->ind)) == 0) {
if (*v_cols == NULL) {
*v_cols = UT_NEW_NOKEY(dict_vcol_set());
/* A change to a base column changes the
complete secondary-index record. Include
every virtual key part of each affected index,
so that a cascaded update has the old values
required for undo. */
for (const auto& v_idx : v_col->v_indexes) {
dict_mem_fill_vcol_has_index(
v_idx.index, v_cols);
}
(*v_cols)->insert(v_col);
}
}
}