mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Backport of:
---------------------------------------------------------- revno: 2617.69.2 committer: Konstantin Osipov <kostja@sun.com> branch nick: 5.4-azalea-bugfixing timestamp: Mon 2009-08-03 19:26:04 +0400 message: A fix and a test case for Bug#45035 "Altering table under LOCK TABLES results in "Error 1213 Deadlock found...". If a user had a table locked with LOCK TABLES for READ and for WRITE in the same connection, ALTER TABLE could fail. Root cause analysis: If a connection issues LOCK TABLE t1 write, t1 a read, t1 b read; the new LOCK TABLES code in 6.0 (part of WL 3726) will create the following list of TABLE_LIST objects (thd->locked_tables_list->m_locked_tables): {"t1" "b" tl_read_no_insert}, {"t1" "a" tl_read_no_insert}, {"t1" "t1" tl_write } Later on, when we try to ALTER table t1, mysql_alter_table() closes all TABLE instances and releases its thr_lock locks, keeping only an exclusive metadata lock on t1. But when ALTER is finished, Locked_table_list::reopen_tables() tries to restore the original list of open and locked tables. Before this patch, it used to do so one by one: Open t1 b, get TL_READ_NO_INSERT lock, Open t1 a, get TL_READ_NO_INSERT lock Open t1, try to get TL_WRITE lock, deadlock. The cause of the deadlock is that thr_lock.c doesn't resolve the situation when the read list only consists of locks taken by the same thread, followed by this very thread trying to take a WRITE lock. Indeed, since thr_lock_multi always gets a sorted list of locks, WRITE locks always precede READ locks in the list to lock. Don't try to fix thr_lock.c deficiency, keep this code simple. Instead, try to take all thr_lock locks at once in ::reopen_tables().
This commit is contained in:
@ -1192,7 +1192,7 @@ private:
|
||||
Therefore, we can't allocate metadata locks on execution memory
|
||||
root -- as well as tables, the locks need to stay around till
|
||||
UNLOCK TABLES is called.
|
||||
The locks are allocated in the memory root encapsulate in this
|
||||
The locks are allocated in the memory root encapsulated in this
|
||||
class.
|
||||
|
||||
Some SQL commands, like FLUSH TABLE or ALTER TABLE, demand that
|
||||
@ -1211,10 +1211,21 @@ private:
|
||||
MEM_ROOT m_locked_tables_root;
|
||||
TABLE_LIST *m_locked_tables;
|
||||
TABLE_LIST **m_locked_tables_last;
|
||||
/** An auxiliary array used only in reopen_tables(). */
|
||||
TABLE **m_reopen_array;
|
||||
/**
|
||||
Count the number of tables in m_locked_tables list. We can't
|
||||
rely on thd->lock->table_count because it excludes
|
||||
non-transactional temporary tables. We need to know
|
||||
an exact number of TABLE objects.
|
||||
*/
|
||||
size_t m_locked_tables_count;
|
||||
public:
|
||||
Locked_tables_list()
|
||||
:m_locked_tables(NULL),
|
||||
m_locked_tables_last(&m_locked_tables)
|
||||
m_locked_tables_last(&m_locked_tables),
|
||||
m_reopen_array(NULL),
|
||||
m_locked_tables_count(0)
|
||||
{
|
||||
init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0);
|
||||
}
|
||||
@ -1228,7 +1239,9 @@ public:
|
||||
MEM_ROOT *locked_tables_root() { return &m_locked_tables_root; }
|
||||
void unlink_from_list(THD *thd, TABLE_LIST *table_list,
|
||||
bool remove_from_locked_tables);
|
||||
void unlink_all_closed_tables();
|
||||
void unlink_all_closed_tables(THD *thd,
|
||||
MYSQL_LOCK *lock,
|
||||
size_t reopen_count);
|
||||
bool reopen_tables(THD *thd);
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user