Commit 8399af81 authored by Monty's avatar Monty

Bug#19784790: ASSERTION `PART_SHARE->PARTITIONS_SHARE_REFS->NUM_PARTS

              >= M_TOT_PARTS' FAILED.

This patch is taken from MySQL, originally written by Mattias Jonsson
Here follows the original commit message:

Problem in handle_alter_part_error(),
result in altered partition_info object was still used
if table was under LOCK TABLES.

Solution was to always close and destroy all table
and table_share instances if exclusive mdl lock was
possible.
If not succeeding in get an exlusive lock (only possible
during rollback of DDL), at least close and destroy this
table instance.

rb#7361.
Approved by Mikael and Aditya.
parent 0126169e
......@@ -236,6 +236,7 @@ DROP TABLE t1;
connect con1,localhost,root,,;
CREATE TABLE t1 ( i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, f INT )
ENGINE = InnoDB PARTITION BY HASH(i) PARTITIONS 2;
INSERT INTO t1 VALUES (2, 2), (3, 3), (4, 4), (5, 5);
connect con2,localhost,root,,;
SET lock_wait_timeout = 2;
connection con1;
......@@ -249,6 +250,19 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction
# Second attempt: says that partition already exists
ALTER TABLE t1 ADD PARTITION PARTITIONS 2;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
# Check that we only can select, not insert/update/delete.
INSERT INTO t1 VALUES (NULL, 6), (NULL, 7), (10, 10), (11, 11);
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
UPDATE t1 SET i = 5 WHERE f = 2;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
DELETE FROM t1 WHERE i = 10;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SELECT * FROM t1;
i f
2 2
3 3
4 4
5 5
connection con1;
# Connection 1 unlocks the table and locks it again:
UNLOCK TABLES;
......@@ -335,3 +349,30 @@ PARTITION BY RANGE(f1) (PARTITION p1 VALUES LESS THAN(10),
PARTITION p2 VALUES LESS THAN (100));
ALTER TABLE t1 convert to charset ascii collate ascii_bin, ALGORITHM=INSTANT;
DROP TABLE t1;
# Test WRITE LOCK.
connect con1,localhost,root,,;
CREATE TABLE t1 ( i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, f INT )
ENGINE = InnoDB PARTITION BY HASH(i) PARTITIONS 2;
INSERT INTO t1 VALUES (3, 3), (4, 4);
connect con2,localhost,root,,;
SET lock_wait_timeout = 2;
connection con1;
#Connection 1 locks the table
LOCK TABLE t1 WRITE;
connection con2;
# Check that we still can SELECT, but not insert/update/delete.
# Check that we only can select, not insert/update/delete.
INSERT INTO t1 VALUES (NULL, 1), (NULL, 2), (10, 10), (11, 11);
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
UPDATE t1 SET i = 5 WHERE f = 2;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
DELETE FROM t1 WHERE i = 10;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SELECT * FROM t1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
connection con1;
UNLOCK TABLES;
connection con2;
DROP TABLE t1;
disconnect con1;
connection default;
......@@ -83,6 +83,7 @@ DROP TABLE t1;
--connect (con1,localhost,root,,)
CREATE TABLE t1 ( i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, f INT )
ENGINE = InnoDB PARTITION BY HASH(i) PARTITIONS 2;
INSERT INTO t1 VALUES (2, 2), (3, 3), (4, 4), (5, 5);
--connect (con2,localhost,root,,)
SET lock_wait_timeout = 2;
......@@ -99,6 +100,15 @@ ALTER TABLE t1 ADD PARTITION PARTITIONS 2;
--echo # Second attempt: says that partition already exists
--error ER_LOCK_WAIT_TIMEOUT
ALTER TABLE t1 ADD PARTITION PARTITIONS 2;
--echo # Check that we only can select, not insert/update/delete.
--error ER_LOCK_WAIT_TIMEOUT
INSERT INTO t1 VALUES (NULL, 6), (NULL, 7), (10, 10), (11, 11);
--error ER_LOCK_WAIT_TIMEOUT
UPDATE t1 SET i = 5 WHERE f = 2;
--error ER_LOCK_WAIT_TIMEOUT
DELETE FROM t1 WHERE i = 10;
--sorted_result
SELECT * FROM t1;
--connection con1
--echo # Connection 1 unlocks the table and locks it again:
......@@ -215,3 +225,37 @@ CREATE TABLE t1(
PARTITION p2 VALUES LESS THAN (100));
ALTER TABLE t1 convert to charset ascii collate ascii_bin, ALGORITHM=INSTANT;
DROP TABLE t1;
--echo # Test WRITE LOCK.
--connect (con1,localhost,root,,)
CREATE TABLE t1 ( i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, f INT )
ENGINE = InnoDB PARTITION BY HASH(i) PARTITIONS 2;
INSERT INTO t1 VALUES (3, 3), (4, 4);
--connect (con2,localhost,root,,)
SET lock_wait_timeout = 2;
--connection con1
--echo #Connection 1 locks the table
LOCK TABLE t1 WRITE;
--connection con2
--echo # Check that we still can SELECT, but not insert/update/delete.
--echo # Check that we only can select, not insert/update/delete.
--error ER_LOCK_WAIT_TIMEOUT
INSERT INTO t1 VALUES (NULL, 1), (NULL, 2), (10, 10), (11, 11);
--error ER_LOCK_WAIT_TIMEOUT
UPDATE t1 SET i = 5 WHERE f = 2;
--error ER_LOCK_WAIT_TIMEOUT
DELETE FROM t1 WHERE i = 10;
--error ER_LOCK_WAIT_TIMEOUT
SELECT * FROM t1;
--connection con1
UNLOCK TABLES;
--connection con2
DROP TABLE t1;
--disconnect con1
--connection default
......@@ -6808,41 +6808,30 @@ static int alter_close_table(ALTER_PARTITION_PARAM_TYPE *lpt)
static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
bool action_completed,
bool drop_partition,
bool frm_install,
bool close_table)
bool frm_install)
{
partition_info *part_info= lpt->part_info;
THD *thd= lpt->thd;
partition_info *part_info= lpt->part_info->get_clone(thd);
TABLE *table= lpt->table;
DBUG_ENTER("handle_alter_part_error");
DBUG_ASSERT(table->m_needs_reopen);
if (close_table)
{
/*
All instances of this table needs to be closed.
Better to do that here, than leave the cleaning up to others.
Aquire EXCLUSIVE mdl lock if not already aquired.
Acquire EXCLUSIVE mdl lock if not already acquired.
*/
if (!thd->mdl_context.is_lock_owner(MDL_key::TABLE, lpt->db.str,
lpt->table_name.str,
MDL_EXCLUSIVE))
{
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
MDL_EXCLUSIVE) &&
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
{
/* At least remove this instance on failure */
goto err_exclusive_lock;
}
}
/* Ensure the share is destroyed and reopened. */
if (part_info)
part_info= part_info->get_clone(thd);
close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
}
else
{
err_exclusive_lock:
/*
Did not succeed in getting exclusive access to the table.
Since we have altered a cached table object (and its part_info) we need
at least to remove this instance so it will not be reused.
Temporarily remove it from the locked table list, so that it will get
reopened.
*/
......@@ -6854,11 +6843,14 @@ static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
the table cache.
*/
mysql_lock_remove(thd, thd->lock, table);
if (part_info)
part_info= part_info->get_clone(thd);
close_thread_table(thd, &thd->open_tables);
lpt->table_list->table= NULL;
}
else
{
/* Ensure the share is destroyed and reopened. */
close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
}
if (part_info->first_log_entry &&
execute_ddl_log_entry(thd, part_info->first_log_entry->entry_pos))
......@@ -6876,17 +6868,20 @@ static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
/* Table is still ok, but we left a shadow frm file behind. */
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,
"%s %s",
"Operation was unsuccessful, table is still intact,",
"but it is possible that a shadow frm file was left behind");
"Operation was unsuccessful, table is still "
"intact, but it is possible that a shadow frm "
"file was left behind");
}
else
{
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,
"%s %s %s %s",
"Operation was unsuccessful, table is still intact,",
"but it is possible that a shadow frm file was left behind.",
"It is also possible that temporary partitions are left behind,",
"these could be empty or more or less filled with records");
"Operation was unsuccessful, table is still "
"intact, but it is possible that a shadow frm "
"file was left behind.",
"It is also possible that temporary partitions "
"are left behind, these could be empty or more "
"or less filled with records");
}
}
else
......@@ -6899,9 +6894,10 @@ static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
*/
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,
"%s %s %s",
"Failed during alter of partitions, table is no longer intact.",
"The frm file is in an unknown state, and a backup",
"is required.");
"Failed during alter of partitions, table is no "
"longer intact.",
"The frm file is in an unknown state, and a "
"backup is required.");
}
else if (drop_partition)
{
......@@ -6913,7 +6909,8 @@ static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
*/
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,
"%s %s",
"Failed during drop of partitions, table is intact.",
"Failed during drop of partitions, table is "
"intact.",
"Manual drop of remaining partitions is required");
}
else
......@@ -6925,9 +6922,10 @@ static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
*/
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,
"%s %s %s",
"Failed during renaming of partitions. We are now in a position",
"where table is not reusable",
"Table is disabled by writing ancient frm file version into it");
"Failed during renaming of partitions. We are now "
"in a position where table is not reusable",
"Table is disabled by writing ancient frm file "
"version into it");
}
}
}
......@@ -6953,8 +6951,8 @@ static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
completed.
*/
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,"%s %s",
"Operation was successfully completed by failure handling,",
"after failure of normal operation");
"Operation was successfully completed by failure "
"handling, after failure of normal operation");
}
}
......@@ -7032,7 +7030,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ALTER_PARTITION_PARAM_TYPE lpt_obj;
ALTER_PARTITION_PARAM_TYPE *lpt= &lpt_obj;
bool action_completed= FALSE;
bool close_table_on_failure= FALSE;
bool frm_install= FALSE;
MDL_ticket *mdl_ticket= table->mdl_ticket;
DBUG_ENTER("fast_alter_partition_table");
......@@ -7171,13 +7168,11 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) ||
ERROR_INJECT_CRASH("crash_drop_partition_3") ||
ERROR_INJECT_ERROR("fail_drop_partition_3") ||
(close_table_on_failure= TRUE, FALSE) ||
write_log_drop_partition(lpt) ||
(action_completed= TRUE, FALSE) ||
ERROR_INJECT_CRASH("crash_drop_partition_4") ||
ERROR_INJECT_ERROR("fail_drop_partition_4") ||
alter_close_table(lpt) ||
(close_table_on_failure= FALSE, FALSE) ||
ERROR_INJECT_CRASH("crash_drop_partition_5") ||
ERROR_INJECT_ERROR("fail_drop_partition_5") ||
((!thd->lex->no_write_to_binlog) &&
......@@ -7197,8 +7192,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_drop_partition_9") ||
ERROR_INJECT_ERROR("fail_drop_partition_9"))
{
handle_alter_part_error(lpt, action_completed, TRUE, frm_install,
close_table_on_failure);
handle_alter_part_error(lpt, action_completed, TRUE, frm_install);
goto err;
}
if (alter_partition_lock_handling(lpt))
......@@ -7246,14 +7240,12 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) ||
ERROR_INJECT_CRASH("crash_add_partition_3") ||
ERROR_INJECT_ERROR("fail_add_partition_3") ||
(close_table_on_failure= TRUE, FALSE) ||
write_log_add_change_partition(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_4") ||
ERROR_INJECT_ERROR("fail_add_partition_4") ||
mysql_change_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_5") ||
ERROR_INJECT_ERROR("fail_add_partition_5") ||
(close_table_on_failure= FALSE, FALSE) ||
alter_close_table(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_6") ||
ERROR_INJECT_ERROR("fail_add_partition_6") ||
......@@ -7275,8 +7267,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_add_partition_10") ||
ERROR_INJECT_ERROR("fail_add_partition_10"))
{
handle_alter_part_error(lpt, action_completed, FALSE, frm_install,
close_table_on_failure);
handle_alter_part_error(lpt, action_completed, FALSE, frm_install);
goto err;
}
if (alter_partition_lock_handling(lpt))
......@@ -7343,7 +7334,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
ERROR_INJECT_CRASH("crash_change_partition_2") ||
ERROR_INJECT_ERROR("fail_change_partition_2") ||
(close_table_on_failure= TRUE, FALSE) ||
write_log_add_change_partition(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_3") ||
ERROR_INJECT_ERROR("fail_change_partition_3") ||
......@@ -7354,7 +7344,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_change_partition_5") ||
ERROR_INJECT_ERROR("fail_change_partition_5") ||
alter_close_table(lpt) ||
(close_table_on_failure= FALSE, FALSE) ||
ERROR_INJECT_CRASH("crash_change_partition_6") ||
ERROR_INJECT_ERROR("fail_change_partition_6") ||
write_log_final_change_partition(lpt) ||
......@@ -7381,8 +7370,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_change_partition_12") ||
ERROR_INJECT_ERROR("fail_change_partition_12"))
{
handle_alter_part_error(lpt, action_completed, FALSE, frm_install,
close_table_on_failure);
handle_alter_part_error(lpt, action_completed, FALSE, frm_install);
goto err;
}
if (alter_partition_lock_handling(lpt))
......
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