BUG#32943: Fixed buggy lock handling of ALTER TABLE for partitioning
mysql-test/r/partition_range.result: Added new test cases for lock tables and ALTER TABLE for partitions, also added a test case with a trigger. mysql-test/t/partition_range.test: Added new test cases for lock tables and ALTER TABLE for partitions, also added a test case with a trigger. sql/mysql_priv.h: Added WFRM_KEEP_SHARE for use of code not to be used otherwise sql/sql_partition.cc: Removed get_name_lock and release_name_lock, use close_data_files_and_morph_locks which leaves an exclusive name lock after completing. Reopen table after completing if under lock tables Updated comments sql/sql_table.cc: Ensure that code to set partition syntax isn't used other than when specifically asked to do it.
This commit is contained in:
parent
4bacd53715
commit
516f95acea
@ -1,4 +1,26 @@
|
|||||||
drop table if exists t1, t2;
|
drop table if exists t1, t2;
|
||||||
|
create table t1 (a integer)
|
||||||
|
partition by range (a)
|
||||||
|
( partition p0 values less than (4),
|
||||||
|
partition p1 values less than (100));
|
||||||
|
create trigger tr1 before insert on t1
|
||||||
|
for each row begin
|
||||||
|
set @a = 1;
|
||||||
|
end|
|
||||||
|
alter table t1 drop partition p0;
|
||||||
|
drop table t1;
|
||||||
|
create table t1 (a integer)
|
||||||
|
partition by range (a)
|
||||||
|
( partition p0 values less than (4),
|
||||||
|
partition p1 values less than (100));
|
||||||
|
LOCK TABLES t1 WRITE;
|
||||||
|
alter table t1 drop partition p0;
|
||||||
|
alter table t1 reorganize partition p1 into
|
||||||
|
( partition p0 values less than (4),
|
||||||
|
partition p1 values less than (100));
|
||||||
|
alter table t1 add partition ( partition p2 values less than (200));
|
||||||
|
UNLOCK TABLES;
|
||||||
|
drop table t1;
|
||||||
create table t1 (a int unsigned)
|
create table t1 (a int unsigned)
|
||||||
partition by range (a)
|
partition by range (a)
|
||||||
(partition pnull values less than (0),
|
(partition pnull values less than (0),
|
||||||
|
@ -9,6 +9,41 @@
|
|||||||
drop table if exists t1, t2;
|
drop table if exists t1, t2;
|
||||||
--enable_warnings
|
--enable_warnings
|
||||||
|
|
||||||
|
#
|
||||||
|
# BUG 32943:
|
||||||
|
# Locking problems in relation to partitioning and triggers
|
||||||
|
# Also fixes and test cases of generic lock issues with
|
||||||
|
# partition change code.
|
||||||
|
#
|
||||||
|
create table t1 (a integer)
|
||||||
|
partition by range (a)
|
||||||
|
( partition p0 values less than (4),
|
||||||
|
partition p1 values less than (100));
|
||||||
|
|
||||||
|
delimiter |;
|
||||||
|
create trigger tr1 before insert on t1
|
||||||
|
for each row begin
|
||||||
|
set @a = 1;
|
||||||
|
end|
|
||||||
|
|
||||||
|
delimiter ;|
|
||||||
|
alter table t1 drop partition p0;
|
||||||
|
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
|
create table t1 (a integer)
|
||||||
|
partition by range (a)
|
||||||
|
( partition p0 values less than (4),
|
||||||
|
partition p1 values less than (100));
|
||||||
|
LOCK TABLES t1 WRITE;
|
||||||
|
alter table t1 drop partition p0;
|
||||||
|
alter table t1 reorganize partition p1 into
|
||||||
|
( partition p0 values less than (4),
|
||||||
|
partition p1 values less than (100));
|
||||||
|
alter table t1 add partition ( partition p2 values less than (200));
|
||||||
|
UNLOCK TABLES;
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
#
|
#
|
||||||
# BUG 18198: Various tests for partition functions
|
# BUG 18198: Various tests for partition functions
|
||||||
#
|
#
|
||||||
|
@ -1635,6 +1635,7 @@ extern pthread_mutex_t LOCK_gdl;
|
|||||||
#define WFRM_WRITE_SHADOW 1
|
#define WFRM_WRITE_SHADOW 1
|
||||||
#define WFRM_INSTALL_SHADOW 2
|
#define WFRM_INSTALL_SHADOW 2
|
||||||
#define WFRM_PACK_FRM 4
|
#define WFRM_PACK_FRM 4
|
||||||
|
#define WFRM_KEEP_SHARE 8
|
||||||
bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags);
|
bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags);
|
||||||
int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt);
|
int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt);
|
||||||
void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt);
|
void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt);
|
||||||
|
@ -5838,32 +5838,32 @@ static void release_log_entries(partition_info *part_info)
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get a lock on table name to avoid that anyone can open the table in
|
Final part of partition changes to handle things when under
|
||||||
a critical part of the ALTER TABLE.
|
LOCK TABLES.
|
||||||
SYNOPSIS
|
SYNPOSIS
|
||||||
get_name_lock()
|
alter_partition_lock_handling()
|
||||||
lpt Struct carrying parameters
|
lpt Struct carrying parameters
|
||||||
RETURN VALUES
|
RETURN VALUES
|
||||||
FALSE Success
|
NONE
|
||||||
TRUE Failure
|
|
||||||
*/
|
*/
|
||||||
|
void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
|
||||||
static int get_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
|
|
||||||
{
|
{
|
||||||
int error= 0;
|
int err;
|
||||||
DBUG_ENTER("get_name_lock");
|
if (lpt->thd->locked_tables)
|
||||||
|
{
|
||||||
bzero(&lpt->table_list, sizeof(lpt->table_list));
|
|
||||||
lpt->table_list.db= (char*)lpt->db;
|
|
||||||
lpt->table_list.table= lpt->table;
|
|
||||||
lpt->table_list.table_name= (char*)lpt->table_name;
|
|
||||||
pthread_mutex_lock(&LOCK_open);
|
pthread_mutex_lock(&LOCK_open);
|
||||||
error= lock_table_name(lpt->thd, &lpt->table_list, FALSE);
|
lpt->thd->in_lock_tables= 1;
|
||||||
|
err= reopen_tables(lpt->thd, 1, 1);
|
||||||
|
lpt->thd->in_lock_tables= 0;
|
||||||
pthread_mutex_unlock(&LOCK_open);
|
pthread_mutex_unlock(&LOCK_open);
|
||||||
DBUG_RETURN(error);
|
if (err)
|
||||||
|
{
|
||||||
|
/* Issue a warning since we weren't able to regain the lock again. */
|
||||||
|
sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Unlock and close table before renaming and dropping partitions
|
Unlock and close table before renaming and dropping partitions
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
@ -5876,35 +5876,16 @@ static int get_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
|
|||||||
static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
|
static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
|
||||||
{
|
{
|
||||||
THD *thd= lpt->thd;
|
THD *thd= lpt->thd;
|
||||||
TABLE *table= lpt->table;
|
const char *db= lpt->db;
|
||||||
|
const char *table_name= lpt->table_name;
|
||||||
DBUG_ENTER("alter_close_tables");
|
DBUG_ENTER("alter_close_tables");
|
||||||
/*
|
/*
|
||||||
We need to also unlock tables and close all handlers.
|
We need to also unlock tables and close all handlers.
|
||||||
We set lock to zero to ensure we don't do this twice
|
We set lock to zero to ensure we don't do this twice
|
||||||
and we set db_stat to zero to ensure we don't close twice.
|
and we set db_stat to zero to ensure we don't close twice.
|
||||||
*/
|
*/
|
||||||
mysql_unlock_tables(thd, thd->lock);
|
|
||||||
thd->lock= 0;
|
|
||||||
table->file->close();
|
|
||||||
table->db_stat= 0;
|
|
||||||
DBUG_RETURN(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Release a lock name
|
|
||||||
SYNOPSIS
|
|
||||||
release_name_lock()
|
|
||||||
lpt
|
|
||||||
RETURN VALUES
|
|
||||||
0
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int release_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
|
|
||||||
{
|
|
||||||
DBUG_ENTER("release_name_lock");
|
|
||||||
pthread_mutex_lock(&LOCK_open);
|
pthread_mutex_lock(&LOCK_open);
|
||||||
unlock_table_name(lpt->thd, &lpt->table_list);
|
close_data_files_and_morph_locks(thd, db, table_name);
|
||||||
pthread_mutex_unlock(&LOCK_open);
|
pthread_mutex_unlock(&LOCK_open);
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
@ -6202,7 +6183,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
|
|||||||
name lock.
|
name lock.
|
||||||
5) Close all tables that have already been opened but didn't stumble on
|
5) Close all tables that have already been opened but didn't stumble on
|
||||||
the abort locked previously. This is done as part of the
|
the abort locked previously. This is done as part of the
|
||||||
get_name_lock call.
|
close_data_files_and_morph_locks call.
|
||||||
6) We are now ready to release all locks we got in this thread.
|
6) We are now ready to release all locks we got in this thread.
|
||||||
7) Write the bin log
|
7) Write the bin log
|
||||||
Unfortunately the writing of the binlog is not synchronised with
|
Unfortunately the writing of the binlog is not synchronised with
|
||||||
@ -6219,8 +6200,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
|
|||||||
9) Prepare handlers for drop of partitions
|
9) Prepare handlers for drop of partitions
|
||||||
10) Drop the partitions
|
10) Drop the partitions
|
||||||
11) Remove entries from ddl log
|
11) Remove entries from ddl log
|
||||||
12) Release name lock so that all other threads can access the table
|
12) Reopen table if under lock tables
|
||||||
again.
|
|
||||||
13) Complete query
|
13) Complete query
|
||||||
|
|
||||||
We insert Error injections at all places where it could be interesting
|
We insert Error injections at all places where it could be interesting
|
||||||
@ -6235,23 +6215,21 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
|
|||||||
(not_completed= FALSE) ||
|
(not_completed= FALSE) ||
|
||||||
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
|
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
|
||||||
ERROR_INJECT_CRASH("crash_drop_partition_4") ||
|
ERROR_INJECT_CRASH("crash_drop_partition_4") ||
|
||||||
get_name_lock(lpt) ||
|
|
||||||
ERROR_INJECT_CRASH("crash_drop_partition_5") ||
|
|
||||||
alter_close_tables(lpt) ||
|
alter_close_tables(lpt) ||
|
||||||
ERROR_INJECT_CRASH("crash_drop_partition_6") ||
|
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,
|
||||||
thd->query, thd->query_length), FALSE)) ||
|
thd->query, thd->query_length), FALSE)) ||
|
||||||
ERROR_INJECT_CRASH("crash_drop_partition_7") ||
|
ERROR_INJECT_CRASH("crash_drop_partition_6") ||
|
||||||
((frm_install= TRUE), FALSE) ||
|
((frm_install= TRUE), FALSE) ||
|
||||||
mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
|
mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
|
||||||
((frm_install= FALSE), FALSE) ||
|
((frm_install= FALSE), FALSE) ||
|
||||||
ERROR_INJECT_CRASH("crash_drop_partition_8") ||
|
ERROR_INJECT_CRASH("crash_drop_partition_7") ||
|
||||||
mysql_drop_partitions(lpt) ||
|
mysql_drop_partitions(lpt) ||
|
||||||
ERROR_INJECT_CRASH("crash_drop_partition_9") ||
|
ERROR_INJECT_CRASH("crash_drop_partition_8") ||
|
||||||
(write_log_completed(lpt, FALSE), FALSE) ||
|
(write_log_completed(lpt, FALSE), FALSE) ||
|
||||||
ERROR_INJECT_CRASH("crash_drop_partition_10") ||
|
ERROR_INJECT_CRASH("crash_drop_partition_9") ||
|
||||||
(release_name_lock(lpt), FALSE))
|
(alter_partition_lock_handling(lpt), FALSE))
|
||||||
{
|
{
|
||||||
handle_alter_part_error(lpt, not_completed, TRUE, frm_install);
|
handle_alter_part_error(lpt, not_completed, TRUE, frm_install);
|
||||||
goto err;
|
goto err;
|
||||||
@ -6283,7 +6261,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
|
|||||||
name lock.
|
name lock.
|
||||||
5) Close all tables that have already been opened but didn't stumble on
|
5) Close all tables that have already been opened but didn't stumble on
|
||||||
the abort locked previously. This is done as part of the
|
the abort locked previously. This is done as part of the
|
||||||
get_name_lock call.
|
close_data_files_and_morph_locks call.
|
||||||
6) Close all table handlers and unlock all handlers but retain name lock
|
6) Close all table handlers and unlock all handlers but retain name lock
|
||||||
7) Write binlog
|
7) Write binlog
|
||||||
8) Now the change is completed except for the installation of the
|
8) Now the change is completed except for the installation of the
|
||||||
@ -6293,7 +6271,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
|
|||||||
added to the table.
|
added to the table.
|
||||||
10)Wait until all accesses using the old frm file has completed
|
10)Wait until all accesses using the old frm file has completed
|
||||||
11)Remove entries from ddl log
|
11)Remove entries from ddl log
|
||||||
12)Release name lock
|
12)Reopen tables if under lock tables
|
||||||
13)Complete query
|
13)Complete query
|
||||||
*/
|
*/
|
||||||
if (write_log_add_change_partition(lpt) ||
|
if (write_log_add_change_partition(lpt) ||
|
||||||
@ -6303,8 +6281,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
|
|||||||
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) || /* Always returns 0 */
|
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
|
||||||
ERROR_INJECT_CRASH("crash_add_partition_3") ||
|
|
||||||
get_name_lock(lpt) ||
|
|
||||||
ERROR_INJECT_CRASH("crash_add_partition_4") ||
|
ERROR_INJECT_CRASH("crash_add_partition_4") ||
|
||||||
alter_close_tables(lpt) ||
|
alter_close_tables(lpt) ||
|
||||||
ERROR_INJECT_CRASH("crash_add_partition_5") ||
|
ERROR_INJECT_CRASH("crash_add_partition_5") ||
|
||||||
@ -6320,7 +6296,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
|
|||||||
ERROR_INJECT_CRASH("crash_add_partition_8") ||
|
ERROR_INJECT_CRASH("crash_add_partition_8") ||
|
||||||
(write_log_completed(lpt, FALSE), FALSE) ||
|
(write_log_completed(lpt, FALSE), FALSE) ||
|
||||||
ERROR_INJECT_CRASH("crash_add_partition_9") ||
|
ERROR_INJECT_CRASH("crash_add_partition_9") ||
|
||||||
(release_name_lock(lpt), FALSE))
|
(alter_partition_lock_handling(lpt), FALSE))
|
||||||
{
|
{
|
||||||
handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
|
handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
|
||||||
goto err;
|
goto err;
|
||||||
@ -6374,15 +6350,15 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
|
|||||||
7) Close all tables opened but not yet locked, after this call we are
|
7) Close all tables opened but not yet locked, after this call we are
|
||||||
certain that no other thread is in the lock wait queue or has
|
certain that no other thread is in the lock wait queue or has
|
||||||
opened the table. The name lock will ensure that they are blocked
|
opened the table. The name lock will ensure that they are blocked
|
||||||
on the open call. This is achieved also by get_name_lock call.
|
on the open call.
|
||||||
|
This is achieved also by close_data_files_and_morph_locks call.
|
||||||
8) Close all partitions opened by this thread, but retain name lock.
|
8) Close all partitions opened by this thread, but retain name lock.
|
||||||
9) Write bin log
|
9) Write bin log
|
||||||
10) Prepare handlers for rename and delete of partitions
|
10) Prepare handlers for rename and delete of partitions
|
||||||
11) Rename and drop the reorged partitions such that they are no
|
11) Rename and drop the reorged partitions such that they are no
|
||||||
longer used and rename those added to their real new names.
|
longer used and rename those added to their real new names.
|
||||||
12) Install the shadow frm file
|
12) Install the shadow frm file
|
||||||
13) Release the name lock to enable other threads to start using the
|
13) Reopen the table if under lock tables
|
||||||
table again.
|
|
||||||
14) Complete query
|
14) Complete query
|
||||||
*/
|
*/
|
||||||
if (write_log_add_change_partition(lpt) ||
|
if (write_log_add_change_partition(lpt) ||
|
||||||
@ -6396,24 +6372,22 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
|
|||||||
(not_completed= FALSE) ||
|
(not_completed= FALSE) ||
|
||||||
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
|
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
|
||||||
ERROR_INJECT_CRASH("crash_change_partition_5") ||
|
ERROR_INJECT_CRASH("crash_change_partition_5") ||
|
||||||
get_name_lock(lpt) ||
|
|
||||||
ERROR_INJECT_CRASH("crash_change_partition_6") ||
|
|
||||||
alter_close_tables(lpt) ||
|
alter_close_tables(lpt) ||
|
||||||
ERROR_INJECT_CRASH("crash_change_partition_7") ||
|
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,
|
||||||
thd->query, thd->query_length), FALSE)) ||
|
thd->query, thd->query_length), FALSE)) ||
|
||||||
ERROR_INJECT_CRASH("crash_change_partition_8") ||
|
ERROR_INJECT_CRASH("crash_change_partition_7") ||
|
||||||
mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
|
mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
|
||||||
ERROR_INJECT_CRASH("crash_change_partition_9") ||
|
ERROR_INJECT_CRASH("crash_change_partition_8") ||
|
||||||
mysql_drop_partitions(lpt) ||
|
mysql_drop_partitions(lpt) ||
|
||||||
ERROR_INJECT_CRASH("crash_change_partition_10") ||
|
ERROR_INJECT_CRASH("crash_change_partition_9") ||
|
||||||
mysql_rename_partitions(lpt) ||
|
mysql_rename_partitions(lpt) ||
|
||||||
((frm_install= TRUE), FALSE) ||
|
((frm_install= TRUE), FALSE) ||
|
||||||
ERROR_INJECT_CRASH("crash_change_partition_11") ||
|
ERROR_INJECT_CRASH("crash_change_partition_10") ||
|
||||||
(write_log_completed(lpt, FALSE), FALSE) ||
|
(write_log_completed(lpt, FALSE), FALSE) ||
|
||||||
ERROR_INJECT_CRASH("crash_change_partition_12") ||
|
ERROR_INJECT_CRASH("crash_change_partition_11") ||
|
||||||
(release_name_lock(lpt), FALSE))
|
(alter_partition_lock_handling(lpt), FALSE))
|
||||||
{
|
{
|
||||||
handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
|
handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -1226,8 +1226,12 @@ uint build_table_shadow_filename(char *buff, size_t bufflen,
|
|||||||
flags Flags as defined below
|
flags Flags as defined below
|
||||||
WFRM_INITIAL_WRITE If set we need to prepare table before
|
WFRM_INITIAL_WRITE If set we need to prepare table before
|
||||||
creating the frm file
|
creating the frm file
|
||||||
WFRM_CREATE_HANDLER_FILES If set we need to create the handler file as
|
WFRM_INSTALL_SHADOW If set we should install the new frm
|
||||||
part of the creation of the frm file
|
WFRM_KEEP_SHARE If set we know that the share is to be
|
||||||
|
retained and thus we should ensure share
|
||||||
|
object is correct, if not set we don't
|
||||||
|
set the new partition syntax string since
|
||||||
|
we know the share object is destroyed.
|
||||||
WFRM_PACK_FRM If set we should pack the frm file and delete
|
WFRM_PACK_FRM If set we should pack the frm file and delete
|
||||||
the frm file
|
the frm file
|
||||||
|
|
||||||
@ -1370,7 +1374,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||||
if (part_info)
|
if (part_info && (flags & WFRM_KEEP_SHARE))
|
||||||
{
|
{
|
||||||
TABLE_SHARE *share= lpt->table->s;
|
TABLE_SHARE *share= lpt->table->s;
|
||||||
char *tmp_part_syntax_str;
|
char *tmp_part_syntax_str;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user