diff options
-rw-r--r-- | mysql-test/include/handler.inc | 163 | ||||
-rw-r--r-- | mysql-test/r/handler_innodb.result | 210 | ||||
-rw-r--r-- | mysql-test/r/handler_myisam.result | 209 | ||||
-rw-r--r-- | sql/mysql_priv.h | 1 | ||||
-rw-r--r-- | sql/sql_class.cc | 16 | ||||
-rw-r--r-- | sql/sql_class.h | 8 | ||||
-rw-r--r-- | sql/sql_handler.cc | 24 | ||||
-rw-r--r-- | sql/sql_parse.cc | 8 |
8 files changed, 580 insertions, 59 deletions
diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc index 3264521c4f5..9c6c6864e05 100644 --- a/mysql-test/include/handler.inc +++ b/mysql-test/include/handler.inc @@ -804,12 +804,10 @@ handler t1 open; handler t2 open; handler t3 open; --echo # ---echo # LOCK TABLES implicitly closes all handlers. ---echo # -lock table t3 read; ---echo # ---echo # No HANDLER sql is available under lock tables anyway. +--echo # No HANDLER sql is allowed under LOCK TABLES. +--echo # But it does not implicitly closes all handlers. --echo # +lock table t1 read; --error ER_LOCK_OR_ACTIVE_TRANSACTION handler t1 open; --error ER_LOCK_OR_ACTIVE_TRANSACTION @@ -818,18 +816,18 @@ handler t1 read next; handler t2 close; --error ER_LOCK_OR_ACTIVE_TRANSACTION handler t3 open; ---echo # After UNLOCK TABLES no handlers are around, they were ---echo # implicitly closed. +--echo # After UNLOCK TABLES handlers should be around and +--echo # we should be able to continue reading through them. unlock tables; -drop temporary table t3; ---error ER_UNKNOWN_TABLE handler t1 read next; ---error ER_UNKNOWN_TABLE +handler t1 close; +handler t2 read next; handler t2 close; ---error ER_UNKNOWN_TABLE handler t3 read next; +handler t3 close; +drop temporary table t3; --echo # ---echo # Other operations also implicitly close handler: +--echo # Other operations that implicitly close handler: --echo # --echo # TRUNCATE --echo # @@ -922,6 +920,93 @@ handler t1 read a prev; handler t1 close; unlock tables; --echo # +--echo # Let us also check that these operations behave in similar +--echo # way under LOCK TABLES. +--echo # +--echo # TRUNCATE under LOCK TABLES. +--echo # +handler t1 open; +lock tables t1 write; +truncate table t1; +unlock tables; +--error ER_UNKNOWN_TABLE +handler t1 read next; +handler t1 open; +--echo # +--echo # CREATE TRIGGER under LOCK TABLES. +--echo # +lock tables t1 write; +create trigger t1_ai after insert on t1 for each row set @a=1; +unlock tables; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # DROP TRIGGER under LOCK TABLES. +--echo # +handler t1 open; +lock tables t1 write; +drop trigger t1_ai; +unlock tables; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # ALTER TABLE under LOCK TABLES. +--echo # +handler t1 open; +lock tables t1 write; +alter table t1 drop column b; +unlock tables; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # ANALYZE TABLE under LOCK TABLES. +--echo # +handler t1 open; +lock tables t1 write; +analyze table t1; +unlock tables; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # OPTIMIZE TABLE under LOCK TABLES. +--echo # +handler t1 open; +lock tables t1 write; +optimize table t1; +unlock tables; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # REPAIR TABLE under LOCK TABLES. +--echo # +handler t1 open; +lock tables t1 write; +repair table t1; +unlock tables; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # DROP TABLE under LOCK TABLES, naturally. +--echo # +handler t1 open; +lock tables t1 write; +drop table t1; +unlock tables; +--error ER_UNKNOWN_TABLE +handler t1 read next; +create table t1 (a int, b int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +--echo # +--echo # FLUSH TABLE doesn't close the table but loses the position +--echo # +handler t1 open; +handler t1 read a prev; +lock tables t1 write; +flush table t1; +unlock tables; +handler t1 read a prev; +handler t1 close; +--echo # --echo # Explore the effect of HANDLER locks on concurrent DDL --echo # handler t1 open; @@ -1433,6 +1518,8 @@ lock table not_exists_write read; --echo # We still have the read lock. --error ER_CANT_UPDATE_WITH_READLOCK drop table t1; +handler t1 read next; +handler t1 close; handler t1 open; select a from t2; handler t1 read next; @@ -1542,3 +1629,55 @@ SELECT table_name, table_comment FROM information_schema.tables HANDLER t1 CLOSE; DROP TABLE t1; + + +--echo # +--echo # Test for bug #50908 "Assertion `handler_tables_hash.records == 0' +--echo # failed in enter_locked_tables_mode". +--echo # +--disable_warnings +drop tables if exists t1, t2; +drop function if exists f1; +--enable_warnings +create table t1 (i int); +insert into t1 values (1), (2); +create table t2 (j int); +insert into t2 values (1); +create function f1() returns int return (select count(*) from t2); +--echo # Check that open HANDLER survives statement executed in +--echo # prelocked mode. +handler t1 open; +handler t1 read next; +--echo # The below statement were aborted due to an assertion failure. +select f1() from t2; +handler t1 read next; +handler t1 close; +--echo # Check that the same happens under GLOBAL READ LOCK. +flush tables with read lock; +handler t1 open; +handler t1 read next; +select f1() from t2; +handler t1 read next; +unlock tables; +handler t1 close; +--echo # Now, check that the same happens if LOCK TABLES is executed. +handler t1 open; +handler t1 read next; +lock table t2 read; +select * from t2; +unlock tables; +handler t1 read next; +handler t1 close; +--echo # Finally, check scenario with GRL and LOCK TABLES. +flush tables with read lock; +handler t1 open; +handler t1 read next; +lock table t2 read; +select * from t2; +--echo # This unlocks both tables and GRL. +unlock tables; +handler t1 read next; +handler t1 close; +--echo # Clean-up. +drop function f1; +drop tables t1, t2; diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result index cc196dcf392..d0d35590e73 100644 --- a/mysql-test/r/handler_innodb.result +++ b/mysql-test/r/handler_innodb.result @@ -788,12 +788,10 @@ handler t1 open; handler t2 open; handler t3 open; # -# LOCK TABLES implicitly closes all handlers. -# -lock table t3 read; -# -# No HANDLER sql is available under lock tables anyway. +# No HANDLER sql is allowed under LOCK TABLES. +# But it does not implicitly closes all handlers. # +lock table t1 read; handler t1 open; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction handler t1 read next; @@ -802,18 +800,24 @@ handler t2 close; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction handler t3 open; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction -# After UNLOCK TABLES no handlers are around, they were -# implicitly closed. +# After UNLOCK TABLES handlers should be around and +# we should be able to continue reading through them. unlock tables; -drop temporary table t3; handler t1 read next; -ERROR 42S02: Unknown table 't1' in HANDLER +a +1 +handler t1 close; +handler t2 read next; +a +1 handler t2 close; -ERROR 42S02: Unknown table 't2' in HANDLER handler t3 read next; -ERROR 42S02: Unknown table 't3' in HANDLER +a +1 +handler t3 close; +drop temporary table t3; # -# Other operations also implicitly close handler: +# Other operations that implicitly close handler: # # TRUNCATE # @@ -923,6 +927,104 @@ NULL 5 handler t1 close; unlock tables; # +# Let us also check that these operations behave in similar +# way under LOCK TABLES. +# +# TRUNCATE under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +truncate table t1; +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t1 open; +# +# CREATE TRIGGER under LOCK TABLES. +# +lock tables t1 write; +create trigger t1_ai after insert on t1 for each row set @a=1; +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TRIGGER under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +drop trigger t1_ai; +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ALTER TABLE under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +alter table t1 drop column b; +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ANALYZE TABLE under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# OPTIMIZE TABLE under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +optimize table t1; +Table Op Msg_type Msg_text +test.t1 optimize note Table does not support optimize, doing recreate + analyze instead +test.t1 optimize status OK +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# REPAIR TABLE under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +repair table t1; +Table Op Msg_type Msg_text +test.t1 repair note The storage engine for the table doesn't support repair +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TABLE under LOCK TABLES, naturally. +# +handler t1 open; +lock tables t1 write; +drop table t1; +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +create table t1 (a int, b int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +# +# FLUSH TABLE doesn't close the table but loses the position +# +handler t1 open; +handler t1 read a prev; +a b +5 NULL +lock tables t1 write; +flush table t1; +unlock tables; +handler t1 read a prev; +a b +5 NULL +handler t1 close; +# # Explore the effect of HANDLER locks on concurrent DDL # handler t1 open; @@ -936,11 +1038,11 @@ drop table t1 ; # Waitng for 'drop table t1' to get blocked... # --> connection default handler t1 read a prev; -b a -NULL 5 +a b +5 NULL handler t1 read a prev; -b a -NULL 4 +a b +4 NULL handler t1 close; # --> connection con1 # Reaping 'drop table t1'... @@ -1383,6 +1485,10 @@ ERROR 42S02: Table 'test.not_exists_write' doesn't exist # We still have the read lock. drop table t1; ERROR HY000: Can't execute the query because you have a conflicting read lock +handler t1 read next; +a b +1 1 +handler t1 close; handler t1 open; select a from t2; a @@ -1489,3 +1595,75 @@ table_name table_comment t1 HANDLER t1 CLOSE; DROP TABLE t1; +# +# Test for bug #50908 "Assertion `handler_tables_hash.records == 0' +# failed in enter_locked_tables_mode". +# +drop tables if exists t1, t2; +drop function if exists f1; +create table t1 (i int); +insert into t1 values (1), (2); +create table t2 (j int); +insert into t2 values (1); +create function f1() returns int return (select count(*) from t2); +# Check that open HANDLER survives statement executed in +# prelocked mode. +handler t1 open; +handler t1 read next; +i +1 +# The below statement were aborted due to an assertion failure. +select f1() from t2; +f1() +1 +handler t1 read next; +i +2 +handler t1 close; +# Check that the same happens under GLOBAL READ LOCK. +flush tables with read lock; +handler t1 open; +handler t1 read next; +i +1 +select f1() from t2; +f1() +1 +handler t1 read next; +i +2 +unlock tables; +handler t1 close; +# Now, check that the same happens if LOCK TABLES is executed. +handler t1 open; +handler t1 read next; +i +1 +lock table t2 read; +select * from t2; +j +1 +unlock tables; +handler t1 read next; +i +2 +handler t1 close; +# Finally, check scenario with GRL and LOCK TABLES. +flush tables with read lock; +handler t1 open; +handler t1 read next; +i +1 +lock table t2 read; +select * from t2; +j +1 +# This unlocks both tables and GRL. +unlock tables; +handler t1 read next; +i +2 +handler t1 close; +# Clean-up. +drop function f1; +drop tables t1, t2; diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result index 4a043bf8161..73ad8609376 100644 --- a/mysql-test/r/handler_myisam.result +++ b/mysql-test/r/handler_myisam.result @@ -786,12 +786,10 @@ handler t1 open; handler t2 open; handler t3 open; # -# LOCK TABLES implicitly closes all handlers. -# -lock table t3 read; -# -# No HANDLER sql is available under lock tables anyway. +# No HANDLER sql is allowed under LOCK TABLES. +# But it does not implicitly closes all handlers. # +lock table t1 read; handler t1 open; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction handler t1 read next; @@ -800,18 +798,24 @@ handler t2 close; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction handler t3 open; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction -# After UNLOCK TABLES no handlers are around, they were -# implicitly closed. +# After UNLOCK TABLES handlers should be around and +# we should be able to continue reading through them. unlock tables; -drop temporary table t3; handler t1 read next; -ERROR 42S02: Unknown table 't1' in HANDLER +a +1 +handler t1 close; +handler t2 read next; +a +1 handler t2 close; -ERROR 42S02: Unknown table 't2' in HANDLER handler t3 read next; -ERROR 42S02: Unknown table 't3' in HANDLER +a +1 +handler t3 close; +drop temporary table t3; # -# Other operations also implicitly close handler: +# Other operations that implicitly close handler: # # TRUNCATE # @@ -920,6 +924,103 @@ NULL 5 handler t1 close; unlock tables; # +# Let us also check that these operations behave in similar +# way under LOCK TABLES. +# +# TRUNCATE under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +truncate table t1; +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t1 open; +# +# CREATE TRIGGER under LOCK TABLES. +# +lock tables t1 write; +create trigger t1_ai after insert on t1 for each row set @a=1; +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TRIGGER under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +drop trigger t1_ai; +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ALTER TABLE under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +alter table t1 drop column b; +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ANALYZE TABLE under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Table is already up to date +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# OPTIMIZE TABLE under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +optimize table t1; +Table Op Msg_type Msg_text +test.t1 optimize status OK +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# REPAIR TABLE under LOCK TABLES. +# +handler t1 open; +lock tables t1 write; +repair table t1; +Table Op Msg_type Msg_text +test.t1 repair status OK +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TABLE under LOCK TABLES, naturally. +# +handler t1 open; +lock tables t1 write; +drop table t1; +unlock tables; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +create table t1 (a int, b int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +# +# FLUSH TABLE doesn't close the table but loses the position +# +handler t1 open; +handler t1 read a prev; +a b +5 NULL +lock tables t1 write; +flush table t1; +unlock tables; +handler t1 read a prev; +a b +5 NULL +handler t1 close; +# # Explore the effect of HANDLER locks on concurrent DDL # handler t1 open; @@ -933,11 +1034,11 @@ drop table t1 ; # Waitng for 'drop table t1' to get blocked... # --> connection default handler t1 read a prev; -b a -NULL 5 +a b +5 NULL handler t1 read a prev; -b a -NULL 4 +a b +4 NULL handler t1 close; # --> connection con1 # Reaping 'drop table t1'... @@ -1380,6 +1481,10 @@ ERROR 42S02: Table 'test.not_exists_write' doesn't exist # We still have the read lock. drop table t1; ERROR HY000: Can't execute the query because you have a conflicting read lock +handler t1 read next; +a b +1 1 +handler t1 close; handler t1 open; select a from t2; a @@ -1487,6 +1592,78 @@ t1 HANDLER t1 CLOSE; DROP TABLE t1; # +# Test for bug #50908 "Assertion `handler_tables_hash.records == 0' +# failed in enter_locked_tables_mode". +# +drop tables if exists t1, t2; +drop function if exists f1; +create table t1 (i int); +insert into t1 values (1), (2); +create table t2 (j int); +insert into t2 values (1); +create function f1() returns int return (select count(*) from t2); +# Check that open HANDLER survives statement executed in +# prelocked mode. +handler t1 open; +handler t1 read next; +i +1 +# The below statement were aborted due to an assertion failure. +select f1() from t2; +f1() +1 +handler t1 read next; +i +2 +handler t1 close; +# Check that the same happens under GLOBAL READ LOCK. +flush tables with read lock; +handler t1 open; +handler t1 read next; +i +1 +select f1() from t2; +f1() +1 +handler t1 read next; +i +2 +unlock tables; +handler t1 close; +# Now, check that the same happens if LOCK TABLES is executed. +handler t1 open; +handler t1 read next; +i +1 +lock table t2 read; +select * from t2; +j +1 +unlock tables; +handler t1 read next; +i +2 +handler t1 close; +# Finally, check scenario with GRL and LOCK TABLES. +flush tables with read lock; +handler t1 open; +handler t1 read next; +i +1 +lock table t2 read; +select * from t2; +j +1 +# This unlocks both tables and GRL. +unlock tables; +handler t1 read next; +i +2 +handler t1 close; +# Clean-up. +drop function f1; +drop tables t1, t2; +# # BUG #46456: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash # CREATE TABLE t1 AS SELECT 1 AS f1; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 6f207ccb00e..a4d9eab2685 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1438,6 +1438,7 @@ bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *, void mysql_ha_flush(THD *thd); void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables); void mysql_ha_cleanup(THD *thd); +void mysql_ha_move_tickets_after_trans_sentinel(THD *thd); /* sql_base.cc */ #define TMP_TABLE_KEY_EXTRA 8 diff --git a/sql/sql_class.cc b/sql/sql_class.cc index ae6d1b1a0a0..bdf83ebe8ec 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3312,6 +3312,22 @@ void THD::set_query_id(query_id_t new_query_id) /** + Leave explicit LOCK TABLES or prelocked mode and restore value of + transaction sentinel in MDL subsystem. +*/ + +void THD::leave_locked_tables_mode() +{ + locked_tables_mode= LTM_NONE; + /* Make sure we don't release the global read lock when leaving LTM. */ + mdl_context.reset_trans_sentinel(global_read_lock.global_shared_lock()); + /* Also ensure that we don't release metadata locks for open HANDLERs. */ + if (handler_tables_hash.records) + mysql_ha_move_tickets_after_trans_sentinel(this); +} + + +/** Mark transaction to rollback and mark error as fatal to a sub-statement. @param thd Thread handle diff --git a/sql/sql_class.h b/sql/sql_class.h index 7c935d376f9..e6ee7139b24 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2683,17 +2683,11 @@ public: void enter_locked_tables_mode(enum_locked_tables_mode mode_arg) { DBUG_ASSERT(locked_tables_mode == LTM_NONE); - DBUG_ASSERT(handler_tables_hash.records == 0); mdl_context.set_trans_sentinel(); locked_tables_mode= mode_arg; } - void leave_locked_tables_mode() - { - locked_tables_mode= LTM_NONE; - /* Make sure we don't release the global read lock when leaving LTM. */ - mdl_context.reset_trans_sentinel(global_read_lock.global_shared_lock()); - } + void leave_locked_tables_mode(); int decide_logging_format(TABLE_LIST *tables); private: diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 9f365d0cf2f..4a69b46ddb7 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -837,3 +837,27 @@ void mysql_ha_cleanup(THD *thd) DBUG_VOID_RETURN; } + +/** + Move tickets for metadata locks corresponding to open HANDLERs + after transaction sentinel in order to protect them from being + released at the end of transaction. + + @param thd Thread identifier. +*/ + +void mysql_ha_move_tickets_after_trans_sentinel(THD *thd) +{ + TABLE_LIST *hash_tables; + DBUG_ENTER("mysql_ha_move_tickets_after_trans_sentinel"); + + for (uint i= 0; i < thd->handler_tables_hash.records; i++) + { + hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); + if (hash_tables->table && hash_tables->table->mdl_ticket) + thd->mdl_context. + move_ticket_after_trans_sentinel(hash_tables->table->mdl_ticket); + } + DBUG_VOID_RETURN; +} + diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1906040d5c6..b0d8614dc84 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3283,14 +3283,6 @@ end_with_restore_list: break; case SQLCOM_LOCK_TABLES: thd->locked_tables_list.unlock_locked_tables(thd); - /* - As of 5.5, entering LOCK TABLES mode implicitly closes all - open HANDLERs. Both HANDLER code and LOCK TABLES mode use - the sentinel mechanism in MDL subsystem and thus could not be - used at the same time. All HANDLER operations are prohibited - under LOCK TABLES anyway. - */ - mysql_ha_cleanup(thd); /* we must end the trasaction first, regardless of anything */ if (trans_commit_implicit(thd)) goto error; |