Commit 548416b3 authored by unknown's avatar unknown

Fix for bug #26380: LOCK TABLES + FLUSH LOGS causes deadlock

The log tables are by nature PERFORMANCE_SCHEMA tables,
which should not be affected by SET GLOBAL READ_ONLY or FLUSH TABLES
WITH READ LOCK.

The implementation of FLUSH TABLES WITH READ LOCK already ignored log tables.
Now with this patch, the implementation of LOCK TABLE also ignore a
global read lock for log tables, which was the missing symmetry.


mysql-test/r/flush.result:
  Fix for bug #26380: LOCK TABLES + FLUSH LOGS causes deadlock
    - test result.
mysql-test/t/flush.test:
  Fix for bug #26380: LOCK TABLES + FLUSH LOGS causes deadlock
    - test case.
sql/lock.cc:
  Fix for bug #26380: LOCK TABLES + FLUSH LOGS causes deadlock
    - logger.is_privileged_thread() used.
sql/log.h:
  Fix for bug #26380: LOCK TABLES + FLUSH LOGS causes deadlock
    - LOGGER::is_privileged_thread() introduced that returns TRUE if a given thread 
      is either a general_log or a slow_log or a privileged thread.
sql/sql_base.cc:
  Fix for bug #26380: LOCK TABLES + FLUSH LOGS causes deadlock
    - pass MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK to the mysql_lock_tables() in case of 
      call from a logger in order not to honor the GLOBAL READ LOCK 
      and to avoid possible deadlocks.
parent a555f7f2
...@@ -55,3 +55,19 @@ flush tables with read lock; ...@@ -55,3 +55,19 @@ flush tables with read lock;
insert into t2 values(1); insert into t2 values(1);
unlock tables; unlock tables;
drop table t1, t2; drop table t1, t2;
End of 5.0 tests
set @old_general_log= @@general_log;
set @old_read_only= @@read_only;
set global general_log= on;
flush tables with read lock;
flush logs;
unlock tables;
set global read_only=1;
flush logs;
unlock tables;
flush tables with read lock;
flush logs;
unlock tables;
set global general_log= @old_general_log;
set global read_only= @old_read_only;
End of 5.1 tests
...@@ -133,4 +133,28 @@ disconnect con3; ...@@ -133,4 +133,28 @@ disconnect con3;
connection default; connection default;
drop table t1, t2; drop table t1, t2;
# End of 5.0 tests --echo End of 5.0 tests
#
# Bug #26380: LOCK TABLES + FLUSH LOGS causes deadlock
#
set @old_general_log= @@general_log;
set @old_read_only= @@read_only;
set global general_log= on;
flush tables with read lock;
flush logs;
unlock tables;
set global read_only=1;
flush logs;
unlock tables;
flush tables with read lock;
flush logs;
unlock tables;
set global general_log= @old_general_log;
set global read_only= @old_read_only;
--echo End of 5.1 tests
...@@ -707,9 +707,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, ...@@ -707,9 +707,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
if (!table_ptr[i]-> file-> if (!table_ptr[i]-> file->
check_if_locking_is_allowed(thd->lex->sql_command, thd->lex->type, check_if_locking_is_allowed(thd->lex->sql_command, thd->lex->type,
table_ptr[i], count, i, &system_count, table_ptr[i], count, i, &system_count,
(thd == logger.get_general_log_thd()) || logger.is_privileged_thread(thd)))
(thd == logger.get_slow_log_thd()) ||
(thd == logger.get_privileged_thread())))
DBUG_RETURN(0); DBUG_RETURN(0);
} }
......
...@@ -600,6 +600,12 @@ class LOGGER ...@@ -600,6 +600,12 @@ class LOGGER
else else
return NULL; return NULL;
} }
bool is_privileged_thread(THD *thd)
{
return thd == get_general_log_thd() ||
thd == get_slow_log_thd() ||
thd == get_privileged_thread();
}
}; };
enum enum_binlog_format { enum enum_binlog_format {
......
...@@ -4018,6 +4018,11 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) ...@@ -4018,6 +4018,11 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
{ {
DBUG_ASSERT(thd->lock == 0); // You must lock everything at once DBUG_ASSERT(thd->lock == 0); // You must lock everything at once
TABLE **start,**ptr; TABLE **start,**ptr;
uint lock_flag= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN;
/* Don't honor the GLOBAL READ LOCK if called from a logger */
if (logger.is_privileged_thread(thd))
lock_flag|= MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK;
if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count))) if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count)))
DBUG_RETURN(-1); DBUG_RETURN(-1);
...@@ -4046,8 +4051,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) ...@@ -4046,8 +4051,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
} }
if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, lock_flag, need_reopen)))
need_reopen)))
{ {
if (thd->lex->requires_prelocking()) if (thd->lex->requires_prelocking())
{ {
......
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