Commit ec7e7833 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.


mysql-test/r/lock.result:
  Added simplified test for bug #48538 "Assertion in thr_lock() on LOAD
  DATA CONCURRENT INFILE".
mysql-test/t/lock.test:
  Added simplified test for bug #48538 "Assertion in thr_lock() on LOAD
  DATA CONCURRENT INFILE".
mysys/thr_lock.c:
  Adjusted assertion to account for situation when
  TL_WRITE_CONCURRENT_INSERT is converted to TL_WRITE inside of the
  thr_lock() function because the engine of the table being locked 
  does not support concurrent inserts.
  This scenario breaks assumption that for the same table one thread
  always requests locks with higher thr_lock_type value first, since
  TL_WRITE on the table (converted from TL_WRITE_CONCURRENT_INSERT)
  can be requested after acquiring a TL_WRITE_LOW_PRIORITY lock on the table.
  Note that it is still safe to grant a new lock without extra checks and
  waiting in such situation since TL_WRITE has the same compatibility
  rules as TL_WRITE_LOW_PRIORITY (their only difference is priority).
parent 2579817d
......@@ -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)));
......
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