summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/suite/handler/aria.result58
-rw-r--r--mysql-test/suite/handler/handler.inc56
-rw-r--r--mysql-test/suite/handler/heap.result58
-rw-r--r--mysql-test/suite/handler/innodb.result58
-rw-r--r--mysql-test/suite/handler/interface.result10
-rw-r--r--mysql-test/suite/handler/interface.test8
-rw-r--r--mysql-test/suite/handler/myisam.result58
-rw-r--r--sql/lock.cc8
-rw-r--r--sql/sql_class.h35
-rw-r--r--sql/sql_handler.cc102
10 files changed, 222 insertions, 229 deletions
diff --git a/mysql-test/suite/handler/aria.result b/mysql-test/suite/handler/aria.result
index e0b98bd36a0..e0751e322a2 100644
--- a/mysql-test/suite/handler/aria.result
+++ b/mysql-test/suite/handler/aria.result
@@ -977,12 +977,10 @@ drop table t1 ;
# --> connection con2
# Waitng for 'drop table t1' to get blocked...
# --> connection default
+# Attempt to upgrade metadata locks to SR from S will lead to
+# deadlock which will result in table being automatically closed.
handler t1 read a prev;
-a b
-5 NULL
-handler t1 read a prev;
-a b
-4 NULL
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# --> connection con1
# Reaping 'drop table t1'...
@@ -1026,20 +1024,16 @@ a
# thus we can reopen it in the handler
handler t1 open;
# We can commit the transaction, it doesn't close the handler
-# and doesn't let DROP to proceed.
+# and doesn't let DROP to proceed immediately.
commit;
+# Waiting for 'drop table t1' to get blocked...
+# --> connection default
+# OTOH the first attempt to read from HANDLER will lead to metadata
+# locks deadlock and thus to HANDLER being automatically closed.
handler t1 read a prev;
-a
-5
-handler t1 read a prev;
-a
-4
-handler t1 read a prev;
-a
-3
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# --> connection con1
-# Now drop can proceed
# Reaping 'drop table t1'...
# --> connection default
#
@@ -1156,15 +1150,13 @@ rollback to savepoint sv;
# Reaping 'drop table t2'...
# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
# lock.
+# --> connection con3
+# Check if 'drop table t1' still blocked...
# --> connection default
+# Demonstrate that the drop will go through as soon as we close
+# or will try to access HANDLER
handler t1 read a next;
-a
-3
-handler t1 read a next;
-a
-4
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# connection con1
# Reaping 'drop table t1'...
@@ -1215,15 +1207,13 @@ rollback to savepoint sv;
# Reaping 'drop table t2'...
# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
# lock.
+# --> connection con3
+# Check if 'drop table t1' is still blocked...
# --> connection default
+# Demonstrate that the drop will go through as soon as we access or
+# close the HANDLER
handler t1 read a next;
-a
-3
-handler t1 read a next;
-a
-4
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# connection con1
# Reaping 'drop table t1'...
@@ -1261,14 +1251,12 @@ drop table t3;
# Let DROP TABLE statement sync in.
# --> connection con2
# Waiting for 'drop table t3' to get blocked...
-# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
-# lock.
+# The fact that DROP TABLE is blocked means that ROLLBACK TO SAVEPOINT
+# didn't release the handler lock.
# --> connection default
+# Drop will go through as soon as we access or close the HANDLER
handler t3 read a next;
-a
-2
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t3' doesn't exist
handler t3 close;
# connection con1
# Reaping 'drop table t3'...
diff --git a/mysql-test/suite/handler/handler.inc b/mysql-test/suite/handler/handler.inc
index c71dc53e5ac..c8ab0210361 100644
--- a/mysql-test/suite/handler/handler.inc
+++ b/mysql-test/suite/handler/handler.inc
@@ -796,7 +796,9 @@ let $wait_condition=select count(*)=1 from information_schema.processlist
--source include/wait_condition.inc
--echo # --> connection default
connection default;
-handler t1 read a prev;
+--echo # Attempt to upgrade metadata locks to SR from S will lead to
+--echo # deadlock which will result in table being automatically closed.
+--error ER_NO_SUCH_TABLE
handler t1 read a prev;
handler t1 close;
--echo # --> connection con1
@@ -835,15 +837,23 @@ select * from t1;
--echo # thus we can reopen it in the handler
handler t1 open;
--echo # We can commit the transaction, it doesn't close the handler
---echo # and doesn't let DROP to proceed.
+--echo # and doesn't let DROP to proceed immediately.
commit;
-handler t1 read a prev;
-handler t1 read a prev;
+connection con2;
+--echo # Waiting for 'drop table t1' to get blocked...
+let $wait_condition=select count(*)=1 from information_schema.processlist
+ where state='Waiting for table metadata lock' and
+ info='drop table t1';
+--source include/wait_condition.inc
+--echo # --> connection default
+connection default;
+--echo # OTOH the first attempt to read from HANDLER will lead to metadata
+--echo # locks deadlock and thus to HANDLER being automatically closed.
+--error ER_NO_SUCH_TABLE
handler t1 read a prev;
handler t1 close;
--echo # --> connection con1
connection con1;
---echo # Now drop can proceed
--echo # Reaping 'drop table t1'...
--reap
--echo # --> connection default
@@ -984,12 +994,19 @@ connection con2;
--reap
--echo # Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
--echo # lock.
+--echo # --> connection con3
+connection con3;
+--echo # Check if 'drop table t1' still blocked...
+let $wait_condition=select count(*)=1 from information_schema.processlist
+ where state='Waiting for table metadata lock' and
+ info='drop table t1';
+--source include/wait_condition.inc
--echo # --> connection default
connection default;
+--echo # Demonstrate that the drop will go through as soon as we close
+--echo # or will try to access HANDLER
+--error ER_NO_SUCH_TABLE
handler t1 read a next;
-handler t1 read a next;
---echo # Demonstrate that the drop will go through as soon as we close the
---echo # HANDLER
handler t1 close;
--echo # connection con1
connection con1;
@@ -1053,12 +1070,19 @@ connection con2;
--reap
--echo # Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
--echo # lock.
+--echo # --> connection con3
+connection con3;
+--echo # Check if 'drop table t1' is still blocked...
+let $wait_condition=select count(*)=1 from information_schema.processlist
+ where state='Waiting for table metadata lock' and
+ info='drop table t1';
+--source include/wait_condition.inc
--echo # --> connection default
connection default;
+--echo # Demonstrate that the drop will go through as soon as we access or
+--echo # close the HANDLER
+--error ER_NO_SUCH_TABLE
handler t1 read a next;
-handler t1 read a next;
---echo # Demonstrate that the drop will go through as soon as we close the
---echo # HANDLER
handler t1 close;
--echo # connection con1
connection con1;
@@ -1100,13 +1124,13 @@ let $wait_condition=select count(*)=1 from information_schema.processlist
where state='Waiting for table metadata lock' and
info='drop table t3';
--source include/wait_condition.inc
---echo # Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
---echo # lock.
+--echo # The fact that DROP TABLE is blocked means that ROLLBACK TO SAVEPOINT
+--echo # didn't release the handler lock.
--echo # --> connection default
connection default;
+--echo # Drop will go through as soon as we access or close the HANDLER
+--error ER_NO_SUCH_TABLE
handler t3 read a next;
---echo # Demonstrate that the drop will go through as soon as we close the
---echo # HANDLER
handler t3 close;
--echo # connection con1
connection con1;
@@ -1214,7 +1238,7 @@ connection con1;
--echo # Waiting for 'handler t1 read a next' to get blocked...
let $wait_condition=
select count(*) = 1 from information_schema.processlist
- where state = "Waiting for table level lock" and
+ where state = "Waiting for table metadata lock" and
info = "handler t1 read a next";
--source include/wait_condition.inc
diff --git a/mysql-test/suite/handler/heap.result b/mysql-test/suite/handler/heap.result
index 527986edb5c..15522c5a71e 100644
--- a/mysql-test/suite/handler/heap.result
+++ b/mysql-test/suite/handler/heap.result
@@ -977,12 +977,10 @@ drop table t1 ;
# --> connection con2
# Waitng for 'drop table t1' to get blocked...
# --> connection default
+# Attempt to upgrade metadata locks to SR from S will lead to
+# deadlock which will result in table being automatically closed.
handler t1 read a prev;
-a b
-5 NULL
-handler t1 read a prev;
-a b
-4 NULL
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# --> connection con1
# Reaping 'drop table t1'...
@@ -1026,20 +1024,16 @@ a
# thus we can reopen it in the handler
handler t1 open;
# We can commit the transaction, it doesn't close the handler
-# and doesn't let DROP to proceed.
+# and doesn't let DROP to proceed immediately.
commit;
+# Waiting for 'drop table t1' to get blocked...
+# --> connection default
+# OTOH the first attempt to read from HANDLER will lead to metadata
+# locks deadlock and thus to HANDLER being automatically closed.
handler t1 read a prev;
-a
-5
-handler t1 read a prev;
-a
-4
-handler t1 read a prev;
-a
-3
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# --> connection con1
-# Now drop can proceed
# Reaping 'drop table t1'...
# --> connection default
#
@@ -1156,15 +1150,13 @@ rollback to savepoint sv;
# Reaping 'drop table t2'...
# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
# lock.
+# --> connection con3
+# Check if 'drop table t1' still blocked...
# --> connection default
+# Demonstrate that the drop will go through as soon as we close
+# or will try to access HANDLER
handler t1 read a next;
-a
-3
-handler t1 read a next;
-a
-4
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# connection con1
# Reaping 'drop table t1'...
@@ -1215,15 +1207,13 @@ rollback to savepoint sv;
# Reaping 'drop table t2'...
# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
# lock.
+# --> connection con3
+# Check if 'drop table t1' is still blocked...
# --> connection default
+# Demonstrate that the drop will go through as soon as we access or
+# close the HANDLER
handler t1 read a next;
-a
-3
-handler t1 read a next;
-a
-4
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# connection con1
# Reaping 'drop table t1'...
@@ -1261,14 +1251,12 @@ drop table t3;
# Let DROP TABLE statement sync in.
# --> connection con2
# Waiting for 'drop table t3' to get blocked...
-# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
-# lock.
+# The fact that DROP TABLE is blocked means that ROLLBACK TO SAVEPOINT
+# didn't release the handler lock.
# --> connection default
+# Drop will go through as soon as we access or close the HANDLER
handler t3 read a next;
-a
-2
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t3' doesn't exist
handler t3 close;
# connection con1
# Reaping 'drop table t3'...
diff --git a/mysql-test/suite/handler/innodb.result b/mysql-test/suite/handler/innodb.result
index 78660b0ef9c..053d9cf2e4b 100644
--- a/mysql-test/suite/handler/innodb.result
+++ b/mysql-test/suite/handler/innodb.result
@@ -981,12 +981,10 @@ drop table t1 ;
# --> connection con2
# Waitng for 'drop table t1' to get blocked...
# --> connection default
+# Attempt to upgrade metadata locks to SR from S will lead to
+# deadlock which will result in table being automatically closed.
handler t1 read a prev;
-a b
-5 NULL
-handler t1 read a prev;
-a b
-4 NULL
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# --> connection con1
# Reaping 'drop table t1'...
@@ -1030,20 +1028,16 @@ a
# thus we can reopen it in the handler
handler t1 open;
# We can commit the transaction, it doesn't close the handler
-# and doesn't let DROP to proceed.
+# and doesn't let DROP to proceed immediately.
commit;
+# Waiting for 'drop table t1' to get blocked...
+# --> connection default
+# OTOH the first attempt to read from HANDLER will lead to metadata
+# locks deadlock and thus to HANDLER being automatically closed.
handler t1 read a prev;
-a
-5
-handler t1 read a prev;
-a
-4
-handler t1 read a prev;
-a
-3
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# --> connection con1
-# Now drop can proceed
# Reaping 'drop table t1'...
# --> connection default
#
@@ -1160,15 +1154,13 @@ rollback to savepoint sv;
# Reaping 'drop table t2'...
# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
# lock.
+# --> connection con3
+# Check if 'drop table t1' still blocked...
# --> connection default
+# Demonstrate that the drop will go through as soon as we close
+# or will try to access HANDLER
handler t1 read a next;
-a
-3
-handler t1 read a next;
-a
-4
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# connection con1
# Reaping 'drop table t1'...
@@ -1219,15 +1211,13 @@ rollback to savepoint sv;
# Reaping 'drop table t2'...
# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
# lock.
+# --> connection con3
+# Check if 'drop table t1' is still blocked...
# --> connection default
+# Demonstrate that the drop will go through as soon as we access or
+# close the HANDLER
handler t1 read a next;
-a
-3
-handler t1 read a next;
-a
-4
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# connection con1
# Reaping 'drop table t1'...
@@ -1265,14 +1255,12 @@ drop table t3;
# Let DROP TABLE statement sync in.
# --> connection con2
# Waiting for 'drop table t3' to get blocked...
-# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
-# lock.
+# The fact that DROP TABLE is blocked means that ROLLBACK TO SAVEPOINT
+# didn't release the handler lock.
# --> connection default
+# Drop will go through as soon as we access or close the HANDLER
handler t3 read a next;
-a
-2
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t3' doesn't exist
handler t3 close;
# connection con1
# Reaping 'drop table t3'...
diff --git a/mysql-test/suite/handler/interface.result b/mysql-test/suite/handler/interface.result
index 89dec29f412..ec613d9f57a 100644
--- a/mysql-test/suite/handler/interface.result
+++ b/mysql-test/suite/handler/interface.result
@@ -273,19 +273,13 @@ ERROR HY000: Storage engine CSV of the table `test`.`t1` doesn't have this optio
handler t1 close;
unlock tables;
drop table t1;
-# Now test case which was reported originally but which no longer
-# triggers execution path which has caused the problem.
+# Now test case which was reported originally.
create table t1 (a int not null);
insert into t1 values (1);
handler t1 open;
alter table t1 engine=csv;
-# Since S metadata lock was already acquired at HANDLER OPEN time
-# and TL_READ lock requested by HANDLER READ is compatible with
-# ALTER's TL_WRITE_ALLOW_READ the below statement should succeed
-# without waiting. The old version of table should be used in it.
handler t1 read next;
-a
-1
+ERROR HY000: Storage engine CSV of the table `test`.`t1` doesn't have this option
handler t1 close;
drop table t1;
USE information_schema;
diff --git a/mysql-test/suite/handler/interface.test b/mysql-test/suite/handler/interface.test
index 2ef617c3ce7..4deaecd6ea8 100644
--- a/mysql-test/suite/handler/interface.test
+++ b/mysql-test/suite/handler/interface.test
@@ -333,8 +333,7 @@ connection con1;
--reap
unlock tables;
drop table t1;
---echo # Now test case which was reported originally but which no longer
---echo # triggers execution path which has caused the problem.
+--echo # Now test case which was reported originally.
connection default;
create table t1 (a int not null);
insert into t1 values (1);
@@ -348,10 +347,7 @@ let $wait_condition=
info = "alter table t1 engine=csv";
--source include/wait_condition.inc
connection default;
---echo # Since S metadata lock was already acquired at HANDLER OPEN time
---echo # and TL_READ lock requested by HANDLER READ is compatible with
---echo # ALTER's TL_WRITE_ALLOW_READ the below statement should succeed
---echo # without waiting. The old version of table should be used in it.
+--error ER_ILLEGAL_HA
handler t1 read next;
handler t1 close;
connection con1;
diff --git a/mysql-test/suite/handler/myisam.result b/mysql-test/suite/handler/myisam.result
index 9081722d866..05b3e787a6d 100644
--- a/mysql-test/suite/handler/myisam.result
+++ b/mysql-test/suite/handler/myisam.result
@@ -977,12 +977,10 @@ drop table t1 ;
# --> connection con2
# Waitng for 'drop table t1' to get blocked...
# --> connection default
+# Attempt to upgrade metadata locks to SR from S will lead to
+# deadlock which will result in table being automatically closed.
handler t1 read a prev;
-a b
-5 NULL
-handler t1 read a prev;
-a b
-4 NULL
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# --> connection con1
# Reaping 'drop table t1'...
@@ -1026,20 +1024,16 @@ a
# thus we can reopen it in the handler
handler t1 open;
# We can commit the transaction, it doesn't close the handler
-# and doesn't let DROP to proceed.
+# and doesn't let DROP to proceed immediately.
commit;
+# Waiting for 'drop table t1' to get blocked...
+# --> connection default
+# OTOH the first attempt to read from HANDLER will lead to metadata
+# locks deadlock and thus to HANDLER being automatically closed.
handler t1 read a prev;
-a
-5
-handler t1 read a prev;
-a
-4
-handler t1 read a prev;
-a
-3
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# --> connection con1
-# Now drop can proceed
# Reaping 'drop table t1'...
# --> connection default
#
@@ -1156,15 +1150,13 @@ rollback to savepoint sv;
# Reaping 'drop table t2'...
# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
# lock.
+# --> connection con3
+# Check if 'drop table t1' still blocked...
# --> connection default
+# Demonstrate that the drop will go through as soon as we close
+# or will try to access HANDLER
handler t1 read a next;
-a
-3
-handler t1 read a next;
-a
-4
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# connection con1
# Reaping 'drop table t1'...
@@ -1215,15 +1207,13 @@ rollback to savepoint sv;
# Reaping 'drop table t2'...
# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
# lock.
+# --> connection con3
+# Check if 'drop table t1' is still blocked...
# --> connection default
+# Demonstrate that the drop will go through as soon as we access or
+# close the HANDLER
handler t1 read a next;
-a
-3
-handler t1 read a next;
-a
-4
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t1' doesn't exist
handler t1 close;
# connection con1
# Reaping 'drop table t1'...
@@ -1261,14 +1251,12 @@ drop table t3;
# Let DROP TABLE statement sync in.
# --> connection con2
# Waiting for 'drop table t3' to get blocked...
-# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler
-# lock.
+# The fact that DROP TABLE is blocked means that ROLLBACK TO SAVEPOINT
+# didn't release the handler lock.
# --> connection default
+# Drop will go through as soon as we access or close the HANDLER
handler t3 read a next;
-a
-2
-# Demonstrate that the drop will go through as soon as we close the
-# HANDLER
+ERROR 42S02: Table 'test.t3' doesn't exist
handler t3 close;
# connection con1
# Reaping 'drop table t3'...
diff --git a/sql/lock.cc b/sql/lock.cc
index c241e635e6b..ad0aedbee40 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -165,18 +165,12 @@ lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
write we must own metadata lock of MDL_SHARED_WRITE or stronger
type. For table to be locked for read we must own metadata lock
of MDL_SHARED_READ or stronger type).
- The only exception are HANDLER statements which are allowed to
- lock table for read while having only MDL_SHARED lock on it.
*/
DBUG_ASSERT(t->s->tmp_table ||
thd->mdl_context.is_lock_owner(MDL_key::TABLE,
t->s->db.str, t->s->table_name.str,
t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE ?
- MDL_SHARED_WRITE : MDL_SHARED_READ) ||
- (t->open_by_handler &&
- thd->mdl_context.is_lock_owner(MDL_key::TABLE,
- t->s->db.str, t->s->table_name.str,
- MDL_SHARED)));
+ MDL_SHARED_WRITE : MDL_SHARED_READ));
/*
Prevent modifications to base tables if READ_ONLY is activated.
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 73637f8d8eb..6b64f710176 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1535,6 +1535,41 @@ private:
/**
+ Internal error handler to process an error from MDL_context::upgrade_lock()
+ and mysql_lock_tables(). Used by implementations of HANDLER READ and
+ LOCK TABLES LOCAL.
+*/
+
+class MDL_deadlock_and_lock_abort_error_handler: public Internal_error_handler
+{
+public:
+ /**
+ Handle an error from MDL_context::upgrade_lock() and mysql_lock_tables().
+ Ignore ER_LOCK_ABORTED and ER_LOCK_DEADLOCK errors.
+ */
+ virtual
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char *sqlstate,
+ Sql_condition::enum_warning_level level,
+ const char* msg,
+ Sql_condition **cond_hdl)
+ {
+ *cond_hdl= NULL;
+ if (sql_errno == ER_LOCK_ABORTED || sql_errno == ER_LOCK_DEADLOCK)
+ m_need_reopen= true;
+
+ return m_need_reopen;
+ }
+
+ bool need_reopen() const { return m_need_reopen; };
+ void init() { m_need_reopen= false; };
+private:
+ bool m_need_reopen;
+};
+
+
+/**
Tables that were locked with LOCK TABLES statement.
Encapsulates a list of TABLE_LIST instances for tables
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 7dcc6fa0e95..e117aa593b5 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -488,56 +488,6 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
/**
- A helper class to process an error from mysql_lock_tables().
- HANDLER READ statement's attempt to lock the subject table
- may get aborted if there is a pending DDL. In that case
- we close the table, reopen it, and try to read again.
- This is implicit and obscure, since HANDLER position
- is lost in the process, but it's the legacy server
- behaviour we should preserve.
-*/
-
-class Sql_handler_lock_error_handler: public Internal_error_handler
-{
-public:
- virtual
- bool handle_condition(THD *thd,
- uint sql_errno,
- const char *sqlstate,
- Sql_condition::enum_warning_level level,
- const char* msg,
- Sql_condition **cond_hdl);
-
- bool need_reopen() const { return m_need_reopen; };
- void init() { m_need_reopen= FALSE; };
-private:
- bool m_need_reopen;
-};
-
-
-/**
- Handle an error from mysql_lock_tables().
- Ignore ER_LOCK_ABORTED errors.
-*/
-
-bool
-Sql_handler_lock_error_handler::
-handle_condition(THD *thd,
- uint sql_errno,
- const char *sqlstate,
- Sql_condition::enum_warning_level level,
- const char* msg,
- Sql_condition **cond_hdl)
-{
- *cond_hdl= NULL;
- if (sql_errno == ER_LOCK_ABORTED)
- m_need_reopen= TRUE;
-
- return m_need_reopen;
-}
-
-
-/**
Finds an open HANDLER table.
@params name Name of handler to open
@@ -733,7 +683,8 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
int error, keyno;
uint num_rows;
uchar *UNINIT_VAR(key);
- Sql_handler_lock_error_handler sql_handler_lock_error;
+ MDL_deadlock_and_lock_abort_error_handler sql_handler_lock_error;
+ MDL_savepoint mdl_savepoint;
DBUG_ENTER("mysql_ha_read");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
@@ -752,6 +703,48 @@ retry:
tables->table= table; // This is used by fix_fields
table->pos_in_table_list= tables;
+ sql_handler_lock_error.init();
+
+ /*
+ For non-temporary tables we need to acquire SR lock in order to ensure
+ that HANDLER READ is blocked by LOCK TABLES WRITE in other connections
+ for storage engines which don't use THR_LOCK locks (e.g. InnoDB).
+
+ To simplify clean-up code we take MDL_savepoint even for temporary tables.
+ */
+ mdl_savepoint= thd->mdl_context.mdl_savepoint();
+
+ if (table->s->tmp_table == NO_TMP_TABLE)
+ {
+ MDL_request read_request;
+
+ read_request.init(&handler->mdl_request.key, MDL_SHARED_READ,
+ MDL_TRANSACTION);
+
+ thd->push_internal_handler(&sql_handler_lock_error);
+
+ error= thd->mdl_context.acquire_lock(&read_request,
+ thd->variables.lock_wait_timeout);
+ thd->pop_internal_handler();
+
+ if (sql_handler_lock_error.need_reopen())
+ {
+ /*
+ HANDLER READ statement's attempt to upgrade lock on the subject table
+ may get aborted if there is a pending DDL. In that case we close the
+ table, reopen it, and try to read again.
+ This is implicit and obscure, since HANDLER position is lost in the
+ process, but it's the legacy server behaviour we should preserve.
+ */
+ DBUG_ASSERT(error && !thd->is_error());
+ mysql_ha_close_table(handler);
+ goto retry;
+ }
+
+ if (error)
+ goto err0;
+ }
+
if (handler->lock->lock_count > 0)
{
int lock_error;
@@ -768,7 +761,7 @@ retry:
*/
thd->set_open_tables(table);
- sql_handler_lock_error.init();
+ /* Re-use Sql_handler_lock_error instance which was initialized earlier. */
thd->push_internal_handler(&sql_handler_lock_error);
lock_error= mysql_lock_tables(thd, handler->lock,
@@ -797,6 +790,7 @@ retry:
*/
DBUG_ASSERT(! thd->transaction_rollback_request);
trans_rollback_stmt(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
mysql_ha_close_table(handler);
if (thd->stmt_arena->is_stmt_execute())
{
@@ -812,7 +806,10 @@ retry:
}
if (lock_error)
+ {
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
goto err0; // mysql_lock_tables() printed error message already
+ }
}
if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 0))
@@ -957,6 +954,7 @@ ok:
*/
trans_commit_stmt(thd);
mysql_unlock_tables(thd, handler->lock, 0);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
my_eof(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);