Commit e118ec40 authored by Alexey Kopytov's avatar Alexey Kopytov

Manual merge from mysql-5.1-bugteam to mysql-trunk-merge.

Conflicts:

Text conflict in sql/sql_base.cc
Text conflict in sql/sql_partition.cc
Text conflict in sql/sql_priv.h
Text conflict in sql/sql_show.cc
parents d9a5541a 6fa04cad
#
# 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';
--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';
...@@ -80,6 +80,7 @@ struct show_table_authors_st show_table_authors[]= { ...@@ -80,6 +80,7 @@ struct show_table_authors_st show_table_authors[]= {
{ "Eric Herman", "Amsterdam, Netherlands", "Bug fixing - federated" }, { "Eric Herman", "Amsterdam, Netherlands", "Bug fixing - federated" },
{ "Andrey Hristov", "Walldorf, Germany", "Event scheduler (5.1)" }, { "Andrey Hristov", "Walldorf, Germany", "Event scheduler (5.1)" },
{ "Alexander (Alexi) Ivanov", "St. Petersburg, Russia", "Replication" }, { "Alexander (Alexi) Ivanov", "St. Petersburg, Russia", "Replication" },
{ "Mattias Jonsson", "Uppsala, Sweden", "Partitioning" },
{ "Alexander (Salle) Keremidarski", "Sofia, Bulgaria", { "Alexander (Salle) Keremidarski", "Sofia, Bulgaria",
"Bug fixing" }, "Bug fixing" },
{ "Mats Kindahl", "Storvreta, Sweden", "Replication" }, { "Mats Kindahl", "Storvreta, Sweden", "Replication" },
......
...@@ -60,6 +60,8 @@ ...@@ -60,6 +60,8 @@
#include "key.h" #include "key.h"
#include "sql_plugin.h" #include "sql_plugin.h"
#include "debug_sync.h"
static const char *ha_par_ext= ".par"; static const char *ha_par_ext= ".par";
#ifdef NOT_USED #ifdef NOT_USED
static int free_share(PARTITION_SHARE * share); static int free_share(PARTITION_SHARE * share);
...@@ -692,6 +694,7 @@ int ha_partition::rename_partitions(const char *path) ...@@ -692,6 +694,7 @@ int ha_partition::rename_partitions(const char *path)
DBUG_ASSERT(!strcmp(path, get_canonical_filename(m_file[0], path, DBUG_ASSERT(!strcmp(path, get_canonical_filename(m_file[0], path,
norm_name_buff))); norm_name_buff)));
DEBUG_SYNC(ha_thd(), "before_rename_partitions");
if (temp_partitions) if (temp_partitions)
{ {
/* /*
...@@ -2684,6 +2687,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) ...@@ -2684,6 +2687,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
DBUG_RETURN(0); DBUG_RETURN(0);
err_handler: err_handler:
DEBUG_SYNC(ha_thd(), "partition_open_error");
while (file-- != m_file) while (file-- != m_file)
(*file)->close(); (*file)->close();
bitmap_free(&m_bulk_insert_started); bitmap_free(&m_bulk_insert_started);
......
...@@ -1418,6 +1418,12 @@ void close_thread_tables(THD *thd) ...@@ -1418,6 +1418,12 @@ void close_thread_tables(THD *thd)
table->s->table_name.str, (long) table)); table->s->table_name.str, (long) table));
#endif #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. */ /* Detach MERGE children after every statement. Even under LOCK TABLES. */
for (table= thd->open_tables; table; table= table->next) for (table= thd->open_tables; table; table= table->next)
{ {
...@@ -5242,8 +5248,8 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, ...@@ -5242,8 +5248,8 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
thd - thread handler thd - thread handler
tables - list of tables for open tables - list of tables for open
flags - bitmap of flags to modify how the tables will be open: flags - bitmap of flags to modify how the tables will be open:
MYSQL_OPEN_IGNORE_FLUSH - open table even if someone has MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has
done a flush or namelock on it. done a flush on it.
RETURN RETURN
FALSE - ok FALSE - ok
...@@ -8781,9 +8787,58 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b) ...@@ -8781,9 +8787,58 @@ 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 SYNOPSIS
abort_and_upgrade_lock() abort_and_upgrade_lock_and_close_table()
lpt Parameter passing struct lpt Parameter passing struct
All parameters passed through the ALTER_PARTITION_PARAM_TYPE object All parameters passed through the ALTER_PARTITION_PARAM_TYPE object
RETURN VALUE RETURN VALUE
...@@ -8792,7 +8847,7 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b) ...@@ -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 Remember old lock level (for possible downgrade later on), abort all
waiting threads and ensure that all keeping locks currently are waiting threads and ensure that all keeping locks currently are
completed such that we own the lock exclusively and no other interaction 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 thd Thread object
table Table object table Table object
...@@ -8801,12 +8856,14 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b) ...@@ -8801,12 +8856,14 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b)
old_lock_level Old lock level 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)) if (wait_while_table_is_used(lpt->thd, lpt->table, HA_EXTRA_FORCE_REOPEN))
DBUG_RETURN(1); DBUG_RETURN(1);
if (alter_close_tables(lpt))
DBUG_RETURN(1);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
......
...@@ -218,7 +218,7 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, ...@@ -218,7 +218,7 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
thr_lock_type lock_type, uint flags); thr_lock_type lock_type, uint flags);
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, 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); 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); int decide_logging_format(THD *thd, TABLE_LIST *tables);
void free_io_cache(TABLE *entry); void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry); void intern_close_table(TABLE *entry);
......
...@@ -6283,54 +6283,6 @@ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt) ...@@ -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"); 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 Handle errors for ALTER TABLE for partitioning
...@@ -6629,9 +6581,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ...@@ -6629,9 +6581,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
write_log_drop_partition(lpt) || write_log_drop_partition(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_3") || ERROR_INJECT_CRASH("crash_drop_partition_3") ||
(not_completed= FALSE) || (not_completed= FALSE) ||
abort_and_upgrade_lock(lpt) || abort_and_upgrade_lock_and_close_table(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_4") ||
alter_close_tables(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_5") || ERROR_INJECT_CRASH("crash_drop_partition_5") ||
((!thd->lex->no_write_to_binlog) && ((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE, (write_bin_log(thd, FALSE,
...@@ -6697,9 +6647,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ...@@ -6697,9 +6647,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_add_partition_2") || ERROR_INJECT_CRASH("crash_add_partition_2") ||
mysql_change_partitions(lpt) || mysql_change_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_3") || ERROR_INJECT_CRASH("crash_add_partition_3") ||
abort_and_upgrade_lock(lpt) || abort_and_upgrade_lock_and_close_table(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_4") ||
alter_close_tables(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_5") || ERROR_INJECT_CRASH("crash_add_partition_5") ||
((!thd->lex->no_write_to_binlog) && ((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE, (write_bin_log(thd, FALSE,
...@@ -6782,9 +6730,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ...@@ -6782,9 +6730,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
write_log_final_change_partition(lpt) || write_log_final_change_partition(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_4") || ERROR_INJECT_CRASH("crash_change_partition_4") ||
(not_completed= FALSE) || (not_completed= FALSE) ||
abort_and_upgrade_lock(lpt) || abort_and_upgrade_lock_and_close_table(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_5") ||
alter_close_tables(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_6") || ERROR_INJECT_CRASH("crash_change_partition_6") ||
((!thd->lex->no_write_to_binlog) && ((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE, (write_bin_log(thd, FALSE,
......
...@@ -49,7 +49,8 @@ ...@@ -49,7 +49,8 @@
#include "event_data_objects.h" #include "event_data_objects.h"
#endif #endif
#include <my_dir.h> #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>") #define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
...@@ -3538,6 +3539,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) ...@@ -3538,6 +3539,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
lex->sql_command= SQLCOM_SHOW_FIELDS; lex->sql_command= SQLCOM_SHOW_FIELDS;
show_table_list->i_s_requested_object= show_table_list->i_s_requested_object=
schema_table->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, res= open_normal_and_derived_tables(thd, show_table_list,
(MYSQL_OPEN_IGNORE_FLUSH | (MYSQL_OPEN_IGNORE_FLUSH |
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL | MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
......
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