From e814e9ac1e753c23438195c82ded0c5fe03d6436 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 22 Jul 2010 11:10:35 +0200 Subject: [PATCH] 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. --- mysql-test/r/show_check.result | 21 +++++++++++++++++++++ mysql-test/t/show_check.test | 33 +++++++++++++++++++++++++++++++++ sql/sql_show.cc | 32 +++++++++++++++++++++++--------- 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index ef2277fef38..f49366cbf08 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -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; diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index fa003c2fe69..89a43f24296 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -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 diff --git a/sql/sql_show.cc b/sql/sql_show.cc index a8ae5832a2a..8647237ce72 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -649,22 +649,30 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) Protocol *protocol= thd->protocol; char buff[2048]; String buffer(buff, sizeof(buff), system_charset_info); + List 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 *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 *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 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 *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 *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,