From fbfa18daf0266131f64edbf088493bca933780c1 Mon Sep 17 00:00:00 2001 From: unknown <ramil/ram@mysql.com/ramil.myoffice.izhnet.ru> Date: Sun, 17 Jun 2007 09:56:33 +0500 Subject: [PATCH] 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. --- mysql-test/r/flush.result | 16 ++++++++++++++++ mysql-test/t/flush.test | 26 +++++++++++++++++++++++++- sql/lock.cc | 4 +--- sql/log.h | 6 ++++++ sql/sql_base.cc | 8 ++++++-- 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result index 7eb7fd16ed..dcbffd6f7c 100644 --- a/mysql-test/r/flush.result +++ b/mysql-test/r/flush.result @@ -55,3 +55,19 @@ flush tables with read lock; insert into t2 values(1); unlock tables; 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 diff --git a/mysql-test/t/flush.test b/mysql-test/t/flush.test index 3a4f2f2f5f..d58d038c3e 100644 --- a/mysql-test/t/flush.test +++ b/mysql-test/t/flush.test @@ -133,4 +133,28 @@ disconnect con3; connection default; 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 diff --git a/sql/lock.cc b/sql/lock.cc index 6f1dd0669e..9785bc4ac0 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -707,9 +707,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, if (!table_ptr[i]-> file-> check_if_locking_is_allowed(thd->lex->sql_command, thd->lex->type, table_ptr[i], count, i, &system_count, - (thd == logger.get_general_log_thd()) || - (thd == logger.get_slow_log_thd()) || - (thd == logger.get_privileged_thread()))) + logger.is_privileged_thread(thd))) DBUG_RETURN(0); } diff --git a/sql/log.h b/sql/log.h index 25bcbd6c62..d92e0117bc 100644 --- a/sql/log.h +++ b/sql/log.h @@ -600,6 +600,12 @@ public: else 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 { diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b798106082..2450429fb6 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -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 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))) DBUG_RETURN(-1); @@ -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), - MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, - need_reopen))) + lock_flag, need_reopen))) { if (thd->lex->requires_prelocking()) { -- 2.30.9