Commit 0902c6c5 authored by Jon Olav Hauglid's avatar Jon Olav Hauglid

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.
parent cdc0f96f
...@@ -1466,3 +1466,24 @@ t1 CREATE TABLE `t1` ( ...@@ -1466,3 +1466,24 @@ t1 CREATE TABLE `t1` (
# Switching to connection 'default'. # Switching to connection 'default'.
UNLOCK TABLES; UNLOCK TABLES;
DROP TABLE t1; 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;
...@@ -1246,6 +1246,39 @@ UNLOCK TABLES; ...@@ -1246,6 +1246,39 @@ UNLOCK TABLES;
DROP TABLE t1; 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 # Wait till all disconnects are completed
--source include/wait_until_count_sessions.inc --source include/wait_until_count_sessions.inc
...@@ -649,22 +649,30 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) ...@@ -649,22 +649,30 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
Protocol *protocol= thd->protocol; Protocol *protocol= thd->protocol;
char buff[2048]; char buff[2048];
String buffer(buff, sizeof(buff), system_charset_info); String buffer(buff, sizeof(buff), system_charset_info);
List<Item> field_list;
bool error= TRUE;
DBUG_ENTER("mysqld_show_create"); DBUG_ENTER("mysqld_show_create");
DBUG_PRINT("enter",("db: %s table: %s",table_list->db, DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
table_list->table_name)); 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. */ /* We want to preserve the tree for views. */
thd->lex->view_prepare_mode= TRUE; thd->lex->view_prepare_mode= TRUE;
{ {
Show_create_error_handler view_error_suppressor(thd, table_list); Show_create_error_handler view_error_suppressor(thd, table_list);
thd->push_internal_handler(&view_error_suppressor); thd->push_internal_handler(&view_error_suppressor);
bool error= bool open_error=
open_normal_and_derived_tables(thd, table_list, open_normal_and_derived_tables(thd, table_list,
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL); MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL);
thd->pop_internal_handler(); thd->pop_internal_handler();
if (error && (thd->killed || thd->is_error())) if (open_error && (thd->killed || thd->is_error()))
DBUG_RETURN(TRUE); goto exit;
} }
/* TODO: add environment variables show when it become possible */ /* TODO: add environment variables show when it become possible */
...@@ -672,7 +680,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) ...@@ -672,7 +680,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
{ {
my_error(ER_WRONG_OBJECT, MYF(0), my_error(ER_WRONG_OBJECT, MYF(0),
table_list->db, table_list->table_name, "VIEW"); table_list->db, table_list->table_name, "VIEW");
DBUG_RETURN(TRUE); goto exit;
} }
buffer.length(0); buffer.length(0);
...@@ -684,9 +692,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) ...@@ -684,9 +692,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
view_store_create_info(thd, table_list, &buffer) : view_store_create_info(thd, table_list, &buffer) :
store_create_info(thd, table_list, &buffer, NULL, store_create_info(thd, table_list, &buffer, NULL,
FALSE /* show_database */))) FALSE /* show_database */)))
DBUG_RETURN(TRUE); goto exit;
List<Item> field_list;
if (table_list->view) if (table_list->view)
{ {
field_list.push_back(new Item_empty_string("View",NAME_CHAR_LEN)); 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) ...@@ -707,7 +714,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
if (protocol->send_result_set_metadata(&field_list, if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE); goto exit;
protocol->prepare_for_resend(); protocol->prepare_for_resend();
if (table_list->view) if (table_list->view)
protocol->store(table_list->view_name.str, system_charset_info); protocol->store(table_list->view_name.str, system_charset_info);
...@@ -735,10 +743,16 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) ...@@ -735,10 +743,16 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
protocol->store(buffer.ptr(), buffer.length(), buffer.charset()); protocol->store(buffer.ptr(), buffer.length(), buffer.charset());
if (protocol->write()) if (protocol->write())
DBUG_RETURN(TRUE); goto exit;
error= FALSE;
my_eof(thd); 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, bool mysqld_show_create_db(THD *thd, char *dbname,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment