List:Commits« Previous MessageNext Message »
From:Jon Olav Hauglid Date:July 22 2010 11:49am
Subject:bzr push into mysql-trunk-runtime branch (jon.hauglid:3081 to 3082) Bug#54905
View as plain text  
 3082 Jon Olav Hauglid	2010-07-22
      Bug #54905 Connection with WRITE lock cannot ALTER table due to
                 concurrent SHOW CREATE
      
      The problem was that a SHOW CREATE TABLE statement issued inside
      a transaction did not release its metadata locks at the end of the
      statement execution. This happened even if SHOW CREATE TABLE is an
      information statement. 
      
      The consequence was that SHOW CREATE TABLE was able to block other
      connections from accessing the table (e.g. using ALTER TABLE).
      
      This patch fixes the problem by explicitly releasing any metadata
      locks taken by SHOW CREATE TABLE after the statement completes.
      
      Test case added to show_check.test.

    modified:
      mysql-test/r/show_check.result
      mysql-test/t/show_check.test
      sql/sql_show.cc
 3081 Jon Olav Hauglid	2010-07-22
      Bug #55223 assert in Protocol::end_statement during CREATE DATABASE
      
      The problem was that a statement could cause an assert if it was aborted by
      KILL QUERY while it waited on a metadata lock. This assert checks that a
      statement either sends OK or an error to the client. If the bug was triggered
      on release builds, it caused OK to be sent to the client instead of
      ER_QUERY_INTERRUPTED.
      
      The root cause of the problem was that there are two separate ways to tell if a
      statement is killed: thd->killed and mysys_var->abort. KILL QUERY causes both
      to be set, thd->killed before mysys_var->abort. Also, both values are reset
      at the end of statement execution. This means that it is possible for
      KILL QUERY to first set thd->killed, then have the killed statement reset
      both thd->killed and mysys_var->abort and finally have KILL QUERY set
      mysys_var->abort. This means that the connection with the killed statement
      will start executing the next statement with the two values out of sync - i.e.
      thd->killed not set but mysys_var->abort set.
      
      Since mysys_var->abort is used to check if a wait for a metadata lock should
      be aborted, the next statement would immediately abort any such waiting.
      When waiting is aborted, no OK message is sent and thd->killed is checked to
      see if ER_QUERY_INTERRUPTED should be sent to the client. But since
      the->killed had been reset, neither OK nor an error message was sent to the
      client. This then triggered the assert.
      
      This patch fixes the problem by changing the metadata lock waiting code to
      check thd->killed.
      
      No test case added as reproducing the assert is dependent on very exact timing
      of two (or more) threads. The patch has been checked using RQG and the grammar
      posted on the bug report.

    modified:
      sql/mdl.cc
      sql/mdl.h
=== modified file 'mysql-test/r/show_check.result'
--- a/mysql-test/r/show_check.result	2010-05-21 13:36:59 +0000
+++ b/mysql-test/r/show_check.result	2010-07-22 09:10:35 +0000
@@ -1466,3 +1466,24 @@ t1	CREATE TABLE `t1` (
 # Switching to connection 'default'.
 UNLOCK TABLES;
 DROP TABLE t1;
+#
+# Bug#54905 Connection with WRITE lock cannot ALTER table due to
+#           concurrent SHOW CREATE
+#
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1(a INT);
+# Connection con1
+LOCK TABLE t1 WRITE;
+# Connection default
+START TRANSACTION;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+# Connection con1
+ALTER TABLE t1 CHARACTER SET = utf8;
+UNLOCK TABLES;
+# Connection default
+COMMIT;
+DROP TABLE t1;

=== modified file 'mysql-test/t/show_check.test'
--- a/mysql-test/t/show_check.test	2010-05-21 13:36:59 +0000
+++ b/mysql-test/t/show_check.test	2010-07-22 09:10:35 +0000
@@ -1246,6 +1246,39 @@ UNLOCK TABLES;
 DROP TABLE t1;
 
 
+--echo #
+--echo # Bug#54905 Connection with WRITE lock cannot ALTER table due to
+--echo #           concurrent SHOW CREATE
+--echo #
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+CREATE TABLE t1(a INT);
+
+--echo # Connection con1
+connect (con1,localhost,root);
+LOCK TABLE t1 WRITE;
+
+--echo # Connection default
+connection default;
+START TRANSACTION;
+SHOW CREATE TABLE t1;
+
+--echo # Connection con1
+connection con1;
+# Used to block
+ALTER TABLE t1 CHARACTER SET = utf8;
+UNLOCK TABLES;
+
+--echo # Connection default
+connection default;
+COMMIT;
+disconnect con1;
+DROP TABLE t1;
+
+
 # Wait till all disconnects are completed
 --source include/wait_until_count_sessions.inc
 

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2010-07-15 15:46:41 +0000
+++ b/sql/sql_show.cc	2010-07-22 09:10:35 +0000
@@ -649,22 +649,30 @@ mysqld_show_create(THD *thd, TABLE_LIST 
   Protocol *protocol= thd->protocol;
   char buff[2048];
   String buffer(buff, sizeof(buff), system_charset_info);
+  List<Item> field_list;
+  bool error= TRUE;
   DBUG_ENTER("mysqld_show_create");
   DBUG_PRINT("enter",("db: %s  table: %s",table_list->db,
                       table_list->table_name));
 
+  /*
+    Metadata locks taken during SHOW CREATE should be released when
+    the statmement completes as it is an information statement.
+  */
+  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+
   /* We want to preserve the tree for views. */
   thd->lex->view_prepare_mode= TRUE;
 
   {
     Show_create_error_handler view_error_suppressor(thd, table_list);
     thd->push_internal_handler(&view_error_suppressor);
-    bool error=
+    bool open_error=
       open_normal_and_derived_tables(thd, table_list,
                                      MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL);
     thd->pop_internal_handler();
-    if (error && (thd->killed || thd->is_error()))
-      DBUG_RETURN(TRUE);
+    if (open_error && (thd->killed || thd->is_error()))
+      goto exit;
   }
 
   /* TODO: add environment variables show when it become possible */
@@ -672,7 +680,7 @@ mysqld_show_create(THD *thd, TABLE_LIST 
   {
     my_error(ER_WRONG_OBJECT, MYF(0),
              table_list->db, table_list->table_name, "VIEW");
-    DBUG_RETURN(TRUE);
+    goto exit;
   }
 
   buffer.length(0);
@@ -684,9 +692,8 @@ mysqld_show_create(THD *thd, TABLE_LIST 
        view_store_create_info(thd, table_list, &buffer) :
        store_create_info(thd, table_list, &buffer, NULL,
                          FALSE /* show_database */)))
-    DBUG_RETURN(TRUE);
+    goto exit;
 
-  List<Item> field_list;
   if (table_list->view)
   {
     field_list.push_back(new Item_empty_string("View",NAME_CHAR_LEN));
@@ -707,7 +714,8 @@ mysqld_show_create(THD *thd, TABLE_LIST 
 
   if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
-    DBUG_RETURN(TRUE);
+    goto exit;
+
   protocol->prepare_for_resend();
   if (table_list->view)
     protocol->store(table_list->view_name.str, system_charset_info);
@@ -735,10 +743,16 @@ mysqld_show_create(THD *thd, TABLE_LIST 
     protocol->store(buffer.ptr(), buffer.length(), buffer.charset());
 
   if (protocol->write())
-    DBUG_RETURN(TRUE);
+    goto exit;
 
+  error= FALSE;
   my_eof(thd);
-  DBUG_RETURN(FALSE);
+
+exit:
+  close_thread_tables(thd);
+  /* Release any metadata locks taken during SHOW CREATE. */
+  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+  DBUG_RETURN(error);
 }
 
 bool mysqld_show_create_db(THD *thd, char *dbname,


Attachment: [text/bzr-bundle] bzr/jon.hauglid@oracle.com-20100722091035-m42t940fw3tsfs66.bundle
Thread
bzr push into mysql-trunk-runtime branch (jon.hauglid:3081 to 3082) Bug#54905Jon Olav Hauglid22 Jul