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 ...@@ -356,5 +356,18 @@ ERROR HY000: Can't execute the given command because you have active locked tabl
UNLOCK TABLES; UNLOCK TABLES;
DROP TABLE t1; 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. # End of 6.0 tests.
# #
...@@ -441,6 +441,28 @@ FLUSH TABLES WITH READ LOCK; ...@@ -441,6 +441,28 @@ FLUSH TABLES WITH READ LOCK;
UNLOCK TABLES; UNLOCK TABLES;
DROP TABLE t1; 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 #
--echo # End of 6.0 tests. --echo # End of 6.0 tests.
--echo # --echo #
...@@ -674,14 +674,23 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, ...@@ -674,14 +674,23 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
write locks are of TL_WRITE_ALLOW_WRITE type. write locks are of TL_WRITE_ALLOW_WRITE type.
Note that, since lock requests for the same table are sorted in Note that, since lock requests for the same table are sorted in
such way that requests with higher thr_lock_type value come first, such way that requests with higher thr_lock_type value come first
lock being requested usually has equal or "weaker" type than one (with one exception (*)), lock being requested usually (**) has
which thread might have already acquired. equal or "weaker" type than one which thread might have already
The exceptions are situations when: acquired.
- old lock type is TL_WRITE_ALLOW_READ and new lock type is *) The only exception to this rule is case when type of old lock
TL_WRITE_ALLOW_WRITE is TL_WRITE_LOW_PRIORITY and type of new lock is changed inside
- when old lock type is TL_WRITE_DELAYED of thr_lock() from TL_WRITE_CONCURRENT_INSERT to TL_WRITE since
But these should never happen within MySQL. 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 Therefore it is OK to allow acquiring write lock on the table if
this thread already holds some write lock on it. this thread already holds some write lock on it.
...@@ -690,7 +699,9 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, ...@@ -690,7 +699,9 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
different types of write lock on the same table). different types of write lock on the same table).
*/ */
DBUG_ASSERT(! has_old_lock(lock->write.data, data->owner) || 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_type < TL_WRITE_ALLOW_READ &&
lock->write.data->type == TL_WRITE_ALLOW_READ) || lock->write.data->type == TL_WRITE_ALLOW_READ) ||
lock->write.data->type == TL_WRITE_DELAYED))); 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