From 43c6953fa1ba3aad4f065bfbd63cca6b5d0c5ce7 Mon Sep 17 00:00:00 2001
From: Sergei Golubchik <sergii@pisem.net>
Date: Mon, 21 Jan 2013 10:52:39 +0100
Subject: [PATCH] MDEV-4029 SELECT on information_schema using a subquery locks
 up the information_schema table due to incorrect mutexes handling

Early evaluation of subqueries in the WHERE conditions on I_S.*_STATUS tables,
otherwise the subquery on this same table will try to acquire LOCK_status twice.
---
 mysql-test/r/information_schema2.result |  8 ++++++++
 mysql-test/t/information_schema2.test   |  9 +++++++++
 sql/sql_show.cc                         | 18 ++++++++++++------
 3 files changed, 29 insertions(+), 6 deletions(-)
 create mode 100644 mysql-test/r/information_schema2.result
 create mode 100644 mysql-test/t/information_schema2.test

diff --git a/mysql-test/r/information_schema2.result b/mysql-test/r/information_schema2.result
new file mode 100644
index 0000000000..60a2094483
--- /dev/null
+++ b/mysql-test/r/information_schema2.result
@@ -0,0 +1,8 @@
+select variable_name from information_schema.session_status where variable_name =
+(select variable_name from information_schema.session_status where variable_name = 'uptime');
+variable_name
+UPTIME
+select variable_name from information_schema.session_variables where variable_name =
+(select variable_name from information_schema.session_variables where variable_name = 'basedir');
+variable_name
+BASEDIR
diff --git a/mysql-test/t/information_schema2.test b/mysql-test/t/information_schema2.test
new file mode 100644
index 0000000000..c2479087f4
--- /dev/null
+++ b/mysql-test/t/information_schema2.test
@@ -0,0 +1,9 @@
+
+#
+# MDEV-4029 SELECT on information_schema using a subquery locks up the information_schema table due to incorrect mutexes handling 
+#
+select variable_name from information_schema.session_status where variable_name =
+(select variable_name from information_schema.session_status where variable_name = 'uptime');
+select variable_name from information_schema.session_variables where variable_name =
+(select variable_name from information_schema.session_variables where variable_name = 'basedir');
+
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index d544ff4c52..f9c2d11459 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2260,7 +2260,6 @@ static bool show_status_array(THD *thd, const char *wild,
   int len;
   LEX_STRING null_lex_str;
   SHOW_VAR tmp, *var;
-  COND *partial_cond= 0;
   enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
   bool res= FALSE;
   CHARSET_INFO *charset= system_charset_info;
@@ -2274,7 +2273,6 @@ static bool show_status_array(THD *thd, const char *wild,
   if (*prefix)
     *prefix_end++= '_';
   len=name_buffer + sizeof(name_buffer) - prefix_end;
-  partial_cond= make_cond_for_info_schema(cond, table->pos_in_table_list);
 
   for (; variables->name; variables++)
   {
@@ -2297,13 +2295,13 @@ static bool show_status_array(THD *thd, const char *wild,
     if (show_type == SHOW_ARRAY)
     {
       show_status_array(thd, wild, (SHOW_VAR *) var->value, value_type,
-                        status_var, name_buffer, table, ucase_names, partial_cond);
+                        status_var, name_buffer, table, ucase_names, cond);
     }
     else
     {
       if (!(wild && wild[0] && wild_case_compare(system_charset_info,
                                                  name_buffer, wild)) &&
-          (!partial_cond || partial_cond->val_int()))
+          (!cond || cond->val_int()))
       {
         char *value=var->value;
         const char *pos, *end;                  // We assign a lot of const's
@@ -5562,9 +5560,12 @@ int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond)
       schema_table_idx == SCH_GLOBAL_VARIABLES)
     option_type= OPT_GLOBAL;
 
+  COND *partial_cond= make_cond_for_info_schema(cond, tables);
+
   rw_rdlock(&LOCK_system_variables_hash);
   res= show_status_array(thd, wild, enumerate_sys_vars(thd, sorted_vars),
-                         option_type, NULL, "", tables->table, upper_case_names, cond);
+                         option_type, NULL, "", tables->table, upper_case_names,
+                         partial_cond);
   rw_unlock(&LOCK_system_variables_hash);
   DBUG_RETURN(res);
 }
@@ -5601,13 +5602,18 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
     tmp1= &thd->status_var;
   }
 
+  COND *partial_cond= make_cond_for_info_schema(cond, tables);
+  // Evaluate and cache const subqueries now, before the mutex.
+  if (partial_cond)
+    partial_cond->val_int();
+
   pthread_mutex_lock(&LOCK_status);
   if (option_type == OPT_GLOBAL)
     calc_sum_of_all_status(&tmp);
   res= show_status_array(thd, wild,
                          (SHOW_VAR *)all_status_vars.buffer,
                          option_type, tmp1, "", tables->table,
-                         upper_case_names, cond);
+                         upper_case_names, partial_cond);
   pthread_mutex_unlock(&LOCK_status);
   DBUG_RETURN(res);
 }
-- 
2.30.9