MDEV-31441 BLOB corruption on UPDATE of PRIMARY KEY with FOREIGN KEY
row_upd_clust_rec_by_insert(): If we are resuming from a lock wait, reset the 'disowned' flag of the BLOB pointers in 'entry' that we copied from 'rec' on which we had invoked btr_cur_disown_inherited_fields() before the lock wait started. In this way, the inserted record with the updated PRIMARY KEY value will have the BLOB ownership associated with itself, like it is supposed to be. Note: If the lock wait had been aborted, then rollback would have invoked btr_cur_unmark_extern_fields() and no corruption would be possible. Reviewed by: Vladislav Lesin Tested by: Matthias Leich
This commit is contained in:
parent
e996f77cd8
commit
cd79f10211
@ -727,7 +727,9 @@ pk a b
|
|||||||
13 0 1
|
13 0 1
|
||||||
14 0 1
|
14 0 1
|
||||||
15 1 0
|
15 1 0
|
||||||
disconnect con1;
|
connection con1;
|
||||||
|
COMMIT;
|
||||||
|
connection default;
|
||||||
InnoDB 0 transactions not purged
|
InnoDB 0 transactions not purged
|
||||||
CHECK TABLE t1;
|
CHECK TABLE t1;
|
||||||
Table Op Msg_type Msg_text
|
Table Op Msg_type Msg_text
|
||||||
@ -906,5 +908,26 @@ CONSTRAINT FK_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id)
|
|||||||
ALTER TABLE t1 MODIFY id INT unsigned AUTO_INCREMENT;
|
ALTER TABLE t1 MODIFY id INT unsigned AUTO_INCREMENT;
|
||||||
DROP TABLE t1,t2;
|
DROP TABLE t1,t2;
|
||||||
#
|
#
|
||||||
# End of 10.4 tests
|
# MDEV-31441 BLOB corruption on UPDATE of PRIMARY KEY with FOREIGN KEY
|
||||||
#
|
#
|
||||||
|
CREATE TABLE t1 (pk INT PRIMARY KEY, t TEXT) ENGINE=InnoDB;
|
||||||
|
CREATE TABLE t2 (pk INT PRIMARY KEY, FOREIGN KEY (pk) REFERENCES t1(pk))
|
||||||
|
ENGINE=InnoDB;
|
||||||
|
SET @blob = REPEAT('A', @@innodb_page_size / 2);
|
||||||
|
INSERT INTO t1 SET pk=1, t=@blob;
|
||||||
|
INSERT INTO t2 SET pk=1;
|
||||||
|
connection con1;
|
||||||
|
BEGIN;
|
||||||
|
DELETE FROM t2;
|
||||||
|
connection default;
|
||||||
|
UPDATE t1 SET pk=12;
|
||||||
|
connection con1;
|
||||||
|
COMMIT;
|
||||||
|
disconnect con1;
|
||||||
|
connection default;
|
||||||
|
UPDATE t1 SET pk=1;
|
||||||
|
SELECT pk,t=@blob FROM t1;
|
||||||
|
pk t=@blob
|
||||||
|
1 1
|
||||||
|
DROP TABLE t2, t1;
|
||||||
|
# End of 10.4 tests
|
||||||
|
@ -732,7 +732,9 @@ SELECT a FROM t1 FORCE INDEX(a);
|
|||||||
# the "goto rollback_to_savept" in row_mysql_handle_errors() is reverted.
|
# the "goto rollback_to_savept" in row_mysql_handle_errors() is reverted.
|
||||||
SELECT * FROM t1;
|
SELECT * FROM t1;
|
||||||
# Allow purge to continue by closing the read view.
|
# Allow purge to continue by closing the read view.
|
||||||
disconnect con1;
|
connection con1;
|
||||||
|
COMMIT;
|
||||||
|
connection default;
|
||||||
|
|
||||||
# Wait for purge. With the fix reverted, the server would crash here.
|
# Wait for purge. With the fix reverted, the server would crash here.
|
||||||
--source include/wait_all_purged.inc
|
--source include/wait_all_purged.inc
|
||||||
@ -954,7 +956,35 @@ ALTER TABLE t1 MODIFY id INT unsigned AUTO_INCREMENT;
|
|||||||
DROP TABLE t1,t2;
|
DROP TABLE t1,t2;
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # End of 10.4 tests
|
--echo # MDEV-31441 BLOB corruption on UPDATE of PRIMARY KEY with FOREIGN KEY
|
||||||
--echo #
|
--echo #
|
||||||
|
|
||||||
|
CREATE TABLE t1 (pk INT PRIMARY KEY, t TEXT) ENGINE=InnoDB;
|
||||||
|
CREATE TABLE t2 (pk INT PRIMARY KEY, FOREIGN KEY (pk) REFERENCES t1(pk))
|
||||||
|
ENGINE=InnoDB;
|
||||||
|
|
||||||
|
SET @blob = REPEAT('A', @@innodb_page_size / 2);
|
||||||
|
INSERT INTO t1 SET pk=1, t=@blob;
|
||||||
|
INSERT INTO t2 SET pk=1;
|
||||||
|
--connection con1
|
||||||
|
BEGIN;
|
||||||
|
DELETE FROM t2;
|
||||||
|
--connection default
|
||||||
|
# The following will be blocked by a FOREIGN KEY check on pk=1 in t2.
|
||||||
|
--send
|
||||||
|
UPDATE t1 SET pk=12;
|
||||||
|
--connection con1
|
||||||
|
let $wait_condition=
|
||||||
|
SELECT count(*) > 0 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE state='Updating';
|
||||||
|
--source include/wait_condition.inc
|
||||||
|
COMMIT;
|
||||||
|
--disconnect con1
|
||||||
|
--connection default
|
||||||
|
--reap
|
||||||
|
UPDATE t1 SET pk=1;
|
||||||
|
SELECT pk,t=@blob FROM t1;
|
||||||
|
DROP TABLE t2, t1;
|
||||||
|
|
||||||
|
--echo # End of 10.4 tests
|
||||||
|
|
||||||
--source include/wait_until_count_sessions.inc
|
--source include/wait_until_count_sessions.inc
|
||||||
|
@ -2698,6 +2698,25 @@ row_upd_clust_rec_by_insert_inherit_func(
|
|||||||
return(inherit);
|
return(inherit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Mark 'disowned' BLOBs as 'owned' and 'inherited' again,
|
||||||
|
after resuming from a lock wait.
|
||||||
|
@param entry clustered index entry */
|
||||||
|
static ATTRIBUTE_COLD void row_upd_reown_inherited_fields(dtuple_t *entry)
|
||||||
|
{
|
||||||
|
for (ulint i= 0; i < entry->n_fields; i++)
|
||||||
|
{
|
||||||
|
const dfield_t *dfield= dtuple_get_nth_field(entry, i);
|
||||||
|
if (dfield_is_ext(dfield))
|
||||||
|
{
|
||||||
|
byte *blob_len= static_cast<byte*>(dfield->data) +
|
||||||
|
dfield->len - (BTR_EXTERN_FIELD_REF_SIZE - BTR_EXTERN_LEN);
|
||||||
|
ut_ad(*blob_len & BTR_EXTERN_OWNER_FLAG);
|
||||||
|
*blob_len= byte(*blob_len & ~BTR_EXTERN_OWNER_FLAG) |
|
||||||
|
BTR_EXTERN_INHERITED_FLAG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************//**
|
/***********************************************************//**
|
||||||
Marks the clustered index record deleted and inserts the updated version
|
Marks the clustered index record deleted and inserts the updated version
|
||||||
of the record to the index. This function should be used when the ordering
|
of the record to the index. This function should be used when the ordering
|
||||||
@ -2776,12 +2795,16 @@ row_upd_clust_rec_by_insert(
|
|||||||
/* If the clustered index record is already delete
|
/* If the clustered index record is already delete
|
||||||
marked, then we are here after a DB_LOCK_WAIT.
|
marked, then we are here after a DB_LOCK_WAIT.
|
||||||
Skip delete marking clustered index and disowning
|
Skip delete marking clustered index and disowning
|
||||||
its blobs. */
|
its blobs. Mark the BLOBs in the index entry
|
||||||
|
(which we copied from the already "disowned" rec)
|
||||||
|
as "owned", like it was on the previous call of
|
||||||
|
row_upd_clust_rec_by_insert(). */
|
||||||
ut_ad(row_get_rec_trx_id(rec, index, offsets)
|
ut_ad(row_get_rec_trx_id(rec, index, offsets)
|
||||||
== trx->id);
|
== trx->id);
|
||||||
ut_ad(!trx_undo_roll_ptr_is_insert(
|
ut_ad(!trx_undo_roll_ptr_is_insert(
|
||||||
row_get_rec_roll_ptr(rec, index,
|
row_get_rec_roll_ptr(rec, index,
|
||||||
offsets)));
|
offsets)));
|
||||||
|
row_upd_reown_inherited_fields(entry);
|
||||||
goto check_fk;
|
goto check_fk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user