Skip to content

Commit 59e8a41

Browse files
committed
Bug#20748660 THD->MDL_CONTEXT.OWNS_EQUAL_OR_STRONGER_LOCK | CREATE_INFO->TABLESPACE
A failing DISCARD/IMPORT TABLESPACE may leave the flag THD::tablespace_op in an incorrect state, causing a succeeding statement to fail. This patch introduces a class utilizing RAII to make sure the flag is set and reset correctly by allocating a local instance of the class on the stack. The flag is reset by the destructor when the instance goes out of scope. Within the server, the THD::tablespace_op flag is used only in 'lock_table_names()' to avoid locking the tablespace name if this is a DISCARD or IMPORT tablespace command (which only support file-per-table tablespaces). This is the situation caught by this bug report. The THD::tablespace_op flag is exported by means of the 'thd_tablespace_op()' function, which is part of the plugin API. This function is used by the SEs Archive, Blackhole, and InnoDB (partitioned and non-partitioned). The usage in Archive and Blackhole is in the overriding 'handler::store_lock()' functions. Within InnoDB, the 'thd_tablespace_op()' function is used when opening a table, to suppress errors normally being thrown when attempting to open a table with no tablespace.
1 parent 92cb41b commit 59e8a41

File tree

3 files changed

+73
-9
lines changed

3 files changed

+73
-9
lines changed

mysql-test/r/alter_table.result

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3345,3 +3345,23 @@ SELECT MERGE_THRESHOLD from INFORMATION_SCHEMA.INNODB_SYS_INDEXES WHERE NAME='ke
33453345
MERGE_THRESHOLD
33463346
45
33473347
DROP TABLE t1;
3348+
#
3349+
# Bug#20748660 THD->MDL_CONTEXT.OWNS_EQUAL_OR_STRONGER_LOCK | CREATE_INFO->TABLESPACE
3350+
#
3351+
# A failing DISCARD/IMPORT TABLESPACE may leave the flag THD::tablespace_op
3352+
# in an incorrect state, causing a succeeding statement to fail.
3353+
3354+
# Create a tablespace with a table.
3355+
CREATE TABLESPACE s ADD DATAFILE 's.ibd' ENGINE InnoDB;
3356+
CREATE TABLE t (i int) TABLESPACE s ENGINE InnoDB;
3357+
# The following statent will fail because the table is not partitioned.
3358+
# Without the fix, the THD::tablespace_op flag will be left set.
3359+
ALTER TABLE t DISCARD PARTITION p TABLESPACE;
3360+
ERROR HY000: Partition management on a not partitioned table is not possible
3361+
# Without the patch, this statement will inherit the tablespace_op flag
3362+
# from the previous statement, causing the statement below to fail due
3363+
# to not acquiring the required MDL lock.
3364+
ALTER TABLE t TABLESPACE s;
3365+
# Drop table and tablespace.
3366+
DROP TABLE t;
3367+
DROP TABLESPACE s ENGINE InnoDB;

mysql-test/t/alter_table.test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2638,3 +2638,27 @@ ALTER TABLE t1 DROP INDEX key1, ADD INDEX key1(fld1) COMMENT
26382638
SHOW INDEX FROM t1;
26392639
SELECT MERGE_THRESHOLD from INFORMATION_SCHEMA.INNODB_SYS_INDEXES WHERE NAME='key1';
26402640
DROP TABLE t1;
2641+
2642+
--echo #
2643+
--echo # Bug#20748660 THD->MDL_CONTEXT.OWNS_EQUAL_OR_STRONGER_LOCK | CREATE_INFO->TABLESPACE
2644+
--echo #
2645+
--echo # A failing DISCARD/IMPORT TABLESPACE may leave the flag THD::tablespace_op
2646+
--echo # in an incorrect state, causing a succeeding statement to fail.
2647+
--echo
2648+
--echo # Create a tablespace with a table.
2649+
CREATE TABLESPACE s ADD DATAFILE 's.ibd' ENGINE InnoDB;
2650+
CREATE TABLE t (i int) TABLESPACE s ENGINE InnoDB;
2651+
2652+
--echo # The following statent will fail because the table is not partitioned.
2653+
--echo # Without the fix, the THD::tablespace_op flag will be left set.
2654+
--error ER_PARTITION_MGMT_ON_NONPARTITIONED
2655+
ALTER TABLE t DISCARD PARTITION p TABLESPACE;
2656+
2657+
--echo # Without the patch, this statement will inherit the tablespace_op flag
2658+
--echo # from the previous statement, causing the statement below to fail due
2659+
--echo # to not acquiring the required MDL lock.
2660+
ALTER TABLE t TABLESPACE s;
2661+
2662+
--echo # Drop table and tablespace.
2663+
DROP TABLE t;
2664+
DROP TABLESPACE s ENGINE InnoDB;

sql/sql_table.cc

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5669,6 +5669,28 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
56695669
}
56705670

56715671

5672+
/**
5673+
Class utilizing RAII for correct set/reset of the
5674+
THD::tablespace_op flag. The destructor will reset
5675+
the flag when a stack allocated instance goes out
5676+
of scope.
5677+
*/
5678+
5679+
class Tablespace_op_flag_handler
5680+
{
5681+
private:
5682+
THD *m_thd;
5683+
public:
5684+
Tablespace_op_flag_handler(THD *thd): m_thd(thd)
5685+
{
5686+
m_thd->tablespace_op= true;
5687+
}
5688+
~Tablespace_op_flag_handler()
5689+
{
5690+
m_thd->tablespace_op= false;
5691+
}
5692+
};
5693+
56725694
/* table_list should contain just one table */
56735695
int mysql_discard_or_import_tablespace(THD *thd,
56745696
TABLE_LIST *table_list,
@@ -5685,11 +5707,13 @@ int mysql_discard_or_import_tablespace(THD *thd,
56855707

56865708
THD_STAGE_INFO(thd, stage_discard_or_import_tablespace);
56875709

5688-
/*
5689-
We set this flag so that ha_innobase::open and ::external_lock() do
5690-
not complain when we lock the table
5691-
*/
5692-
thd->tablespace_op= true;
5710+
/*
5711+
Set thd->tablespace_op, and reset it when the variable leaves scope.
5712+
We set this flag so that ha_innobase::open and ::external_lock() do
5713+
not complain when we lock the table
5714+
*/
5715+
Tablespace_op_flag_handler set_tablespace_op(thd);
5716+
56935717
/*
56945718
Adjust values of table-level and metadata which was set in parser
56955719
for the case general ALTER TABLE.
@@ -5702,7 +5726,6 @@ int mysql_discard_or_import_tablespace(THD *thd,
57025726
if (open_and_lock_tables(thd, table_list, 0, &alter_prelocking_strategy))
57035727
{
57045728
/* purecov: begin inspected */
5705-
thd->tablespace_op= false;
57065729
DBUG_RETURN(-1);
57075730
/* purecov: end */
57085731
}
@@ -5747,7 +5770,6 @@ int mysql_discard_or_import_tablespace(THD *thd,
57475770
MDL_EXCLUSIVE,
57485771
thd->variables.lock_wait_timeout))
57495772
{
5750-
thd->tablespace_op= false;
57515773
DBUG_RETURN(-1);
57525774
}
57535775

@@ -5780,8 +5802,6 @@ int mysql_discard_or_import_tablespace(THD *thd,
57805802
table_list->table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
57815803
}
57825804

5783-
thd->tablespace_op= false;
5784-
57855805
if (error == 0)
57865806
{
57875807
my_ok(thd);

0 commit comments

Comments
 (0)