summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/innodb_mysql_lock2.result65
-rw-r--r--mysql-test/suite/rpl_ndb/r/rpl_ndb_binlog_format_errors.result4
-rw-r--r--mysql-test/suite/rpl_ndb/t/rpl_ndb_binlog_format_errors.test4
-rw-r--r--mysql-test/t/innodb_mysql_lock2.test103
-rw-r--r--sql/ha_partition.cc2
-rw-r--r--sql/lock.cc4
-rw-r--r--sql/mdl.cc5
-rw-r--r--sql/sql_base.cc4
-rw-r--r--sql/sql_table.cc4
-rw-r--r--sql/sql_trigger.cc2
-rw-r--r--sql/sql_yacc.yy8
11 files changed, 186 insertions, 19 deletions
diff --git a/mysql-test/r/innodb_mysql_lock2.result b/mysql-test/r/innodb_mysql_lock2.result
index aed704e6b3e..70cfbee6463 100644
--- a/mysql-test/r/innodb_mysql_lock2.result
+++ b/mysql-test/r/innodb_mysql_lock2.result
@@ -562,3 +562,68 @@ drop view v1, v2;
drop procedure p1;
drop procedure p2;
drop table t1, t2, t3, t4, t5;
+#
+# Test for bug#51263 "Deadlock between transactional SELECT
+# and ALTER TABLE ... REBUILD PARTITION".
+#
+drop table if exists t1, t2;
+create table t1 (i int auto_increment not null primary key) engine=innodb;
+create table t2 (i int) engine=innodb;
+insert into t1 values (1), (2), (3), (4), (5);
+begin;
+# Acquire SR metadata lock on t1 and LOCK_S row-locks on its rows.
+insert into t2 select count(*) from t1;
+# Switching to connection 'con1'.
+# Sending:
+alter table t1 add column j int;
+# Switching to connection 'default'.
+# Wait until ALTER is blocked because it tries to upgrade SNW
+# metadata lock to X lock.
+# It should not be blocked during copying data to new version of
+# table as it acquires LOCK_S locks on rows of old version, which
+# are compatible with locks acquired by connection 'con1'.
+# The below statement will deadlock because it will try to acquire
+# SW lock on t1, which will conflict with ALTER's SNW lock. And
+# ALTER will be waiting for this connection to release its SR lock.
+# This deadlock should be detected by an MDL subsystem and this
+# statement should be aborted with an appropriate error.
+insert into t1 values (6);
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+# Unblock ALTER TABLE.
+commit;
+# Switching to connection 'con1'.
+# Reaping ALTER TABLE.
+# Switching to connection 'default'.
+#
+# Now test for scenario in which bug was reported originally.
+#
+drop tables t1, t2;
+create table t1 (i int auto_increment not null primary key) engine=innodb
+partition by hash (i) partitions 4;
+create table t2 (i int) engine=innodb;
+insert into t1 values (1), (2), (3), (4), (5);
+begin;
+# Acquire SR metadata lock on t1.
+select * from t1;
+i
+1
+2
+3
+4
+5
+# Switching to connection 'con1'.
+# Sending:
+alter table t1 rebuild partition p0;
+# Switching to connection 'default'.
+# Wait until ALTER is blocked because of active SR lock.
+# The below statement should succeed as transaction
+# has SR metadata lock on t1 and only going to read
+# rows from it.
+insert into t2 select count(*) from t1;
+# Unblock ALTER TABLE.
+commit;
+# Switching to connection 'con1'.
+# Reaping ALTER TABLE.
+# Switching to connection 'default'.
+# Clean-up.
+drop tables t1, t2;
diff --git a/mysql-test/suite/rpl_ndb/r/rpl_ndb_binlog_format_errors.result b/mysql-test/suite/rpl_ndb/r/rpl_ndb_binlog_format_errors.result
index 36b8e022dd2..ab126026fe4 100644
--- a/mysql-test/suite/rpl_ndb/r/rpl_ndb_binlog_format_errors.result
+++ b/mysql-test/suite/rpl_ndb/r/rpl_ndb_binlog_format_errors.result
@@ -43,9 +43,9 @@ SELECT * FROM t /* Should be empty */;
a
* Modify both row-only and stmt-only table
CREATE TRIGGER trig_2 AFTER INSERT ON t_stmt FOR EACH ROW BEGIN INSERT INTO t_row VALUES(1); END;
-ERROR HY000: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = ROW and at least one table uses a storage engine limited to statement-based logging.
INSERT INTO t_stmt VALUES (1);
-ERROR HY000: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = ROW and at least one table uses a storage engine limited to statement-based logging.
+ERROR HY000: Cannot execute statement: impossible to write to binary log since both row-incapable engines and statement-incapable engines are involved.
+DROP trigger trig_2;
SELECT * FROM t_stmt /* should be empty */;
a
* Stmt-only table and binlog_format=row
diff --git a/mysql-test/suite/rpl_ndb/t/rpl_ndb_binlog_format_errors.test b/mysql-test/suite/rpl_ndb/t/rpl_ndb_binlog_format_errors.test
index 481db5f6564..f6331cd0808 100644
--- a/mysql-test/suite/rpl_ndb/t/rpl_ndb_binlog_format_errors.test
+++ b/mysql-test/suite/rpl_ndb/t/rpl_ndb_binlog_format_errors.test
@@ -97,10 +97,10 @@ SELECT * FROM t_self_logging /* Should be empty */;
SELECT * FROM t /* Should be empty */;
--echo * Modify both row-only and stmt-only table
---error ER_BINLOG_ROW_MODE_AND_STMT_ENGINE
--eval CREATE TRIGGER trig_2 AFTER INSERT ON t_stmt FOR EACH ROW BEGIN INSERT INTO t_row VALUES(1); END
---error ER_BINLOG_ROW_MODE_AND_STMT_ENGINE
+--error ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE
INSERT INTO t_stmt VALUES (1);
+DROP trigger trig_2;
SELECT * FROM t_stmt /* should be empty */;
--echo * Stmt-only table and binlog_format=row
diff --git a/mysql-test/t/innodb_mysql_lock2.test b/mysql-test/t/innodb_mysql_lock2.test
index 5111d56225a..1f01f4cb010 100644
--- a/mysql-test/t/innodb_mysql_lock2.test
+++ b/mysql-test/t/innodb_mysql_lock2.test
@@ -3,7 +3,12 @@
# This test requires statement/mixed mode binary logging.
# Row-based mode puts weaker serializability requirements
# so weaker locks are acquired for it.
+# Also in ROW mode LOCK_S row locks won't be acquired for DML
+# and test for bug#51263 won't trigger execution path on which
+# this bug was encountered.
--source include/have_binlog_format_mixed_or_statement.inc
+# Original test case for bug#51263 needs partitioning.
+--source include/have_partition.inc
# Save the initial number of concurrent sessions.
--source include/count_sessions.inc
@@ -760,6 +765,104 @@ drop procedure p2;
drop table t1, t2, t3, t4, t5;
disconnect con1;
+
+--echo #
+--echo # Test for bug#51263 "Deadlock between transactional SELECT
+--echo # and ALTER TABLE ... REBUILD PARTITION".
+--echo #
+connect (con1,localhost,root,,test,,);
+connection default;
+--disable_warnings
+drop table if exists t1, t2;
+--enable_warnings
+create table t1 (i int auto_increment not null primary key) engine=innodb;
+create table t2 (i int) engine=innodb;
+insert into t1 values (1), (2), (3), (4), (5);
+
+begin;
+--echo # Acquire SR metadata lock on t1 and LOCK_S row-locks on its rows.
+insert into t2 select count(*) from t1;
+
+--echo # Switching to connection 'con1'.
+connection con1;
+--echo # Sending:
+--send alter table t1 add column j int
+
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Wait until ALTER is blocked because it tries to upgrade SNW
+--echo # metadata lock to X lock.
+--echo # It should not be blocked during copying data to new version of
+--echo # table as it acquires LOCK_S locks on rows of old version, which
+--echo # are compatible with locks acquired by connection 'con1'.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist where state =
+ "Waiting for table" and info = "alter table t1 add column j int";
+--source include/wait_condition.inc
+
+--echo # The below statement will deadlock because it will try to acquire
+--echo # SW lock on t1, which will conflict with ALTER's SNW lock. And
+--echo # ALTER will be waiting for this connection to release its SR lock.
+--echo # This deadlock should be detected by an MDL subsystem and this
+--echo # statement should be aborted with an appropriate error.
+--error ER_LOCK_DEADLOCK
+insert into t1 values (6);
+--echo # Unblock ALTER TABLE.
+commit;
+
+--echo # Switching to connection 'con1'.
+connection con1;
+--echo # Reaping ALTER TABLE.
+--reap
+
+--echo # Switching to connection 'default'.
+connection default;
+
+--echo #
+--echo # Now test for scenario in which bug was reported originally.
+--echo #
+drop tables t1, t2;
+create table t1 (i int auto_increment not null primary key) engine=innodb
+ partition by hash (i) partitions 4;
+create table t2 (i int) engine=innodb;
+insert into t1 values (1), (2), (3), (4), (5);
+
+begin;
+--echo # Acquire SR metadata lock on t1.
+select * from t1;
+
+--echo # Switching to connection 'con1'.
+connection con1;
+--echo # Sending:
+--send alter table t1 rebuild partition p0
+
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Wait until ALTER is blocked because of active SR lock.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for table" and info = "alter table t1 rebuild partition p0";
+--source include/wait_condition.inc
+
+--echo # The below statement should succeed as transaction
+--echo # has SR metadata lock on t1 and only going to read
+--echo # rows from it.
+insert into t2 select count(*) from t1;
+--echo # Unblock ALTER TABLE.
+commit;
+
+--echo # Switching to connection 'con1'.
+connection con1;
+--echo # Reaping ALTER TABLE.
+--reap
+
+--echo # Switching to connection 'default'.
+connection default;
+disconnect con1;
+--echo # Clean-up.
+drop tables t1, t2;
+
+
# Check that all connections opened by test cases in this file are really
# gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index b8831127e3f..cd12e5de4d6 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -1302,7 +1302,7 @@ int ha_partition::prepare_new_partition(TABLE *tbl,
assumes that external_lock() is last call that may fail here.
Otherwise see description for cleanup_new_partition().
*/
- if ((error= file->ha_external_lock(ha_thd(), m_lock_type)))
+ if ((error= file->ha_external_lock(ha_thd(), F_WRLCK)))
goto error_external_lock;
DBUG_PRINT("info", ("partition %s external locked", part_name));
diff --git a/sql/lock.cc b/sql/lock.cc
index 8e91bd9360e..fbe15fde3b0 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -415,7 +415,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
THR_LOCK_DATA **lock=sql_lock->locks;
for (i=found=0 ; i < sql_lock->lock_count ; i++)
{
- if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
+ if (sql_lock->locks[i]->type > TL_WRITE_ALLOW_WRITE)
{
swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]);
lock++;
@@ -435,7 +435,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
for (i=found=0 ; i < sql_lock->table_count ; i++)
{
DBUG_ASSERT(sql_lock->table[i]->lock_position == i);
- if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
+ if ((uint) sql_lock->table[i]->reginfo.lock_type > TL_WRITE_ALLOW_WRITE)
{
swap_variables(TABLE *, *table, sql_lock->table[i]);
table++;
diff --git a/sql/mdl.cc b/sql/mdl.cc
index ddf518fbb1c..ba938f8714b 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -1689,9 +1689,8 @@ err:
shared mode).
@note There can be only one upgrader for a lock or we will have deadlock.
- This invariant is ensured by code outside of metadata subsystem usually
- by obtaining some sort of exclusive table-level lock (e.g. TL_WRITE,
- TL_WRITE_ALLOW_READ) before performing upgrade of metadata lock.
+ This invariant is ensured by the fact that upgradeable locks SNW
+ and SNRW are not compatible with each other and themselves.
@retval FALSE Success
@retval TRUE Failure (thread was killed)
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index fa484abf0be..4203a556e65 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -5081,8 +5081,8 @@ static bool check_lock_and_start_stmt(THD *thd,
else
lock_type= table_list->lock_type;
- if ((int) lock_type >= (int) TL_WRITE_ALLOW_READ &&
- (int) table_list->table->reginfo.lock_type < (int) TL_WRITE_ALLOW_READ)
+ if ((int) lock_type > (int) TL_WRITE_ALLOW_WRITE &&
+ (int) table_list->table->reginfo.lock_type <= (int) TL_WRITE_ALLOW_WRITE)
{
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_list->alias);
DBUG_RETURN(1);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index bad88476c09..c27ebce744f 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -4669,7 +4669,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
To allow concurrent execution of read-only operations we acquire
weak metadata lock for them.
*/
- table->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_READ) ?
+ table->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
MDL_SHARED_NO_READ_WRITE : MDL_SHARED_READ);
/* open only one table from local list of command */
{
@@ -7926,7 +7926,7 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
/* Same applies to MDL ticket. */
table_list->mdl_request.ticket= NULL;
/* Set lock type which is appropriate for ALTER TABLE. */
- table_list->lock_type= TL_WRITE_ALLOW_READ;
+ table_list->lock_type= TL_READ_NO_INSERT;
/* Same applies to MDL request. */
table_list->mdl_request.set_type(MDL_SHARED_NO_WRITE);
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index ae09898ada2..e9330574b34 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -489,7 +489,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
else
{
tables->table= open_n_lock_single_table(thd, tables,
- TL_WRITE_ALLOW_READ, 0);
+ TL_READ_NO_INSERT, 0);
if (! tables->table)
goto end;
tables->table->use_all_columns();
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index fdc8af942d1..879f65c0fa4 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -697,7 +697,7 @@ static bool add_create_index_prepare (LEX *lex, Table_ident *table)
lex->sql_command= SQLCOM_CREATE_INDEX;
if (!lex->current_select->add_table_to_list(lex->thd, table, NULL,
TL_OPTION_UPDATING,
- TL_WRITE_ALLOW_READ,
+ TL_READ_NO_INSERT,
MDL_SHARED_NO_WRITE))
return TRUE;
lex->alter_info.reset();
@@ -6157,7 +6157,7 @@ alter:
lex->duplicates= DUP_ERROR;
if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
TL_OPTION_UPDATING,
- TL_WRITE_ALLOW_READ,
+ TL_READ_NO_INSERT,
MDL_SHARED_NO_WRITE))
MYSQL_YYABORT;
lex->col_list.empty();
@@ -10168,7 +10168,7 @@ drop:
lex->alter_info.drop_list.push_back(ad);
if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
TL_OPTION_UPDATING,
- TL_WRITE_ALLOW_READ,
+ TL_READ_NO_INSERT,
MDL_SHARED_NO_WRITE))
MYSQL_YYABORT;
}
@@ -14094,7 +14094,7 @@ trigger_tail:
if (!lex->select_lex.add_table_to_list(YYTHD, $9,
(LEX_STRING*) 0,
TL_OPTION_UPDATING,
- TL_WRITE_ALLOW_READ,
+ TL_READ_NO_INSERT,
MDL_SHARED_NO_WRITE))
MYSQL_YYABORT;
}