Commit 4a8291fc authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-30531 Corrupt index(es) on busy table when using FOREIGN KEY

lock_wait(): Never return the transient error code DB_LOCK_WAIT.
In commit 78a04a4c (MDEV-29869)
some assignments assign trx->error_state = DB_SUCCESS were removed,
and it was possible that the field was left at its initial value
DB_LOCK_WAIT.

The test case for this is nondeterministic; without this fix, it
would only occasionally fail.

Reviewed by: Vladislav Lesin
parent e039720b
...@@ -1029,7 +1029,39 @@ CREATE OR REPLACE TABLE t1 (b INT) ENGINE=InnoDB; ...@@ -1029,7 +1029,39 @@ CREATE OR REPLACE TABLE t1 (b INT) ENGINE=InnoDB;
connection default; connection default;
INSERT INTO t2 VALUES (1); INSERT INTO t2 VALUES (1);
connection con1; connection con1;
disconnect con1;
connection default; connection default;
DROP TABLE IF EXISTS t2, t1; DROP TABLE IF EXISTS t2, t1;
#
# MDEV-30531 Corrupt index(es) on busy table when using FOREIGN KEY
#
SET @freq=@@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
CREATE TABLE collections (
id int(11) unsigned NOT NULL AUTO_INCREMENT,
collectionhash varchar(255) NOT NULL DEFAULT '0',
PRIMARY KEY (id),
UNIQUE KEY ix_collection_collectionhash (collectionhash)
) ENGINE=InnoDB;
CREATE TABLE binaries (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
collections_id int(11) unsigned NOT NULL DEFAULT 0,
binaryhash binary(16) NOT NULL DEFAULT '0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
PRIMARY KEY (id),
UNIQUE KEY ix_binary_binaryhash (binaryhash),
CONSTRAINT FK_collections FOREIGN KEY (collections_id) REFERENCES collections (id) ON DELETE CASCADE
) ENGINE=InnoDB;
INSERT INTO collections (id) VALUES (NULL);
connection con1;
INSERT INTO binaries (id,collections_id) VALUES (NULL,1);
REPLACE INTO collections (id) VALUES (NULL);
connection default;
REPLACE INTO binaries (id) VALUES (NULL);
SET GLOBAL innodb_max_purge_lag_wait=0;
CHECK TABLE binaries, collections EXTENDED;
Table Op Msg_type Msg_text
test.binaries check status OK
test.collections check status OK
SET GLOBAL innodb_purge_rseg_truncate_frequency=@freq;
disconnect con1;
DROP TABLE binaries, collections;
# End of 10.6 tests # End of 10.6 tests
...@@ -1071,12 +1071,53 @@ INSERT INTO t2 VALUES (1); ...@@ -1071,12 +1071,53 @@ INSERT INTO t2 VALUES (1);
--reap --reap
# Cleanup # Cleanup
--disconnect con1
--connection default --connection default
--disable_warnings --disable_warnings
DROP TABLE IF EXISTS t2, t1; DROP TABLE IF EXISTS t2, t1;
--enable_warnings --enable_warnings
--echo #
--echo # MDEV-30531 Corrupt index(es) on busy table when using FOREIGN KEY
--echo #
SET @freq=@@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
CREATE TABLE collections (
id int(11) unsigned NOT NULL AUTO_INCREMENT,
collectionhash varchar(255) NOT NULL DEFAULT '0',
PRIMARY KEY (id),
UNIQUE KEY ix_collection_collectionhash (collectionhash)
) ENGINE=InnoDB;
CREATE TABLE binaries (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
collections_id int(11) unsigned NOT NULL DEFAULT 0,
binaryhash binary(16) NOT NULL DEFAULT '0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
PRIMARY KEY (id),
UNIQUE KEY ix_binary_binaryhash (binaryhash),
CONSTRAINT FK_collections FOREIGN KEY (collections_id) REFERENCES collections (id) ON DELETE CASCADE
) ENGINE=InnoDB;
INSERT INTO collections (id) VALUES (NULL);
--connection con1
INSERT INTO binaries (id,collections_id) VALUES (NULL,1);
--send
REPLACE INTO collections (id) VALUES (NULL);
--connection default
--error 0,ER_LOCK_DEADLOCK,ER_NO_REFERENCED_ROW_2
REPLACE INTO binaries (id) VALUES (NULL);
SET GLOBAL innodb_max_purge_lag_wait=0;
CHECK TABLE binaries, collections EXTENDED;
SET GLOBAL innodb_purge_rseg_truncate_frequency=@freq;
--disconnect con1
# Cleanup
DROP TABLE binaries, collections;
--echo # End of 10.6 tests --echo # End of 10.6 tests
--source include/wait_until_count_sessions.inc --source include/wait_until_count_sessions.inc
...@@ -1903,7 +1903,6 @@ dberr_t lock_wait(que_thr_t *thr) ...@@ -1903,7 +1903,6 @@ dberr_t lock_wait(que_thr_t *thr)
thd_wait_begin(trx->mysql_thd, (type_mode & LOCK_TABLE) thd_wait_begin(trx->mysql_thd, (type_mode & LOCK_TABLE)
? THD_WAIT_TABLE_LOCK : THD_WAIT_ROW_LOCK); ? THD_WAIT_TABLE_LOCK : THD_WAIT_ROW_LOCK);
int err= 0;
mysql_mutex_lock(&lock_sys.wait_mutex); mysql_mutex_lock(&lock_sys.wait_mutex);
/* Now that we are holding lock_sys.wait_mutex, we must reload /* Now that we are holding lock_sys.wait_mutex, we must reload
trx->lock.wait_mutex. It cannot be cleared as long as we are holding trx->lock.wait_mutex. It cannot be cleared as long as we are holding
...@@ -1956,30 +1955,51 @@ dberr_t lock_wait(que_thr_t *thr) ...@@ -1956,30 +1955,51 @@ dberr_t lock_wait(que_thr_t *thr)
wait_lock= lock_wait_rpl_report(trx); wait_lock= lock_wait_rpl_report(trx);
#endif #endif
if (trx->error_state != DB_SUCCESS) switch (trx->error_state) {
goto check_trx_error; case DB_SUCCESS:
break;
case DB_LOCK_WAIT:
trx->error_state= DB_SUCCESS;
break;
default:
#ifdef UNIV_DEBUG
ut_ad("invalid state" == 0);
break;
case DB_DEADLOCK:
case DB_INTERRUPTED:
#endif
goto end_loop;
}
while (wait_lock) while (wait_lock)
{ {
int err;
ut_ad(trx->lock.wait_lock); ut_ad(trx->lock.wait_lock);
DEBUG_SYNC_C("lock_wait_before_suspend"); DEBUG_SYNC_C("lock_wait_before_suspend");
if (no_timeout) if (no_timeout)
{
my_cond_wait(&trx->lock.cond, &lock_sys.wait_mutex.m_mutex); my_cond_wait(&trx->lock.cond, &lock_sys.wait_mutex.m_mutex);
err= 0;
}
else else
err= my_cond_timedwait(&trx->lock.cond, &lock_sys.wait_mutex.m_mutex, err= my_cond_timedwait(&trx->lock.cond, &lock_sys.wait_mutex.m_mutex,
&abstime); &abstime);
wait_lock= trx->lock.wait_lock; wait_lock= trx->lock.wait_lock;
check_trx_error:
switch (trx->error_state) { switch (trx->error_state) {
case DB_DEADLOCK: case DB_DEADLOCK:
case DB_INTERRUPTED: case DB_INTERRUPTED:
break; break;
#ifdef UNIV_DEBUG
case DB_LOCK_WAIT_TIMEOUT:
case DB_LOCK_WAIT:
ut_ad("invalid state" == 0);
break;
#endif
default: default:
ut_ad(trx->error_state != DB_LOCK_WAIT_TIMEOUT);
/* Dictionary transactions must ignore KILL, because they could /* Dictionary transactions must ignore KILL, because they could
be executed as part of a multi-transaction DDL operation, be executed as part of a multi-transaction DDL operation,
such as rollback_inplace_alter_table() or ha_innobase::delete_table(). */ such as rollback_inplace_alter_table() or ha_innobase::delete_table(). */
...@@ -2001,6 +2021,7 @@ dberr_t lock_wait(que_thr_t *thr) ...@@ -2001,6 +2021,7 @@ dberr_t lock_wait(que_thr_t *thr)
break; break;
} }
end_loop:
if (row_lock_wait) if (row_lock_wait)
lock_sys.wait_resume(trx->mysql_thd, suspend_time, my_hrtime_coarse()); lock_sys.wait_resume(trx->mysql_thd, suspend_time, my_hrtime_coarse());
...@@ -2025,6 +2046,18 @@ dberr_t lock_wait(que_thr_t *thr) ...@@ -2025,6 +2046,18 @@ dberr_t lock_wait(que_thr_t *thr)
}); });
thd_wait_end(trx->mysql_thd); thd_wait_end(trx->mysql_thd);
#ifdef UNIV_DEBUG
switch (trx->error_state) {
case DB_SUCCESS:
case DB_DEADLOCK:
case DB_INTERRUPTED:
case DB_LOCK_WAIT_TIMEOUT:
break;
default:
ut_ad("invalid state" == 0);
}
#endif
return trx->error_state; return trx->error_state;
} }
......
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