summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/suite/parts/r/partition_debug_sync_innodb.result61
-rw-r--r--mysql-test/suite/parts/t/partition_debug_sync_innodb-master.opt1
-rw-r--r--mysql-test/suite/parts/t/partition_debug_sync_innodb.test44
-rw-r--r--sql/authors.h1
-rw-r--r--sql/ha_partition.cc4
-rw-r--r--sql/sql_base.cc69
-rw-r--r--sql/sql_base.h2
-rw-r--r--sql/sql_partition.cc60
-rw-r--r--sql/sql_show.cc4
9 files changed, 181 insertions, 65 deletions
diff --git a/mysql-test/suite/parts/r/partition_debug_sync_innodb.result b/mysql-test/suite/parts/r/partition_debug_sync_innodb.result
new file mode 100644
index 00000000000..9e72259a150
--- /dev/null
+++ b/mysql-test/suite/parts/r/partition_debug_sync_innodb.result
@@ -0,0 +1,61 @@
+#
+# Bug#50561: ALTER PARTITIONS does not have adequate lock, breaks with
+# concurrent I_S query
+create table t1 (a int)
+engine = innodb
+partition by range (a)
+(partition p0 values less than MAXVALUE);
+insert into t1 values (1), (11), (21), (33);
+SELECT * FROM t1;
+a
+1
+11
+21
+33
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY RANGE (a)
+(PARTITION p0 VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
+t1#P#p0.ibd
+t1.frm
+t1.par
+SET DEBUG_SYNC='before_open_in_get_all_tables SIGNAL parked WAIT_FOR open';
+SET DEBUG_SYNC='partition_open_error SIGNAL alter WAIT_FOR finish';
+SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't1' AND TABLE_SCHEMA = 'test';
+SET DEBUG_SYNC = 'now WAIT_FOR parked';
+# When waiting for the name lock in get_all_tables in sql_show.cc
+# this will not be concurrent any more, thus the TIMEOUT
+SET DEBUG_SYNC = 'before_rename_partitions SIGNAL open WAIT_FOR alter TIMEOUT 1';
+# Needs to be executed twice, since first is this 'SET DEBUG_SYNC ...'
+SET DEBUG_SYNC = 'before_close_thread_tables SIGNAL finish EXECUTE 2';
+ALTER TABLE t1 REORGANIZE PARTITION p0 INTO
+(PARTITION p0 VALUES LESS THAN (10),
+PARTITION p10 VALUES LESS THAN MAXVALUE);
+Warnings:
+Warning 1639 debug sync point wait timed out
+TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME
+def test t1 p0 NULL 1 NULL RANGE NULL a NULL 10 1 16384 16384 NULL 0 0 NULL NULL NULL NULL default NULL
+def test t1 p10 NULL 2 NULL RANGE NULL a NULL MAXVALUE 3 5461 16384 NULL 0 0 NULL NULL NULL NULL default NULL
+t1#P#p0.ibd
+t1#P#p10.ibd
+t1.frm
+t1.par
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` int(11) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY RANGE (a)
+(PARTITION p0 VALUES LESS THAN (10) ENGINE = InnoDB,
+ PARTITION p10 VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
+SELECT * FROM t1;
+a
+1
+11
+21
+33
+drop table t1;
+SET DEBUG_SYNC = 'RESET';
diff --git a/mysql-test/suite/parts/t/partition_debug_sync_innodb-master.opt b/mysql-test/suite/parts/t/partition_debug_sync_innodb-master.opt
new file mode 100644
index 00000000000..9854fda301d
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_debug_sync_innodb-master.opt
@@ -0,0 +1 @@
+--innodb_file_per_table=1
diff --git a/mysql-test/suite/parts/t/partition_debug_sync_innodb.test b/mysql-test/suite/parts/t/partition_debug_sync_innodb.test
new file mode 100644
index 00000000000..79ef3d537bf
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_debug_sync_innodb.test
@@ -0,0 +1,44 @@
+--source include/have_innodb.inc
+--source include/have_partition.inc
+--source include/have_debug_sync.inc
+
+let $MYSQLD_DATADIR=`SELECT @@datadir`;
+
+--echo #
+--echo # Bug#50561: ALTER PARTITIONS does not have adequate lock, breaks with
+--echo # concurrent I_S query
+create table t1 (a int)
+engine = innodb
+partition by range (a)
+(partition p0 values less than MAXVALUE);
+insert into t1 values (1), (11), (21), (33);
+SELECT * FROM t1;
+SHOW CREATE TABLE t1;
+--list_files $MYSQLD_DATADIR/test
+
+SET DEBUG_SYNC='before_open_in_get_all_tables SIGNAL parked WAIT_FOR open';
+SET DEBUG_SYNC='partition_open_error SIGNAL alter WAIT_FOR finish';
+send
+SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't1' AND TABLE_SCHEMA = 'test';
+
+connect (con1, localhost, root,,);
+SET DEBUG_SYNC = 'now WAIT_FOR parked';
+--echo # When waiting for the name lock in get_all_tables in sql_show.cc
+--echo # this will not be concurrent any more, thus the TIMEOUT
+SET DEBUG_SYNC = 'before_rename_partitions SIGNAL open WAIT_FOR alter TIMEOUT 1';
+--echo # Needs to be executed twice, since first is this 'SET DEBUG_SYNC ...'
+SET DEBUG_SYNC = 'before_close_thread_tables SIGNAL finish EXECUTE 2';
+--error 0,ER_TABLE_EXISTS_ERROR
+ALTER TABLE t1 REORGANIZE PARTITION p0 INTO
+(PARTITION p0 VALUES LESS THAN (10),
+ PARTITION p10 VALUES LESS THAN MAXVALUE);
+
+disconnect con1;
+connection default;
+--reap
+--list_files $MYSQLD_DATADIR/test
+SHOW CREATE TABLE t1;
+SELECT * FROM t1;
+drop table t1;
+--list_files $MYSQLD_DATADIR/test
+SET DEBUG_SYNC = 'RESET';
diff --git a/sql/authors.h b/sql/authors.h
index 555fe2ae43a..5de2659f098 100644
--- a/sql/authors.h
+++ b/sql/authors.h
@@ -80,6 +80,7 @@ struct show_table_authors_st show_table_authors[]= {
{ "Eric Herman", "Amsterdam, Netherlands", "Bug fixing - federated" },
{ "Andrey Hristov", "Walldorf, Germany", "Event scheduler (5.1)" },
{ "Alexander (Alexi) Ivanov", "St. Petersburg, Russia", "Replication" },
+ { "Mattias Jonsson", "Uppsala, Sweden", "Partitioning" },
{ "Alexander (Salle) Keremidarski", "Sofia, Bulgaria",
"Bug fixing" },
{ "Mats Kindahl", "Storvreta, Sweden", "Replication" },
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index b8831127e3f..06bee47c7ef 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -60,6 +60,8 @@
#include "key.h"
#include "sql_plugin.h"
+#include "debug_sync.h"
+
static const char *ha_par_ext= ".par";
#ifdef NOT_USED
static int free_share(PARTITION_SHARE * share);
@@ -692,6 +694,7 @@ int ha_partition::rename_partitions(const char *path)
DBUG_ASSERT(!strcmp(path, get_canonical_filename(m_file[0], path,
norm_name_buff)));
+ DEBUG_SYNC(ha_thd(), "before_rename_partitions");
if (temp_partitions)
{
/*
@@ -2684,6 +2687,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
DBUG_RETURN(0);
err_handler:
+ DEBUG_SYNC(ha_thd(), "partition_open_error");
while (file-- != m_file)
(*file)->close();
bitmap_free(&m_bulk_insert_started);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index baf13431d77..fd0dfc5189a 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1418,6 +1418,12 @@ void close_thread_tables(THD *thd)
table->s->table_name.str, (long) table));
#endif
+#if defined(ENABLED_DEBUG_SYNC)
+ /* debug_sync may not be initialized for some slave threads */
+ if (thd->debug_sync_control)
+ DEBUG_SYNC(thd, "before_close_thread_tables");
+#endif
+
/* Detach MERGE children after every statement. Even under LOCK TABLES. */
for (table= thd->open_tables; table; table= table->next)
{
@@ -5242,8 +5248,8 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
thd - thread handler
tables - list of tables for open
flags - bitmap of flags to modify how the tables will be open:
- MYSQL_OPEN_IGNORE_FLUSH - open table even if someone has
- done a flush or namelock on it.
+ MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has
+ done a flush on it.
RETURN
FALSE - ok
@@ -8782,8 +8788,57 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b)
/*
+ Unlock and close table before renaming and dropping partitions
+ SYNOPSIS
+ alter_close_tables()
+ lpt Struct carrying parameters
+ RETURN VALUES
+ 0
+*/
+
+static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ TABLE_SHARE *share= lpt->table->s;
+ THD *thd= lpt->thd;
+ TABLE *table;
+ DBUG_ENTER("alter_close_tables");
+ /*
+ We must keep LOCK_open while manipulating with thd->open_tables.
+ Another thread may be working on it.
+ */
+ mysql_mutex_lock(&LOCK_open);
+ /*
+ We can safely remove locks for all tables with the same name:
+ later they will all be closed anyway in
+ alter_partition_lock_handling().
+ */
+ for (table= thd->open_tables; table ; table= table->next)
+ {
+ if (!strcmp(table->s->table_name.str, share->table_name.str) &&
+ !strcmp(table->s->db.str, share->db.str))
+ {
+ mysql_lock_remove(thd, thd->lock, table);
+ table->file->close();
+ table->db_stat= 0; // Mark file closed
+ /*
+ Ensure that we won't end up with a crippled table instance
+ in the table cache if an error occurs before we reach
+ alter_partition_lock_handling() and the table is closed
+ by close_thread_tables() instead.
+ */
+ tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
+ table->s->db.str,
+ table->s->table_name.str);
+ }
+ }
+ mysql_mutex_unlock(&LOCK_open);
+ DBUG_RETURN(0);
+}
+
+
+/*
SYNOPSIS
- abort_and_upgrade_lock()
+ abort_and_upgrade_lock_and_close_table()
lpt Parameter passing struct
All parameters passed through the ALTER_PARTITION_PARAM_TYPE object
RETURN VALUE
@@ -8792,7 +8847,7 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b)
Remember old lock level (for possible downgrade later on), abort all
waiting threads and ensure that all keeping locks currently are
completed such that we own the lock exclusively and no other interaction
- is ongoing.
+ is ongoing. Close the table and hold the name lock.
thd Thread object
table Table object
@@ -8801,12 +8856,14 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b)
old_lock_level Old lock level
*/
-int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
+int abort_and_upgrade_lock_and_close_table(ALTER_PARTITION_PARAM_TYPE *lpt)
{
- DBUG_ENTER("abort_and_upgrade_lock");
+ DBUG_ENTER("abort_and_upgrade_lock_and_close_table");
if (wait_while_table_is_used(lpt->thd, lpt->table, HA_EXTRA_FORCE_REOPEN))
DBUG_RETURN(1);
+ if (alter_close_tables(lpt))
+ DBUG_RETURN(1);
DBUG_RETURN(0);
}
diff --git a/sql/sql_base.h b/sql/sql_base.h
index a57666afe49..4d6be93b532 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -218,7 +218,7 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
thr_lock_type lock_type, uint flags);
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags);
-int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt);
+int abort_and_upgrade_lock_and_close_table(ALTER_PARTITION_PARAM_TYPE *lpt);
int decide_logging_format(THD *thd, TABLE_LIST *tables);
void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index 0eb92dc23a9..a3007d1875c 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -6283,54 +6283,6 @@ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
}
-/*
- Unlock and close table before renaming and dropping partitions
- SYNOPSIS
- alter_close_tables()
- lpt Struct carrying parameters
- RETURN VALUES
- 0
-*/
-
-static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
-{
- TABLE_SHARE *share= lpt->table->s;
- THD *thd= lpt->thd;
- TABLE *table;
- DBUG_ENTER("alter_close_tables");
- /*
- We must keep LOCK_open while manipulating with thd->open_tables.
- Another thread may be working on it.
- */
- mysql_mutex_lock(&LOCK_open);
- /*
- We can safely remove locks for all tables with the same name:
- later they will all be closed anyway in
- alter_partition_lock_handling().
- */
- for (table= thd->open_tables; table ; table= table->next)
- {
- if (!strcmp(table->s->table_name.str, share->table_name.str) &&
- !strcmp(table->s->db.str, share->db.str))
- {
- mysql_lock_remove(thd, thd->lock, table);
- table->file->close();
- table->db_stat= 0; // Mark file closed
- /*
- Ensure that we won't end up with a crippled table instance
- in the table cache if an error occurs before we reach
- alter_partition_lock_handling() and the table is closed
- by close_thread_tables() instead.
- */
- tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
- table->s->db.str,
- table->s->table_name.str);
- }
- }
- mysql_mutex_unlock(&LOCK_open);
- DBUG_RETURN(0);
-}
-
/*
Handle errors for ALTER TABLE for partitioning
@@ -6629,9 +6581,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
write_log_drop_partition(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_3") ||
(not_completed= FALSE) ||
- abort_and_upgrade_lock(lpt) ||
- ERROR_INJECT_CRASH("crash_drop_partition_4") ||
- alter_close_tables(lpt) ||
+ abort_and_upgrade_lock_and_close_table(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_5") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
@@ -6697,9 +6647,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_add_partition_2") ||
mysql_change_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_3") ||
- abort_and_upgrade_lock(lpt) ||
- ERROR_INJECT_CRASH("crash_add_partition_4") ||
- alter_close_tables(lpt) ||
+ abort_and_upgrade_lock_and_close_table(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_5") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
@@ -6782,9 +6730,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
write_log_final_change_partition(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_4") ||
(not_completed= FALSE) ||
- abort_and_upgrade_lock(lpt) ||
- ERROR_INJECT_CRASH("crash_change_partition_5") ||
- alter_close_tables(lpt) ||
+ abort_and_upgrade_lock_and_close_table(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_6") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index f336237d273..412fcdbe9bd 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -49,7 +49,8 @@
#include "event_data_objects.h"
#endif
#include <my_dir.h>
-#include "lock.h" // MYSQL_LOCK_IGNORE_FLUSH
+#include "debug_sync.h"
+#include "lock.h" // MYSQL_OPEN_IGNORE_FLUSH
#define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
@@ -3538,6 +3539,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
lex->sql_command= SQLCOM_SHOW_FIELDS;
show_table_list->i_s_requested_object=
schema_table->i_s_requested_object;
+ DEBUG_SYNC(thd, "before_open_in_get_all_tables");
res= open_normal_and_derived_tables(thd, show_table_list,
(MYSQL_OPEN_IGNORE_FLUSH |
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |