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