Fix for bug #48538 "Assertion in thr_lock() on LOAD DATA CONCURRENT
INFILE". Attempts to execute an INSERT statement for a MEMORY table which invoked a trigger or called a stored function which tried to perform LOW_PRIORITY update on the table being inserted into, resulted in debug servers aborting due to an assertion failure. On non-debug servers such INSERTs failed with "Can't update table t1 in stored function/trigger because it is already used by statement which invoked this stored function/trigger" as expected. The problem was that in the above scenario TL_WRITE_CONCURRENT_INSERT is converted to TL_WRITE inside the thr_lock() function since the MEMORY engine does not support concurrent inserts. This triggered an assertion which assumed that for the same table, one thread always requests locks with higher thr_lock_type value first. When TL_WRITE_CONCURRENT_INSERT is upgraded to TL_WRITE after the locks have been sorted, this is no longer true. In this case, TL_WRITE was requested after acquiring a TL_WRITE_LOW_PRIORITY lock on the table, triggering the assert. This fix solves the problem by adjusting this assert to take this scenario into account. An alternative approach to change handler::store_locks() methods for all engines which do not support concurrent inserts in such way that TL_WRITE_CONCURRENT_INSERT is upgraded to TL_WRITE there instead, was considered too intrusive. Commit on behalf of Dmitry Lenev.
This commit is contained in:
parent
236539b471
commit
0bce0c9041
@ -356,5 +356,18 @@ ERROR HY000: Can't execute the given command because you have active locked tabl
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# Simplified test for bug #48538 "Assertion in thr_lock() on LOAD DATA
|
||||
# CONCURRENT INFILE".
|
||||
#
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1 (f1 INT, f2 INT) ENGINE = MEMORY;
|
||||
CREATE TRIGGER t1_ai AFTER INSERT ON t1 FOR EACH ROW
|
||||
UPDATE LOW_PRIORITY t1 SET f2 = 7;
|
||||
# Statement below should fail with ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
|
||||
# error instead of failing on assertion in table-level locking subsystem.
|
||||
INSERT INTO t1(f1) VALUES(0);
|
||||
ERROR HY000: Can't update table 't1' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# End of 6.0 tests.
|
||||
#
|
||||
|
@ -441,6 +441,28 @@ FLUSH TABLES WITH READ LOCK;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t1;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # Simplified test for bug #48538 "Assertion in thr_lock() on LOAD DATA
|
||||
--echo # CONCURRENT INFILE".
|
||||
--echo #
|
||||
|
||||
--disable_warnings
|
||||
DROP TABLE IF EXISTS t1;
|
||||
--enable_warnings
|
||||
|
||||
CREATE TABLE t1 (f1 INT, f2 INT) ENGINE = MEMORY;
|
||||
CREATE TRIGGER t1_ai AFTER INSERT ON t1 FOR EACH ROW
|
||||
UPDATE LOW_PRIORITY t1 SET f2 = 7;
|
||||
|
||||
--echo # Statement below should fail with ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
|
||||
--echo # error instead of failing on assertion in table-level locking subsystem.
|
||||
--error ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
|
||||
INSERT INTO t1(f1) VALUES(0);
|
||||
|
||||
DROP TABLE t1;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # End of 6.0 tests.
|
||||
--echo #
|
||||
|
@ -674,14 +674,23 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
|
||||
write locks are of TL_WRITE_ALLOW_WRITE type.
|
||||
|
||||
Note that, since lock requests for the same table are sorted in
|
||||
such way that requests with higher thr_lock_type value come first,
|
||||
lock being requested usually has equal or "weaker" type than one
|
||||
which thread might have already acquired.
|
||||
The exceptions are situations when:
|
||||
- old lock type is TL_WRITE_ALLOW_READ and new lock type is
|
||||
TL_WRITE_ALLOW_WRITE
|
||||
- when old lock type is TL_WRITE_DELAYED
|
||||
But these should never happen within MySQL.
|
||||
such way that requests with higher thr_lock_type value come first
|
||||
(with one exception (*)), lock being requested usually (**) has
|
||||
equal or "weaker" type than one which thread might have already
|
||||
acquired.
|
||||
*) The only exception to this rule is case when type of old lock
|
||||
is TL_WRITE_LOW_PRIORITY and type of new lock is changed inside
|
||||
of thr_lock() from TL_WRITE_CONCURRENT_INSERT to TL_WRITE since
|
||||
engine turns out to be not supporting concurrent inserts.
|
||||
Note that since TL_WRITE has the same compatibility rules as
|
||||
TL_WRITE_LOW_PRIORITY (their only difference is priority),
|
||||
it is OK to grant new lock without additional checks in such
|
||||
situation.
|
||||
**) The exceptions are situations when:
|
||||
- old lock type is TL_WRITE_ALLOW_READ and new lock type is
|
||||
TL_WRITE_ALLOW_WRITE
|
||||
- when old lock type is TL_WRITE_DELAYED
|
||||
But these should never happen within MySQL.
|
||||
Therefore it is OK to allow acquiring write lock on the table if
|
||||
this thread already holds some write lock on it.
|
||||
|
||||
@ -690,7 +699,9 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
|
||||
different types of write lock on the same table).
|
||||
*/
|
||||
DBUG_ASSERT(! has_old_lock(lock->write.data, data->owner) ||
|
||||
(lock_type <= lock->write.data->type &&
|
||||
((lock_type <= lock->write.data->type ||
|
||||
(lock_type == TL_WRITE &&
|
||||
lock->write.data->type == TL_WRITE_LOW_PRIORITY)) &&
|
||||
! ((lock_type < TL_WRITE_ALLOW_READ &&
|
||||
lock->write.data->type == TL_WRITE_ALLOW_READ) ||
|
||||
lock->write.data->type == TL_WRITE_DELAYED)));
|
||||
|
Loading…
x
Reference in New Issue
Block a user