Commit 0bce0c90 authored by Jon Olav Hauglid's avatar Jon Olav Hauglid

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.
parent 236539b4
......@@ -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,10 +674,19 @@ 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:
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
......@@ -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)));
......
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