From 69b9761f2913dfa9fa2989ef7f3e01b8d2d3e334 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Mon, 30 Nov 2009 18:55:03 +0300 Subject: Initial import of WL#3726 "DDL locking for all metadata objects". Backport of: ------------------------------------------------------------ revno: 2630.4.1 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Fri 2008-05-23 17:54:03 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. ------------------------------------------------------------ This is the first patch in series. It transforms the metadata locking subsystem to use a dedicated module (mdl.h,cc). No significant changes in the locking protocol. The import passes the test suite with the exception of deprecated/removed 6.0 features, and MERGE tables. The latter are subject to a fix by WL#4144. Unfortunately, the original changeset comments got lost in a merge, thus this import has its own (largely insufficient) comments. This patch fixes Bug#25144 "replication / binlog with view breaks". Warning: this patch introduces an incompatible change: Under LOCK TABLES, it's no longer possible to FLUSH a table that was not locked for WRITE. Under LOCK TABLES, it's no longer possible to DROP a table or VIEW that was not locked for WRITE. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.2 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sat 2008-05-24 14:03:45 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.3 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sat 2008-05-24 14:08:51 +0400 message: WL#3726 "DDL locking for all metadata objects" Fixed failing Windows builds by adding mdl.cc to the lists of files needed to build server/libmysqld on Windows. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.4 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sat 2008-05-24 21:57:58 +0400 message: WL#3726 "DDL locking for all metadata objects". Fix for assert failures in kill.test which occured when one tried to kill ALTER TABLE statement on merge table while it was waiting in wait_while_table_is_used() for other connections to close this table. These assert failures stemmed from the fact that cleanup code in this case assumed that temporary table representing new version of table was open with adding to THD::temporary_tables list while code which were opening this temporary table wasn't always fulfilling this. This patch changes code that opens new version of table to always do this linking in. It also streamlines cleanup process for cases when error occurs while we have new version of table open. ****** WL#3726 "DDL locking for all metadata objects" Add libmysqld/mdl.cc to .bzrignore. ****** Backport of: ------------------------------------------------------------ revno: 2630.4.6 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sun 2008-05-25 00:33:22 +0400 message: WL#3726 "DDL locking for all metadata objects". Addition to the fix of assert failures in kill.test caused by changes for this worklog. Make sure we close the new table only once. --- mysql-test/include/handler.inc | 33 ++++- mysql-test/r/create.result | 7 +- mysql-test/r/flush.result | 5 + mysql-test/r/flush_table.result | 17 +-- mysql-test/r/handler_innodb.result | 2 + mysql-test/r/handler_myisam.result | 2 + mysql-test/r/information_schema.result | 77 +++++++--- mysql-test/r/kill.result | 103 +++++++++++++ mysql-test/r/lock.result | 3 +- mysql-test/r/partition_column_prune.result | 6 +- mysql-test/r/partition_pruning.result | 2 +- mysql-test/r/ps_ddl.result | 4 +- mysql-test/r/sp.result | 6 +- mysql-test/r/view.result | 4 +- mysql-test/r/view_grant.result | 4 +- mysql-test/r/view_multi.result | 48 ++++++ mysql-test/suite/rpl/t/disabled.def | 3 + mysql-test/t/create.test | 8 +- mysql-test/t/disabled.def | 1 + mysql-test/t/flush.test | 14 +- mysql-test/t/flush_table.test | 25 +--- mysql-test/t/information_schema.test | 93 +++++++++--- mysql-test/t/kill.test | 226 +++++++++++++++++++++++++++++ mysql-test/t/lock.test | 3 +- mysql-test/t/lock_multi.test | 34 ++--- mysql-test/t/ps_ddl.test | 4 +- mysql-test/t/sp.test | 4 +- mysql-test/t/trigger_notembedded.test | 2 +- mysql-test/t/view.test | 4 +- mysql-test/t/view_grant.test | 4 +- mysql-test/t/view_multi.test | 110 ++++++++++++++ 31 files changed, 723 insertions(+), 135 deletions(-) create mode 100644 mysql-test/r/view_multi.result create mode 100644 mysql-test/t/view_multi.test (limited to 'mysql-test') diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc index 6e7f53ba9b2..8ff38c7e7a1 100644 --- a/mysql-test/include/handler.inc +++ b/mysql-test/include/handler.inc @@ -518,12 +518,15 @@ connect (flush,localhost,root,,); connection flush; --echo connection: flush --send flush tables; -connection default; ---echo connection: default +connect (waiter,localhost,root,,); +connection waiter; +--echo connection: waiter let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Flushing tables"; --source include/wait_condition.inc +connection default; +--echo connection: default handler t2 open; handler t2 read first; handler t1 read next; @@ -550,12 +553,14 @@ connect (flush,localhost,root,,); connection flush; --echo connection: flush --send rename table t1 to t2; -connection default; ---echo connection: default +connection waiter; +--echo connection: waiter let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "rename table t1 to t2"; --source include/wait_condition.inc +connection default; +--echo connection: default handler t2 open; handler t2 read first; --error ER_NO_SUCH_TABLE @@ -566,7 +571,13 @@ connection flush; reap; connection default; drop table t2; +connection flush; disconnect flush; +--source include/wait_until_disconnected.inc +connection waiter; +disconnect waiter; +--source include/wait_until_disconnected.inc +connection default; # # Bug#30882 Dropping a temporary table inside a stored function may cause a server crash @@ -699,19 +710,24 @@ handler t1 read a next; # Bug#41112: crash in mysql_ha_close_table/get_lock_data with alter table # +connect(con1,localhost,root,,); +connect(con2,localhost,root,,); + +connection default; --disable_warnings drop table if exists t1; --enable_warnings create table t1 (a int); insert into t1 values (1); handler t1 open; -connect(con1,localhost,root,,); +connection con1; send alter table t1 engine=memory; -connection default; +connection con2; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "rename result table" and info = "alter table t1 engine=memory"; + where state = "Waiting for table" and info = "alter table t1 engine=memory"; --source include/wait_condition.inc +connection default; --error ER_ILLEGAL_HA handler t1 read a next; handler t1 close; @@ -720,6 +736,9 @@ connection con1; drop table t1; disconnect con1; --source include/wait_until_disconnected.inc +connection con2; +disconnect con2; +--source include/wait_until_disconnected.inc connection default; # diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index 471cc6e9a3d..cf424b8b058 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -785,7 +785,7 @@ drop table t1; create table t1 select * from t2; ERROR 42S02: Table 'test.t2' doesn't exist create table t1 select * from t1; -ERROR HY000: You can't specify target table 't1' for update in FROM clause +ERROR 42S02: Table 'test.t1' doesn't exist create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin); ERROR HY000: Illegal mix of collations (latin1_swedish_ci,EXPLICIT) and (latin1_bin,EXPLICIT) for operation 'coalesce' create table t1 (primary key(a)) select "b" as b; @@ -805,6 +805,11 @@ Note 1050 Table 't1' already exists select * from t1; i 1 +create table if not exists t1 select * from t1; +ERROR HY000: You can't specify target table 't1' for update in FROM clause +select * from t1; +i +1 create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin); ERROR HY000: Illegal mix of collations (latin1_swedish_ci,EXPLICIT) and (latin1_bin,EXPLICIT) for operation 'coalesce' select * from t1; diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result index b978304f59d..2be426d3a4a 100644 --- a/mysql-test/r/flush.result +++ b/mysql-test/r/flush.result @@ -33,6 +33,9 @@ flush tables with read lock; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction lock table t1 read; flush tables with read lock; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +unlock tables; +flush tables with read lock; lock table t1 write; ERROR HY000: Can't execute the query because you have a conflicting read lock lock table t1 read; @@ -46,6 +49,7 @@ flush tables with read lock; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction lock table t1 read, t2 read, t3 read; flush tables with read lock; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction unlock tables; drop table t1, t2, t3; create table t1 (c1 int); @@ -69,6 +73,7 @@ ERROR HY000: Can't execute the given command because you have active locked tabl unlock tables; lock tables t1 read; flush tables with read lock; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction unlock tables; drop table t1, t2; set session low_priority_updates=default; diff --git a/mysql-test/r/flush_table.result b/mysql-test/r/flush_table.result index 8821bade6b4..2b0ee1cb205 100644 --- a/mysql-test/r/flush_table.result +++ b/mysql-test/r/flush_table.result @@ -3,21 +3,14 @@ create table t1 (a int not null auto_increment primary key); insert into t1 values(0); lock table t1 read; flush table t1; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +unlock tables; +lock table t1 write; +flush table t1; check table t1; Table Op Msg_type Msg_text test.t1 check status OK unlock tables; -lock table t1 read; -lock table t1 read; -flush table t1; -select * from t1; -a -1 -unlock tables; -select * from t1; -a -1 -unlock tables; lock table t1 write; lock table t1 read; flush table t1; @@ -26,7 +19,7 @@ a 1 unlock tables; unlock tables; -lock table t1 read; +lock table t1 write; lock table t1 write; flush table t1; select * from t1; diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result index 957fc30acef..5990b19062b 100644 --- a/mysql-test/r/handler_innodb.result +++ b/mysql-test/r/handler_innodb.result @@ -548,6 +548,7 @@ c1 1 connection: flush flush tables;; +connection: waiter connection: default handler t2 open; handler t2 read first; @@ -567,6 +568,7 @@ handler t1 read first; c1 connection: flush rename table t1 to t2;; +connection: waiter connection: default handler t2 open; handler t2 read first; diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result index 90a1bdfe6be..f7b0ff2e04e 100644 --- a/mysql-test/r/handler_myisam.result +++ b/mysql-test/r/handler_myisam.result @@ -547,6 +547,7 @@ c1 1 connection: flush flush tables;; +connection: waiter connection: default handler t2 open; handler t2 read first; @@ -566,6 +567,7 @@ handler t1 read first; c1 connection: flush rename table t1 to t2;; +connection: waiter connection: default handler t2 open; handler t2 read first; diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 04234eb3cc4..a66e494dcd2 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -1644,6 +1644,57 @@ TEST_RESULT OK SET TIMESTAMP=DEFAULT; End of 5.1 tests. +# +# Additional test for WL#3726 "DDL locking for all metadata objects" +# To avoid possible deadlocks process of filling of I_S tables should +# use high-priority metadata lock requests when opening tables. +# Below we just test that we really use high-priority lock request +# since reproducing a deadlock will require much more complex test. +# +drop tables if exists t1, t2, t3; +create table t1 (i int); +create table t2 (j int primary key auto_increment); +# Switching to connection 'con3726_1' +lock table t2 read; +# Switching to connection 'con3726_2' +# RENAME below will be blocked by 'lock table t2 read' above but +# will add two pending requests for exclusive metadata locks. +rename table t2 to t3; +# Switching to connection 'default' +# These statements should not be blocked by pending lock requests +select table_name, column_name, data_type from information_schema.columns +where table_schema = 'test' and table_name in ('t1', 't2'); +table_name column_name data_type +t1 i int +t2 j int +select table_name, auto_increment from information_schema.tables +where table_schema = 'test' and table_name in ('t1', 't2'); +table_name auto_increment +t1 NULL +t2 1 +# Switching to connection 'con3726_1' +unlock tables; +# Switching to connection 'con3726_2' +# Switching to connection 'default' +drop tables t1, t3; +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE KEY_COLUMN_USAGE ALL NULL NULL NULL NULL NULL Open_full_table; Scanned all databases +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE PARTITIONS ALL NULL TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 1 database +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS +WHERE CONSTRAINT_SCHEMA='test'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE REFERENTIAL_CONSTRAINTS ALL NULL CONSTRAINT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS +WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE TABLE_CONSTRAINTS ALL NULL TABLE_SCHEMA,TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 0 databases +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS +WHERE EVENT_OBJECT_SCHEMA='test'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE TRIGGERS ALL NULL EVENT_OBJECT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database create table information_schema.t1 (f1 INT); ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' drop table information_schema.t1; @@ -1682,28 +1733,10 @@ ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_ LOCK TABLES t1 READ, information_schema.tables READ; ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema' DROP TABLE t1; -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE KEY_COLUMN_USAGE ALL NULL NULL NULL NULL NULL Open_full_table; Scanned all databases -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE PARTITIONS ALL NULL TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 1 database -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS -WHERE CONSTRAINT_SCHEMA='test'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE REFERENTIAL_CONSTRAINTS ALL NULL CONSTRAINT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS -WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE TABLE_CONSTRAINTS ALL NULL TABLE_SCHEMA,TABLE_NAME NULL NULL NULL Using where; Open_full_table; Scanned 0 databases -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS -WHERE EVENT_OBJECT_SCHEMA='test'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE TRIGGERS ALL NULL EVENT_OBJECT_SCHEMA NULL NULL NULL Using where; Open_full_table; Scanned 1 database SELECT * -FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE -LEFT JOIN INFORMATION_SCHEMA.COLUMNS -USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME) -WHERE COLUMNS.TABLE_SCHEMA = 'test' +FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE +LEFT JOIN INFORMATION_SCHEMA.COLUMNS +USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME) +WHERE COLUMNS.TABLE_SCHEMA = 'test' AND COLUMNS.TABLE_NAME = 't1'; TABLE_SCHEMA TABLE_NAME COLUMN_NAME CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG ORDINAL_POSITION POSITION_IN_UNIQUE_CONSTRAINT REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME TABLE_CATALOG ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME COLUMN_TYPE COLUMN_KEY EXTRA PRIVILEGES COLUMN_COMMENT diff --git a/mysql-test/r/kill.result b/mysql-test/r/kill.result index 8b6830d4798..1f4f4bb32eb 100644 --- a/mysql-test/r/kill.result +++ b/mysql-test/r/kill.result @@ -138,4 +138,107 @@ KILL CONNECTION_ID(); # of close of the connection socket SELECT 1; Got one of the listed errors +# +# Additional test for WL#3726 "DDL locking for all metadata objects" +# Check that DDL and DML statements waiting for metadata locks can +# be killed. Note that we don't cover all situations here since it +# can be tricky to write test case for some of them (e.g. REPAIR or +# ALTER and other statements under LOCK TABLES). +# +drop tables if exists t1, t2, t3; +create table t1 (i int primary key); +# Test for RENAME TABLE +# Switching to connection 'blocker' +lock table t1 read; +# Switching to connection 'ddl' +rename table t1 to t2; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Test for DROP TABLE +drop table t1; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Test for CREATE TRIGGER +create trigger t1_bi before insert on t1 for each row set @a:=1; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# +# Tests for various kinds of ALTER TABLE +# +# Full-blown ALTER which should copy table +alter table t1 add column j int; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Two kinds of simple ALTER +alter table t1 rename to t2; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +alter table t1 disable keys; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Fast ALTER +alter table t1 alter column i set default 100; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Special case which is triggered only for MERGE tables. +# Switching to connection 'blocker' +unlock tables; +create table t2 (i int primary key) engine=merge union=(t1); +lock tables t2 read; +# Switching to connection 'ddl' +alter table t2 alter column i set default 100; +# Switching to connection 'default' +kill query ID; +# Switching to connection 'ddl' +ERROR 70100: Query execution was interrupted +# Test for DML waiting for meta-data lock +# Switching to connection 'blocker' +unlock tables; +drop table t2; +create table t2 (k int); +lock tables t1 read; +# Switching to connection 'ddl' +rename tables t1 to t3, t2 to t1; +# Switching to connection 'dml' +insert into t2 values (1); +# Switching to connection 'default' +kill query ID2; +# Switching to connection 'dml' +ERROR 70100: Query execution was interrupted +# Switching to connection 'blocker' +unlock tables; +# Switching to connection 'ddl' +# Test for DML waiting for tables to be flushed +# Switching to connection 'blocker' +lock tables t1 read; +# Switching to connection 'ddl' +# Let us mark locked table t1 as old +flush tables; +# Switching to connection 'dml' +select * from t1; +# Switching to connection 'default' +kill query ID2; +# Switching to connection 'dml' +ERROR 70100: Query execution was interrupted +# Switching to connection 'blocker' +unlock tables; +# Switching to connection 'ddl' +# Cleanup. +# Switching to connection 'default' +drop table t3; +drop table t1; set @@global.concurrent_insert= @old_concurrent_insert; diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 1f8f6aa04ae..a60c5c8383f 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -128,13 +128,14 @@ select * from v_bug5719; 1 1 drop view v_bug5719; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction sic: did not left LOCK TABLES mode automatically select * from t1; ERROR HY000: Table 't1' was not locked with LOCK TABLES unlock tables; -create view v_bug5719 as select * from t1; +create or replace view v_bug5719 as select * from t1; lock tables v_bug5719 write; select * from v_bug5719; a diff --git a/mysql-test/r/partition_column_prune.result b/mysql-test/r/partition_column_prune.result index 82c49453d43..844429d24a6 100644 --- a/mysql-test/r/partition_column_prune.result +++ b/mysql-test/r/partition_column_prune.result @@ -56,11 +56,11 @@ insert into t1 values (2,5), (2,15), (2,25), insert into t1 select * from t1; explain partitions select * from t1 where a=2; id select_type table partitions type possible_keys key key_len ref rows Extra -1 SIMPLE t1 p01,p02,p03,p11 ALL NULL NULL NULL NULL 13 Using where +1 SIMPLE t1 p01,p02,p03,p11 ALL NULL NULL NULL NULL 8 Using where explain partitions select * from t1 where a=4; id select_type table partitions type possible_keys key key_len ref rows Extra -1 SIMPLE t1 p11,p12,p13,p21 ALL NULL NULL NULL NULL 16 Using where +1 SIMPLE t1 p11,p12,p13,p21 ALL NULL NULL NULL NULL 14 Using where explain partitions select * from t1 where a=2 and b < 22; id select_type table partitions type possible_keys key key_len ref rows Extra -1 SIMPLE t1 p01,p02,p03 ALL NULL NULL NULL NULL 16 Using where +1 SIMPLE t1 p01,p02,p03 ALL NULL NULL NULL NULL 14 Using where drop table t1; diff --git a/mysql-test/r/partition_pruning.result b/mysql-test/r/partition_pruning.result index d7790cd8075..4c22c25ddb9 100644 --- a/mysql-test/r/partition_pruning.result +++ b/mysql-test/r/partition_pruning.result @@ -1960,7 +1960,7 @@ id select_type table partitions type possible_keys key key_len ref rows Extra explain partitions select * from t1 X, t1 Y where X.a = Y.a and (X.a=1 or X.a=2); id select_type table partitions type possible_keys key key_len ref rows Extra -1 SIMPLE X p1,p2 ALL a NULL NULL NULL 4 Using where +1 SIMPLE X p1,p2 ALL a NULL NULL NULL 2 Using where 1 SIMPLE Y p1,p2 ref a a 4 test.X.a 2 drop table t1; create table t1 (a int) partition by hash(a) partitions 20; diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index c7e8812320c..af0ffaf4ae5 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -850,7 +850,7 @@ flush table t1; execute stmt; f1() 6 -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); SUCCESS execute stmt; @@ -1706,7 +1706,7 @@ SUCCESS drop temporary table t2; execute stmt; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); SUCCESS drop table t2; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index a73abf787d8..47d441ee182 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1083,11 +1083,9 @@ select f0()| f0() 100 select * from v0| -f0() -100 +ERROR HY000: Table 'v0' was not locked with LOCK TABLES select *, f0() from v0, (select 123) as d1| -f0() 123 f0() -100 123 100 +ERROR HY000: Table 'v0' was not locked with LOCK TABLES select id, f3() from t1| ERROR HY000: Table 't1' was not locked with LOCK TABLES select f4()| diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 2df2b0bafa6..5f16d88a0dc 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1102,10 +1102,8 @@ select * from v1; a select * from t2; ERROR HY000: Table 't2' was not locked with LOCK TABLES -drop view v1; -drop table t1, t2; -ERROR HY000: Table 't1' was locked with a READ lock and can't be updated unlock tables; +drop view v1; drop table t1, t2; create table t1 (a int); create view v1 as select * from t1 where a < 2 with check option; diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 65fbf2d87b6..0c74d8ed91b 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -779,9 +779,9 @@ GRANT CREATE VIEW ON db26813.v2 TO u26813@localhost; GRANT DROP, CREATE VIEW ON db26813.v3 TO u26813@localhost; GRANT SELECT ON db26813.t1 TO u26813@localhost; ALTER VIEW v1 AS SELECT f2 FROM t1; -ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation +ERROR 42000: CREATE VIEW command denied to user 'u26813'@'localhost' for table 'v1' ALTER VIEW v2 AS SELECT f2 FROM t1; -ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation +ERROR 42000: DROP command denied to user 'u26813'@'localhost' for table 'v2' ALTER VIEW v3 AS SELECT f2 FROM t1; ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation SHOW CREATE VIEW v3; diff --git a/mysql-test/r/view_multi.result b/mysql-test/r/view_multi.result new file mode 100644 index 00000000000..95a8d572be4 --- /dev/null +++ b/mysql-test/r/view_multi.result @@ -0,0 +1,48 @@ +reset master; +create table t1 (i int); +create table t2 (i int); +create view v1 as select * from t1; +select get_lock("lock_bg25144", 1); +get_lock("lock_bg25144", 1) +1 +insert into v1 values (get_lock("lock_bg25144", 100));; +drop view v1;; +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +select * from t1; +i +1 +create view v1 as select * from t1; +select get_lock("lock_bg25144", 1); +get_lock("lock_bg25144", 1) +1 +insert into v1 values (get_lock("lock_bg25144", 100));; +alter view v1 as select * from t2;; +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +select * from t1; +i +1 +1 +select * from t2; +i +show binlog events in 'master-bin.000001' from 107; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query 1 # use `test`; create table t1 (i int) +master-bin.000001 # Query 1 # use `test`; create table t2 (i int) +master-bin.000001 # Query 1 # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1 +master-bin.000001 # Query 1 # use `test`; insert into v1 values (get_lock("lock_bg25144", 100)) +master-bin.000001 # Query 1 # use `test`; drop view v1 +master-bin.000001 # Query 1 # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1 +master-bin.000001 # Query 1 # use `test`; insert into v1 values (get_lock("lock_bg25144", 100)) +master-bin.000001 # Query 1 # use `test`; ALTER ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t2 +drop table t1, t2; +drop view v1; diff --git a/mysql-test/suite/rpl/t/disabled.def b/mysql-test/suite/rpl/t/disabled.def index bed019f9c79..da962a7fb9d 100644 --- a/mysql-test/suite/rpl/t/disabled.def +++ b/mysql-test/suite/rpl/t/disabled.def @@ -13,3 +13,6 @@ rpl_get_master_version_and_clock: # Bug#46931 2009-10-17 joro rpl.rpl_get_master_version_and_clock fails rpl_cross_version : BUG#43913 2009-10-22 luis rpl_cross_version fails with symptom in described in bug report rpl_spec_variables : BUG#47661 2009-10-27 jasonh rpl_spec_variables fails on PB2 hpux +rpl_empty_master_crash : BUG#48048 +rpl_load_from_master : BUG#48048 +rpl_load_table_from_master : BUG#48048 diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index 9f3c3a88151..c07014bfc19 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -683,8 +683,8 @@ drop table t1; # Error during open_and_lock_tables() of tables --error ER_NO_SUCH_TABLE create table t1 select * from t2; -# Rather special error which also caught during open tables pahse ---error ER_UPDATE_TABLE_USED +# A special case which is also caught during open tables pahse +--error ER_NO_SUCH_TABLE create table t1 select * from t1; # Error which happens before select_create::prepare() --error ER_CANT_AGGREGATE_2COLLATIONS @@ -706,6 +706,10 @@ create table t1 (i int); create table t1 select 1 as i; create table if not exists t1 select 1 as i; select * from t1; +# Error which is detected after successfull table open. +--error ER_UPDATE_TABLE_USED +create table if not exists t1 select * from t1; +select * from t1; # Error before select_create::prepare() --error ER_CANT_AGGREGATE_2COLLATIONS create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin); diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index ad7617b9403..bb2749e902b 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -15,3 +15,4 @@ partition_innodb_builtin : Bug#32430 2009-09-25 mattiasj Waiting for push of Inn partition_innodb_plugin : Bug#32430 2009-09-25 mattiasj Waiting for push of Innodb changes innodb-autoinc : Bug#48482 2009-11-02 svoj innodb-autoinc.test fails with results difference rpl_killed_ddl : Bug#45520: rpl_killed_ddl fails sporadically in pb2 +merge : WL#4144 diff --git a/mysql-test/t/flush.test b/mysql-test/t/flush.test index f27d4cf2fad..4172230a54d 100644 --- a/mysql-test/t/flush.test +++ b/mysql-test/t/flush.test @@ -68,10 +68,13 @@ drop table t1; create table t1 (c1 int); lock table t1 write; # Cannot get the global read lock with write locked tables. ---error 1192 +--error ER_LOCK_OR_ACTIVE_TRANSACTION flush tables with read lock; lock table t1 read; -# Can get the global read lock with read locked tables. +# Cannot get the global read lock with read locked tables. +--error ER_LOCK_OR_ACTIVE_TRANSACTION +flush tables with read lock; +unlock tables; flush tables with read lock; --error 1223 lock table t1 write; @@ -84,12 +87,12 @@ create table t2 (c1 int); create table t3 (c1 int); lock table t1 read, t2 read, t3 write; # Cannot get the global read lock with write locked tables. ---error 1192 +--error ER_LOCK_OR_ACTIVE_TRANSACTION flush tables with read lock; lock table t1 read, t2 read, t3 read; -# Can get the global read lock with read locked tables. +# Cannot get the global read lock with read locked tables. +--error ER_LOCK_OR_ACTIVE_TRANSACTION flush tables with read lock; -# Release all table locks and the global read lock. unlock tables; drop table t1, t2, t3; @@ -157,6 +160,7 @@ flush tables with read lock; unlock tables; lock tables t1 read; +--error ER_LOCK_OR_ACTIVE_TRANSACTION flush tables with read lock; unlock tables; diff --git a/mysql-test/t/flush_table.test b/mysql-test/t/flush_table.test index 50e7e91419a..e4fc1b0c39f 100644 --- a/mysql-test/t/flush_table.test +++ b/mysql-test/t/flush_table.test @@ -15,30 +15,21 @@ insert into t1 values(0); # Test for with read lock + flush lock table t1 read; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE flush table t1; -check table t1; unlock tables; -# Test for with 2 read lock in different thread + flush +# Test for with write lock + flush -lock table t1 read; -connect (locker,localhost,root,,test); -connection locker; -lock table t1 read; -connection default; -send flush table t1; -connection locker; ---sleep 2 -select * from t1; -unlock tables; -connection default; -reap; -select * from t1; +lock table t1 write; +flush table t1; +check table t1; unlock tables; # Test for with a write lock and a waiting read lock + flush lock table t1 write; +connect (locker,localhost,root,,test); connection locker; send lock table t1 read; connection default; @@ -51,9 +42,9 @@ reap; unlock tables; connection default; -# Test for with a read lock and a waiting write lock + flush +# Test for with a write lock and a waiting write lock + flush -lock table t1 read; +lock table t1 write; connection locker; send lock table t1 write; connection default; diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 9da7cc1042d..c6cb28a4a30 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -544,6 +544,7 @@ AND table_name not like 'ndb%' AND table_name not like 'innodb_%' GROUP BY TABLE_SCHEMA; + # # TRIGGERS table test # @@ -914,8 +915,8 @@ DROP PROCEDURE p1; DROP USER mysql_bug20230@localhost; # -# Bug#2123 query with a simple non-correlated subquery over -# INFORMARTION_SCHEMA.TABLES +# Bug#21231 query with a simple non-correlated subquery over +# INFORMARTION_SCHEMA.TABLES # SELECT MAX(table_name) FROM information_schema.tables WHERE table_schema IN ('mysql', 'INFORMATION_SCHEMA', 'test'); @@ -1391,9 +1392,66 @@ SET TIMESTAMP=DEFAULT; --echo End of 5.1 tests. +--echo # +--echo # Additional test for WL#3726 "DDL locking for all metadata objects" +--echo # To avoid possible deadlocks process of filling of I_S tables should +--echo # use high-priority metadata lock requests when opening tables. +--echo # Below we just test that we really use high-priority lock request +--echo # since reproducing a deadlock will require much more complex test. +--echo # +--disable_warnings +drop tables if exists t1, t2, t3; +--enable_warnings +create table t1 (i int); +create table t2 (j int primary key auto_increment); +connect (con3726_1,localhost,root,,test); +--echo # Switching to connection 'con3726_1' +connection con3726_1; +lock table t2 read; +connect (con3726_2,localhost,root,,test); +--echo # Switching to connection 'con3726_2' +connection con3726_2; +--echo # RENAME below will be blocked by 'lock table t2 read' above but +--echo # will add two pending requests for exclusive metadata locks. +--send rename table t2 to t3 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info like "rename table t2 to t3"; +--source include/wait_condition.inc +--echo # These statements should not be blocked by pending lock requests +select table_name, column_name, data_type from information_schema.columns + where table_schema = 'test' and table_name in ('t1', 't2'); +select table_name, auto_increment from information_schema.tables + where table_schema = 'test' and table_name in ('t1', 't2'); +--echo # Switching to connection 'con3726_1' +connection con3726_1; +unlock tables; +--echo # Switching to connection 'con3726_2' +connection con3726_2; +--reap +--echo # Switching to connection 'default' +connection default; +disconnect con3726_1; +disconnect con3726_2; +drop tables t1, t3; + +# +# Bug#39270 I_S optimization algorithm does not work properly in some cases +# +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE; +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1'; +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS + WHERE CONSTRAINT_SCHEMA='test'; +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS + WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test'; +EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS + WHERE EVENT_OBJECT_SCHEMA='test'; + # # Bug#24062 Incorrect error msg after execute DROP TABLE IF EXISTS on information_schema -# +# --error ER_DBACCESS_DENIED_ERROR create table information_schema.t1 (f1 INT); --error ER_DBACCESS_DENIED_ERROR @@ -1429,27 +1487,18 @@ DROP TABLE t1, information_schema.tables; LOCK TABLES t1 READ, information_schema.tables READ; DROP TABLE t1; -# -# Bug#39270 I_S optimization algorithm does not work properly in some cases -# -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE; -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='t1'; -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS - WHERE CONSTRAINT_SCHEMA='test'; -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_NAME='t1' and TABLE_SCHEMA='test'; -EXPLAIN SELECT * FROM INFORMATION_SCHEMA.TRIGGERS - WHERE EVENT_OBJECT_SCHEMA='test'; +# Wait till all disconnects are completed +--source include/wait_until_count_sessions.inc # -# Bug #43834 Assertion in Natural_join_column::db_name() on an I_S query +# Bug #43834 Assertion in Natural_join_column::db_name() on an I_S query # + SELECT * -FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE -LEFT JOIN INFORMATION_SCHEMA.COLUMNS -USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME) -WHERE COLUMNS.TABLE_SCHEMA = 'test' -AND COLUMNS.TABLE_NAME = 't1'; +FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE +LEFT JOIN INFORMATION_SCHEMA.COLUMNS +USING (TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME) +WHERE COLUMNS.TABLE_SCHEMA = 'test' +AND COLUMNS.TABLE_NAME = 't1'; + -# Wait till all disconnects are completed ---source include/wait_until_count_sessions.inc diff --git a/mysql-test/t/kill.test b/mysql-test/t/kill.test index 02b033df2e5..98cb9f64d98 100644 --- a/mysql-test/t/kill.test +++ b/mysql-test/t/kill.test @@ -329,6 +329,232 @@ KILL CONNECTION_ID(); SELECT 1; --connection default +--echo # +--echo # Additional test for WL#3726 "DDL locking for all metadata objects" +--echo # Check that DDL and DML statements waiting for metadata locks can +--echo # be killed. Note that we don't cover all situations here since it +--echo # can be tricky to write test case for some of them (e.g. REPAIR or +--echo # ALTER and other statements under LOCK TABLES). +--echo # +--disable_warnings +drop tables if exists t1, t2, t3; +--enable_warnings + +create table t1 (i int primary key); +connect (blocker, localhost, root, , ); +connect (dml, localhost, root, , ); +connect (ddl, localhost, root, , ); + +--echo # Test for RENAME TABLE +--echo # Switching to connection 'blocker' +connection blocker; +lock table t1 read; +--echo # Switching to connection 'ddl' +connection ddl; +let $ID= `select connection_id()`; +--send rename table t1 to t2 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t1 to t2"; +--source include/wait_condition.inc +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap + +--echo # Test for DROP TABLE +--send drop table t1 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "drop table t1"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap + +--echo # Test for CREATE TRIGGER +--send create trigger t1_bi before insert on t1 for each row set @a:=1 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "create trigger t1_bi before insert on t1 for each row set @a:=1"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap + +--echo # +--echo # Tests for various kinds of ALTER TABLE +--echo # +--echo # Full-blown ALTER which should copy table +--send alter table t1 add column j int +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter table t1 add column j int"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap + +--echo # Two kinds of simple ALTER +--send alter table t1 rename to t2 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter table t1 rename to t2"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap +--send alter table t1 disable keys +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter table t1 disable keys"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap +--echo # Fast ALTER +--send alter table t1 alter column i set default 100 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter table t1 alter column i set default 100"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap +--echo # Special case which is triggered only for MERGE tables. +--echo # Switching to connection 'blocker' +connection blocker; +unlock tables; +create table t2 (i int primary key) engine=merge union=(t1); +lock tables t2 read; +--echo # Switching to connection 'ddl' +connection ddl; +--send alter table t2 alter column i set default 100 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter table t2 alter column i set default 100"; +--replace_result $ID ID +eval kill query $ID; +--echo # Switching to connection 'ddl' +connection ddl; +--error ER_QUERY_INTERRUPTED +--reap + +--echo # Test for DML waiting for meta-data lock +--echo # Switching to connection 'blocker' +connection blocker; +unlock tables; +drop table t2; +create table t2 (k int); +lock tables t1 read; +--echo # Switching to connection 'ddl' +connection ddl; +# Let us add pending exclusive metadata lock on t2 +--send rename tables t1 to t3, t2 to t1 +--echo # Switching to connection 'dml' +connection dml; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "rename tables t1 to t3, t2 to t1"; +let $ID2= `select connection_id()`; +--send insert into t2 values (1) +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "insert into t2 values (1)"; +--replace_result $ID2 ID2 +eval kill query $ID2; +--echo # Switching to connection 'dml' +connection dml; +--error ER_QUERY_INTERRUPTED +--reap +--echo # Switching to connection 'blocker' +connection blocker; +unlock tables; +--echo # Switching to connection 'ddl' +connection ddl; +--reap + +--echo # Test for DML waiting for tables to be flushed +--echo # Switching to connection 'blocker' +connection blocker; +lock tables t1 read; +--echo # Switching to connection 'ddl' +connection ddl; +--echo # Let us mark locked table t1 as old +--send flush tables +--echo # Switching to connection 'dml' +connection dml; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Flushing tables" and + info = "flush tables"; +--send select * from t1 +--echo # Switching to connection 'default' +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "select * from t1"; +--replace_result $ID2 ID2 +eval kill query $ID2; +--echo # Switching to connection 'dml' +connection dml; +--error ER_QUERY_INTERRUPTED +--reap +--echo # Switching to connection 'blocker' +connection blocker; +unlock tables; +--echo # Switching to connection 'ddl' +connection ddl; +--reap + +--echo # Cleanup. +--echo # Switching to connection 'default' +connection default; +drop table t3; +drop table t1; + ########################################################################### # Restore global concurrent_insert value. Keep in the end of the test file. diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 04994e3e48f..856ae020492 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -178,6 +178,7 @@ select * from t2; --error ER_TABLE_NOT_LOCKED select * from t3; select * from v_bug5719; +--error ER_LOCK_OR_ACTIVE_TRANSACTION drop view v_bug5719; --echo --echo sic: did not left LOCK TABLES mode automatically @@ -185,7 +186,7 @@ drop view v_bug5719; --error ER_TABLE_NOT_LOCKED select * from t1; unlock tables; -create view v_bug5719 as select * from t1; +create or replace view v_bug5719 as select * from t1; lock tables v_bug5719 write; select * from v_bug5719; --echo diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index 75ee6d07723..58f46941920 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -164,7 +164,7 @@ connection locker; # Sleep a bit till the select of connection reader is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Waiting for table" and info = + where state = "Locked" and info = "SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1"; --source include/wait_condition.inc # Make test case independent from earlier grants. @@ -196,7 +196,7 @@ connection writer; # Sleep a bit till the flush of connection locker is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables" and info = "FLUSH TABLES WITH READ LOCK"; + where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK"; --source include/wait_condition.inc # This must not block. CREATE TABLE t2 (c1 int); @@ -226,7 +226,7 @@ connection writer; # Sleep a bit till the flush of connection locker is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables" and info = "FLUSH TABLES WITH READ LOCK"; + where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK"; --source include/wait_condition.inc --error ER_TABLE_NOT_LOCKED CREATE TABLE t2 AS SELECT * FROM t1; @@ -337,10 +337,10 @@ connection con2; send flush tables with read lock; connection con5; --echo # con5 -let $show_statement= SHOW PROCESSLIST; -let $field= State; -let $condition= = 'Flushing tables'; ---source include/wait_show_condition.inc +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "flush tables with read lock"; +--source include/wait_condition.inc --echo # global read lock is taken connection con3; --echo # con3 @@ -528,7 +528,7 @@ connection default; --echo connection: default let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc alter table t1 add column j int; connect (insert,localhost,root,,test,,); @@ -536,7 +536,7 @@ connection insert; --echo connection: insert let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc --send insert into t1 values (1,2); --echo connection: default @@ -586,18 +586,14 @@ connection default; --echo connection: default let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc flush tables; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc unlock tables; -let $wait_condition= - select count(*) = 0 from information_schema.processlist - where state = "Flushing tables"; ---source include/wait_condition.inc connection flush; --reap connection default; @@ -656,18 +652,14 @@ connection default; --echo connection: default let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc flush tables; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc drop table t1; -let $wait_condition= - select count(*) = 0 from information_schema.processlist - where state = "Flushing tables"; ---source include/wait_condition.inc connection flush; --reap connection default; diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index fee235cd36c..6e771d44b3f 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -745,7 +745,7 @@ execute stmt; call p_verify_reprepare_count(1); flush table t1; execute stmt; -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); execute stmt; --echo # Test 18-c: dependent VIEW has changed @@ -1453,7 +1453,7 @@ execute stmt; call p_verify_reprepare_count(0); drop temporary table t2; execute stmt; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); drop table t2; execute stmt; call p_verify_reprepare_count(0); diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index b0342491a34..b477b5a3e24 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -1308,9 +1308,11 @@ select f3()| select id, f3() from t1 as t11 order by id| # Degenerate cases work too :) select f0()| +# But these should not (particularly views should be locked explicitly). +--error ER_TABLE_NOT_LOCKED select * from v0| +--error ER_TABLE_NOT_LOCKED select *, f0() from v0, (select 123) as d1| -# But these should not ! --error ER_TABLE_NOT_LOCKED select id, f3() from t1| --error ER_TABLE_NOT_LOCKED diff --git a/mysql-test/t/trigger_notembedded.test b/mysql-test/t/trigger_notembedded.test index 7a7e6c6bc85..8a570a7e87d 100644 --- a/mysql-test/t/trigger_notembedded.test +++ b/mysql-test/t/trigger_notembedded.test @@ -896,7 +896,7 @@ connection default; --echo connection: default let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Flushing tables"; + where state = "Waiting for table"; --source include/wait_condition.inc create trigger t1_bi before insert on t1 for each row begin end; unlock tables; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index abf8dac2870..c3ff58880c9 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1016,10 +1016,8 @@ lock tables t1 read, v1 read; select * from v1; -- error ER_TABLE_NOT_LOCKED select * from t2; -drop view v1; ---error ER_TABLE_NOT_LOCKED_FOR_WRITE -drop table t1, t2; unlock tables; +drop view v1; drop table t1, t2; # diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index f01edb1e499..d94bcfc29ed 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -1047,9 +1047,9 @@ GRANT SELECT ON db26813.t1 TO u26813@localhost; connect (u1,localhost,u26813,,db26813); connection u1; ---error ER_SPECIFIC_ACCESS_DENIED_ERROR +--error ER_TABLEACCESS_DENIED_ERROR ALTER VIEW v1 AS SELECT f2 FROM t1; ---error ER_SPECIFIC_ACCESS_DENIED_ERROR +--error ER_TABLEACCESS_DENIED_ERROR ALTER VIEW v2 AS SELECT f2 FROM t1; --error ER_SPECIFIC_ACCESS_DENIED_ERROR ALTER VIEW v3 AS SELECT f2 FROM t1; diff --git a/mysql-test/t/view_multi.test b/mysql-test/t/view_multi.test new file mode 100644 index 00000000000..a61e7738095 --- /dev/null +++ b/mysql-test/t/view_multi.test @@ -0,0 +1,110 @@ +# +# QQ: Should we find a better place for this test? +# May be binlog or rpl suites ? +# +--source include/have_log_bin.inc +--source include/have_binlog_format_mixed_or_statement.inc + +# +# Bug #25144 "replication / binlog with view breaks". +# Statements that used views didn't ensure that view were not modified +# during their execution. Indeed this led to incorrect binary log with +# statement based logging. +# +--disable_parsing +drop table if not exists t1, t2; +drop view if exists v1; +--enable_parsing + +# We are going to use binary log later to check that statements are +# logged in proper order, so it is good idea to reset it here. +reset master; + +connect (addconn1,localhost,root,,); +connect (addconn2,localhost,root,,); +connection default; + +create table t1 (i int); +create table t2 (i int); +create view v1 as select * from t1; + +# First we try to concurrently execute statement that uses view +# and statement that drops it. We use "user" locks as means to +# suspend execution of first statement once it opens our view. +select get_lock("lock_bg25144", 1); + +connection addconn1; +--send insert into v1 values (get_lock("lock_bg25144", 100)); + +connection addconn2; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "User lock" and info like "insert into v1 %lock_bg25144%"; +--source include/wait_condition.inc +--send drop view v1; + +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "drop view v1"; +--source include/wait_condition.inc + +select release_lock("lock_bg25144"); + +connection addconn1; +--reap +select release_lock("lock_bg25144"); + +connection addconn2; +--reap + +connection default; +# Check that insertion through view did happen. +select * from t1; +# At the end of test we will check that statements were +# logged in proper order. + +# Now we will repeat the test by trying concurrently execute +# statement that uses a view and statement that alters it. +create view v1 as select * from t1; + +select get_lock("lock_bg25144", 1); + +connection addconn1; +--send insert into v1 values (get_lock("lock_bg25144", 100)); + +connection addconn2; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "User lock" and info like "insert into v1 %lock_bg25144%"; +--source include/wait_condition.inc +--send alter view v1 as select * from t2; + +connection default; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter view v1 as select * from t2"; +--source include/wait_condition.inc + +select release_lock("lock_bg25144"); + +connection addconn1; +--reap +select release_lock("lock_bg25144"); + +connection addconn2; +--reap + +connection default; + +# Second insertion should go to t1 as well. +select * from t1; +select * from t2; + +# Now let us check that statements were logged in proper order +--replace_column 2 # 5 # +show binlog events in 'master-bin.000001' from 107; + +drop table t1, t2; +drop view v1; -- cgit v1.2.1 From b2ac2b8b1d010b5364f83c6400e9145b01b70a87 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Mon, 30 Nov 2009 19:09:42 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2630.4.7 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Sun 2008-05-25 11:19:02 +0400 message: WL#3726 "DDL locking for all metadata objects". Fixed silly mistake in test case which caused sporadic kill.test failures. --- mysql-test/t/kill.test | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/t/kill.test b/mysql-test/t/kill.test index 98cb9f64d98..b91feb3a1d5 100644 --- a/mysql-test/t/kill.test +++ b/mysql-test/t/kill.test @@ -374,6 +374,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "drop table t1"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -389,6 +390,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "create trigger t1_bi before insert on t1 for each row set @a:=1"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -407,6 +409,7 @@ 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 --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -422,6 +425,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "alter table t1 rename to t2"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -435,6 +439,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "alter table t1 disable keys"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -449,6 +454,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "alter table t1 alter column i set default 100"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -470,6 +476,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "alter table t2 alter column i set default 100"; +--source include/wait_condition.inc --replace_result $ID ID eval kill query $ID; --echo # Switching to connection 'ddl' @@ -494,6 +501,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "rename tables t1 to t3, t2 to t1"; +--source include/wait_condition.inc let $ID2= `select connection_id()`; --send insert into t2 values (1) --echo # Switching to connection 'default' @@ -502,6 +510,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "insert into t2 values (1)"; +--source include/wait_condition.inc --replace_result $ID2 ID2 eval kill query $ID2; --echo # Switching to connection 'dml' @@ -529,6 +538,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Flushing tables" and info = "flush tables"; +--source include/wait_condition.inc --send select * from t1 --echo # Switching to connection 'default' connection default; @@ -536,6 +546,7 @@ let $wait_condition= select count(*) = 1 from information_schema.processlist where state = "Waiting for table" and info = "select * from t1"; +--source include/wait_condition.inc --replace_result $ID2 ID2 eval kill query $ID2; --echo # Switching to connection 'dml' -- cgit v1.2.1 From 076722c4af766fef454d23326c943df8bb633fc0 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Mon, 30 Nov 2009 19:21:40 +0300 Subject: Backport of: ---------------------------------------------------------------- 2630.4.9 Dmitry Lenev 2008-05-26 WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Adjusted test case according to review. --- mysql-test/r/view_multi.result | 48 ---------- mysql-test/suite/rpl/r/rpl_view_multi.result | 90 ++++++++++++++++++ mysql-test/suite/rpl/t/rpl_view_multi.test | 134 +++++++++++++++++++++++++++ mysql-test/t/view_multi.test | 110 ---------------------- 4 files changed, 224 insertions(+), 158 deletions(-) delete mode 100644 mysql-test/r/view_multi.result create mode 100644 mysql-test/suite/rpl/r/rpl_view_multi.result create mode 100644 mysql-test/suite/rpl/t/rpl_view_multi.test delete mode 100644 mysql-test/t/view_multi.test (limited to 'mysql-test') diff --git a/mysql-test/r/view_multi.result b/mysql-test/r/view_multi.result deleted file mode 100644 index 95a8d572be4..00000000000 --- a/mysql-test/r/view_multi.result +++ /dev/null @@ -1,48 +0,0 @@ -reset master; -create table t1 (i int); -create table t2 (i int); -create view v1 as select * from t1; -select get_lock("lock_bg25144", 1); -get_lock("lock_bg25144", 1) -1 -insert into v1 values (get_lock("lock_bg25144", 100));; -drop view v1;; -select release_lock("lock_bg25144"); -release_lock("lock_bg25144") -1 -select release_lock("lock_bg25144"); -release_lock("lock_bg25144") -1 -select * from t1; -i -1 -create view v1 as select * from t1; -select get_lock("lock_bg25144", 1); -get_lock("lock_bg25144", 1) -1 -insert into v1 values (get_lock("lock_bg25144", 100));; -alter view v1 as select * from t2;; -select release_lock("lock_bg25144"); -release_lock("lock_bg25144") -1 -select release_lock("lock_bg25144"); -release_lock("lock_bg25144") -1 -select * from t1; -i -1 -1 -select * from t2; -i -show binlog events in 'master-bin.000001' from 107; -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query 1 # use `test`; create table t1 (i int) -master-bin.000001 # Query 1 # use `test`; create table t2 (i int) -master-bin.000001 # Query 1 # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1 -master-bin.000001 # Query 1 # use `test`; insert into v1 values (get_lock("lock_bg25144", 100)) -master-bin.000001 # Query 1 # use `test`; drop view v1 -master-bin.000001 # Query 1 # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t1 -master-bin.000001 # Query 1 # use `test`; insert into v1 values (get_lock("lock_bg25144", 100)) -master-bin.000001 # Query 1 # use `test`; ALTER ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select * from t2 -drop table t1, t2; -drop view v1; diff --git a/mysql-test/suite/rpl/r/rpl_view_multi.result b/mysql-test/suite/rpl/r/rpl_view_multi.result new file mode 100644 index 00000000000..b3f10584a24 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_view_multi.result @@ -0,0 +1,90 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +# +# Bug #25144 "replication / binlog with view breaks". +# Statements that used views didn't ensure that view were not modified +# during their execution. Indeed this led to incorrect binary log with +# statement based logging and as result to broken replication. +# +drop tables if exists t1, t2; +drop view if exists v1; +# Syncing slave with master and switching to connection 'slave' +# Switching to connection 'master' +create table t1 (i int); +create table t2 (i int); +create view v1 as select * from t1; +# First we try to concurrently execute statement that uses view +# and statement that drops it. We use "user" locks as means to +# suspend execution of first statement once it opens our view. +select get_lock("lock_bg25144", 1); +get_lock("lock_bg25144", 1) +1 +# Switching to connection 'master1' +insert into v1 values (get_lock("lock_bg25144", 100)); +# Switching to connection 'master2' +drop view v1; +# Switching to connection 'master' +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +# Switching to connection 'master1' +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +# Switching to connection 'master2' +# Switching to connection 'master' +# Check that insertion through view did happen. +select * from t1; +i +1 +# Syncing slave with master and switching to connection 'slave' +# Check that slave was able to replicate this sequence +# which means that we got correct binlog order. +select * from t1; +i +1 +# Switching to connection 'master' +# Now we will repeat the test by trying concurrently execute +# statement that uses a view and statement that alters it. +create view v1 as select * from t1; +select get_lock("lock_bg25144", 1); +get_lock("lock_bg25144", 1) +1 +# Switching to connection 'master1' +insert into v1 values (get_lock("lock_bg25144", 100)); +# Switching to connection 'master2' +alter view v1 as select * from t2; +# Switching to connection 'master' +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +# Switching to connection 'master1' +select release_lock("lock_bg25144"); +release_lock("lock_bg25144") +1 +# Switching to connection 'master2' +# Switching to connection 'master' +# Second insertion should go to t1 as well. +select * from t1; +i +1 +1 +select * from t2; +i +# Syncing slave with master and switching to connection 'slave' +# Now let us check that statements were logged in proper order +# So we have same result on slave. +select * from t1; +i +1 +1 +select * from t2; +i +# Switching to connection 'master' +drop table t1, t2; +drop view v1; +# Syncing slave with master and switching to connection 'slave' diff --git a/mysql-test/suite/rpl/t/rpl_view_multi.test b/mysql-test/suite/rpl/t/rpl_view_multi.test new file mode 100644 index 00000000000..777ccb2a945 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_view_multi.test @@ -0,0 +1,134 @@ +# +# This file contains test cases for bugs which involve views, several +# concurren connections and manifest themselves as wrong binary log +# sequence which results in broken replication. In principle we are +# mostly interested in SBR here but this test will also work with RBR. +# +--source include/master-slave.inc + +--echo # +--echo # Bug #25144 "replication / binlog with view breaks". +--echo # Statements that used views didn't ensure that view were not modified +--echo # during their execution. Indeed this led to incorrect binary log with +--echo # statement based logging and as result to broken replication. +--echo # + +--disable_warnings +drop tables if exists t1, t2; +drop view if exists v1; +--enable_warnings +--echo # Syncing slave with master and switching to connection 'slave' +--sync_slave_with_master + +connect (master2,127.0.0.1,root,,test,$MASTER_MYPORT,); + +--echo # Switching to connection 'master' +connection master; +create table t1 (i int); +create table t2 (i int); +create view v1 as select * from t1; + +--echo # First we try to concurrently execute statement that uses view +--echo # and statement that drops it. We use "user" locks as means to +--echo # suspend execution of first statement once it opens our view. +select get_lock("lock_bg25144", 1); + +--echo # Switching to connection 'master1' +connection master1; +--send insert into v1 values (get_lock("lock_bg25144", 100)) + +--echo # Switching to connection 'master2' +connection master2; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "User lock" and info like "insert into v1 %lock_bg25144%"; +--source include/wait_condition.inc +--send drop view v1 + +--echo # Switching to connection 'master' +connection master; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "drop view v1"; +--source include/wait_condition.inc + +select release_lock("lock_bg25144"); + +--echo # Switching to connection 'master1' +connection master1; +--reap +select release_lock("lock_bg25144"); + +--echo # Switching to connection 'master2' +connection master2; +--reap + +--echo # Switching to connection 'master' +connection master; +--echo # Check that insertion through view did happen. +select * from t1; +--echo # Syncing slave with master and switching to connection 'slave' +--sync_slave_with_master +--echo # Check that slave was able to replicate this sequence +--echo # which means that we got correct binlog order. +select * from t1; + +--echo # Switching to connection 'master' +connection master; +--echo # Now we will repeat the test by trying concurrently execute +--echo # statement that uses a view and statement that alters it. +create view v1 as select * from t1; + +select get_lock("lock_bg25144", 1); + +--echo # Switching to connection 'master1' +connection master1; +--send insert into v1 values (get_lock("lock_bg25144", 100)) + +--echo # Switching to connection 'master2' +connection master2; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "User lock" and info like "insert into v1 %lock_bg25144%"; +--source include/wait_condition.inc +--send alter view v1 as select * from t2 + +--echo # Switching to connection 'master' +connection master; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info = "alter view v1 as select * from t2"; +--source include/wait_condition.inc + +select release_lock("lock_bg25144"); + +--echo # Switching to connection 'master1' +connection master1; +--reap +select release_lock("lock_bg25144"); + +--echo # Switching to connection 'master2' +connection master2; +--reap + +--echo # Switching to connection 'master' +connection master; + +--echo # Second insertion should go to t1 as well. +select * from t1; +select * from t2; + +--echo # Syncing slave with master and switching to connection 'slave' +--sync_slave_with_master +--echo # Now let us check that statements were logged in proper order +--echo # So we have same result on slave. +select * from t1; +select * from t2; + +--echo # Switching to connection 'master' +connection master; +drop table t1, t2; +drop view v1; +--echo # Syncing slave with master and switching to connection 'slave' +--sync_slave_with_master diff --git a/mysql-test/t/view_multi.test b/mysql-test/t/view_multi.test deleted file mode 100644 index a61e7738095..00000000000 --- a/mysql-test/t/view_multi.test +++ /dev/null @@ -1,110 +0,0 @@ -# -# QQ: Should we find a better place for this test? -# May be binlog or rpl suites ? -# ---source include/have_log_bin.inc ---source include/have_binlog_format_mixed_or_statement.inc - -# -# Bug #25144 "replication / binlog with view breaks". -# Statements that used views didn't ensure that view were not modified -# during their execution. Indeed this led to incorrect binary log with -# statement based logging. -# ---disable_parsing -drop table if not exists t1, t2; -drop view if exists v1; ---enable_parsing - -# We are going to use binary log later to check that statements are -# logged in proper order, so it is good idea to reset it here. -reset master; - -connect (addconn1,localhost,root,,); -connect (addconn2,localhost,root,,); -connection default; - -create table t1 (i int); -create table t2 (i int); -create view v1 as select * from t1; - -# First we try to concurrently execute statement that uses view -# and statement that drops it. We use "user" locks as means to -# suspend execution of first statement once it opens our view. -select get_lock("lock_bg25144", 1); - -connection addconn1; ---send insert into v1 values (get_lock("lock_bg25144", 100)); - -connection addconn2; -let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "User lock" and info like "insert into v1 %lock_bg25144%"; ---source include/wait_condition.inc ---send drop view v1; - -connection default; -let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "Waiting for table" and info = "drop view v1"; ---source include/wait_condition.inc - -select release_lock("lock_bg25144"); - -connection addconn1; ---reap -select release_lock("lock_bg25144"); - -connection addconn2; ---reap - -connection default; -# Check that insertion through view did happen. -select * from t1; -# At the end of test we will check that statements were -# logged in proper order. - -# Now we will repeat the test by trying concurrently execute -# statement that uses a view and statement that alters it. -create view v1 as select * from t1; - -select get_lock("lock_bg25144", 1); - -connection addconn1; ---send insert into v1 values (get_lock("lock_bg25144", 100)); - -connection addconn2; -let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "User lock" and info like "insert into v1 %lock_bg25144%"; ---source include/wait_condition.inc ---send alter view v1 as select * from t2; - -connection default; -let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "Waiting for table" and - info = "alter view v1 as select * from t2"; ---source include/wait_condition.inc - -select release_lock("lock_bg25144"); - -connection addconn1; ---reap -select release_lock("lock_bg25144"); - -connection addconn2; ---reap - -connection default; - -# Second insertion should go to t1 as well. -select * from t1; -select * from t2; - -# Now let us check that statements were logged in proper order ---replace_column 2 # 5 # -show binlog events in 'master-bin.000001' from 107; - -drop table t1, t2; -drop view v1; -- cgit v1.2.1 From cf4a4ba6fdce6e37c0a97bcf9b6653456a5ff374 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 22:13:01 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2630.9.3 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w3 timestamp: Wed 2008-06-11 08:33:36 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Changed close_cached_tables() not to flush all unused TABLE instances when flushing individual table. Renamed expel_table_from_cache() to tdc_remove_table() and added enum parameter to be able more explicitly specify type of removal, rewrote its code to be more efficient. ****** Backport of: ------------------------------------------------------------ revno: 2630.9.4 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w3 timestamp: Wed 2008-06-11 15:53:53 +0400 message: WL#3726 "DDL locking for all metadata objects". After-review fixes in progress. Minor changes in order to improve code readability and simplify debugging. --- mysql-test/r/ps_ddl.result | 2 +- mysql-test/t/ps_ddl.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index af0ffaf4ae5..f411328ed7c 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -850,7 +850,7 @@ flush table t1; execute stmt; f1() 6 -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); SUCCESS execute stmt; diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index 6e771d44b3f..1ba193983b2 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -745,7 +745,7 @@ execute stmt; call p_verify_reprepare_count(1); flush table t1; execute stmt; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); execute stmt; --echo # Test 18-c: dependent VIEW has changed -- cgit v1.2.1 From bcae0d9bab24165bc58090187ba0a51babe0e8ac Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 2 Dec 2009 18:22:15 +0300 Subject: Backport of: ---------------------------------------------------------- revno: 2630.10.1 committer: Konstantin Osipov branch nick: mysql-6.0-lock-tables-tidyup timestamp: Wed 2008-06-11 15:49:58 +0400 message: WL#3726, review fixes. Now that we have metadata locks, we don't need to keep a crippled TABLE instance in the table cache to indicate that a table is locked. Remove all code that used this technique. Instead, rely on metadata locks and use the standard open_table() and close_thread_table() to manipulate with the table cache tables. Removes a list of functions that have become unused (see the comment for sql_base.cc for details). Under LOCK TABLES, keep a TABLE_LIST instance for each table that may be temporarily closed. For that, implement an own class for LOCK TABLES mode, Locked_tables_list. This is a pre-requisite patch for WL#4144. This is not exactly a backport: there is no new online ALTER table in Celosia, so the old alter table code was changed to work with the new table cache API. --- mysql-test/r/lock.result | 45 +++++++++++++++++++++++++++++++ mysql-test/r/trigger-compat.result | 2 -- mysql-test/suite/rpl/r/rpl_trigger.result | 2 -- mysql-test/t/lock.test | 43 +++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 4 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index a60c5c8383f..676a8c41bb6 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -201,3 +201,48 @@ ERROR HY000: Table 't2' was locked with a READ lock and can't be updated UNLOCK TABLES; DROP TABLE t1,t2; End of 5.1 tests. +# +# Ensure that FLUSH TABLES doesn't substitute a base locked table +# with a temporary one. +# +drop table if exists t1, t2; +create table t1 (a int); +create table t2 (a int); +lock table t1 write, t2 write; +create temporary table t1 (a int); +flush table t1; +drop temporary table t1; +select * from t1; +a +unlock tables; +drop table t1, t2; +# +# Ensure that REPAIR .. USE_FRM works under LOCK TABLES. +# +drop table if exists t1, t2; +create table t1 (a int); +create table t2 (a int); +lock table t1 write, t2 write; +repair table t1 use_frm; +Table Op Msg_type Msg_text +test.t1 repair status OK +repair table t1 use_frm; +Table Op Msg_type Msg_text +test.t1 repair status OK +select * from t1; +a +select * from t2; +a +repair table t2 use_frm; +Table Op Msg_type Msg_text +test.t2 repair status OK +repair table t2 use_frm; +Table Op Msg_type Msg_text +test.t2 repair status OK +select * from t1; +a +unlock tables; +drop table t1, t2; +# +# End of 6.0 tests. +# diff --git a/mysql-test/r/trigger-compat.result b/mysql-test/r/trigger-compat.result index 14ef4f54f27..14949c227bd 100644 --- a/mysql-test/r/trigger-compat.result +++ b/mysql-test/r/trigger-compat.result @@ -34,8 +34,6 @@ TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_MANIPULATION EVENT_OBJECT_CATA def mysqltest_db1 wl2818_trg1 INSERT def mysqltest_db1 t1 0 NULL INSERT INTO t2 VALUES(CURRENT_USER()) ROW BEFORE NULL NULL OLD NEW NULL latin1 latin1_swedish_ci latin1_swedish_ci def mysqltest_db1 wl2818_trg2 INSERT def mysqltest_db1 t1 0 NULL INSERT INTO t2 VALUES(CURRENT_USER()) ROW AFTER NULL NULL OLD NEW NULL mysqltest_dfn@localhost latin1 latin1_swedish_ci latin1_swedish_ci DROP TRIGGER wl2818_trg1; -Warnings: -Warning 1454 No definer attribute for trigger 'mysqltest_db1'.'wl2818_trg1'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger. DROP TRIGGER wl2818_trg2; use mysqltest_db1; DROP TABLE t1; diff --git a/mysql-test/suite/rpl/r/rpl_trigger.result b/mysql-test/suite/rpl/r/rpl_trigger.result index 3d7757613a7..01d886c4709 100644 --- a/mysql-test/suite/rpl/r/rpl_trigger.result +++ b/mysql-test/suite/rpl/r/rpl_trigger.result @@ -893,8 +893,6 @@ s @ root@localhost DROP TRIGGER trg1; -Warnings: -Warning 1454 No definer attribute for trigger 'test'.'trg1'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger. DROP TABLE t1; DROP TABLE t2; STOP SLAVE; diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 856ae020492..7effaaeb20d 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -252,3 +252,46 @@ UNLOCK TABLES; DROP TABLE t1,t2; --echo End of 5.1 tests. + +--echo # +--echo # Ensure that FLUSH TABLES doesn't substitute a base locked table +--echo # with a temporary one. +--echo # + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +lock table t1 write, t2 write; +create temporary table t1 (a int); +flush table t1; +drop temporary table t1; +select * from t1; +unlock tables; +drop table t1, t2; + +--echo # +--echo # Ensure that REPAIR .. USE_FRM works under LOCK TABLES. +--echo # + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +lock table t1 write, t2 write; +repair table t1 use_frm; +repair table t1 use_frm; +select * from t1; +select * from t2; +repair table t2 use_frm; +repair table t2 use_frm; +select * from t1; +unlock tables; +drop table t1, t2; + + +--echo # +--echo # End of 6.0 tests. +--echo # -- cgit v1.2.1 From 416a626368b9383a96399ea57dd79f56a665c2a6 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 2 Dec 2009 23:47:23 +0300 Subject: Backport of: ---------------------------------------------------------- revno: 2630.4.35 committer: Konstantin Osipov branch nick: mysql-6.0-3726 timestamp: Wed 2008-06-25 16:44:00 +0400 message: Fix a MyISAM-specific bug in the new implementation of LOCK TABLES (WL#3726). If more than one instance of a MyISAM table are open in the same connection, all of them must share the same status_param. Otherwise, unlock of a table may lead to lost records. See also comments in thr_lock.c. --- mysql-test/r/lock.result | 18 ++++++++++++++++++ mysql-test/t/lock.test | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 676a8c41bb6..092c376b34a 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -244,5 +244,23 @@ a unlock tables; drop table t1, t2; # +# Ensure that mi_copy_status is called for two instances +# of the same table when it is reopened after a flush. +# +drop table if exists t1; +drop view if exists v1; +create table t1 (c1 int); +create view v1 as select * from t1; +lock tables t1 write, v1 write; +flush table t1; +insert into t1 values (33); +flush table t1; +select * from t1; +c1 +33 +unlock tables; +drop table t1; +drop view v1; +# # End of 6.0 tests. # diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 7effaaeb20d..51900be4df8 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -291,6 +291,24 @@ select * from t1; unlock tables; drop table t1, t2; +--echo # +--echo # Ensure that mi_copy_status is called for two instances +--echo # of the same table when it is reopened after a flush. +--echo # +--disable_warnings +drop table if exists t1; +drop view if exists v1; +--enable_warnings +create table t1 (c1 int); +create view v1 as select * from t1; +lock tables t1 write, v1 write; +flush table t1; +insert into t1 values (33); +flush table t1; +select * from t1; +unlock tables; +drop table t1; +drop view v1; --echo # --echo # End of 6.0 tests. -- cgit v1.2.1 From ef15a335b3c924b779ba986acafffad41ff07ba8 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 02:09:22 +0300 Subject: Backport of: ---------------------------------------------------------- revno: 2630.4.38 committer: Konstantin Osipov branch nick: mysql-6.0-4144 timestamp: Wed 2008-06-25 22:07:06 +0400 message: WL#4144 - Lock MERGE engine children. Committing a version of the patch merged with WL#3726 on behalf of Ingo. Step #1: Move locking from parent to children. MERGE children are now left in the query list of tables after inserted there in open_tables(). So they are locked by lock_tables() as all other tables are. The MERGE parent does not store locks any more. It appears in a MYSQL_LOCK with zero lock data. This is kind of a "dummy" lock. All other lock handling is also done directly on the children. To protect against parent or child modifications during LOCK TABLES, the children are detached after every statement and attached before every statement, even under LOCK TABLES. The children table list is removed from the query list of tables on every detach and on close of the parent. Step #2: Move MERGE specific functionality from SQL layer into table handler. Functionality moved from SQL layer (mainly sql_base.cc) to the table handler (ha_myisammrg.cc). Unnecessary code is removed from the SQL layer. Step #3: Moved all MERGE specific members from TABLE to ha_myisammrg. Moved members from TABLE to ha_myisammrg. Renamed some mebers. Fixed comments. Step #4: Valgrind and coverage testing Valgrind did not uncover new problems. Added purecov comments. Added a new test for DATA/INDEX DIRECTORY options. Changed handling of ::reset() for non-attached children. Fixed the merge-big test. Step #5: Fixed crashes detected during review Changed detection when to attach/detach. Added new tests. Backport also the fix for Bug#44040 "MySQL allows creating a MERGE table upon VIEWs but crashes when using it" --- mysql-test/r/merge.result | 285 +++++++++++++++++++++++++++++++++++--- mysql-test/t/disabled.def | 1 - mysql-test/t/merge-big.test | 4 +- mysql-test/t/merge.test | 326 +++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 578 insertions(+), 38 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 83152a0dd8d..479645744ec 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -578,23 +578,23 @@ select max(b) from t1 where a = 2; max(b) 1 drop table t3,t1,t2; -create table t1 (a int not null); -create table t2 (a int not null); -insert into t1 values (1); -insert into t2 values (2); -create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); -select * from t3; +CREATE TABLE t1 (c1 INT NOT NULL); +CREATE TABLE t2 (c1 INT NOT NULL); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +CREATE TEMPORARY TABLE t3 (c1 INT NOT NULL) ENGINE=MRG_MYISAM UNION=(t1,t2); +SELECT * FROM t3; ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist -create temporary table t4 (a int not null); -create temporary table t5 (a int not null); -insert into t4 values (1); -insert into t5 values (2); -create temporary table t6 (a int not null) ENGINE=MERGE UNION=(t4,t5); -select * from t6; -a -1 -2 -drop table t6, t3, t1, t2, t4, t5; +CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL); +CREATE TEMPORARY TABLE t5 (c1 INT NOT NULL); +INSERT INTO t4 VALUES (4); +INSERT INTO t5 VALUES (5); +CREATE TEMPORARY TABLE t6 (c1 INT NOT NULL) ENGINE=MRG_MYISAM UNION=(t4,t5); +SELECT * FROM t6; +c1 +4 +5 +DROP TABLE t6, t3, t1, t2, t4, t5; create temporary table t1 (a int not null); create temporary table t2 (a int not null); insert into t1 values (1); @@ -1258,7 +1258,7 @@ c1 LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; ALTER TABLE t2 RENAME TO t5; SELECT * FROM t3 ORDER BY c1; -ERROR HY000: Table 't3' was not locked with LOCK TABLES +ERROR HY000: Table 't2' was not locked with LOCK TABLES ALTER TABLE t5 RENAME TO t2; ERROR HY000: Table 't5' was not locked with LOCK TABLES UNLOCK TABLES; @@ -1330,9 +1330,9 @@ LOCK TABLES t1 WRITE, t2 WRITE; INSERT INTO t1 VALUES (1); DROP TABLE t1; SELECT * FROM t2; -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +ERROR HY000: Table 't1' was not locked with LOCK TABLES SELECT * FROM t1; -ERROR 42S02: Table 'test.t1' doesn't exist +ERROR HY000: Table 't1' was not locked with LOCK TABLES UNLOCK TABLES; DROP TABLE t2; # @@ -2256,3 +2256,250 @@ deallocate prepare stmt; # drop table t_parent; set @@global.table_definition_cache=@save_table_definition_cache; +DROP DATABASE IF EXISTS mysql_test1; +CREATE DATABASE mysql_test1; +CREATE TABLE t1 ... DATA DIRECTORY=... INDEX DIRECTORY=... +CREATE TABLE mysql_test1.t2 ... DATA DIRECTORY=... INDEX DIRECTORY=... +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,mysql_test1.t2) +INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO mysql_test1.t2 VALUES (2); +SELECT * FROM m1; +c1 +1 +2 +DROP TABLE t1, mysql_test1.t2, m1; +DROP DATABASE mysql_test1; +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +INSERT INTO t1 (c1) VALUES (1); +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) INSERT_METHOD=FIRST; +CREATE TABLE t3 (c1 INT); +INSERT INTO t3 (c1) VALUES (1); +CREATE FUNCTION f1() RETURNS INT RETURN (SELECT MAX(c1) FROM t3); +CREATE VIEW v1 AS SELECT foo.c1 c1, f1() c2, bar.c1 c3, f1() c4 +FROM tm1 foo, tm1 bar, t3; +SELECT * FROM v1; +c1 c2 c3 c4 +1 1 1 1 +DROP FUNCTION f1; +DROP VIEW v1; +DROP TABLE tm1, t1, t2, t3; +CREATE TEMPORARY TABLE t1 (c1 INT); +CREATE TEMPORARY TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) +INSERT_METHOD=FIRST; +CREATE FUNCTION f1() RETURNS INT RETURN (SELECT MAX(c1) FROM tm1); +INSERT INTO tm1 (c1) VALUES (1); +SELECT f1() FROM (SELECT 1) AS c1; +f1() +1 +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2; +CREATE FUNCTION f1() RETURNS INT +BEGIN +CREATE TEMPORARY TABLE t1 (c1 INT); +CREATE TEMPORARY TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2); +INSERT INTO t1 (c1) VALUES (1); +RETURN (SELECT MAX(c1) FROM tm1); +END| +SELECT f1() FROM (SELECT 1 UNION SELECT 1) c1; +f1() +1 +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2; +CREATE TEMPORARY TABLE t1 (c1 INT); +INSERT INTO t1 (c1) VALUES (1); +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1); +CREATE FUNCTION f1() RETURNS INT +BEGIN +CREATE TEMPORARY TABLE t2 (c1 INT); +ALTER TEMPORARY TABLE tm1 UNION=(t1,t2); +INSERT INTO t2 (c1) VALUES (2); +RETURN (SELECT MAX(c1) FROM tm1); +END| +ERROR 0A000: ALTER VIEW is not allowed in stored procedures +DROP TABLE tm1, t1; +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +DROP TABLE tm1, t1; +CREATE FUNCTION f1() RETURNS INT +BEGIN +INSERT INTO tm1 VALUES (1); +RETURN (SELECT MAX(c1) FROM tm1); +END| +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +SELECT f1(); +f1() +1 +DROP FUNCTION f1; +DROP TABLE tm1, t1; +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +LOCK TABLE tm1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +UNLOCK TABLES; +DROP TABLE tm1, t1; +CREATE FUNCTION f1() RETURNS INT +BEGIN +INSERT INTO tm1 VALUES (1); +RETURN (SELECT MAX(c1) FROM tm1); +END| +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +LOCK TABLE tm1 WRITE; +SELECT f1(); +f1() +1 +UNLOCK TABLES; +DROP FUNCTION f1; +DROP TABLE tm1, t1; +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +CREATE TRIGGER t2_ai AFTER INSERT ON t2 +FOR EACH ROW INSERT INTO tm1 VALUES(11); +LOCK TABLE t2 WRITE; +INSERT INTO t2 VALUES (2); +SELECT * FROM tm1; +c1 +11 +SELECT * FROM t2; +c1 +2 +UNLOCK TABLES; +DROP TRIGGER t2_ai; +DROP TABLE tm1, t1, t2; +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +DROP TABLE tm1, t1; +CREATE FUNCTION f1() RETURNS INT +BEGIN +INSERT INTO tm1 VALUES (1); +RETURN (SELECT MAX(c1) FROM tm1); +END| +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +SELECT f1(); +f1() +1 +DROP FUNCTION f1; +DROP TABLE tm1, t1; +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +CREATE TABLE t9 (c1 INT) ENGINE=MyISAM; +LOCK TABLE t9 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +UNLOCK TABLES; +DROP TABLE tm1, t1, t9; +CREATE FUNCTION f1() RETURNS INT +BEGIN +INSERT INTO tm1 VALUES (1); +RETURN (SELECT MAX(c1) FROM tm1); +END| +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +CREATE TABLE t9 (c1 INT) ENGINE=MyISAM; +LOCK TABLE t9 WRITE; +SELECT f1(); +f1() +1 +UNLOCK TABLES; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t9; +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TRIGGER t2_ai AFTER INSERT ON t2 +FOR EACH ROW INSERT INTO tm1 VALUES(11); +LOCK TABLE t2 WRITE; +INSERT INTO t2 VALUES (2); +SELECT * FROM tm1; +c1 +11 +SELECT * FROM t2; +c1 +2 +UNLOCK TABLES; +DROP TRIGGER t2_ai; +DROP TABLE tm1, t1, t2; +# +# Don't select MERGE child when trying to get prelocked table. +# +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +CREATE TRIGGER tm1_ai AFTER INSERT ON tm1 +FOR EACH ROW INSERT INTO t1 VALUES(11); +LOCK TABLE tm1 WRITE, t1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +11 +UNLOCK TABLES; +LOCK TABLE t1 WRITE, tm1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +c1 +1 +11 +1 +11 +UNLOCK TABLES; +DROP TRIGGER tm1_ai; +DROP TABLE tm1, t1; +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t3 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t4 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t5 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2,t3,t4,t5) +INSERT_METHOD=LAST; +CREATE TRIGGER t2_au AFTER UPDATE ON t2 +FOR EACH ROW INSERT INTO t3 VALUES(33); +CREATE FUNCTION f1() RETURNS INT +RETURN (SELECT MAX(c1) FROM t4); +LOCK TABLE tm1 WRITE, t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE, t5 WRITE; +INSERT INTO t1 VALUES(1); +INSERT INTO t2 VALUES(2); +INSERT INTO t3 VALUES(3); +INSERT INTO t4 VALUES(4); +INSERT INTO t5 VALUES(5); +UPDATE t2, tm1 SET t2.c1=f1(); +FLUSH TABLES; +FLUSH TABLES; +UNLOCK TABLES; +SELECT * FROM tm1; +c1 +1 +4 +3 +33 +4 +5 +DROP TRIGGER t2_au; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2, t3, t4, t5; +End of 6.0 tests diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index bb2749e902b..ad7617b9403 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -15,4 +15,3 @@ partition_innodb_builtin : Bug#32430 2009-09-25 mattiasj Waiting for push of Inn partition_innodb_plugin : Bug#32430 2009-09-25 mattiasj Waiting for push of Innodb changes innodb-autoinc : Bug#48482 2009-11-02 svoj innodb-autoinc.test fails with results difference rpl_killed_ddl : Bug#45520: rpl_killed_ddl fails sporadically in pb2 -merge : WL#4144 diff --git a/mysql-test/t/merge-big.test b/mysql-test/t/merge-big.test index b687973c9d1..33bd93791f1 100644 --- a/mysql-test/t/merge-big.test +++ b/mysql-test/t/merge-big.test @@ -51,7 +51,7 @@ connection default; #--sleep 8 #SELECT ID,STATE,INFO FROM INFORMATION_SCHEMA.PROCESSLIST; let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST - WHERE ID = $con1_id AND STATE = 'Locked'; + WHERE ID = $con1_id AND STATE = 'Table lock'; --source include/wait_condition.inc #SELECT NOW(); --echo # Kick INSERT out of thr_multi_lock(). @@ -61,7 +61,7 @@ FLUSH TABLES; #--sleep 8 #SELECT ID,STATE,INFO FROM INFORMATION_SCHEMA.PROCESSLIST; let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST - WHERE ID = $con1_id AND STATE = 'Waiting for table'; + WHERE ID = $con1_id AND STATE = 'Table lock'; --source include/wait_condition.inc #SELECT NOW(); --echo # Unlock and close table and wait for con1 to close too. diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 63ad5a1e97c..f7ce6ba700b 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -216,20 +216,20 @@ drop table t3,t1,t2; # # temporary merge tables # -create table t1 (a int not null); -create table t2 (a int not null); -insert into t1 values (1); -insert into t2 values (2); -create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); +CREATE TABLE t1 (c1 INT NOT NULL); +CREATE TABLE t2 (c1 INT NOT NULL); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +CREATE TEMPORARY TABLE t3 (c1 INT NOT NULL) ENGINE=MRG_MYISAM UNION=(t1,t2); --error ER_WRONG_MRG_TABLE -select * from t3; -create temporary table t4 (a int not null); -create temporary table t5 (a int not null); -insert into t4 values (1); -insert into t5 values (2); -create temporary table t6 (a int not null) ENGINE=MERGE UNION=(t4,t5); -select * from t6; -drop table t6, t3, t1, t2, t4, t5; +SELECT * FROM t3; +CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL); +CREATE TEMPORARY TABLE t5 (c1 INT NOT NULL); +INSERT INTO t4 VALUES (4); +INSERT INTO t5 VALUES (5); +CREATE TEMPORARY TABLE t6 (c1 INT NOT NULL) ENGINE=MRG_MYISAM UNION=(t4,t5); +SELECT * FROM t6; +DROP TABLE t6, t3, t1, t2, t4, t5; # # Bug#19627 - temporary merge table locking # MERGE table and its children must match in temporary type. @@ -556,7 +556,7 @@ CREATE TABLE t1(a INT); SELECT * FROM tm1; CHECK TABLE tm1; CREATE TABLE t2(a BLOB); ---error 1168 +--error ER_WRONG_MRG_TABLE SELECT * FROM tm1; CHECK TABLE tm1; ALTER TABLE t2 MODIFY a INT; @@ -969,9 +969,9 @@ CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1) LOCK TABLES t1 WRITE, t2 WRITE; INSERT INTO t1 VALUES (1); DROP TABLE t1; ---error 1168 +--error ER_TABLE_NOT_LOCKED SELECT * FROM t2; ---error ER_NO_SUCH_TABLE +--error ER_TABLE_NOT_LOCKED SELECT * FROM t1; UNLOCK TABLES; DROP TABLE t2; @@ -1407,6 +1407,7 @@ FLUSH TABLES m1, t1; UNLOCK TABLES; DROP TABLE t1, m1; + # # Bug#35068 - Assertion fails when reading from i_s.tables # and there is incorrect merge table @@ -1694,3 +1695,296 @@ while ($1) --enable_query_log drop table t_parent; set @@global.table_definition_cache=@save_table_definition_cache; + +# +# WL#4144 - Lock MERGE engine children +# +# Test DATA/INDEX DIRECTORY +# +--disable_warnings +DROP DATABASE IF EXISTS mysql_test1; +--enable_warnings +CREATE DATABASE mysql_test1; +--disable_query_log +# data/index directory don't work in HAVE_purify builds. Disable +# build-dependent warnings. +--disable_warnings +--echo CREATE TABLE t1 ... DATA DIRECTORY=... INDEX DIRECTORY=... +eval CREATE TABLE t1 (c1 INT) + DATA DIRECTORY='$MYSQLTEST_VARDIR/tmp' + INDEX DIRECTORY='$MYSQLTEST_VARDIR/tmp'; +--echo CREATE TABLE mysql_test1.t2 ... DATA DIRECTORY=... INDEX DIRECTORY=... +eval CREATE TABLE mysql_test1.t2 (c1 INT) + DATA DIRECTORY='$MYSQLTEST_VARDIR/tmp' + INDEX DIRECTORY='$MYSQLTEST_VARDIR/tmp'; +--enable_query_log +--enable_warnings +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,mysql_test1.t2) + INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO mysql_test1.t2 VALUES (2); +SELECT * FROM m1; +#--copy_file $MYSQLTEST_VARDIR/master-data/test/m1.MRG /tmp/mysql-test-m1.MRG +DROP TABLE t1, mysql_test1.t2, m1; +DROP DATABASE mysql_test1; +# +# Review detected Crash #1. Detaching main tables while in sub statement. +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +INSERT INTO t1 (c1) VALUES (1); +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) INSERT_METHOD=FIRST; +CREATE TABLE t3 (c1 INT); +INSERT INTO t3 (c1) VALUES (1); +CREATE FUNCTION f1() RETURNS INT RETURN (SELECT MAX(c1) FROM t3); +CREATE VIEW v1 AS SELECT foo.c1 c1, f1() c2, bar.c1 c3, f1() c4 + FROM tm1 foo, tm1 bar, t3; +SELECT * FROM v1; +DROP FUNCTION f1; +DROP VIEW v1; +DROP TABLE tm1, t1, t2, t3; +# +# Review detected Crash #2. Trying to attach temporary table twice. +# +CREATE TEMPORARY TABLE t1 (c1 INT); +CREATE TEMPORARY TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) + INSERT_METHOD=FIRST; +CREATE FUNCTION f1() RETURNS INT RETURN (SELECT MAX(c1) FROM tm1); +INSERT INTO tm1 (c1) VALUES (1); +SELECT f1() FROM (SELECT 1) AS c1; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2; +# +# Review suggested test. DDL in a stored function. +# +DELIMITER |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + CREATE TEMPORARY TABLE t1 (c1 INT); + CREATE TEMPORARY TABLE t2 (c1 INT); + CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2); + INSERT INTO t1 (c1) VALUES (1); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +SELECT f1() FROM (SELECT 1 UNION SELECT 1) c1; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2; +# +CREATE TEMPORARY TABLE t1 (c1 INT); +INSERT INTO t1 (c1) VALUES (1); +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1); +DELIMITER |; +--error ER_SP_BADSTATEMENT +CREATE FUNCTION f1() RETURNS INT +BEGIN + CREATE TEMPORARY TABLE t2 (c1 INT); + ALTER TEMPORARY TABLE tm1 UNION=(t1,t2); + INSERT INTO t2 (c1) VALUES (2); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +DROP TABLE tm1, t1; +# +# Base table. No LOCK TABLES, no functions/triggers. +# +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +DROP TABLE tm1, t1; +# +# Base table. No LOCK TABLES, sub-statement that is run inside a function. +# +DELIMITER |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + INSERT INTO tm1 VALUES (1); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +SELECT f1(); +DROP FUNCTION f1; +DROP TABLE tm1, t1; +# +# Base table. LOCK TABLES, no functions/triggers. +# +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +LOCK TABLE tm1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +UNLOCK TABLES; +DROP TABLE tm1, t1; +# +# Base table. LOCK TABLES, sub-statement that is run inside a function. +# +DELIMITER |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + INSERT INTO tm1 VALUES (1); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +LOCK TABLE tm1 WRITE; +SELECT f1(); +UNLOCK TABLES; +DROP FUNCTION f1; +DROP TABLE tm1, t1; +# +# Base table. LOCK TABLES statement that locks a table that has a trigger +# that inserts into a merge table, so an attempt is made to lock tables +# of a sub-statement. +# +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +CREATE TRIGGER t2_ai AFTER INSERT ON t2 + FOR EACH ROW INSERT INTO tm1 VALUES(11); +LOCK TABLE t2 WRITE; +INSERT INTO t2 VALUES (2); +SELECT * FROM tm1; +SELECT * FROM t2; +UNLOCK TABLES; +DROP TRIGGER t2_ai; +DROP TABLE tm1, t1, t2; +# +# Temporary. No LOCK TABLES, no functions/triggers. +# +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +DROP TABLE tm1, t1; +# +# Temporary. No LOCK TABLES, sub-statement that is run inside a function. +# +DELIMITER |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + INSERT INTO tm1 VALUES (1); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +SELECT f1(); +DROP FUNCTION f1; +DROP TABLE tm1, t1; +# +# Temporary. LOCK TABLES, no functions/triggers. +# +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +CREATE TABLE t9 (c1 INT) ENGINE=MyISAM; +LOCK TABLE t9 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +UNLOCK TABLES; +DROP TABLE tm1, t1, t9; +# +# Temporary. LOCK TABLES, sub-statement that is run inside a function. +# +DELIMITER |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + INSERT INTO tm1 VALUES (1); + RETURN (SELECT MAX(c1) FROM tm1); +END| +DELIMITER ;| +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +CREATE TABLE t9 (c1 INT) ENGINE=MyISAM; +LOCK TABLE t9 WRITE; +SELECT f1(); +UNLOCK TABLES; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t9; +# +# Temporary. LOCK TABLES statement that locks a table that has a trigger +# that inserts into a merge table, so an attempt is made to lock tables +# of a sub-statement. +# +CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TRIGGER t2_ai AFTER INSERT ON t2 + FOR EACH ROW INSERT INTO tm1 VALUES(11); +LOCK TABLE t2 WRITE; +INSERT INTO t2 VALUES (2); +SELECT * FROM tm1; +SELECT * FROM t2; +UNLOCK TABLES; +DROP TRIGGER t2_ai; +DROP TABLE tm1, t1, t2; +--echo # +--echo # Don't select MERGE child when trying to get prelocked table. +--echo # +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +CREATE TRIGGER tm1_ai AFTER INSERT ON tm1 + FOR EACH ROW INSERT INTO t1 VALUES(11); +LOCK TABLE tm1 WRITE, t1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +UNLOCK TABLES; +LOCK TABLE t1 WRITE, tm1 WRITE; +INSERT INTO tm1 VALUES (1); +SELECT * FROM tm1; +UNLOCK TABLES; +DROP TRIGGER tm1_ai; +DROP TABLE tm1, t1; + +# Don't resurrect chopped off prelocked tables. +# The problem is not visible by test results; only by debugging. +# +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t3 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t4 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t5 (c1 INT) ENGINE=MyISAM; +CREATE TABLE tm1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2,t3,t4,t5) + INSERT_METHOD=LAST; +CREATE TRIGGER t2_au AFTER UPDATE ON t2 + FOR EACH ROW INSERT INTO t3 VALUES(33); +CREATE FUNCTION f1() RETURNS INT + RETURN (SELECT MAX(c1) FROM t4); +LOCK TABLE tm1 WRITE, t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE, t5 WRITE; +INSERT INTO t1 VALUES(1); +INSERT INTO t2 VALUES(2); +INSERT INTO t3 VALUES(3); +INSERT INTO t4 VALUES(4); +INSERT INTO t5 VALUES(5); + connect (con1,localhost,root,,); + send UPDATE t2, tm1 SET t2.c1=f1(); +connection default; +# Force reopen in other thread. +#sleep 1; +FLUSH TABLES; +#sleep 1; +FLUSH TABLES; +#sleep 1; +UNLOCK TABLES; + connection con1; + reap; + disconnect con1; +connection default; +SELECT * FROM tm1; +DROP TRIGGER t2_au; +DROP FUNCTION f1; +DROP TABLE tm1, t1, t2, t3, t4, t5; + + + +--echo End of 6.0 tests -- cgit v1.2.1 From 4ae05129dc51f87f3ff3528640465d2122db96f3 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 18:47:20 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2630.13.16 committer: Davi Arnaut branch nick: WL#4284 timestamp: Sat 2008-07-26 13:38:20 -0300 message: WL#4284: Transactional DDL locking SQL statements' effect on transactions. Currently the MySQL server and its storage engines are not capable of rolling back operations that define or modify data structures (also known as DDL statements) or operations that alter any of the system tables (the mysql database). Allowing these group of statements to participate in transactions is unfeasible at this time (since rollback has no effect whatsoever on them) and goes against the design of our metadata locking subsystem. The solution is to issue implicit commits before and after those statements execution. This effectively confines each of those statements to its own special transaction and ensures that metadata locks taken during this special transaction are not leaked into posterior statements/transactions. --- mysql-test/include/commit.inc | 8 +- mysql-test/include/implicit_commit_helper.inc | 5 + mysql-test/r/commit_1innodb.result | 8 +- mysql-test/r/implicit_commit.result | 1061 ++++++++++++++++++++++ mysql-test/t/implicit_commit.test | 1162 +++++++++++++++++++++++++ 5 files changed, 2236 insertions(+), 8 deletions(-) create mode 100644 mysql-test/include/implicit_commit_helper.inc create mode 100644 mysql-test/r/implicit_commit.result create mode 100644 mysql-test/t/implicit_commit.test (limited to 'mysql-test') diff --git a/mysql-test/include/commit.inc b/mysql-test/include/commit.inc index d91ba8291fd..d5f10a6d78e 100644 --- a/mysql-test/include/commit.inc +++ b/mysql-test/include/commit.inc @@ -725,15 +725,15 @@ call p_verify_status_increment(4, 4, 4, 4); alter table t3 add column (b int); call p_verify_status_increment(2, 0, 2, 0); alter table t3 rename t4; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(4, 4, 4, 4); rename table t4 to t3; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(0, 0, 0, 0); truncate table t3; call p_verify_status_increment(4, 4, 4, 4); create view v1 as select * from t2; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 0, 2, 0); check table t1; -call p_verify_status_increment(3, 0, 3, 0); +call p_verify_status_increment(2, 0, 2, 0); --echo # Sic: after this bug is fixed, CHECK leaves no pending transaction commit; call p_verify_status_increment(0, 0, 0, 0); diff --git a/mysql-test/include/implicit_commit_helper.inc b/mysql-test/include/implicit_commit_helper.inc new file mode 100644 index 00000000000..5e87b2db079 --- /dev/null +++ b/mysql-test/include/implicit_commit_helper.inc @@ -0,0 +1,5 @@ +INSERT INTO db1.trans (a) VALUES (1); +--disable_result_log +eval $statement; +--enable_result_log +CALL db1.test_if_commit(); diff --git a/mysql-test/r/commit_1innodb.result b/mysql-test/r/commit_1innodb.result index 51c4ac3002c..bbff677ab3f 100644 --- a/mysql-test/r/commit_1innodb.result +++ b/mysql-test/r/commit_1innodb.result @@ -841,11 +841,11 @@ call p_verify_status_increment(2, 0, 2, 0); SUCCESS alter table t3 rename t4; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(4, 4, 4, 4); SUCCESS rename table t4 to t3; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(0, 0, 0, 0); SUCCESS truncate table t3; @@ -853,13 +853,13 @@ call p_verify_status_increment(4, 4, 4, 4); SUCCESS create view v1 as select * from t2; -call p_verify_status_increment(1, 0, 1, 0); +call p_verify_status_increment(2, 0, 2, 0); SUCCESS check table t1; Table Op Msg_type Msg_text test.t1 check status OK -call p_verify_status_increment(3, 0, 3, 0); +call p_verify_status_increment(2, 0, 2, 0); SUCCESS # Sic: after this bug is fixed, CHECK leaves no pending transaction diff --git a/mysql-test/r/implicit_commit.result b/mysql-test/r/implicit_commit.result new file mode 100644 index 00000000000..8c330550a3b --- /dev/null +++ b/mysql-test/r/implicit_commit.result @@ -0,0 +1,1061 @@ +SET GLOBAL EVENT_SCHEDULER = OFF; +SET BINLOG_FORMAT = STATEMENT; +CREATE DATABASE db1; +USE db1; +CREATE TABLE t1 (a INT, KEY a(a)) ENGINE=INNODB; +INSERT INTO t1 VALUES (1),(2),(3),(4),(5); +CREATE TABLE t3 (a INT) ENGINE=MyISAM; +INSERT INTO t3 SELECT * FROM t1; +CREATE TABLE trans (a INT) ENGINE=INNODB; +CREATE PROCEDURE test_if_commit() +BEGIN +ROLLBACK; +SELECT IF (COUNT(*) > 0, "YES", "NO") AS "IMPLICIT COMMIT" FROM trans; +DELETE FROM trans; +COMMIT; +END| +SET AUTOCOMMIT = FALSE; +# +# SQLCOM_SELECT +# +INSERT INTO db1.trans (a) VALUES (1); +select 1 as res from t1 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_TABLE LIKE +# +INSERT INTO db1.trans (a) VALUES (1); +create table t2 like t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SHOW_CREATE +# +INSERT INTO db1.trans (a) VALUES (1); +show create table t2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DROP_TABLE +# +INSERT INTO db1.trans (a) VALUES (1); +drop table t2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CREATE_TABLE TEMPORARY +# +INSERT INTO db1.trans (a) VALUES (1); +create temporary table t2 as select * from t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DROP_TABLE TEMPORARY +# +INSERT INTO db1.trans (a) VALUES (1); +drop temporary table t2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_TABLE +# +INSERT INTO db1.trans (a) VALUES (1); +create table t2 as select * from t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_UPDATE +# +INSERT INTO db1.trans (a) VALUES (1); +update t2 set a=a+1 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_INSERT +# +INSERT INTO db1.trans (a) VALUES (1); +insert into t2 set a=((1) in (select * from t1)); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_INSERT_SELECT +# +INSERT INTO db1.trans (a) VALUES (1); +insert into t2 select * from t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_REPLACE +# +INSERT INTO db1.trans (a) VALUES (1); +replace t2 set a=((1) in (select * from t1)); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_REPLACE_SELECT +# +INSERT INTO db1.trans (a) VALUES (1); +replace t2 select * from t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DELETE +# +INSERT INTO db1.trans (a) VALUES (1); +delete from t2 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DELETE_MULTI +# +INSERT INTO db1.trans (a) VALUES (1); +delete t2, t3 from t2, t3 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_UPDATE_MULTI +# +select * from t2; +a +INSERT INTO db1.trans (a) VALUES (1); +update t2, t3 set t3.a=t2.a, t2.a=null where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_LOAD +# +create table t4 (a varchar(100)); +INSERT INTO db1.trans (a) VALUES (1); +load data infile '../../std_data/words.dat' into table t4; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +drop table t4; +# +# SQLCOM_SHOW_DATABASES +# +INSERT INTO db1.trans (a) VALUES (1); +show databases where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_TABLES +# +INSERT INTO db1.trans (a) VALUES (1); +show tables where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_FIELDS +# +INSERT INTO db1.trans (a) VALUES (1); +show fields from t1 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_KEYS +# +INSERT INTO db1.trans (a) VALUES (1); +show keys from t1 where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_VARIABLES +# +INSERT INTO db1.trans (a) VALUES (1); +show variables where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STATUS +# +INSERT INTO db1.trans (a) VALUES (1); +show status where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_ENGINE_MUTEX +# +INSERT INTO db1.trans (a) VALUES (1); +show engine all mutex; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_PROCESSLIST +# +INSERT INTO db1.trans (a) VALUES (1); +show processlist; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_ENGINE_LOGS +# +INSERT INTO db1.trans (a) VALUES (1); +show engine all logs; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_ENGINE_STATUS +# +INSERT INTO db1.trans (a) VALUES (1); +show engine all status; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_CHARSETS +# +INSERT INTO db1.trans (a) VALUES (1); +show charset where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_COLLATIONS +# +INSERT INTO db1.trans (a) VALUES (1); +show collation where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_TABLE_STATUS +# +INSERT INTO db1.trans (a) VALUES (1); +show table status where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_TRIGGERS +# +INSERT INTO db1.trans (a) VALUES (1); +show triggers where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_OPEN_TABLES +# +INSERT INTO db1.trans (a) VALUES (1); +show open tables where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STATUS_PROC +# +INSERT INTO db1.trans (a) VALUES (1); +show procedure status where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STATUS_FUNC +# +INSERT INTO db1.trans (a) VALUES (1); +show function status where (1) in (select * from t1); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SET_OPTION +# +INSERT INTO db1.trans (a) VALUES (1); +set @a=((1) in (select * from t1)); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DO +# +INSERT INTO db1.trans (a) VALUES (1); +do ((1) in (select * from t1)); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CALL +# +create procedure p1(a int) begin end; +INSERT INTO db1.trans (a) VALUES (1); +call p1((1) in (select * from t1)); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +drop procedure p1; +# +# SQLCOM_CREATE_VIEW +# +INSERT INTO db1.trans (a) VALUES (1); +create view v1 as select * from t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_VIEW +# +INSERT INTO db1.trans (a) VALUES (1); +alter view v1 as select 2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_DROP_VIEW +# +INSERT INTO db1.trans (a) VALUES (1); +drop view v1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CREATE_INDEX +# +INSERT INTO db1.trans (a) VALUES (1); +create index idx1 on t1(a); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_DROP_INDEX +# +INSERT INTO db1.trans (a) VALUES (1); +drop index idx1 on t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_TABLE +# +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 add column b int; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 change b c int; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 drop column c; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_TABLE TEMPORARY +# +create temporary table t4 (a int); +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 add column b int; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 change b c int; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +alter table t1 drop column c; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +drop table t4; +# +# SQLCOM_TRUNCATE +# +insert into t2 select * from t1; +INSERT INTO db1.trans (a) VALUES (1); +truncate table t2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +insert into t2 select * from t1; +# +# SQLCOM_TRUNCATE TEMPORARY +# +create temporary table t4 as select * from t1; +INSERT INTO db1.trans (a) VALUES (1); +truncate table t4; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +drop temporary table t4; +# +# SQLCOM_SHOW_MASTER_STAT +# +INSERT INTO db1.trans (a) VALUES (1); +show master status; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_SLAVE_STAT +# +INSERT INTO db1.trans (a) VALUES (1); +show slave status; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_GRANT +# +INSERT INTO db1.trans (a) VALUES (1); +grant all on test.t1 to mysqltest_2@localhost with grant option; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_REVOKE +# +INSERT INTO db1.trans (a) VALUES (1); +revoke select on test.t1 from mysqltest_2@localhost; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_REVOKE_ALL +# +INSERT INTO db1.trans (a) VALUES (1); +revoke all on test.t1 from mysqltest_2@localhost; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +drop user mysqltest_2@localhost; +# +# SQLCOM_SHOW_GRANTS +# +INSERT INTO db1.trans (a) VALUES (1); +show grants; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +INSERT INTO db1.trans (a) VALUES (1); +show grants for current_user(); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_LOCK_TABLES +# +INSERT INTO db1.trans (a) VALUES (1); +lock tables t1 write, trans write; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_UNLOCK_TABLES +# +INSERT INTO db1.trans (a) VALUES (1); +unlock tables; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CREATE_DB +# +INSERT INTO db1.trans (a) VALUES (1); +create database db2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CHANGE_DB +# +create table db2.t1 (a int); +insert into db2.t1 values (1); +commit; +INSERT INTO db1.trans (a) VALUES (1); +use db2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_CREATE_DB +# +INSERT INTO db1.trans (a) VALUES (1); +show create database db2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_ALTER_DB +# +# +# SQLCOM_ALTER_DB_UPGRADE +# +# +# SQLCOM_DROP_DB +# +use db1; +INSERT INTO db1.trans (a) VALUES (1); +drop database db2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_REPAIR +# +INSERT INTO db1.trans (a) VALUES (1); +repair table t2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +repair table t2 use_frm; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_OPTIMIZE +# +INSERT INTO db1.trans (a) VALUES (1); +optimize table t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CHECK +# +INSERT INTO db1.trans (a) VALUES (1); +check table t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +check table t1 extended; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ASSIGN_TO_KEYCACHE +# +set global keycache.key_buffer_size=128*1024; +INSERT INTO db1.trans (a) VALUES (1); +cache index t3 in keycache; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +set global keycache.key_buffer_size=0; +# +# SQLCOM_PRELOAD_KEYS +# +INSERT INTO db1.trans (a) VALUES (1); +load index into cache t3; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_FLUSH +# +INSERT INTO db1.trans (a) VALUES (1); +flush local privileges; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +flush privileges; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_KILL +# +# +# SQLCOM_ANALYZE +# +INSERT INTO db1.trans (a) VALUES (1); +analyze table t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ROLLBACK +# +INSERT INTO db1.trans (a) VALUES (1); +rollback; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_ROLLBACK_TO_SAVEPOINT +# +# +# SQLCOM_COMMIT +# +INSERT INTO db1.trans (a) VALUES (1); +commit; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SAVEPOINT +# +INSERT INTO db1.trans (a) VALUES (1); +savepoint sp1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_RELEASE_SAVEPOINT +# +# +# SQLCOM_SLAVE_START +# +# +# SQLCOM_SLAVE_STOP +# +# +# SQLCOM_BEGIN +# +INSERT INTO db1.trans (a) VALUES (1); +begin; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CHANGE_MASTER +# +# +# SQLCOM_RENAME_TABLE +# +INSERT INTO db1.trans (a) VALUES (1); +rename table t3 to t4; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +INSERT INTO db1.trans (a) VALUES (1); +rename table t4 to t3; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_RESET +# +# +# SQLCOM_PURGE +# +# +# SQLCOM_PURGE_BEFORE +# +# +# SQLCOM_SHOW_BINLOGS +# +# +# SQLCOM_HA_OPEN +# +INSERT INTO db1.trans (a) VALUES (1); +handler t1 open as ha1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_HA_READ +# +INSERT INTO db1.trans (a) VALUES (1); +handler ha1 read a first; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_HA_CLOSE +# +INSERT INTO db1.trans (a) VALUES (1); +handler ha1 close; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_SLAVE_HOSTS +# +INSERT INTO db1.trans (a) VALUES (1); +show slave hosts; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_BINLOG_EVENTS +# +INSERT INTO db1.trans (a) VALUES (1); +show binlog events; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_NEW_MASTER +# +# +# SQLCOM_SHOW_WARNS +# +INSERT INTO db1.trans (a) VALUES (1); +show warnings; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_EMPTY_QUERY +# +# +# SQLCOM_SHOW_ERRORS +# +INSERT INTO db1.trans (a) VALUES (1); +show errors; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STORAGE_ENGINES +# +INSERT INTO db1.trans (a) VALUES (1); +show engines; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_PRIVILEGES +# +INSERT INTO db1.trans (a) VALUES (1); +show privileges; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_HELP +# +INSERT INTO db1.trans (a) VALUES (1); +help 'foo'; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_USER +# +INSERT INTO db1.trans (a) VALUES (1); +create user trxusr1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_RENAME_USER +# +INSERT INTO db1.trans (a) VALUES (1); +rename user 'trxusr1' to 'trxusr2'; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_DROP_USER +# +INSERT INTO db1.trans (a) VALUES (1); +drop user trxusr2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CHECKSUM +# +INSERT INTO db1.trans (a) VALUES (1); +checksum table t1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_PROCEDURE +# +INSERT INTO db1.trans (a) VALUES (1); +create procedure p1(a int) begin end; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_PROCEDURE +# +INSERT INTO db1.trans (a) VALUES (1); +alter procedure p1 comment 'foobar'; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SHOW_CREATE_PROC +# +INSERT INTO db1.trans (a) VALUES (1); +show create procedure p1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STATUS_PROC +# +INSERT INTO db1.trans (a) VALUES (1); +show procedure status; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_PROC_CODE +# +INSERT INTO db1.trans (a) VALUES (1); +show procedure code p1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DROP_PROCEDURE +# +INSERT INTO db1.trans (a) VALUES (1); +drop procedure p1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_CREATE_FUNCTION +# +# +# SQLCOM_DROP_FUNCTION +# +# +# SQLCOM_CREATE_SPFUNCTION +# +INSERT INTO db1.trans (a) VALUES (1); +create function f1() returns int return 69; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_FUNCTION +# +INSERT INTO db1.trans (a) VALUES (1); +alter function f1 comment 'comment'; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SHOW_CREATE_FUNC +# +INSERT INTO db1.trans (a) VALUES (1); +show create function f1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_STATUS_FUNC +# +INSERT INTO db1.trans (a) VALUES (1); +show function status like '%f%'; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_FUNC_CODE +# +INSERT INTO db1.trans (a) VALUES (1); +show function code f1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_PREPARE +# +INSERT INTO db1.trans (a) VALUES (1); +prepare stmt1 from "insert into t1 values (5)"; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_EXECUTE +# +INSERT INTO db1.trans (a) VALUES (1); +execute stmt1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DEALLOCATE_PREPARE +# +INSERT INTO db1.trans (a) VALUES (1); +deallocate prepare stmt1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_TRIGGER +# +INSERT INTO db1.trans (a) VALUES (1); +create trigger trg1 before insert on t1 for each row set @a:=1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SHOW_CREATE_TRIGGER +# +INSERT INTO db1.trans (a) VALUES (1); +show create trigger trg1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DROP_TRIGGER +# +INSERT INTO db1.trans (a) VALUES (1); +drop trigger trg1; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_XA_START +# +# +# SQLCOM_XA_END +# +# +# SQLCOM_XA_PREPARE +# +# +# SQLCOM_XA_COMMIT +# +# +# SQLCOM_XA_ROLLBACK +# +# +# SQLCOM_XA_RECOVER +# +# +# SQLCOM_ALTER_TABLESPACE +# +# +# SQLCOM_INSTALL_PLUGIN +# +# +# SQLCOM_SHOW_PLUGINS +# +# +# SQLCOM_UNINSTALL_PLUGIN +# +# +# SQLCOM_SHOW_AUTHORS +# +INSERT INTO db1.trans (a) VALUES (1); +show authors; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_BINLOG_BASE64_EVENT +# +# +# SQLCOM_SHOW_CONTRIBUTORS +# +INSERT INTO db1.trans (a) VALUES (1); +show contributors; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_CREATE_SERVER +# +# +# SQLCOM_ALTER_SERVER +# +# +# SQLCOM_DROP_SERVER +# +# +# SQLCOM_CREATE_EVENT +# +INSERT INTO db1.trans (a) VALUES (1); +create event ev1 on schedule every 1 second do insert into t1 values (6); +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_ALTER_EVENT +# +INSERT INTO db1.trans (a) VALUES (1); +alter event ev1 rename to ev2 disable; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_SHOW_CREATE_EVENT +# +INSERT INTO db1.trans (a) VALUES (1); +show create event ev2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_EVENTS +# +INSERT INTO db1.trans (a) VALUES (1); +show events; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_DROP_EVENT +# +INSERT INTO db1.trans (a) VALUES (1); +drop event ev2; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +YES +# +# SQLCOM_BACKUP +# +# +# SQLCOM_SHOW_ARCHIVE +# +# +# SQLCOM_RESTORE +# +# +# SQLCOM_BACKUP_TEST +# +# +# SQLCOM_SHOW_PROFILE +# +INSERT INTO db1.trans (a) VALUES (1); +show profile memory; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +# +# SQLCOM_SHOW_PROFILES +# +INSERT INTO db1.trans (a) VALUES (1); +show profiles; +CALL db1.test_if_commit(); +IMPLICIT COMMIT +NO +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; +USE test; +DROP DATABASE db1; +End of tests diff --git a/mysql-test/t/implicit_commit.test b/mysql-test/t/implicit_commit.test new file mode 100644 index 00000000000..ed451877c77 --- /dev/null +++ b/mysql-test/t/implicit_commit.test @@ -0,0 +1,1162 @@ +source include/have_innodb.inc; +source include/have_log_bin.inc; + +SET GLOBAL EVENT_SCHEDULER = OFF; +SET BINLOG_FORMAT = STATEMENT; + +LET $OLD_DB= `SELECT DATABASE()`; + +CREATE DATABASE db1; +USE db1; +CREATE TABLE t1 (a INT, KEY a(a)) ENGINE=INNODB; +INSERT INTO t1 VALUES (1),(2),(3),(4),(5); +CREATE TABLE t3 (a INT) ENGINE=MyISAM; +INSERT INTO t3 SELECT * FROM t1; +CREATE TABLE trans (a INT) ENGINE=INNODB; + +DELIMITER |; + +CREATE PROCEDURE test_if_commit() +BEGIN + ROLLBACK; + SELECT IF (COUNT(*) > 0, "YES", "NO") AS "IMPLICIT COMMIT" FROM trans; + DELETE FROM trans; + COMMIT; +END| + +DELIMITER ;| + +SET AUTOCOMMIT = FALSE; + +--echo # +--echo # SQLCOM_SELECT +--echo # + +let $statement= + select 1 as res from t1 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_TABLE LIKE +--echo # + +let $statement= + create table t2 like t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE +--echo # + +let $statement= + show create table t2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_TABLE +--echo # + +let $statement= + drop table t2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_TABLE TEMPORARY +--echo # + +let $statement= + create temporary table t2 as select * from t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_TABLE TEMPORARY +--echo # + +let $statement= + drop temporary table t2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_TABLE +--echo # + +let $statement= + create table t2 as select * from t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_UPDATE +--echo # + +let $statement= + update t2 set a=a+1 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_INSERT +--echo # + +let $statement= + insert into t2 set a=((1) in (select * from t1)); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_INSERT_SELECT +--echo # + +let $statement= + insert into t2 select * from t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_REPLACE +--echo # + +let $statement= + replace t2 set a=((1) in (select * from t1)); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_REPLACE_SELECT +--echo # + +let $statement= + replace t2 select * from t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DELETE +--echo # + +let $statement= + delete from t2 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DELETE_MULTI +--echo # + +let $statement= + delete t2, t3 from t2, t3 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_UPDATE_MULTI +--echo # + +select * from t2; +let $statement= + update t2, t3 set t3.a=t2.a, t2.a=null where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_LOAD +--echo # + +create table t4 (a varchar(100)); + +let $statement= + load data infile '../../std_data/words.dat' into table t4; +source include/implicit_commit_helper.inc; + +drop table t4; + +--echo # +--echo # SQLCOM_SHOW_DATABASES +--echo # + +let $statement= + show databases where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_TABLES +--echo # + +let $statement= + show tables where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_FIELDS +--echo # + +let $statement= + show fields from t1 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_KEYS +--echo # + +let $statement= + show keys from t1 where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_VARIABLES +--echo # + +let $statement= + show variables where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STATUS +--echo # + +let $statement= + show status where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_ENGINE_MUTEX +--echo # + +let $statement= + show engine all mutex; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_PROCESSLIST +--echo # + +let $statement= + show processlist; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_ENGINE_LOGS +--echo # + +let $statement= + show engine all logs; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_ENGINE_STATUS +--echo # + +let $statement= + show engine all status; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CHARSETS +--echo # + +let $statement= + show charset where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_COLLATIONS +--echo # + +let $statement= + show collation where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_TABLE_STATUS +--echo # + +let $statement= + show table status where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_TRIGGERS +--echo # + +let $statement= + show triggers where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_OPEN_TABLES +--echo # + +let $statement= + show open tables where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STATUS_PROC +--echo # + +let $statement= + show procedure status where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STATUS_FUNC +--echo # + +let $statement= + show function status where (1) in (select * from t1); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SET_OPTION +--echo # + +let $statement= + set @a=((1) in (select * from t1)); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DO +--echo # + +let $statement= + do ((1) in (select * from t1)); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CALL +--echo # + +create procedure p1(a int) begin end; + +let $statement= + call p1((1) in (select * from t1)); +source include/implicit_commit_helper.inc; + +drop procedure p1; + +--echo # +--echo # SQLCOM_CREATE_VIEW +--echo # + +let $statement= + create view v1 as select * from t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_VIEW +--echo # + +let $statement= + alter view v1 as select 2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_VIEW +--echo # + +let $statement= + drop view v1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_INDEX +--echo # + +let $statement= + create index idx1 on t1(a); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_INDEX +--echo # + +let $statement= + drop index idx1 on t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_TABLE +--echo # + +let $statement= + alter table t1 add column b int; +source include/implicit_commit_helper.inc; + +let $statement= + alter table t1 change b c int; +source include/implicit_commit_helper.inc; + +let $statement= + alter table t1 drop column c; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_TABLE TEMPORARY +--echo # + +create temporary table t4 (a int); + +let $statement= + alter table t1 add column b int; +source include/implicit_commit_helper.inc; + +let $statement= + alter table t1 change b c int; +source include/implicit_commit_helper.inc; + +let $statement= + alter table t1 drop column c; +source include/implicit_commit_helper.inc; + +drop table t4; + +--echo # +--echo # SQLCOM_TRUNCATE +--echo # + +insert into t2 select * from t1; +let $statement= + truncate table t2; +source include/implicit_commit_helper.inc; +insert into t2 select * from t1; + +--echo # +--echo # SQLCOM_TRUNCATE TEMPORARY +--echo # + +create temporary table t4 as select * from t1; +let $statement= + truncate table t4; +source include/implicit_commit_helper.inc; +drop temporary table t4; + +--echo # +--echo # SQLCOM_SHOW_MASTER_STAT +--echo # + +let $statement= + show master status; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_SLAVE_STAT +--echo # + +let $statement= + show slave status; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_GRANT +--echo # + +let $statement= + grant all on test.t1 to mysqltest_2@localhost with grant option; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_REVOKE +--echo # +let $statement= + revoke select on test.t1 from mysqltest_2@localhost; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_REVOKE_ALL +--echo # + +let $statement= + revoke all on test.t1 from mysqltest_2@localhost; +source include/implicit_commit_helper.inc; + +drop user mysqltest_2@localhost; + +--echo # +--echo # SQLCOM_SHOW_GRANTS +--echo # + +let $statement= + show grants; +source include/implicit_commit_helper.inc; + +let $statement= + show grants for current_user(); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_LOCK_TABLES +--echo # + +let $statement= + lock tables t1 write, trans write; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_UNLOCK_TABLES +--echo # + +let $statement= + unlock tables; +source include/implicit_commit_helper.inc; + +# +# Missing test for lock tables transactional. +# + +--echo # +--echo # SQLCOM_CREATE_DB +--echo # + +let $statement= + create database db2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CHANGE_DB +--echo # + +create table db2.t1 (a int); +insert into db2.t1 values (1); +commit; + +let $statement= + use db2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE_DB +--echo # + +let $statement= + show create database db2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_DB +--echo # + +#let $statement= +# alter database db2 character set koi8r; +#source include/implicit_commit_helper.inc; + +#let $statement= +# alter database db2 collate cp1251_general_cs; +#source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_DB_UPGRADE +--echo # + +#let $statement= +# alter database `#mysql50#db3` upgrade data directory name; +#source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_DB +--echo # + +use db1; + +let $statement= + drop database db2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_REPAIR +--echo # + +let $statement= + repair table t2; +source include/implicit_commit_helper.inc; + +let $statement= + repair table t2 use_frm; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_OPTIMIZE +--echo # + +let $statement= + optimize table t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CHECK +--echo # + +let $statement= + check table t1; +source include/implicit_commit_helper.inc; + +let $statement= + check table t1 extended; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ASSIGN_TO_KEYCACHE +--echo # + +set global keycache.key_buffer_size=128*1024; + +let $statement= + cache index t3 in keycache; +source include/implicit_commit_helper.inc; + +set global keycache.key_buffer_size=0; + +--echo # +--echo # SQLCOM_PRELOAD_KEYS +--echo # + +let $statement= + load index into cache t3; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_FLUSH +--echo # + +let $statement= + flush local privileges; +source include/implicit_commit_helper.inc; + +let $statement= + flush privileges; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_KILL +--echo # + +--echo # +--echo # SQLCOM_ANALYZE +--echo # + +let $statement= + analyze table t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ROLLBACK +--echo # + +let $statement= + rollback; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ROLLBACK_TO_SAVEPOINT +--echo # + + +--echo # +--echo # SQLCOM_COMMIT +--echo # + +let $statement= + commit; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SAVEPOINT +--echo # + +let $statement= + savepoint sp1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_RELEASE_SAVEPOINT +--echo # + +--echo # +--echo # SQLCOM_SLAVE_START +--echo # + +--echo # +--echo # SQLCOM_SLAVE_STOP +--echo # + +--echo # +--echo # SQLCOM_BEGIN +--echo # + +let $statement= + begin; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CHANGE_MASTER +--echo # + +--echo # +--echo # SQLCOM_RENAME_TABLE +--echo # + +let $statement= + rename table t3 to t4; +source include/implicit_commit_helper.inc; + +let $statement= + rename table t4 to t3; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_RESET +--echo # + +--echo # +--echo # SQLCOM_PURGE +--echo # + +--echo # +--echo # SQLCOM_PURGE_BEFORE +--echo # + +--echo # +--echo # SQLCOM_SHOW_BINLOGS +--echo # + +--echo # +--echo # SQLCOM_HA_OPEN +--echo # + +let $statement= + handler t1 open as ha1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_HA_READ +--echo # + +let $statement= + handler ha1 read a first; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_HA_CLOSE +--echo # + +let $statement= + handler ha1 close; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_SLAVE_HOSTS +--echo # + +let $statement= + show slave hosts; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_BINLOG_EVENTS +--echo # + +let $statement= + show binlog events; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_NEW_MASTER +--echo # + +--echo # +--echo # SQLCOM_SHOW_WARNS +--echo # + +let $statement= + show warnings; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_EMPTY_QUERY +--echo # + +--echo # +--echo # SQLCOM_SHOW_ERRORS +--echo # + +let $statement= + show errors; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STORAGE_ENGINES +--echo # + +let $statement= + show engines; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_PRIVILEGES +--echo # + +let $statement= + show privileges; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_HELP +--echo # + +let $statement= + help 'foo'; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_USER +--echo # + +let $statement= + create user trxusr1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_RENAME_USER +--echo # + +let $statement= + rename user 'trxusr1' to 'trxusr2'; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_USER +--echo # + +let $statement= + drop user trxusr2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CHECKSUM +--echo # + +let $statement= + checksum table t1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_PROCEDURE +--echo # + +let $statement= + create procedure p1(a int) begin end; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_PROCEDURE +--echo # + +let $statement= + alter procedure p1 comment 'foobar'; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE_PROC +--echo # + +let $statement= + show create procedure p1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STATUS_PROC +--echo # + +let $statement= + show procedure status; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_PROC_CODE +--echo # + +# +# Available only on servers with debugging support. +# + +--disable_abort_on_error +let $statement= + show procedure code p1; +source include/implicit_commit_helper.inc; +--enable_abort_on_error + +--echo # +--echo # SQLCOM_DROP_PROCEDURE +--echo # + +let $statement= + drop procedure p1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_FUNCTION +--echo # + +--echo # +--echo # SQLCOM_DROP_FUNCTION +--echo # + +--echo # +--echo # SQLCOM_CREATE_SPFUNCTION +--echo # + +let $statement= + create function f1() returns int return 69; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_FUNCTION +--echo # + +let $statement= + alter function f1 comment 'comment'; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE_FUNC +--echo # + +let $statement= + show create function f1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_STATUS_FUNC +--echo # + +let $statement= + show function status like '%f%'; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_FUNC_CODE +--echo # + +# +# Available only on servers with debugging support. +# + +--disable_abort_on_error +let $statement= + show function code f1; +source include/implicit_commit_helper.inc; +--enable_abort_on_error + +--echo # +--echo # SQLCOM_PREPARE +--echo # + +let $statement= + prepare stmt1 from "insert into t1 values (5)"; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_EXECUTE +--echo # + +let $statement= + execute stmt1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DEALLOCATE_PREPARE +--echo # + +let $statement= + deallocate prepare stmt1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_TRIGGER +--echo # + +let $statement= + create trigger trg1 before insert on t1 for each row set @a:=1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE_TRIGGER +--echo # + +let $statement= + show create trigger trg1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_TRIGGER +--echo # + +let $statement= + drop trigger trg1; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_XA_START +--echo # + +--echo # +--echo # SQLCOM_XA_END +--echo # + +--echo # +--echo # SQLCOM_XA_PREPARE +--echo # + +--echo # +--echo # SQLCOM_XA_COMMIT +--echo # + +--echo # +--echo # SQLCOM_XA_ROLLBACK +--echo # + +--echo # +--echo # SQLCOM_XA_RECOVER +--echo # + +--echo # +--echo # SQLCOM_ALTER_TABLESPACE +--echo # + +--echo # +--echo # SQLCOM_INSTALL_PLUGIN +--echo # + +--echo # +--echo # SQLCOM_SHOW_PLUGINS +--echo # + +--echo # +--echo # SQLCOM_UNINSTALL_PLUGIN +--echo # + +--echo # +--echo # SQLCOM_SHOW_AUTHORS +--echo # + +let $statement= + show authors; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_BINLOG_BASE64_EVENT +--echo # + +--echo # +--echo # SQLCOM_SHOW_CONTRIBUTORS +--echo # + +let $statement= + show contributors; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_CREATE_SERVER +--echo # + +--echo # +--echo # SQLCOM_ALTER_SERVER +--echo # + +--echo # +--echo # SQLCOM_DROP_SERVER +--echo # + +--echo # +--echo # SQLCOM_CREATE_EVENT +--echo # + +let $statement= + create event ev1 on schedule every 1 second do insert into t1 values (6); +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_ALTER_EVENT +--echo # + +let $statement= + alter event ev1 rename to ev2 disable; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_CREATE_EVENT +--echo # + +let $statement= + show create event ev2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_EVENTS +--echo # + +let $statement= + show events; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_DROP_EVENT +--echo # + +let $statement= + drop event ev2; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_BACKUP +--echo # + +#create database backup_db; +# +#let $statement= +# backup database db1 to 'backup_db1.ba'; +#source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_ARCHIVE +--echo # + +# +# --error ER_NOT_ALLOWED_COMMAND +# +#let $statement= +# show backup 'backup_db1.ba'; +#source include/implicit_commit_helper.inc; +# + +--echo # +--echo # SQLCOM_RESTORE +--echo # + +#let $statement= +# restore from 'backup_db1.ba'; +#source include/implicit_commit_helper.inc; + +#--remove_file $MYSQLTEST_VARDIR/master-data/backup_db1.ba +# +#drop database backup_db; + +--echo # +--echo # SQLCOM_BACKUP_TEST +--echo # + +# BACKUP_TEST + +--echo # +--echo # SQLCOM_SHOW_PROFILE +--echo # + +let $statement= + show profile memory; +source include/implicit_commit_helper.inc; + +--echo # +--echo # SQLCOM_SHOW_PROFILES +--echo # + +let $statement= + show profiles; +source include/implicit_commit_helper.inc; + +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; +eval USE $OLD_DB; +DROP DATABASE db1; + +--echo End of tests -- cgit v1.2.1 From 3a8a7509199b15c26a88bab5ed97ec093a29f387 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 18:52:21 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2630.13.17 committer: Davi Arnaut branch nick: 4284-6.0 timestamp: Sun 2008-07-27 10:14:46 -0300 message: Post-merge fixes: Remove dependency on binlog, require not embedded as test uses the event scheduler and disable abort on error for syntax only available on servers built with debugging support. This is a patch in scope of WL#4284 "Transactional DDL locking" --- mysql-test/t/implicit_commit.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'mysql-test') diff --git a/mysql-test/t/implicit_commit.test b/mysql-test/t/implicit_commit.test index ed451877c77..d8ffd6e9452 100644 --- a/mysql-test/t/implicit_commit.test +++ b/mysql-test/t/implicit_commit.test @@ -1,5 +1,5 @@ source include/have_innodb.inc; -source include/have_log_bin.inc; +source include/not_embedded.inc; SET GLOBAL EVENT_SCHEDULER = OFF; SET BINLOG_FORMAT = STATEMENT; -- cgit v1.2.1 From c44665aeb1b45bed1f21bce8faeca7525e73d672 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 3 Dec 2009 23:08:27 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 3035.4.1 committer: Davi Arnaut branch nick: 39897-6.0 timestamp: Thu 2009-01-15 12:17:57 -0200 message: Bug#39897: lock_multi fails in pushbuild: timeout waiting for processlist The problem is that relying on the "Table lock" thread state in its current position to detect that a thread is waiting on a lock is race prone. The "Table lock" state change happens before the thread actually tries to grab a lock on a table. The solution is to move the "Table lock" state so that its set only when a thread is actually going to wait for a lock. The state change happens after the thread fails to grab the lock (because it is owned by other thread) and proceeds to wait on a condition. This is considered part of work related to WL#4284 "Transactional DDL locking" Warning: this patch contains an incompatible change. When waiting on a lock in thr_lock.c, the server used to display "Locked" processlist state. After this patch, the state is "Table lock". The new state was actually intended to be display since year 2002, when Monty added it. But up until removal of thd->locked boolean member, this state was ignored by SHOW PROCESSLIST code. --- mysql-test/r/lock_multi.result | 2 +- mysql-test/r/sp-threads.result | 2 +- mysql-test/t/delayed.test | 2 +- mysql-test/t/insert_notembedded.test | 2 +- mysql-test/t/lock_multi.test | 28 ++++++++++++++-------------- mysql-test/t/lock_sync.test | 2 +- mysql-test/t/multi_update.test | 4 ++-- mysql-test/t/query_cache_28249.test | 6 +++--- mysql-test/t/sp_notembedded.test | 2 +- mysql-test/t/status.test | 2 +- 10 files changed, 26 insertions(+), 26 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 3f1165fd069..930983bae27 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -203,7 +203,7 @@ drop table if exists t1,t2; create table t1 (a int); flush status; lock tables t1 read; -insert into t1 values(1);; +insert into t1 values(1); unlock tables; drop table t1; select @tlwa < @tlwb; diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result index 953830ecc87..d974cfb9605 100644 --- a/mysql-test/r/sp-threads.result +++ b/mysql-test/r/sp-threads.result @@ -35,7 +35,7 @@ call bug9486(); show processlist; Id User Host db Command Time State Info # root localhost test Sleep # NULL -# root localhost test Query # Locked update t1, t2 set val= 1 where id1=id2 +# root localhost test Query # Table lock update t1, t2 set val= 1 where id1=id2 # root localhost test Query # NULL show processlist # root localhost test Sleep # NULL unlock tables; diff --git a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test index ee658d70d16..86311784b6a 100644 --- a/mysql-test/t/delayed.test +++ b/mysql-test/t/delayed.test @@ -307,7 +307,7 @@ connection update; connection default; let $wait_condition= select count(*) = 1 from information_schema.processlist - where command = "Delayed insert" and state = "upgrading lock"; + where command = "Delayed insert" and state = "Table lock"; --source include/wait_condition.inc connect (select,localhost,root,,); --echo connection: select diff --git a/mysql-test/t/insert_notembedded.test b/mysql-test/t/insert_notembedded.test index 2950acff3cc..510dc56e8f7 100644 --- a/mysql-test/t/insert_notembedded.test +++ b/mysql-test/t/insert_notembedded.test @@ -174,7 +174,7 @@ connection default; # we must wait till the insert opens and locks the table let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and id = $ID; + where state = "Table lock" and id = $ID; --source include/wait_condition.inc connect (select,localhost,root,,); --echo connection: select diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index 58f46941920..c82e351dfef 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -22,7 +22,7 @@ connection reader; # Sleep a bit till the update of connection writer is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "update low_priority t1 set n = 4"; + where state = "Table lock" and info = "update low_priority t1 set n = 4"; --source include/wait_condition.inc send select n from t1; @@ -30,7 +30,7 @@ connection locker; # Sleep a bit till the select of connection reader is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "select n from t1"; + where state = "Table lock" and info = "select n from t1"; --source include/wait_condition.inc unlock tables; connection writer; @@ -50,7 +50,7 @@ connection reader; # Sleep a bit till the update of connection writer is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "update low_priority t1 set n = 4"; + where state = "Table lock" and info = "update low_priority t1 set n = 4"; --source include/wait_condition.inc select n from t1; connection locker; @@ -95,7 +95,7 @@ insert t1 select * from t2; connection locker; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "insert t1 select * from t2"; + where state = "Table lock" and info = "insert t1 select * from t2"; --source include/wait_condition.inc drop table t2; connection reader; @@ -119,7 +119,7 @@ connection locker; # Sleep a bit till the insert of connection reader is in work and hangs let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "insert t1 select * from t2"; + where state = "Table lock" and info = "insert t1 select * from t2"; --source include/wait_condition.inc drop table t2; connection reader; @@ -163,8 +163,8 @@ SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1; connection locker; # Sleep a bit till the select of connection reader is in work and hangs let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE state = "Table lock" AND info = "SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1"; --source include/wait_condition.inc # Make test case independent from earlier grants. @@ -298,7 +298,7 @@ connection reader; # Wait till connection writer is blocked let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "alter table t1 auto_increment=0"; + where state = "Table lock" and info = "alter table t1 auto_increment=0"; --source include/wait_condition.inc send alter table t1 auto_increment=0; @@ -306,7 +306,7 @@ connection locker; # Wait till connection reader is blocked let $wait_condition= select count(*) = 2 from information_schema.processlist - where state = "Locked" and info = "alter table t1 auto_increment=0"; + where state = "Table lock" and info = "alter table t1 auto_increment=0"; --source include/wait_condition.inc unlock tables; connection writer; @@ -461,16 +461,16 @@ update t1 set i= 10; connection reader; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "update t1 set i= 10"; + where state = "Table lock" and info = "update t1 set i= 10"; --source include/wait_condition.inc send select * from t1; connection default; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "select * from t1"; + where state = "Table lock" and info = "select * from t1"; --source include/wait_condition.inc -let $ID= `select id from information_schema.processlist where state = "Locked" and info = "update t1 set i= 10"`; +let $ID= `select id from information_schema.processlist where state = "Table lock" and info = "update t1 set i= 10"`; --replace_result $ID ID eval kill query $ID; connection reader; @@ -613,11 +613,11 @@ lock tables t1 read; let $tlwa= `show status like 'Table_locks_waited'`; connect (waiter,localhost,root,,); connection waiter; ---send insert into t1 values(1); +send insert into t1 values(1); connection default; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and info = "insert into t1 values(1)"; + where state = "Table lock" and info = "insert into t1 values(1)"; --source include/wait_condition.inc let $tlwb= `show status like 'Table_locks_waited'`; unlock tables; diff --git a/mysql-test/t/lock_sync.test b/mysql-test/t/lock_sync.test index 73289685114..de8a1d7e43e 100644 --- a/mysql-test/t/lock_sync.test +++ b/mysql-test/t/lock_sync.test @@ -71,7 +71,7 @@ set debug_sync= 'now WAIT_FOR parked'; connection default; --echo # Wait until this LOCK TABLES statement starts waiting for table lock. let $wait_condition= select count(*)= 1 from information_schema.processlist - where state= 'Locked' and + where state= 'Table lock' and info='lock table t1 write'; --source include/wait_condition.inc --echo # Allow SELECT ... FOR UPDATE to resume. diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index fc37fd6a27d..6f8cc94e6b7 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -496,7 +496,7 @@ connection updater; # Wait till "alter table t1 ..." of session changer is in work. # = There is one session is in state "Locked". let $wait_condition= select count(*)= 1 from information_schema.processlist - where state= 'Locked'; + where state= 'Table lock'; --source include/wait_condition.inc send update t1, v1 set t1.b=t1.a+t1.b+v1.b where t1.a=v1.a; @@ -507,7 +507,7 @@ connection locker; # are in work. # = There are two session is in state "Locked". let $wait_condition= select count(*)= 2 from information_schema.processlist - where state= 'Locked'; + where state= 'Table lock'; --source include/wait_condition.inc unlock tables; diff --git a/mysql-test/t/query_cache_28249.test b/mysql-test/t/query_cache_28249.test index 390a1ce6e3d..c95d7553988 100644 --- a/mysql-test/t/query_cache_28249.test +++ b/mysql-test/t/query_cache_28249.test @@ -58,18 +58,18 @@ connection user3; # Typical information_schema.processlist content after sufficient sleep time # ID USER COMMAND TIME STATE INFO # .... -# 2 root Query 5 Locked SELECT *, (SELECT COUNT(*) FROM t2) FROM t1 +# 2 root Query 5 Table lock SELECT *, (SELECT COUNT(*) FROM t2) FROM t1 # .... # XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # The values marked with 'X' must be reached. --echo # Poll till the select of connection user1 is blocked by the write lock on t1. let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist -WHERE state = 'Locked' +WHERE state = 'Table lock' AND info = '$select_for_qc'; --source include/wait_condition.inc eval SELECT user,command,state,info FROM information_schema.processlist -WHERE state = 'Locked' +WHERE state = 'Table lock' AND info = '$select_for_qc'; INSERT INTO t1 VALUES (4); diff --git a/mysql-test/t/sp_notembedded.test b/mysql-test/t/sp_notembedded.test index f593e184ad2..f7984952e33 100644 --- a/mysql-test/t/sp_notembedded.test +++ b/mysql-test/t/sp_notembedded.test @@ -322,7 +322,7 @@ set session low_priority_updates=on; connection rl_wait; let $wait_condition= select count(*) = 1 from information_schema.processlist - where state = "Locked" and + where state = "Table lock" and info = "update t1 set value='updated' where value='old'"; --source include/wait_condition.inc diff --git a/mysql-test/t/status.test b/mysql-test/t/status.test index f951218f5c8..cca54f6c762 100644 --- a/mysql-test/t/status.test +++ b/mysql-test/t/status.test @@ -58,7 +58,7 @@ let $ID= `select connection_id()`; connection con2; --echo # Switched to connection: con2 # wait for the other query to start executing -let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Locked"; +let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Table lock"; --source include/wait_condition.inc unlock tables; -- cgit v1.2.1 From 911c673edf45616137d71f43f472be410ecb7511 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 4 Dec 2009 02:29:40 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2617.23.18 committer: Davi Arnaut branch nick: 4284-6.0 timestamp: Mon 2009-03-02 18:18:26 -0300 message: Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order WL#4284: Transactional DDL locking This is a prerequisite patch: These changes are intended to split lock requests from granted locks and to allow the memory and lifetime of granted locks to be managed within the MDL subsystem. Furthermore, tickets can now be shared and therefore are used to satisfy multiple lock requests, but only shared locks can be recursive. The problem is that the MDL subsystem morphs lock requests into granted locks locks but does not manage the memory and lifetime of lock requests, and hence, does not manage the memory of granted locks either. This can be problematic because it puts the burden of tracking references on the users of the subsystem and it can't be easily done in transactional contexts where the locks have to be kept around for the duration of a transaction. Another issue is that recursive locks (when the context trying to acquire a lock already holds a lock on the same object) requires that each time the lock is granted, a unique lock request/granted lock structure structure must be kept around until the lock is released. This can lead to memory leaks in transactional contexts as locks taken during the transaction should only be released at the end of the transaction. This also leads to unnecessary wake ups (broadcasts) in the MDL subsystem if the context still holds a equivalent of the lock being released. These issues are exacerbated due to the fact that WL#4284 low-level design says that the implementation should "2) Store metadata locks in transaction memory root, rather than statement memory root" but this is not possible because a memory root, as implemented in mysys, requires all objects allocated from it to be freed all at once. This patch combines review input and significant code contributions from Konstantin Osipov (kostja) and Dmitri Lenev (dlenev). --- mysql-test/r/mdl_sync.result | 21 ++++++++++++++ mysql-test/t/mdl_sync.test | 69 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 mysql-test/r/mdl_sync.result create mode 100644 mysql-test/t/mdl_sync.test (limited to 'mysql-test') diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result new file mode 100644 index 00000000000..37f18746c57 --- /dev/null +++ b/mysql-test/r/mdl_sync.result @@ -0,0 +1,21 @@ +SET DEBUG_SYNC= 'RESET'; +drop table if exists t1,t2,t3; +create table t1 (i int); +create table t2 (i int); +connection: default +lock tables t2 read; +connection: con1 +set debug_sync='mdl_upgrade_shared_lock_to_exclusive SIGNAL parked WAIT_FOR go'; +alter table t1 rename t3; +connection: default +set debug_sync= 'now WAIT_FOR parked'; +connection: con2 +set debug_sync='mdl_acquire_exclusive_locks_wait SIGNAL go'; +drop table t1,t2; +connection: con1 +connection: default +unlock tables; +connection: con2 +ERROR 42S02: Unknown table 't1' +drop table t3; +SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test new file mode 100644 index 00000000000..ed03a9a5cb0 --- /dev/null +++ b/mysql-test/t/mdl_sync.test @@ -0,0 +1,69 @@ +# +# We need the Debug Sync Facility. +# +--source include/have_debug_sync.inc + +# Clean up resources used in this test case. +--disable_warnings +SET DEBUG_SYNC= 'RESET'; +--enable_warnings + +# +# Test the case of when a exclusive lock request waits for a +# shared lock being upgraded to a exclusive lock. +# + +connect (con1,localhost,root,,test,,); +connect (con2,localhost,root,,test,,); +connect (con3,localhost,root,,test,,); + +connection default; + +--disable_warnings +drop table if exists t1,t2,t3; +--enable_warnings + +create table t1 (i int); +create table t2 (i int); + +--echo connection: default +lock tables t2 read; + +connection con1; +--echo connection: con1 +set debug_sync='mdl_upgrade_shared_lock_to_exclusive SIGNAL parked WAIT_FOR go'; +--send alter table t1 rename t3 + +connection default; +--echo connection: default +set debug_sync= 'now WAIT_FOR parked'; + +connection con2; +--echo connection: con2 +set debug_sync='mdl_acquire_exclusive_locks_wait SIGNAL go'; +--send drop table t1,t2 + +connection con1; +--echo connection: con1 +--reap + +connection default; +--echo connection: default +unlock tables; + +connection con2; +--echo connection: con2 +--error ER_BAD_TABLE_ERROR +--reap + +connection default; +drop table t3; + +disconnect con1; +disconnect con2; +disconnect con3; + +# Clean up resources used in this test case. +--disable_warnings +SET DEBUG_SYNC= 'RESET'; +--enable_warnings -- cgit v1.2.1 From 0b39c189baf40a9f0f9bc1588a31947cb941bb1a Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Sat, 5 Dec 2009 02:02:48 +0300 Subject: Backport of revno ## 2617.31.1, 2617.31.3, 2617.31.4, 2617.31.5, 2617.31.12, 2617.31.15, 2617.31.15, 2617.31.16, 2617.43.1 - initial changeset that introduced the fix for Bug#989 and follow up fixes for all test suite failures introduced in the initial changeset. ------------------------------------------------------------ revno: 2617.31.1 committer: Davi Arnaut branch nick: 4284-6.0 timestamp: Fri 2009-03-06 19:17:00 -0300 message: Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order WL#4284: Transactional DDL locking Currently the MySQL server does not keep metadata locks on schema objects for the duration of a transaction, thus failing to guarantee the integrity of the schema objects being used during the transaction and to protect then from concurrent DDL operations. This also poses a problem for replication as a DDL operation might be replicated even thought there are active transactions using the object being modified. The solution is to defer the release of metadata locks until a active transaction is either committed or rolled back. This prevents other statements from modifying the table for the entire duration of the transaction. This provides commitment ordering for guaranteeing serializability across multiple transactions. - Incompatible change: If MySQL's metadata locking system encounters a lock conflict, the usual schema is to use the try and back-off technique to avoid deadlocks -- this schema consists in releasing all locks and trying to acquire them all in one go. But in a transactional context this algorithm can't be utilized as its not possible to release locks acquired during the course of the transaction without breaking the transaction commitments. To avoid deadlocks in this case, the ER_LOCK_DEADLOCK will be returned if a lock conflict is encountered during a transaction. Let's consider an example: A transaction has two statements that modify table t1, then table t2, and then commits. The first statement of the transaction will acquire a shared metadata lock on table t1, and it will be kept utill COMMIT to ensure serializability. At the moment when the second statement attempts to acquire a shared metadata lock on t2, a concurrent ALTER or DROP statement might have locked t2 exclusively. The prescription of the current locking protocol is that the acquirer of the shared lock backs off -- gives up all his current locks and retries. This implies that the entire multi-statement transaction has to be rolled back. - Incompatible change: FLUSH commands such as FLUSH PRIVILEGES and FLUSH TABLES WITH READ LOCK won't cause locked tables to be implicitly unlocked anymore. --- mysql-test/extra/binlog_tests/drop_table.test | 34 +++++++++++ .../binlog_tests/mix_innodb_myisam_binlog.test | 4 ++ mysql-test/include/mix1.inc | 1 + mysql-test/include/mix2.inc | 2 + mysql-test/r/flush_block_commit.result | 18 ++++-- mysql-test/r/flush_block_commit_notembedded.result | 15 ++++- mysql-test/r/innodb.result | 3 +- mysql-test/r/innodb_mysql.result | 1 + mysql-test/r/lock.result | 22 ++++++- mysql-test/r/mix2_myisam.result | 2 + mysql-test/r/not_embedded_server.result | 16 +++++- .../r/partition_innodb_semi_consistent.result | 2 +- mysql-test/r/partition_sync.result | 27 +-------- mysql-test/r/ps.result | 28 ++++++++- mysql-test/r/read_only_innodb.result | 6 +- .../suite/binlog/r/binlog_row_drop_tbl.result | 16 ++++++ .../binlog/r/binlog_row_mix_innodb_myisam.result | 1 + .../suite/binlog/r/binlog_stm_drop_tbl.result | 13 +++++ .../binlog/r/binlog_stm_mix_innodb_myisam.result | 1 + mysql-test/suite/binlog/r/binlog_unsafe.result | 20 +++---- mysql-test/suite/binlog/t/binlog_row_drop_tbl.test | 5 ++ mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test | 5 ++ mysql-test/suite/binlog/t/binlog_stm_row.test | 2 +- mysql-test/suite/ndb/r/ndb_index_ordered.result | 15 ----- mysql-test/suite/ndb/t/disabled.def | 1 + mysql-test/suite/ndb/t/ndb_index_ordered.test | 36 +++++++----- mysql-test/suite/rpl/t/disabled.def | 2 + mysql-test/suite/sys_vars/r/autocommit_func.result | 2 + mysql-test/suite/sys_vars/t/autocommit_func.test | 4 ++ mysql-test/t/flush_block_commit.test | 36 +++++++----- mysql-test/t/flush_block_commit_notembedded.test | 27 +++++++-- mysql-test/t/innodb.test | 10 ++-- mysql-test/t/lock.test | 37 +++++++++++- mysql-test/t/not_embedded_server.test | 23 ++++++-- mysql-test/t/partition_innodb_semi_consistent.test | 2 +- mysql-test/t/partition_sync.test | 67 +++++++++++----------- mysql-test/t/ps.test | 41 ++++++++++++- mysql-test/t/read_only_innodb.test | 14 ++++- mysql-test/t/xa.test | 3 +- 39 files changed, 413 insertions(+), 151 deletions(-) create mode 100644 mysql-test/extra/binlog_tests/drop_table.test create mode 100644 mysql-test/suite/binlog/r/binlog_row_drop_tbl.result create mode 100644 mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result create mode 100644 mysql-test/suite/binlog/t/binlog_row_drop_tbl.test create mode 100644 mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test (limited to 'mysql-test') diff --git a/mysql-test/extra/binlog_tests/drop_table.test b/mysql-test/extra/binlog_tests/drop_table.test new file mode 100644 index 00000000000..c55cbb67560 --- /dev/null +++ b/mysql-test/extra/binlog_tests/drop_table.test @@ -0,0 +1,34 @@ +# +# Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order +# + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); + +connection con1; +RESET MASTER; +CREATE TABLE t1 (a INT); +SET AUTOCOMMIT=OFF; +BEGIN; +INSERT INTO t1 VALUES(1); + +connection con2; +--send DROP TABLE t1; + +connection con1; +COMMIT; + +connection con2; +--reap + +connection default; + +--disconnect con1 +--disconnect con2 + +let $VERSION=`select version()`; +source include/show_binlog_events.inc; diff --git a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test index da0b77fbc23..7c928b565dd 100644 --- a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test +++ b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test @@ -204,6 +204,10 @@ select (@after:=unix_timestamp())*0; # always give repeatable output # the bug, the reap would return immediately after the insert into t2. select (@after-@before) >= 2; +connection con3; +commit; + +connection con2; drop table t1,t2; commit; diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc index 194d9e41108..3eaaf37cd83 100644 --- a/mysql-test/include/mix1.inc +++ b/mysql-test/include/mix1.inc @@ -1392,6 +1392,7 @@ SELECT * FROM t1; connection con2; --reap SELECT * FROM t1; +COMMIT; --echo # Switch to connection con1 connection con1; diff --git a/mysql-test/include/mix2.inc b/mysql-test/include/mix2.inc index b4c4a9b8836..001d4cf44d4 100644 --- a/mysql-test/include/mix2.inc +++ b/mysql-test/include/mix2.inc @@ -1994,6 +1994,7 @@ commit; connection b; set autocommit = 0; update t1 set b = 5 where a = 2; +commit; connection a; delimiter |; create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | @@ -2056,6 +2057,7 @@ update t2 set b = b + 5 where a = 1; update t3 set b = b + 5 where a = 1; update t4 set b = b + 5 where a = 1; insert into t5(a) values(20); +commit; connection b; set autocommit = 0; insert into t1(a) values(7); diff --git a/mysql-test/r/flush_block_commit.result b/mysql-test/r/flush_block_commit.result index d2197beaaab..da09d07b813 100644 --- a/mysql-test/r/flush_block_commit.result +++ b/mysql-test/r/flush_block_commit.result @@ -1,3 +1,4 @@ +# Save the initial number of concurrent sessions # Establish connection con1 (user=root) # Establish connection con2 (user=root) # Establish connection con3 (user=root) @@ -8,13 +9,15 @@ BEGIN; INSERT INTO t1 VALUES(1); # Switch to connection con2 FLUSH TABLES WITH READ LOCK; -SELECT * FROM t1; -a # Switch to connection con1 +# Sending: COMMIT; # Switch to connection con2 +# Wait until COMMIT gets blocked. +# Verify that 'con1' was blocked and data did not move. SELECT * FROM t1; a +1 UNLOCK TABLES; # Switch to connection con1 # Switch to connection con1 @@ -32,6 +35,7 @@ COMMIT; # Switch to connection con2 a 1 +COMMIT; # Switch to connection con3 UNLOCK TABLES; # Switch to connection con2 @@ -40,8 +44,6 @@ COMMIT; BEGIN; INSERT INTO t1 VALUES(10); FLUSH TABLES WITH READ LOCK; -COMMIT; -UNLOCK TABLES; # Switch to connection con2 FLUSH TABLES WITH READ LOCK; UNLOCK TABLES; @@ -53,5 +55,11 @@ a SHOW CREATE DATABASE test; Database Create Database test CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET latin1 */ -DROP TABLE t1; +COMMIT; +# Cleanup # Switch to connection default and close connections con1, con2, con3 +# We commit open transactions when we disconnect: only then we can +# drop the table. +DROP TABLE t1; +# End of 4.1 tests +# Wait till all disconnects are completed diff --git a/mysql-test/r/flush_block_commit_notembedded.result b/mysql-test/r/flush_block_commit_notembedded.result index 4348dbd67e5..6d8af3f5864 100644 --- a/mysql-test/r/flush_block_commit_notembedded.result +++ b/mysql-test/r/flush_block_commit_notembedded.result @@ -1,17 +1,20 @@ +# Save the initial number of concurrent sessions # Establish connection con1 (user=root) # Establish connection con2 (user=root) # Switch to connection con1 CREATE TABLE t1 (a INT) ENGINE=innodb; RESET MASTER; SET AUTOCOMMIT=0; -INSERT t1 VALUES (1); +SELECT 1; +1 +1 # Switch to connection con2 FLUSH TABLES WITH READ LOCK; SHOW MASTER STATUS; File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 107 # Switch to connection con1 -COMMIT; +INSERT INTO t1 VALUES (1); # Switch to connection con2 SHOW MASTER STATUS; File Position Binlog_Do_DB Binlog_Ignore_DB @@ -20,4 +23,12 @@ UNLOCK TABLES; # Switch to connection con1 DROP TABLE t1; SET AUTOCOMMIT=1; +create table t1 (a int) engine=innodb; +flush tables with read lock; +begin; +insert into t1 values (1);; +unlock tables; +commit; +drop table t1; # Switch to connection default and close connections con1 and con2 +# Wait till all disconnects are completed diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 17b679d99e4..268bc839483 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -2836,10 +2836,10 @@ t2 CREATE TABLE `t2` ( DROP TABLE t2,t1; create table t1(a int not null, b int, c int, d int, primary key(a)) engine=innodb; insert into t1(a) values (1),(2),(3); +create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | commit; set autocommit = 0; update t1 set b = 5 where a = 2; -create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | set autocommit = 0; insert into t1(a) values (10),(20),(30),(40),(50),(60),(70),(80),(90),(100), (11),(21),(31),(41),(51),(61),(71),(81),(91),(101), @@ -2887,6 +2887,7 @@ insert into t2(a) values(8); delete from t2 where a = 3; update t4 set b = b + 1 where a = 3; commit; +commit; drop trigger t1t; drop trigger t2t; drop trigger t3t; diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index d6aa6a02468..0fe704c13f6 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -1618,6 +1618,7 @@ a b SELECT * FROM t1; a b 1 init+con1+con2 +COMMIT; # Switch to connection con1 # 3. test for updated key column: TRUNCATE t1; diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 092c376b34a..c60ec4bc1bd 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -1,4 +1,4 @@ -drop table if exists t1,t2; +drop table if exists t1,t2,t3; CREATE TABLE t1 ( `id` int(11) NOT NULL default '0', `id2` int(11) NOT NULL default '0', `id3` int(11) NOT NULL default '0', `dummy1` char(30) default NULL, PRIMARY KEY (`id`,`id2`), KEY `index_id3` (`id3`)) ENGINE=MyISAM; insert into t1 (id,id2) values (1,1),(1,2),(1,3); LOCK TABLE t1 WRITE; @@ -262,5 +262,25 @@ unlock tables; drop table t1; drop view v1; # +# WL#4284: Transactional DDL locking +# +drop table if exists t1; +create table t1 (a int); +set autocommit= 0; +insert into t1 values (1); +lock table t1 write; +# Disconnect +# Ensure that metadata locks will be released if there is an open +# transaction (autocommit=off) in conjunction with lock tables. +drop table t1; +# Same problem but now for BEGIN +drop table if exists t1; +create table t1 (a int); +begin; +insert into t1 values (1); +# Disconnect +# Ensure that metadata locks held by the transaction are released. +drop table t1; +# # End of 6.0 tests. # diff --git a/mysql-test/r/mix2_myisam.result b/mysql-test/r/mix2_myisam.result index cabc4de8d21..99596c7774d 100644 --- a/mysql-test/r/mix2_myisam.result +++ b/mysql-test/r/mix2_myisam.result @@ -2063,6 +2063,7 @@ insert into t1(a) values (1),(2),(3); commit; set autocommit = 0; update t1 set b = 5 where a = 2; +commit; create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | set autocommit = 0; insert into t1(a) values (10),(20),(30),(40),(50),(60),(70),(80),(90),(100), @@ -2105,6 +2106,7 @@ update t2 set b = b + 5 where a = 1; update t3 set b = b + 5 where a = 1; update t4 set b = b + 5 where a = 1; insert into t5(a) values(20); +commit; set autocommit = 0; insert into t1(a) values(7); insert into t2(a) values(8); diff --git a/mysql-test/r/not_embedded_server.result b/mysql-test/r/not_embedded_server.result index 60c92bd0196..fac38624695 100644 --- a/mysql-test/r/not_embedded_server.result +++ b/mysql-test/r/not_embedded_server.result @@ -1,6 +1,16 @@ -select 1; -1 -1 +call mtr.add_suppression("Can't open and lock privilege tables: Table 'host' was not locked with LOCK TABLES"); SHOW VARIABLES like 'slave_skip_errors'; Variable_name Value slave_skip_errors OFF +# +# WL#4284: Transactional DDL locking +# +# FLUSH PRIVILEGES should not implicitly unlock locked tables. +# +drop table if exists t1; +create table t1 (c1 int); +lock tables t1 read; +flush privileges; +ERROR HY000: Table 'host' was not locked with LOCK TABLES +unlock tables; +drop table t1; diff --git a/mysql-test/r/partition_innodb_semi_consistent.result b/mysql-test/r/partition_innodb_semi_consistent.result index 471da4c1c2e..48a1bb3d258 100644 --- a/mysql-test/r/partition_innodb_semi_consistent.result +++ b/mysql-test/r/partition_innodb_semi_consistent.result @@ -102,7 +102,7 @@ a b # Switch to connection con1 # 3. test for updated key column: TRUNCATE t1; -TRUNCATE t2; +DELETE FROM t2; INSERT INTO t1 VALUES (1,'init'); BEGIN; UPDATE t1 SET a = 2, b = CONCAT(b, '+con1') WHERE a = 1; diff --git a/mysql-test/r/partition_sync.result b/mysql-test/r/partition_sync.result index 31cf0569464..41ca19426fe 100644 --- a/mysql-test/r/partition_sync.result +++ b/mysql-test/r/partition_sync.result @@ -1,25 +1,2 @@ -# -# Bug #43867 ALTER TABLE on a partitioned table -# causes unnecessary deadlocks -# -CREATE TABLE t1 (a int) PARTITION BY RANGE (a) -(PARTITION p0 VALUES LESS THAN (1), -PARTITION p1 VALUES LESS THAN (2)); -INSERT INTO t1 VALUES (0),(1); -# Connection 2 -BEGIN; -SELECT * FROM t1; -a -0 -1 -# Connection 1 -ALTER TABLE t1 DROP PARTITION p3; -ERROR HY000: Error in list of partitions to DROP -# Connection 2 -# This failed with deadlock and should not do so. -SELECT * FROM t1; -a -0 -1 -# Connection 1 -DROP TABLE t1; +# Disabled until Bug#46654 False deadlock on concurrent DML/DDL +# with partitions, inconsistent behavior is backported diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 6c7e83134d7..2526049c539 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -3086,5 +3086,29 @@ DROP PROCEDURE p1; DROP PROCEDURE p2; # End of WL#4435. - -End of 6.0 tests. +# +# WL#4284: Transactional DDL locking +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a INT); +BEGIN; +SELECT * FROM t1; +a +# Test that preparing a CREATE TABLE does not take a exclusive metdata lock. +PREPARE stmt1 FROM "CREATE TABLE t1 AS SELECT 1"; +EXECUTE stmt1; +ERROR 42S01: Table 't1' already exists +DEALLOCATE PREPARE stmt1; +DROP TABLE t1; +# +# WL#4284: Transactional DDL locking +# +# Test that metadata locks taken during prepare are released. +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a INT); +BEGIN; +PREPARE stmt1 FROM "SELECT * FROM t1"; +DROP TABLE t1; +# +# End of 6.0 tests. diff --git a/mysql-test/r/read_only_innodb.result b/mysql-test/r/read_only_innodb.result index 690de085bf9..4cba98900a1 100644 --- a/mysql-test/r/read_only_innodb.result +++ b/mysql-test/r/read_only_innodb.result @@ -7,12 +7,10 @@ insert into table_11733 values(11733); set global read_only=1; select @@global.read_only; @@global.read_only -1 +0 select * from table_11733 ; -a -11733 +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction COMMIT; -ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement set global read_only=0; drop table table_11733 ; drop user test@localhost; diff --git a/mysql-test/suite/binlog/r/binlog_row_drop_tbl.result b/mysql-test/suite/binlog/r/binlog_row_drop_tbl.result new file mode 100644 index 00000000000..8b32e9e5a45 --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_row_drop_tbl.result @@ -0,0 +1,16 @@ +DROP TABLE IF EXISTS t1; +RESET MASTER; +CREATE TABLE t1 (a INT); +SET AUTOCOMMIT=OFF; +BEGIN; +INSERT INTO t1 VALUES(1); +DROP TABLE t1;; +COMMIT; +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT) +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Query # # use `test`; DROP TABLE t1 diff --git a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result index 4ccc3b5e797..c1254643a18 100644 --- a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result +++ b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result @@ -238,6 +238,7 @@ select (@after:=unix_timestamp())*0; select (@after-@before) >= 2; (@after-@before) >= 2 1 +commit; drop table t1,t2; commit; begin; diff --git a/mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result b/mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result new file mode 100644 index 00000000000..f4596b808cf --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_stm_drop_tbl.result @@ -0,0 +1,13 @@ +DROP TABLE IF EXISTS t1; +RESET MASTER; +CREATE TABLE t1 (a INT); +SET AUTOCOMMIT=OFF; +BEGIN; +INSERT INTO t1 VALUES(1); +DROP TABLE t1;; +COMMIT; +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT) +master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES(1) +master-bin.000001 # Query # # use `test`; DROP TABLE t1 diff --git a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result index ea081183cd1..f05e184976c 100644 --- a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result +++ b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result @@ -207,6 +207,7 @@ select (@after:=unix_timestamp())*0; select (@after-@before) >= 2; (@after-@before) >= 2 1 +commit; drop table t1,t2; commit; begin; diff --git a/mysql-test/suite/binlog/r/binlog_unsafe.result b/mysql-test/suite/binlog/r/binlog_unsafe.result index 58738a0d97c..3885acd079b 100644 --- a/mysql-test/suite/binlog/r/binlog_unsafe.result +++ b/mysql-test/suite/binlog/r/binlog_unsafe.result @@ -320,10 +320,10 @@ INSERT INTO t2 SET a = func_modify_t1(); SHOW BINLOG EVENTS FROM 12283; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 12283 Query 1 12351 BEGIN -master-bin.000001 12351 Table_map 1 12393 table_id: 44 (test.t2) -master-bin.000001 12393 Table_map 1 12435 table_id: 45 (test.t1) -master-bin.000001 12435 Write_rows 1 12473 table_id: 45 -master-bin.000001 12473 Write_rows 1 12511 table_id: 44 flags: STMT_END_F +master-bin.000001 12351 Table_map 1 12393 table_id: 41 (test.t2) +master-bin.000001 12393 Table_map 1 12435 table_id: 42 (test.t1) +master-bin.000001 12435 Write_rows 1 12473 table_id: 42 +master-bin.000001 12473 Write_rows 1 12511 table_id: 41 flags: STMT_END_F master-bin.000001 12511 Query 1 12580 COMMIT DROP TABLE t1,t2; DROP FUNCTION func_modify_t1; @@ -347,12 +347,12 @@ INSERT INTO t1 SET a = 2; SHOW BINLOG EVENTS FROM 13426; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 13426 Query 1 13494 BEGIN -master-bin.000001 13494 Table_map 1 13535 table_id: 47 (test.t1) -master-bin.000001 13535 Table_map 1 13577 table_id: 48 (test.t3) -master-bin.000001 13577 Table_map 1 13619 table_id: 49 (test.t2) -master-bin.000001 13619 Write_rows 1 13657 table_id: 49 -master-bin.000001 13657 Write_rows 1 13695 table_id: 48 -master-bin.000001 13695 Write_rows 1 13729 table_id: 47 flags: STMT_END_F +master-bin.000001 13494 Table_map 1 13535 table_id: 44 (test.t1) +master-bin.000001 13535 Table_map 1 13577 table_id: 45 (test.t3) +master-bin.000001 13577 Table_map 1 13619 table_id: 46 (test.t2) +master-bin.000001 13619 Write_rows 1 13657 table_id: 46 +master-bin.000001 13657 Write_rows 1 13695 table_id: 45 +master-bin.000001 13695 Write_rows 1 13729 table_id: 44 flags: STMT_END_F master-bin.000001 13729 Query 1 13798 COMMIT DROP TABLE t1,t2,t3; "End of tests" diff --git a/mysql-test/suite/binlog/t/binlog_row_drop_tbl.test b/mysql-test/suite/binlog/t/binlog_row_drop_tbl.test new file mode 100644 index 00000000000..06854900612 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_row_drop_tbl.test @@ -0,0 +1,5 @@ +# This is a wrapper for drop_table.test so that the same test case can be used +# For both statement and row based bin logs + +-- source include/have_binlog_format_row.inc +-- source extra/binlog_tests/drop_table.test diff --git a/mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test b/mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test new file mode 100644 index 00000000000..f2b07bb69f6 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_stm_drop_tbl.test @@ -0,0 +1,5 @@ +# This is a wrapper for drop_table.test so that the same test case can be used +# For both statement and row based bin logs + +-- source include/have_binlog_format_mixed_or_statement.inc +-- source extra/binlog_tests/drop_table.test diff --git a/mysql-test/suite/binlog/t/binlog_stm_row.test b/mysql-test/suite/binlog/t/binlog_stm_row.test index e923faae940..c501df324e8 100644 --- a/mysql-test/suite/binlog/t/binlog_stm_row.test +++ b/mysql-test/suite/binlog/t/binlog_stm_row.test @@ -57,7 +57,7 @@ let $wait_condition= --echo # con1 let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist WHERE - state = "Locked" and info = "INSERT INTO t2 VALUES (3)"; + state = "Table lock" and info = "INSERT INTO t2 VALUES (3)"; --source include/wait_condition.inc SELECT RELEASE_LOCK('Bug#34306'); --connection con2 diff --git a/mysql-test/suite/ndb/r/ndb_index_ordered.result b/mysql-test/suite/ndb/r/ndb_index_ordered.result index a29b5343d7c..c99db354314 100644 --- a/mysql-test/suite/ndb/r/ndb_index_ordered.result +++ b/mysql-test/suite/ndb/r/ndb_index_ordered.result @@ -637,21 +637,6 @@ select count(*)- 4 from t1 use index (v) where v > 0000965.00042; count(*)- 4 0 drop table t1; -create table t1(a int primary key, b int not null, index(b)); -insert into t1 values (1,1), (2,2); -set autocommit=0; -begin; -select count(*) from t1; -count(*) -2 -ALTER TABLE t1 ADD COLUMN c int; -select a from t1 where b = 2; -a -2 -show tables; -Tables_in_test -t1 -drop table t1; create table t1 (a int, c varchar(10), primary key using hash (a), index(c)) engine=ndb; insert into t1 (a, c) values (1,'aaa'),(3,'bbb'); diff --git a/mysql-test/suite/ndb/t/disabled.def b/mysql-test/suite/ndb/t/disabled.def index 0fc9a5d3ad6..b2aa3e515be 100644 --- a/mysql-test/suite/ndb/t/disabled.def +++ b/mysql-test/suite/ndb/t/disabled.def @@ -13,3 +13,4 @@ ndb_partition_error2 : Bug#40989 ndb_partition_error2 needs maintenance # the below testcase have been reworked to avoid the bug, test contains comment, keep bug open +ndb_alter_table3 : Bug#45621 2009-06-10 alik A few test files are disabled due to WL#4284 diff --git a/mysql-test/suite/ndb/t/ndb_index_ordered.test b/mysql-test/suite/ndb/t/ndb_index_ordered.test index 782f17ca5b2..c8dfc1de59f 100644 --- a/mysql-test/suite/ndb/t/ndb_index_ordered.test +++ b/mysql-test/suite/ndb/t/ndb_index_ordered.test @@ -333,21 +333,29 @@ select count(*)- 4 from t1 use index (v) where v > 0000965.00042; drop table t1; +# +# Disabled due to WL#4284 +# +# Needs to be reworked. It's not possible anymore to do a non-fast alter table +# on a table that is being used by a pending transaction (transaction holds a +# metadata lock on the table). +# # bug#7798 -create table t1(a int primary key, b int not null, index(b)); -insert into t1 values (1,1), (2,2); -connect (con1,localhost,root,,test); -connect (con2,localhost,root,,test); -connection con1; -set autocommit=0; -begin; -select count(*) from t1; -connection con2; -ALTER TABLE t1 ADD COLUMN c int; -connection con1; -select a from t1 where b = 2; -show tables; -drop table t1; +# create table t1(a int primary key, b int not null, c int, index(b)); +# insert into t1 values (1,1,1), (2,2,2); +# connect (con1,localhost,root,,test); +# connect (con2,localhost,root,,test); +# connection con1; +# set autocommit=0; +# begin; +# select count(*) from t1; +# connection con2; +# ALTER TABLE t1 ADD COLUMN c int +# connection con1; +# select a from t1 where b = 2; +# show tables; +# drop table t1; +# # mysqld 5.0.13 crash, no bug# create table t1 (a int, c varchar(10), diff --git a/mysql-test/suite/rpl/t/disabled.def b/mysql-test/suite/rpl/t/disabled.def index 485ba229257..6bd4e7e833a 100644 --- a/mysql-test/suite/rpl/t/disabled.def +++ b/mysql-test/suite/rpl/t/disabled.def @@ -12,3 +12,5 @@ rpl_cross_version : BUG#43913 2009-10-22 luis rpl_cross_version fails with symptom in described in bug report rpl_spec_variables : BUG#47661 2009-10-27 jasonh rpl_spec_variables fails on PB2 hpux +rpl_failed_optimize : WL#4284: Can't optimize table used by a pending transaction (there is metadata lock on the table). +rpl_read_only : WL#4284: Setting Read only won't succeed until all metadata locks are released. diff --git a/mysql-test/suite/sys_vars/r/autocommit_func.result b/mysql-test/suite/sys_vars/r/autocommit_func.result index 47c2c921022..cb59c9a7b32 100644 --- a/mysql-test/suite/sys_vars/r/autocommit_func.result +++ b/mysql-test/suite/sys_vars/r/autocommit_func.result @@ -104,6 +104,8 @@ id name 2 Record_2 4 Record_4 5 Record_5 +## Commit changes +COMMIT; ## Dropping table t1 ## DROP table t1; ## Disconnecting both connections ## diff --git a/mysql-test/suite/sys_vars/t/autocommit_func.test b/mysql-test/suite/sys_vars/t/autocommit_func.test index 07e15ce40da..716189bb7be 100644 --- a/mysql-test/suite/sys_vars/t/autocommit_func.test +++ b/mysql-test/suite/sys_vars/t/autocommit_func.test @@ -153,6 +153,10 @@ SELECT * from t1; CONNECTION test_con2; SELECT * from t1; +--echo ## Commit changes +CONNECTION test_con1; +COMMIT; + --echo ## Dropping table t1 ## DROP table t1; diff --git a/mysql-test/t/flush_block_commit.test b/mysql-test/t/flush_block_commit.test index 74892def63f..98bca8cdad7 100644 --- a/mysql-test/t/flush_block_commit.test +++ b/mysql-test/t/flush_block_commit.test @@ -6,7 +6,7 @@ # And it requires InnoDB --source include/have_innodb.inc -# Save the initial number of concurrent sessions +--echo # Save the initial number of concurrent sessions --source include/count_sessions.inc --echo # Establish connection con1 (user=root) @@ -29,19 +29,26 @@ BEGIN; INSERT INTO t1 VALUES(1); --echo # Switch to connection con2 connection con2; -FLUSH TABLES WITH READ LOCK; -SELECT * FROM t1; +--send FLUSH TABLES WITH READ LOCK --echo # Switch to connection con1 connection con1; -send COMMIT; # blocked by con2 -sleep 1; +--echo # Sending: +COMMIT; --echo # Switch to connection con2 connection con2; -SELECT * FROM t1; # verify con1 was blocked and data did not move +--reap +--echo # Wait until COMMIT gets blocked. +#let $wait_condition= +# select count(*) = 1 from information_schema.processlist +# where state = "Waiting for release of readlock" and info = "COMMIT"; +#--source include/wait_condition.inc +--echo # Verify that 'con1' was blocked and data did not move. +SELECT * FROM t1; UNLOCK TABLES; --echo # Switch to connection con1 connection con1; -reap; +#--echo # Reaping COMMIT +#--reap # No deadlock ? @@ -63,6 +70,7 @@ COMMIT; # should not be blocked by con3 --echo # Switch to connection con2 connection con2; reap; +COMMIT; --echo # Switch to connection con3 connection con3; reap; @@ -79,8 +87,6 @@ connection con1; BEGIN; INSERT INTO t1 VALUES(10); FLUSH TABLES WITH READ LOCK; -COMMIT; -UNLOCK TABLES; --echo # Switch to connection con2 connection con2; FLUSH TABLES WITH READ LOCK; # bug caused hang here @@ -91,19 +97,21 @@ UNLOCK TABLES; BEGIN; SELECT * FROM t1; SHOW CREATE DATABASE test; - -DROP TABLE t1; +COMMIT; -# Cleanup +--echo # Cleanup --echo # Switch to connection default and close connections con1, con2, con3 connection default; disconnect con1; disconnect con2; disconnect con3; -# End of 4.1 tests +--echo # We commit open transactions when we disconnect: only then we can +--echo # drop the table. +DROP TABLE t1; +--echo # End of 4.1 tests -# Wait till all disconnects are completed +--echo # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/mysql-test/t/flush_block_commit_notembedded.test b/mysql-test/t/flush_block_commit_notembedded.test index aea38250218..d7ffbd475b4 100644 --- a/mysql-test/t/flush_block_commit_notembedded.test +++ b/mysql-test/t/flush_block_commit_notembedded.test @@ -9,7 +9,7 @@ --source include/have_log_bin.inc --source include/have_innodb.inc -# Save the initial number of concurrent sessions +--echo # Save the initial number of concurrent sessions --source include/count_sessions.inc @@ -24,14 +24,14 @@ connection con1; CREATE TABLE t1 (a INT) ENGINE=innodb; RESET MASTER; SET AUTOCOMMIT=0; -INSERT t1 VALUES (1); +SELECT 1; --echo # Switch to connection con2 connection con2; FLUSH TABLES WITH READ LOCK; SHOW MASTER STATUS; --echo # Switch to connection con1 connection con1; -send COMMIT; +send INSERT INTO t1 VALUES (1); --echo # Switch to connection con2 connection con2; sleep 1; @@ -43,11 +43,30 @@ reap; DROP TABLE t1; SET AUTOCOMMIT=1; +# GLR blocks new transactions +create table t1 (a int) engine=innodb; +connection con1; +flush tables with read lock; +connection con2; +begin; +--send insert into t1 values (1); +connection con1; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for release of readlock" and + info = "insert into t1 values (1)"; +--source include/wait_condition.inc +unlock tables; +connection con2; +--reap +commit; +drop table t1; + --echo # Switch to connection default and close connections con1 and con2 connection default; disconnect con1; disconnect con2; -# Wait till all disconnects are completed +--echo # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index 2879a4c0b9f..6e7d9b5e780 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -1849,16 +1849,15 @@ connect (b,localhost,root,,); connection a; create table t1(a int not null, b int, c int, d int, primary key(a)) engine=innodb; insert into t1(a) values (1),(2),(3); +delimiter |; +create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | +delimiter ;| commit; connection b; set autocommit = 0; update t1 set b = 5 where a = 2; connection a; -delimiter |; -create trigger t1t before insert on t1 for each row begin set NEW.b = NEW.a * 10 + 5, NEW.c = NEW.a / 10; end | -delimiter ;| set autocommit = 0; -connection a; insert into t1(a) values (10),(20),(30),(40),(50),(60),(70),(80),(90),(100), (11),(21),(31),(41),(51),(61),(71),(81),(91),(101), (12),(22),(32),(42),(52),(62),(72),(82),(92),(102), @@ -1922,6 +1921,9 @@ insert into t2(a) values(8); delete from t2 where a = 3; update t4 set b = b + 1 where a = 3; commit; +connection a; +commit; +connection b; drop trigger t1t; drop trigger t2t; drop trigger t3t; diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 51900be4df8..c2aeac05cd1 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -3,7 +3,7 @@ # --disable_warnings -drop table if exists t1,t2; +drop table if exists t1,t2,t3; --enable_warnings CREATE TABLE t1 ( `id` int(11) NOT NULL default '0', `id2` int(11) NOT NULL default '0', `id3` int(11) NOT NULL default '0', `dummy1` char(30) default NULL, PRIMARY KEY (`id`,`id2`), KEY `index_id3` (`id3`)) ENGINE=MyISAM; insert into t1 (id,id2) values (1,1),(1,2),(1,3); @@ -310,6 +310,41 @@ unlock tables; drop table t1; drop view v1; +--echo # +--echo # WL#4284: Transactional DDL locking +--echo # + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +connect(con1,localhost,root,,); +set autocommit= 0; +insert into t1 values (1); +lock table t1 write; +--echo # Disconnect +--echo # Ensure that metadata locks will be released if there is an open +--echo # transaction (autocommit=off) in conjunction with lock tables. +disconnect con1; +connection default; +drop table t1; + +--echo # Same problem but now for BEGIN + +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a int); +connect(con1,localhost,root,,); +begin; +insert into t1 values (1); +--echo # Disconnect +--echo # Ensure that metadata locks held by the transaction are released. +disconnect con1; +connection default; +drop table t1; + + --echo # --echo # End of 6.0 tests. --echo # diff --git a/mysql-test/t/not_embedded_server.test b/mysql-test/t/not_embedded_server.test index fa2b659ec57..917d5871682 100644 --- a/mysql-test/t/not_embedded_server.test +++ b/mysql-test/t/not_embedded_server.test @@ -4,12 +4,6 @@ -- source include/not_embedded.inc -# -# Produce output -# - -select 1; - # The following fails sporadically because 'check-testcase' runs # queries before this test and there is no way to guarantee that any # previous process finishes. The purpose of the test is not clearly @@ -36,6 +30,8 @@ select 1; #execute stmt1; #deallocate prepare stmt1; +call mtr.add_suppression("Can't open and lock privilege tables: Table 'host' was not locked with LOCK TABLES"); + # # Bug#43835: SHOW VARIABLES does not include 0 for slave_skip_errors # @@ -43,3 +39,18 @@ select 1; SHOW VARIABLES like 'slave_skip_errors'; # End of 5.1 tests + +--echo # +--echo # WL#4284: Transactional DDL locking +--echo # +--echo # FLUSH PRIVILEGES should not implicitly unlock locked tables. +--echo # +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (c1 int); +lock tables t1 read; +--error ER_TABLE_NOT_LOCKED +flush privileges; +unlock tables; +drop table t1; diff --git a/mysql-test/t/partition_innodb_semi_consistent.test b/mysql-test/t/partition_innodb_semi_consistent.test index 6a6a7cf958e..2bf879603a4 100644 --- a/mysql-test/t/partition_innodb_semi_consistent.test +++ b/mysql-test/t/partition_innodb_semi_consistent.test @@ -157,7 +157,7 @@ connection con1; --echo # 3. test for updated key column: TRUNCATE t1; -TRUNCATE t2; +DELETE FROM t2; INSERT INTO t1 VALUES (1,'init'); diff --git a/mysql-test/t/partition_sync.test b/mysql-test/t/partition_sync.test index a732b35b8b9..5d2b25e87f3 100644 --- a/mysql-test/t/partition_sync.test +++ b/mysql-test/t/partition_sync.test @@ -2,38 +2,41 @@ # Save the initial number of concurrent sessions. --source include/count_sessions.inc ---echo # ---echo # Bug #43867 ALTER TABLE on a partitioned table ---echo # causes unnecessary deadlocks ---echo # - -CREATE TABLE t1 (a int) PARTITION BY RANGE (a) -(PARTITION p0 VALUES LESS THAN (1), - PARTITION p1 VALUES LESS THAN (2)); - -INSERT INTO t1 VALUES (0),(1); - -connect(con1,localhost,root); - ---echo # Connection 2 -connection con1; -BEGIN; -SELECT * FROM t1; - ---echo # Connection 1 -connection default; ---error ER_DROP_PARTITION_NON_EXISTENT -ALTER TABLE t1 DROP PARTITION p3; - ---echo # Connection 2 -connection con1; ---echo # This failed with deadlock and should not do so. -SELECT * FROM t1; - ---echo # Connection 1 -connection default; -disconnect con1; -DROP TABLE t1; +--echo # Disabled until Bug#46654 False deadlock on concurrent DML/DDL +--echo # with partitions, inconsistent behavior is backported + +#--echo # +#--echo # Bug #43867 ALTER TABLE on a partitioned table +#--echo # causes unnecessary deadlocks +#--echo # +# +#CREATE TABLE t1 (a int) PARTITION BY RANGE (a) +#(PARTITION p0 VALUES LESS THAN (1), +# PARTITION p1 VALUES LESS THAN (2)); +# +#INSERT INTO t1 VALUES (0),(1); +# +#connect(con1,localhost,root); +# +#--echo # Connection 2 +#connection con1; +#BEGIN; +#SELECT * FROM t1; +# +#--echo # Connection 1 +#connection default; +#--error ER_DROP_PARTITION_NON_EXISTENT +#ALTER TABLE t1 DROP PARTITION p3; +# +#--echo # Connection 2 +#connection con1; +#--echo # This failed with deadlock and should not do so. +#SELECT * FROM t1; +# +#--echo # Connection 1 +#connection default; +#disconnect con1; +#DROP TABLE t1; # Check that all connections opened by test cases in this file are really diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 844be582290..6cebbe6c7a1 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -3211,7 +3211,44 @@ DROP PROCEDURE p2; ########################################################################### ---echo ---echo End of 6.0 tests. + +--echo # +--echo # WL#4284: Transactional DDL locking +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +CREATE TABLE t1 (a INT); +BEGIN; +SELECT * FROM t1; +--echo # Test that preparing a CREATE TABLE does not take a exclusive metdata lock. +PREPARE stmt1 FROM "CREATE TABLE t1 AS SELECT 1"; +--error ER_TABLE_EXISTS_ERROR +EXECUTE stmt1; +DEALLOCATE PREPARE stmt1; +DROP TABLE t1; + +--echo # +--echo # WL#4284: Transactional DDL locking +--echo # +--echo # Test that metadata locks taken during prepare are released. +--echo # + +connect(con1,localhost,root,,); +connection default; +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +CREATE TABLE t1 (a INT); +connection con1; +BEGIN; +PREPARE stmt1 FROM "SELECT * FROM t1"; +connection default; +DROP TABLE t1; +disconnect con1; + +--echo # +--echo # End of 6.0 tests. ########################################################################### diff --git a/mysql-test/t/read_only_innodb.test b/mysql-test/t/read_only_innodb.test index f8c25fdee1d..98e704e25c7 100644 --- a/mysql-test/t/read_only_innodb.test +++ b/mysql-test/t/read_only_innodb.test @@ -16,6 +16,8 @@ DROP TABLE IF EXISTS table_11733 ; grant CREATE, SELECT, DROP on *.* to test@localhost; connect (con1,localhost,test,,test); +connect (con2,localhost,root,,); + connection default; set global read_only=0; @@ -28,15 +30,22 @@ BEGIN; insert into table_11733 values(11733); connection default; -set global read_only=1; +send set global read_only=1; + +connection con2; +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Flushing tables" and info = "set global read_only=1"; +--source include/wait_condition.inc connection con1; select @@global.read_only; +--error ER_LOCK_DEADLOCK select * from table_11733 ; --- error ER_OPTION_PREVENTS_STATEMENT COMMIT; connection default; +reap; set global read_only=0; drop table table_11733 ; drop user test@localhost; @@ -81,5 +90,6 @@ DROP TABLE t1; DROP USER test@localhost; disconnect con1; +disconnect con2; --echo echo End of 5.1 tests diff --git a/mysql-test/t/xa.test b/mysql-test/t/xa.test index f84d822170f..0f705ae20c6 100644 --- a/mysql-test/t/xa.test +++ b/mysql-test/t/xa.test @@ -76,9 +76,10 @@ xa rollback 'testa','testb'; xa start 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'; select * from t1; -drop table t1; disconnect con1; +connection default; +drop table t1; # # Bug#28323: Server crashed in xid cache operations -- cgit v1.2.1 From a7c244e75685496292d2533fc419fc6fb09d5ae8 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 10:39:49 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2617.31.7 committer: Davi Arnaut branch nick: mysql-6.0-runtime timestamp: Wed 2009-03-25 19:22:00 -0300 message: WL#4284: Transactional DDL locking Post-merge fixes for test cases. --- mysql-test/include/mix1.inc | 2 ++ mysql-test/r/innodb_mysql.result | 2 ++ mysql-test/suite/parts/r/partition_special_innodb.result | 5 ----- mysql-test/suite/parts/t/partition_special_innodb.test | 3 --- 4 files changed, 4 insertions(+), 8 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc index 3eaaf37cd83..66648aaf1bf 100644 --- a/mysql-test/include/mix1.inc +++ b/mysql-test/include/mix1.inc @@ -899,6 +899,8 @@ CREATE PROCEDURE p1 () BEGIN DECLARE i INT DEFAULT 50; DECLARE cnt INT; + # Continue even in the presence of ER_LOCK_DEADLOCK. + DECLARE CONTINUE HANDLER FOR 1213 BEGIN END; START TRANSACTION; ALTER TABLE t1 ENGINE=InnoDB; COMMIT; diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 0fe704c13f6..54ca46a098b 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -1105,6 +1105,8 @@ CREATE PROCEDURE p1 () BEGIN DECLARE i INT DEFAULT 50; DECLARE cnt INT; +# Continue even in the presence of ER_LOCK_DEADLOCK. +DECLARE CONTINUE HANDLER FOR 1213 BEGIN END; START TRANSACTION; ALTER TABLE t1 ENGINE=InnoDB; COMMIT; diff --git a/mysql-test/suite/parts/r/partition_special_innodb.result b/mysql-test/suite/parts/r/partition_special_innodb.result index 26c1ac9356c..3c64c5c3ca4 100644 --- a/mysql-test/suite/parts/r/partition_special_innodb.result +++ b/mysql-test/suite/parts/r/partition_special_innodb.result @@ -214,9 +214,4 @@ INSERT INTO t1 VALUES (NULL, 'first row t2'); SET autocommit=OFF; ALTER TABLE t1 AUTO_INCREMENT = 10; ERROR HY000: Lock wait timeout exceeded; try restarting transaction -INSERT INTO t1 VALUES (NULL, 'second row t2'); -SELECT a,b FROM t1 ORDER BY a; -a b -1 first row t2 -2 second row t2 DROP TABLE t1; diff --git a/mysql-test/suite/parts/t/partition_special_innodb.test b/mysql-test/suite/parts/t/partition_special_innodb.test index eac19f6d588..7583f953b32 100644 --- a/mysql-test/suite/parts/t/partition_special_innodb.test +++ b/mysql-test/suite/parts/t/partition_special_innodb.test @@ -71,9 +71,6 @@ SET autocommit=OFF; --error ER_LOCK_WAIT_TIMEOUT ALTER TABLE t1 AUTO_INCREMENT = 10; ---connection con1 -INSERT INTO t1 VALUES (NULL, 'second row t2'); -SELECT a,b FROM t1 ORDER BY a; --disconnect con2 --disconnect con1 --connection default -- cgit v1.2.1 From 90e88642afeb9abc4aa5bc417306fae00d6a85bd Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 10:53:40 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2617.43.3 committer: Davi Arnaut branch nick: 40188-6.0 timestamp: Thu 2009-05-07 13:15:54 +0200 message: Sort results as the file list of the database directory is not sorted (MY_DONT_SORT). (This is a follow-up fix for WL#4284). --- mysql-test/r/drop_debug.result | 4 ++++ mysql-test/t/drop_debug.test | 3 +++ 2 files changed, 7 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/drop_debug.result b/mysql-test/r/drop_debug.result index 75346b88bc6..f2c89034451 100644 --- a/mysql-test/r/drop_debug.result +++ b/mysql-test/r/drop_debug.result @@ -7,12 +7,16 @@ DROP DATABASE IF EXISTS mysql_test; CREATE DATABASE mysql_test; CREATE TABLE mysql_test.t1(a INT); +CREATE TABLE mysql_test.t2(b INT); +CREATE TABLE mysql_test.t3(c INT); SET SESSION DEBUG = "+d,bug43138"; DROP DATABASE mysql_test; Warnings: Error 1051 Unknown table 't1' +Error 1051 Unknown table 't2' +Error 1051 Unknown table 't3' SET SESSION DEBUG = "-d,bug43138"; diff --git a/mysql-test/t/drop_debug.test b/mysql-test/t/drop_debug.test index 97ee5847d0a..63c85d9246b 100644 --- a/mysql-test/t/drop_debug.test +++ b/mysql-test/t/drop_debug.test @@ -17,11 +17,14 @@ DROP DATABASE IF EXISTS mysql_test; --echo CREATE DATABASE mysql_test; CREATE TABLE mysql_test.t1(a INT); +CREATE TABLE mysql_test.t2(b INT); +CREATE TABLE mysql_test.t3(c INT); --echo SET SESSION DEBUG = "+d,bug43138"; --echo +--sorted_result DROP DATABASE mysql_test; --echo -- cgit v1.2.1 From 4a8a1c568d8785dc608cc111e74e1ed389f1353e Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 11:38:45 +0300 Subject: Backport of: ---------------------------------------------------------- revno: 2617.69.2 committer: Konstantin Osipov branch nick: 5.4-azalea-bugfixing timestamp: Mon 2009-08-03 19:26:04 +0400 message: A fix and a test case for Bug#45035 "Altering table under LOCK TABLES results in "Error 1213 Deadlock found...". If a user had a table locked with LOCK TABLES for READ and for WRITE in the same connection, ALTER TABLE could fail. Root cause analysis: If a connection issues LOCK TABLE t1 write, t1 a read, t1 b read; the new LOCK TABLES code in 6.0 (part of WL 3726) will create the following list of TABLE_LIST objects (thd->locked_tables_list->m_locked_tables): {"t1" "b" tl_read_no_insert}, {"t1" "a" tl_read_no_insert}, {"t1" "t1" tl_write } Later on, when we try to ALTER table t1, mysql_alter_table() closes all TABLE instances and releases its thr_lock locks, keeping only an exclusive metadata lock on t1. But when ALTER is finished, Locked_table_list::reopen_tables() tries to restore the original list of open and locked tables. Before this patch, it used to do so one by one: Open t1 b, get TL_READ_NO_INSERT lock, Open t1 a, get TL_READ_NO_INSERT lock Open t1, try to get TL_WRITE lock, deadlock. The cause of the deadlock is that thr_lock.c doesn't resolve the situation when the read list only consists of locks taken by the same thread, followed by this very thread trying to take a WRITE lock. Indeed, since thr_lock_multi always gets a sorted list of locks, WRITE locks always precede READ locks in the list to lock. Don't try to fix thr_lock.c deficiency, keep this code simple. Instead, try to take all thr_lock locks at once in ::reopen_tables(). --- mysql-test/r/lock.result | 37 +++++++++++++++++++++++++++++++++++++ mysql-test/t/lock.test | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index c60ec4bc1bd..46ce618b99b 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -282,5 +282,42 @@ insert into t1 values (1); # Ensure that metadata locks held by the transaction are released. drop table t1; # +# Bug#45035 " Altering table under LOCK TABLES results in +# "Error 1213 Deadlock found..." +# +# When reopening tables under LOCK TABLES after ALTER TABLE, +# 6.0 used to be taking thr_lock locks one by one, and +# that would lead to a lock conflict. +# Check that taking all locks at once works. +# +drop table if exists t1; +create table t1 (i int); +lock tables t1 write, t1 as a read, t1 as b read; +alter table t1 add column j int; +unlock tables; +drop table t1; +create temporary table t1 (i int); +# +# This is just for test coverage purposes, +# when this is allowed, remove the --error. +# +lock tables t1 write, t1 as a read, t1 as b read; +ERROR HY000: Can't reopen table: 't1' +alter table t1 add column j int; +unlock tables; +drop table t1; +# +# Separate case for partitioned tables is important +# because each partition has an own thr_lock object. +# +create table t1 (i int) partition by list (i) +(partition p0 values in (1), +partition p1 values in (2,3), +partition p2 values in (4,5)); +lock tables t1 write, t1 as a read, t1 as b read; +alter table t1 add column j int; +unlock tables; +drop table t1; +# # End of 6.0 tests. # diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index c2aeac05cd1..eaba2693904 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -345,6 +345,46 @@ connection default; drop table t1; +--echo # +--echo # Bug#45035 " Altering table under LOCK TABLES results in +--echo # "Error 1213 Deadlock found..." +--echo # +--echo # When reopening tables under LOCK TABLES after ALTER TABLE, +--echo # 6.0 used to be taking thr_lock locks one by one, and +--echo # that would lead to a lock conflict. +--echo # Check that taking all locks at once works. +--echo # +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (i int); +lock tables t1 write, t1 as a read, t1 as b read; +alter table t1 add column j int; +unlock tables; +drop table t1; +create temporary table t1 (i int); +--echo # +--echo # This is just for test coverage purposes, +--echo # when this is allowed, remove the --error. +--echo # +--error ER_CANT_REOPEN_TABLE +lock tables t1 write, t1 as a read, t1 as b read; +alter table t1 add column j int; +unlock tables; +drop table t1; +--echo # +--echo # Separate case for partitioned tables is important +--echo # because each partition has an own thr_lock object. +--echo # +create table t1 (i int) partition by list (i) + (partition p0 values in (1), + partition p1 values in (2,3), + partition p2 values in (4,5)); +lock tables t1 write, t1 as a read, t1 as b read; +alter table t1 add column j int; +unlock tables; +drop table t1; + --echo # --echo # End of 6.0 tests. --echo # -- cgit v1.2.1 From 92ba2f2a4f8fed3d36021face47c51937d10c872 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 8 Dec 2009 14:14:05 +0100 Subject: Backport of revno: 2617.56.21 Bug #45066 FLUSH TABLES WITH READ LOCK deadlocks against LOCK TABLE Test coverage for combinations of LOCK TABLE READ / WRITE and FLUSH TABLES / FLUSH TABLES WITH READ LOCK added to lock.test. LOCK and FLUSH are executed sequentially from one connection. --- mysql-test/r/lock.result | 22 ++++++++++++++++++++++ mysql-test/t/lock.test | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 46ce618b99b..afb444f8ae9 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -319,5 +319,27 @@ alter table t1 add column j int; unlock tables; drop table t1; # +# Bug#45066 FLUSH TABLES WITH READ LOCK deadlocks against +# LOCK TABLE +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a INT); +LOCK TABLE t1 READ; +FLUSH TABLES; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +LOCK TABLE t1 WRITE; +FLUSH TABLES; +# +# If you allow the next combination, you reintroduce bug Bug#45066 +# +LOCK TABLE t1 READ; +FLUSH TABLES WITH READ LOCK; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +LOCK TABLE t1 WRITE; +FLUSH TABLES WITH READ LOCK; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +UNLOCK TABLES; +DROP TABLE t1; +# # End of 6.0 tests. # diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index eaba2693904..4d610559077 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -385,6 +385,38 @@ alter table t1 add column j int; unlock tables; drop table t1; +--echo # +--echo # Bug#45066 FLUSH TABLES WITH READ LOCK deadlocks against +--echo # LOCK TABLE +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1(a INT); + +LOCK TABLE t1 READ; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +FLUSH TABLES; + +LOCK TABLE t1 WRITE; +FLUSH TABLES; + +--echo # +--echo # If you allow the next combination, you reintroduce bug Bug#45066 +--echo # +LOCK TABLE t1 READ; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +FLUSH TABLES WITH READ LOCK; + +LOCK TABLE t1 WRITE; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +FLUSH TABLES WITH READ LOCK; + +UNLOCK TABLES; +DROP TABLE t1; + --echo # --echo # End of 6.0 tests. --echo # -- cgit v1.2.1 From 4f48b423c251de10fd566a83768571c8efba46e1 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 8 Dec 2009 14:22:26 +0100 Subject: Backport of revno: 2617.62.1 Bug #39675 rename tables on innodb tables with pending transactions causes slave data issue Bug was already fixed as part of patch for Bug#989 (If DROP TABLE while there's an active transaction, wrong binlog order) Test case added to rpl_innodb.test. --- mysql-test/suite/rpl/r/rpl_innodb.result | 45 +++++++++++++++++++++ mysql-test/suite/rpl/t/rpl_innodb.test | 67 +++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 1 deletion(-) (limited to 'mysql-test') diff --git a/mysql-test/suite/rpl/r/rpl_innodb.result b/mysql-test/suite/rpl/r/rpl_innodb.result index bf6c3cb8c86..0d83f29d0fb 100644 --- a/mysql-test/suite/rpl/r/rpl_innodb.result +++ b/mysql-test/suite/rpl/r/rpl_innodb.result @@ -82,3 +82,48 @@ FLUSH LOGS; FLUSH LOGS; DROP DATABASE mysqltest1; End of 5.1 tests +# +# Bug#39675 rename tables on innodb tables with pending +# transactions causes slave data issue. +# +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t3; +CREATE TABLE t1 ( +id INT PRIMARY KEY auto_increment, +b INT DEFAULT NULL +) ENGINE=InnoDB; +CREATE TABLE t2 ( +id INT PRIMARY KEY auto_increment, +b INT DEFAULT NULL +) ENGINE=InnoDB; +INSERT INTO t1 (b) VALUES (1),(2),(3); +BEGIN; +INSERT INTO t1(b) VALUES (4); +-------- switch to master1 -------- +RENAME TABLE t1 TO t3, t2 TO t1;; +-------- switch to master -------- +COMMIT; +-------- switch to master1 -------- +-------- switch to master -------- +SELECT * FROM t1; +id b +SELECT * FROM t3; +id b +1 1 +2 2 +3 3 +4 4 +-------- switch to slave -------- +SELECT * FROM t1; +id b +SELECT * FROM t3; +id b +1 1 +2 2 +3 3 +4 4 +-------- switch to master -------- +DROP TABLE t1; +DROP TABLE t3; +End of 6.0 tests diff --git a/mysql-test/suite/rpl/t/rpl_innodb.test b/mysql-test/suite/rpl/t/rpl_innodb.test index 64a85d27c88..7ee65027abc 100644 --- a/mysql-test/suite/rpl/t/rpl_innodb.test +++ b/mysql-test/suite/rpl/t/rpl_innodb.test @@ -120,6 +120,71 @@ connection master; FLUSH LOGS; DROP DATABASE mysqltest1; --- source include/master-slave-end.inc --echo End of 5.1 tests + +--echo # +--echo # Bug#39675 rename tables on innodb tables with pending +--echo # transactions causes slave data issue. +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t3; +--enable_warnings + +CREATE TABLE t1 ( + id INT PRIMARY KEY auto_increment, + b INT DEFAULT NULL +) ENGINE=InnoDB; + +CREATE TABLE t2 ( + id INT PRIMARY KEY auto_increment, + b INT DEFAULT NULL +) ENGINE=InnoDB; + +INSERT INTO t1 (b) VALUES (1),(2),(3); + +BEGIN; +INSERT INTO t1(b) VALUES (4); + +--echo -------- switch to master1 -------- +connection master1; +--send RENAME TABLE t1 TO t3, t2 TO t1; + +--echo -------- switch to master -------- +connection master; +# Need to wait until RENAME is received +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE info = "RENAME TABLE t1 TO t3, t2 TO t1" and + state = "Waiting for table"; +--source include/wait_condition.inc + +COMMIT; + +--echo -------- switch to master1 -------- +connection master1; +--reap + +--echo -------- switch to master -------- +connection master; +SELECT * FROM t1; +SELECT * FROM t3; + +sync_slave_with_master; + +--echo -------- switch to slave -------- +connection slave; +SELECT * FROM t1; +SELECT * FROM t3; + +--echo -------- switch to master -------- +connection master; +DROP TABLE t1; +DROP TABLE t3; + +--echo End of 6.0 tests + +--source include/master-slave-end.inc -- cgit v1.2.1 From 502b4ea47f2d1bdc96c34ce77ce6d25d3df9e1dc Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 8 Dec 2009 14:27:33 +0100 Subject: Backport of revno: 2617.65.1 Bug #22876 Four-way deadlock This bug was fixed as a part of Bug#989 "If DROP TABLE while there's an active transaction, wrong binlog order" A statement which would have caused circular wait will now be aborted with ER_LOCK_DEADLOCK. Test case based on bug description added to innodb_mysql_lock.test. Note that innodb_lock_wait_timeout is set to 5 mins to prevent race conditions in the test. --- mysql-test/r/innodb_mysql_lock.result | 24 +++++++++++++ mysql-test/t/innodb_mysql_lock-master.opt | 1 + mysql-test/t/innodb_mysql_lock.test | 58 +++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 mysql-test/r/innodb_mysql_lock.result create mode 100644 mysql-test/t/innodb_mysql_lock-master.opt create mode 100644 mysql-test/t/innodb_mysql_lock.test (limited to 'mysql-test') diff --git a/mysql-test/r/innodb_mysql_lock.result b/mysql-test/r/innodb_mysql_lock.result new file mode 100644 index 00000000000..147267d5550 --- /dev/null +++ b/mysql-test/r/innodb_mysql_lock.result @@ -0,0 +1,24 @@ +# +# Bug #22876 Four-way deadlock +# +DROP TABLE IF EXISTS t1; +# Connection 1 +set @@autocommit=0; +CREATE TABLE t1(s1 INT UNIQUE) ENGINE=innodb; +INSERT INTO t1 VALUES (1); +# Connection 2 +set @@autocommit=0; +INSERT INTO t1 VALUES (2); +INSERT INTO t1 VALUES (1); +# Connection 3 +set @@autocommit=0; +DROP TABLE t1; +# Connection 1 +# Connection 1 is now holding the lock. +# Issuing insert from connection 1 while connection 2&3 +# is waiting for the lock should give a deadlock error. +INSERT INTO t1 VALUES (2); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Cleanup +commit; +commit; diff --git a/mysql-test/t/innodb_mysql_lock-master.opt b/mysql-test/t/innodb_mysql_lock-master.opt new file mode 100644 index 00000000000..0041949b829 --- /dev/null +++ b/mysql-test/t/innodb_mysql_lock-master.opt @@ -0,0 +1 @@ +--innodb_lock_wait_timeout=300 diff --git a/mysql-test/t/innodb_mysql_lock.test b/mysql-test/t/innodb_mysql_lock.test new file mode 100644 index 00000000000..daee94bedb5 --- /dev/null +++ b/mysql-test/t/innodb_mysql_lock.test @@ -0,0 +1,58 @@ +-- source include/have_innodb.inc + +--echo # +--echo # Bug #22876 Four-way deadlock +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); +connect (con3,localhost,root,,); + +--echo # Connection 1 +connection con1; +set @@autocommit=0; +CREATE TABLE t1(s1 INT UNIQUE) ENGINE=innodb; +INSERT INTO t1 VALUES (1); + +--echo # Connection 2 +connection con2; +set @@autocommit=0; +INSERT INTO t1 VALUES (2); +--send INSERT INTO t1 VALUES (1) + +--echo # Connection 3 +connection con3; +set @@autocommit=0; +--send DROP TABLE t1 + +--echo # Connection 1 +connection con1; +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE info = "INSERT INTO t1 VALUES (1)" and + state = "update"; +--source include/wait_condition.inc +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE info = "DROP TABLE t1" and + state = "Waiting for table"; +--source include/wait_condition.inc +--echo # Connection 1 is now holding the lock. +--echo # Issuing insert from connection 1 while connection 2&3 +--echo # is waiting for the lock should give a deadlock error. +--error ER_LOCK_DEADLOCK +INSERT INTO t1 VALUES (2); + +--echo # Cleanup +connection con2; +--reap +commit; +connection con1; +commit; +connection con3; +--reap +connection default; -- cgit v1.2.1 From 22630531cbea0bf85217ecba4e2670b7e3e21bbb Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 8 Dec 2009 16:57:25 +0300 Subject: Backport of revid 2617.69.21, 2617.69.22, 2617.29.23: ---------------------------------------------------------- revno: 2617.69.21 committer: Konstantin Osipov branch nick: 5.4-4284-1-assert timestamp: Thu 2009-08-13 20:13:55 +0400 message: A fix and a test case for Bug#46610 "MySQL 5.4.4: MyISAM MRG engine crash on auto-repair of child". Also fixes Bug#42862 "Crash on failed attempt to open a children of a merge table". MERGE engine needs to extend the global table list with TABLE_LIST elements for child tables, so that they are opened and locked. Previously these table list elements were allocated in memory of ha_myisammrg object (MERGE engine handler). That would lead to access to freed memory in recover_from_failed_open_table_attempt(), which would try to recover a MERGE table child (MyISAM table) and use for that TABLE_LIST of that child. But by the time recover_from_failed_open_table_attempt() is invoked, ha_myisammrg object that owns this TABLE_LIST may be destroyed, and thus TABLE_LIST memory freed. The fix is to ensure that TABLE_LIST elements that are added to the global table list (lex->query_tables) are always allocated in thd->mem_root, which is not destroyed until end of execution. If previously TABLE_LIST elements were allocated at ha_myisammrg::open() (i.e. when the TABLE object was created and added to the table cache), now they are allocated in ha_myisammrg::add_chidlren_list() (i.e. right after "open" of the merge parent in open_tables()). We still create a list of children names at ha_myisammrg::open() to use as a basis for creation of TABLE_LISTs, that allows to avoid reading the merge handler data file on every execution. --- mysql-test/r/merge_recover.result | 103 +++++++++++++++++++++++++++++++ mysql-test/t/merge_recover-master.opt | 1 + mysql-test/t/merge_recover.test | 113 ++++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 mysql-test/r/merge_recover.result create mode 100644 mysql-test/t/merge_recover-master.opt create mode 100644 mysql-test/t/merge_recover.test (limited to 'mysql-test') diff --git a/mysql-test/r/merge_recover.result b/mysql-test/r/merge_recover.result new file mode 100644 index 00000000000..871c12ca4c0 --- /dev/null +++ b/mysql-test/r/merge_recover.result @@ -0,0 +1,103 @@ +# +# Test of MyISAM MRG tables with corrupted children. +# Run with --myisam-recover=force option. +# +# Preparation: we need to make sure that the merge parent +# is never left in the table cache when closed, since this may +# have effect on merge children. +# For that, we set the table cache to minimal size and populate it +# in a concurrent connection. +# +# Switching to connection con1 +# +# +# Minimal values. +# +call mtr.add_suppression("Got an error from thread_id=.*ha_myisam.cc:"); +call mtr.add_suppression("MySQL thread id .*, query id .* localhost.*root Checking table"); +call mtr.add_suppression(" '\..test.t1'"); +set global table_open_cache=256; +set global table_definition_cache=400; +drop procedure if exists p_create; +create procedure p_create() +begin +declare i int default 1; +set @lock_table_stmt="lock table "; +set @drop_table_stmt="drop table "; +while i < @@global.table_definition_cache + 1 do +set @table_name=concat("t_", i); +set @opt_comma=if(i=1, "", ", "); +set @lock_table_stmt=concat(@lock_table_stmt, @opt_comma, +@table_name, " read"); +set @drop_table_stmt=concat(@drop_table_stmt, @opt_comma, @table_name); +set @create_table_stmt=concat("create table if not exists ", +@table_name, " (a int)"); +prepare stmt from @create_table_stmt; +execute stmt; +deallocate prepare stmt; +set i= i+1; +end while; +end| +call p_create(); +drop procedure p_create; +# +# Switching to connection 'default' +# +# +# We have to disable the ps-protocol, to avoid +# "Prepared statement needs to be re-prepared" errors +# -- table def versions change all the time with full table cache. +# +drop table if exists t1, t1_mrg, t1_copy; +# +# Prepare a MERGE engine table, that refers to a corrupted +# child. +# +create table t1 (a int, key(a)) engine=myisam; +create table t1_mrg (a int) union (t1) engine=merge; +# +# Create a table with a corrupted index file: +# save an old index file, insert more rows, +# overwrite the new index file with the old one. +# +insert into t1 (a) values (1), (2), (3); +flush table t1; +insert into t1 (a) values (4), (5), (6); +flush table t1; +# check table is needed to mark the table as crashed. +check table t1; +Table Op Msg_type Msg_text +test.t1 check warning Size of datafile is: 42 Should be: 21 +test.t1 check error Record-count is not ok; is 6 Should be: 3 +test.t1 check warning Found 6 key parts. Should be: 3 +test.t1 check error Corrupt +# +# At this point we have a merge table t1_mrg pointing to t1, +# and t1 is corrupted, and will be auto-repaired at open. +# Check that this doesn't lead to memory corruption. +# +select * from t1_mrg; +a +1 +2 +3 +4 +5 +6 +Warnings: +Error 145 Table 't1' is marked as crashed and should be repaired +Error 1194 Table 't1' is marked as crashed and should be repaired +Error 1034 Number of rows changed from 3 to 6 +# +# Cleanup +# +drop table t1, t1_mrg; +# +# Switching to connection con1 +# +unlock tables; +prepare stmt from @drop_table_stmt; +execute stmt; +deallocate prepare stmt; +set @@global.table_definition_cache=default; +set @@global.table_open_cache=default; diff --git a/mysql-test/t/merge_recover-master.opt b/mysql-test/t/merge_recover-master.opt new file mode 100644 index 00000000000..875a25ad513 --- /dev/null +++ b/mysql-test/t/merge_recover-master.opt @@ -0,0 +1 @@ +--myisam-recover=force diff --git a/mysql-test/t/merge_recover.test b/mysql-test/t/merge_recover.test new file mode 100644 index 00000000000..f2cb204eeb6 --- /dev/null +++ b/mysql-test/t/merge_recover.test @@ -0,0 +1,113 @@ +--echo # +--echo # Test of MyISAM MRG tables with corrupted children. +--echo # Run with --myisam-recover=force option. +--echo # +--echo # Preparation: we need to make sure that the merge parent +--echo # is never left in the table cache when closed, since this may +--echo # have effect on merge children. +--echo # For that, we set the table cache to minimal size and populate it +--echo # in a concurrent connection. +connect(con1,localhost,root,,test,,); +--echo # +--echo # Switching to connection con1 +--echo # +connection con1; +--echo # +--echo # Minimal values. +--echo # + +call mtr.add_suppression("Got an error from thread_id=.*ha_myisam.cc:"); +call mtr.add_suppression("MySQL thread id .*, query id .* localhost.*root Checking table"); +call mtr.add_suppression(" '\..test.t1'"); + +set global table_open_cache=256; +set global table_definition_cache=400; +--disable_warnings +drop procedure if exists p_create; +--enable_warnings +delimiter |; +create procedure p_create() +begin + declare i int default 1; + set @lock_table_stmt="lock table "; + set @drop_table_stmt="drop table "; + while i < @@global.table_definition_cache + 1 do + set @table_name=concat("t_", i); + set @opt_comma=if(i=1, "", ", "); + set @lock_table_stmt=concat(@lock_table_stmt, @opt_comma, + @table_name, " read"); + set @drop_table_stmt=concat(@drop_table_stmt, @opt_comma, @table_name); + set @create_table_stmt=concat("create table if not exists ", + @table_name, " (a int)"); + prepare stmt from @create_table_stmt; + execute stmt; + deallocate prepare stmt; + set i= i+1; + end while; +end| +delimiter ;| +call p_create(); +drop procedure p_create; +--disable_query_log +let $lock=`select @lock_table_stmt`; +eval $lock; +--enable_query_log +--echo # +--echo # Switching to connection 'default' +--echo # +connection default; +--echo # +--echo # We have to disable the ps-protocol, to avoid +--echo # "Prepared statement needs to be re-prepared" errors +--echo # -- table def versions change all the time with full table cache. +--echo # +--disable_ps_protocol +--disable_warnings +drop table if exists t1, t1_mrg, t1_copy; +--enable_warnings +let $MYSQLD_DATADIR=`select @@datadir`; +--echo # +--echo # Prepare a MERGE engine table, that refers to a corrupted +--echo # child. +--echo # +create table t1 (a int, key(a)) engine=myisam; +create table t1_mrg (a int) union (t1) engine=merge; +--echo # +--echo # Create a table with a corrupted index file: +--echo # save an old index file, insert more rows, +--echo # overwrite the new index file with the old one. +--echo # +insert into t1 (a) values (1), (2), (3); +flush table t1; +--copy_file $MYSQLD_DATADIR/test/t1.MYI $MYSQLD_DATADIR/test/t1_copy.MYI +insert into t1 (a) values (4), (5), (6); +flush table t1; +--remove_file $MYSQLD_DATADIR/test/t1.MYI +--copy_file $MYSQLD_DATADIR/test/t1_copy.MYI $MYSQLD_DATADIR/test/t1.MYI +--remove_file $MYSQLD_DATADIR/test/t1_copy.MYI +--echo # check table is needed to mark the table as crashed. +check table t1; +--echo # +--echo # At this point we have a merge table t1_mrg pointing to t1, +--echo # and t1 is corrupted, and will be auto-repaired at open. +--echo # Check that this doesn't lead to memory corruption. +--echo # +--replace_regex /'.*[\/\\]/'/ +select * from t1_mrg; +--echo # +--echo # Cleanup +--echo # +drop table t1, t1_mrg; +--echo # +--echo # Switching to connection con1 +--echo # +connection con1; +unlock tables; +prepare stmt from @drop_table_stmt; +execute stmt; +deallocate prepare stmt; +set @@global.table_definition_cache=default; +set @@global.table_open_cache=default; +disconnect con1; +connection default; +--enable_ps_protocol -- cgit v1.2.1 From 9e1cb3fe1b19aad0bd6cb0daf56f3243afd69c13 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 8 Dec 2009 15:56:06 +0100 Subject: Backport of revno: 2617.68.9 Bug #43272 HANDLER SQL command does not work under LOCK TABLES HANDLER commands are now explicitly disallowed in LOCK TABLES mode. Before, HANDLER OPEN gave the misleading error message: "Table x was not locked with LOCK TABLES". This patch changes HANDLER OPEN/READ/CLOSE to give ER_LOCK_OR_ACTIVE_TRANSACTION "Can't execute the given command because you have active locked tables or an active transaction" in LOCK TABLES mode. Test case added to lock.test. --- mysql-test/r/lock.result | 15 +++++++++++++++ mysql-test/t/lock.test | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index afb444f8ae9..a542d70c5b9 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -319,6 +319,21 @@ alter table t1 add column j int; unlock tables; drop table t1; # +# Bug #43272 HANDLER SQL command does not work under LOCK TABLES +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a INT); +LOCK TABLE t1 WRITE; +# HANDLER commands are not allowed in LOCK TABLES mode +HANDLER t1 OPEN; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +HANDLER t1 READ FIRST; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +HANDLER t1 CLOSE; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +UNLOCK TABLES; +DROP TABLE t1; +# # Bug#45066 FLUSH TABLES WITH READ LOCK deadlocks against # LOCK TABLE # diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 4d610559077..64003c9d861 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -385,6 +385,30 @@ alter table t1 add column j int; unlock tables; drop table t1; + +--echo # +--echo # Bug #43272 HANDLER SQL command does not work under LOCK TABLES +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (a INT); +LOCK TABLE t1 WRITE; + +--echo # HANDLER commands are not allowed in LOCK TABLES mode +--error ER_LOCK_OR_ACTIVE_TRANSACTION +HANDLER t1 OPEN; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +HANDLER t1 READ FIRST; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +HANDLER t1 CLOSE; + +UNLOCK TABLES; +DROP TABLE t1; + + --echo # --echo # Bug#45066 FLUSH TABLES WITH READ LOCK deadlocks against --echo # LOCK TABLE -- cgit v1.2.1 From 8817b0d3af8333afc834ab0f44a1f713241e244b Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 09:32:29 +0100 Subject: Backport of revno: 2599.169.2 Bug #42074 concurrent optimize table and alter table = Assertion failed: thd->is_error() This assertion could occur if OPTIMIZE TABLE was started on a InnoDB table and the table was altered to different storage engine after OPTIMIZE had started. This allowed OPTIMIZE to pass the initial checks for storage engine support, but fail once it reached "recreate+analyze" if this operation was not supported by the new storage engine. The bug had no consequences for non-debug builds of the server. In detail, the assertion was triggered when ha_analyze() returned HA_ADMIN_NOT_IMPLEMENTED. This led to a code path which included an assert checking for diagnostics area contents. Since this area had not been filled, the assertion was triggered. The diagnostics area is in this case only used to provide more detailed information about why optimize failed. The triggered code path sends this information to the client and clears the diagnostic area. This patch fixed the problem by adding an error message to the diagnostic area if ha_analyze() fails. This error message contains the error code returned by ha_analyze(). Test case added to innodb_mysql_sync.test. --- mysql-test/r/innodb_mysql_sync.result | 26 +++++++++++++++++++ mysql-test/t/innodb_mysql_sync.test | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 mysql-test/r/innodb_mysql_sync.result create mode 100644 mysql-test/t/innodb_mysql_sync.test (limited to 'mysql-test') diff --git a/mysql-test/r/innodb_mysql_sync.result b/mysql-test/r/innodb_mysql_sync.result new file mode 100644 index 00000000000..039d8b74c07 --- /dev/null +++ b/mysql-test/r/innodb_mysql_sync.result @@ -0,0 +1,26 @@ +# +# Bug 42074 concurrent optimize table and +# alter table = Assertion failed: thd->is_error() +# +DROP TABLE IF EXISTS t1; +# Create InnoDB table +CREATE TABLE t1 (id INT) engine=innodb; +# Connection 1 +# Start optimizing table +SET DEBUG_SYNC='ha_admin_try_alter SIGNAL optimize_started WAIT_FOR table_altered'; +OPTIMIZE TABLE t1; +# Connection 2 +# Change table to engine=memory +SET DEBUG_SYNC='now WAIT_FOR optimize_started'; +ALTER TABLE t1 engine=memory; +SET DEBUG_SYNC='now SIGNAL table_altered'; +# Connection 1 +# Complete optimization +Table Op Msg_type Msg_text +test.t1 optimize note Table does not support optimize, doing recreate + analyze instead +test.t1 optimize error Got error -1 from storage engine +test.t1 optimize status Operation failed +Warnings: +Error 1030 Got error -1 from storage engine +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; diff --git a/mysql-test/t/innodb_mysql_sync.test b/mysql-test/t/innodb_mysql_sync.test new file mode 100644 index 00000000000..3f061e30293 --- /dev/null +++ b/mysql-test/t/innodb_mysql_sync.test @@ -0,0 +1,48 @@ +# +# Test file for InnoDB tests that require the debug sync facility +# +--source include/have_innodb.inc +--source include/have_debug_sync.inc +# Save the initial number of concurrent sessions. +--source include/count_sessions.inc + + +--echo # +--echo # Bug 42074 concurrent optimize table and +--echo # alter table = Assertion failed: thd->is_error() +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +--echo # Create InnoDB table +CREATE TABLE t1 (id INT) engine=innodb; +connect (con2, localhost, root); + +--echo # Connection 1 +--echo # Start optimizing table +connection default; +SET DEBUG_SYNC='ha_admin_try_alter SIGNAL optimize_started WAIT_FOR table_altered'; +--send OPTIMIZE TABLE t1 + +--echo # Connection 2 +--echo # Change table to engine=memory +connection con2; +SET DEBUG_SYNC='now WAIT_FOR optimize_started'; +ALTER TABLE t1 engine=memory; +SET DEBUG_SYNC='now SIGNAL table_altered'; + +--echo # Connection 1 +--echo # Complete optimization +connection default; +--reap + +disconnect con2; +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; + + +# 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 -- cgit v1.2.1 From 0104ea3ca022629c15d9b59ced959e2e638ac177 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 12:29:36 +0300 Subject: ---------------------------------------------------------- revno: 2617.69.33 committer: Konstantin Osipov branch nick: mysql-next-46452 timestamp: Wed 2009-08-19 18:39:31 +0400 message: Bug#46452 "Crash in MDL, HANDLER OPEN + TRUNCATE TABLE". Flush open HANDLER tables before TRUNCATE, which is a DDL. --- mysql-test/r/truncate.result | 14 ++++++++++++++ mysql-test/t/truncate.test | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/truncate.result b/mysql-test/r/truncate.result index b194f9b7dc6..8ce2ad9be21 100644 --- a/mysql-test/r/truncate.result +++ b/mysql-test/r/truncate.result @@ -60,3 +60,17 @@ truncate table v1; ERROR 42S02: Table 'test.v1' doesn't exist drop view v1; drop table t1; +# +# Bug#46452 Crash in MDL, HANDLER OPEN + TRUNCATE TABLE +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 AS SELECT 1 AS f1; +HANDLER t1 OPEN; +# Here comes the crash. +TRUNCATE t1; +# Currently TRUNCATE, just like other DDL, implicitly closes +# open HANDLER table. +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +DROP TABLE t1; +# End of 6.0 tests diff --git a/mysql-test/t/truncate.test b/mysql-test/t/truncate.test index ba5364bd324..feec4051e35 100644 --- a/mysql-test/t/truncate.test +++ b/mysql-test/t/truncate.test @@ -69,3 +69,26 @@ drop table t1; # End of 5.0 tests +--echo # +--echo # Bug#46452 Crash in MDL, HANDLER OPEN + TRUNCATE TABLE +--echo # +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 AS SELECT 1 AS f1; + +HANDLER t1 OPEN; +--echo # Here comes the crash. +TRUNCATE t1; + +--echo # Currently TRUNCATE, just like other DDL, implicitly closes +--echo # open HANDLER table. +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +# Cleanup +DROP TABLE t1; + +--echo # End of 6.0 tests + -- cgit v1.2.1 From 7fa9f986671a2b434a5f824baed27fa654ba7150 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 12:37:54 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2617.69.32 committer: Dmitry Lenev branch nick: mysql-next-bg46747 timestamp: Wed 2009-08-19 18:12:27 +0400 message: Fix for bug #46747 "Crash in MDL_ticket::upgrade_shared_lock_to_exclusive on TRIGGER + TEMP table". Server crashed when one tried to drop trigger which had its subject table shadowed by a temporary table with the same name. This problem occured because in such situation DROP TRIGGER has opened temporary table instead of base table on which trigger was defined. Attempt to upgrade metadata lock on this temporary table led to crash (we don't acquire metadata locks for temporary tables). This fix ensures that DROP TRIGGER ignores temporary tables when trying to open table on which trigger to be dropped is defined. --- mysql-test/r/trigger.result | 20 ++++++++++++++++++++ mysql-test/t/trigger.test | 25 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 47b50b233b3..5c63604a325 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -2162,3 +2162,23 @@ Warning 1265 Data truncated for column 'trg2' at row 1 DROP TRIGGER trg1; DROP TRIGGER trg2; DROP TABLE t1; +# +# Bug #46747 "Crash in MDL_ticket::upgrade_shared_lock_to_exclusive +# on TRIGGER + TEMP table". +# +drop trigger if exists t1_bi; +drop temporary table if exists t1; +drop table if exists t1; +create table t1 (i int); +create trigger t1_bi before insert on t1 for each row set @a:=1; +# Create temporary table which shadows base table with trigger. +create temporary table t1 (j int); +# Dropping of trigger should succeed. +drop trigger t1_bi; +select trigger_name from information_schema.triggers +where event_object_schema = 'test' and event_object_table = 't1'; +trigger_name +# Clean-up. +drop temporary table t1; +drop table t1; +End of 6.0 tests. diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index f3b9d6bb91e..7ff61bd96e1 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -2489,3 +2489,28 @@ DROP TRIGGER trg1; DROP TRIGGER trg2; DROP TABLE t1; + +--echo # +--echo # Bug #46747 "Crash in MDL_ticket::upgrade_shared_lock_to_exclusive +--echo # on TRIGGER + TEMP table". +--echo # + +--disable_warnings +drop trigger if exists t1_bi; +drop temporary table if exists t1; +drop table if exists t1; +--enable_warnings + +create table t1 (i int); +create trigger t1_bi before insert on t1 for each row set @a:=1; +--echo # Create temporary table which shadows base table with trigger. +create temporary table t1 (j int); +--echo # Dropping of trigger should succeed. +drop trigger t1_bi; +select trigger_name from information_schema.triggers + where event_object_schema = 'test' and event_object_table = 't1'; +--echo # Clean-up. +drop temporary table t1; +drop table t1; + +--echo End of 6.0 tests. -- cgit v1.2.1 From e1992b30269fea83c1519e39de700ed51793623f Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 10:44:01 +0100 Subject: Backport of revno: 2617.68.39 Bug #47249 assert in MDL_global_lock::is_lock_type_compatible This assert could be triggered if LOCK TABLES were used to lock both a table and a view that used the same table. The table would have to be first WRITE locked and then READ locked. So "LOCK TABLES v1 WRITE, t1 READ" would eventually trigger the assert, "LOCK TABLES v1 READ, t1 WRITE" would not. The reason is that the ordering of locks in the interal representation made a difference when executing FLUSH TABLE on the table. During FLUSH TABLE, a lock was upgraded to exclusive. If this lock was of type MDL_SHARED and not MDL_SHARED_UPGRADABLE, an internal counter in the MDL subsystem would get out of sync. This would happen if the *last* mention of the table in LOCK TABLES was a READ lock. The counter in question is the number exclusive locks (active or intention). This is used to make sure a global metadata lock is only taken when the counter is zero (= no conflicts). The counter is increased when a MDL_EXCLUSIVE or MDL_SHARED_UPGRADABLE lock is taken, but not when upgrade_shared_lock_to_exclusive() is used to upgrade directly from MDL_SHARED to MDL_EXCLUSIVE. This patch fixes the problem by searching for a TABLE instance locked with MDL_SHARED_UPGRADABLE or MDL_EXCLUSIVE before calling upgrade_shared_lock_to_exclusive(). The patch also adds an assert checking that only MDL_SHARED_UPGRADABLE locks are upgraded to exclusive. Test case added to lock_multi.test. --- mysql-test/r/lock_multi.result | 31 ++++++++++++++++++++++ mysql-test/t/lock_multi.test | 58 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 930983bae27..ef9292ad8c0 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -219,3 +219,34 @@ flush tables with read lock;; connection: default flush tables; drop table t1; +# +# Bug#47249 assert in MDL_global_lock::is_lock_type_compatible +# +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +# +# Test 1: LOCK TABLES v1 WRITE, t1 READ; +# +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; +# Connection 2 +LOCK TABLES v1 WRITE, t1 READ; +FLUSH TABLE t1; +# Connection 1 +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; +DROP TABLE t1; +DROP VIEW v1; +# +# Test 2: LOCK TABLES t1 WRITE, v1 READ; +# +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; +# Connection 2 +LOCK TABLES t1 WRITE, v1 READ; +FLUSH TABLE t1; +# Connection 1 +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; +DROP TABLE t1; +DROP VIEW v1; diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index c82e351dfef..cbb99c04967 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -664,5 +664,63 @@ connection flush; --reap connection default; disconnect flush; + + +--echo # +--echo # Bug#47249 assert in MDL_global_lock::is_lock_type_compatible +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +--enable_warnings + +--echo # +--echo # Test 1: LOCK TABLES v1 WRITE, t1 READ; +--echo # + +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; + +--echo # Connection 2 +connect (con2,localhost,root); +LOCK TABLES v1 WRITE, t1 READ; +FLUSH TABLE t1; +disconnect con2; +--source include/wait_until_disconnected.inc + +--echo # Connection 1 +connection default; +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; # Assertion happened here + +# Cleanup +DROP TABLE t1; +DROP VIEW v1; + +--echo # +--echo # Test 2: LOCK TABLES t1 WRITE, v1 READ; +--echo # + +CREATE TABLE t1 ( f1 integer ); +CREATE VIEW v1 AS SELECT f1 FROM t1 ; + +--echo # Connection 2 +connect (con2,localhost,root); +LOCK TABLES t1 WRITE, v1 READ; +FLUSH TABLE t1; +disconnect con2; +--source include/wait_until_disconnected.inc + +--echo # Connection 1 +connection default; +LOCK TABLES t1 WRITE; +FLUSH TABLE t1; # Assertion happened here + +# Cleanup +DROP TABLE t1; +DROP VIEW v1; + + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc -- cgit v1.2.1 From 289d2bf72b7ee99b2f7e5f60e9aabb0e0d41bef3 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 12:44:05 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2617.69.37 committer: Dmitry Lenev branch nick: mysql-next-bg46748 timestamp: Fri 2009-08-21 18:17:02 +0400 message: Fix for bug #46748 "Assertion in MDL_context::wait_for_locks() on INSERT + CREATE TRIGGER". Concurrent execution of statements involving stored functions or triggers which were using several tables and DDL statements which affected those tables on debug build of server might have led to assertion failures in MDL_context::wait_for_locks(). Non-debug build was not affected. The problem was that during back-off which happens when open_tables() encounters conflicting metadata lock for one of the tables being open we didn't reset MDL_request::ticket value for requests which correspond to tables from extended prelocking set. Since these requests are part of of list of requests to be waited for in Open_table_context this broke assumption that ticket value for them is 0 in MDL_context::wait_for_locks() and caused assertion failure. This fix ensures that close_tables_for_reopen(), which performs this back-off resets MDL_request::ticket value not only for tables directly used by the statement but also for tables from extended prelocking set, thus satisfying assumption described above. --- mysql-test/r/mdl_sync.result | 43 +++++++++++++++++++++++++ mysql-test/t/mdl_sync.test | 77 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index 37f18746c57..36451985a86 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -19,3 +19,46 @@ connection: con2 ERROR 42S02: Unknown table 't1' drop table t3; SET DEBUG_SYNC= 'RESET'; +# +# Test for bug #46748 "Assertion in MDL_context::wait_for_locks() +# on INSERT + CREATE TRIGGER". +# +drop tables if exists t1, t2, t3, t4, t5; +# Let us simulate scenario in which we open some tables from extended +# part of prelocking set but then encounter conflicting metadata lock, +# so have to back-off and wait for it to go away. +create table t1 (i int); +create table t2 (j int); +create table t3 (k int); +create table t4 (l int); +create trigger t1_bi before insert on t1 for each row +insert into t2 values (new.i); +create trigger t2_bi before insert on t2 for each row +insert into t3 values (new.j); +# +# Switching to connection 'con1root'. +lock tables t4 read; +# +# Switching to connection 'con2root'. +# Send : +rename table t3 to t5, t4 to t3;; +# +# Switching to connection 'default'. +# Wait until the above RENAME TABLE adds pending requests for exclusive +# metadata lock on its tables and blocks due to 't4' being used by LOCK +# TABLES. +# Send : +insert into t1 values (1);; +# +# Switching to connection 'con1root'. +# Wait until INSERT statement waits due to encountering pending +# exclusive metadata lock on 't3'. +unlock tables; +# +# Switching to connection 'con2root'. +# Reap RENAME TABLE. +# +# Switching to connection 'default'. +# Reap INSERT. +# Clean-up. +drop tables t1, t2, t3, t5; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index ed03a9a5cb0..124e76bbc1f 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -3,6 +3,10 @@ # --source include/have_debug_sync.inc +# Save the initial number of concurrent sessions. +--source include/count_sessions.inc + + # Clean up resources used in this test case. --disable_warnings SET DEBUG_SYNC= 'RESET'; @@ -67,3 +71,76 @@ disconnect con3; --disable_warnings SET DEBUG_SYNC= 'RESET'; --enable_warnings + + +--echo # +--echo # Test for bug #46748 "Assertion in MDL_context::wait_for_locks() +--echo # on INSERT + CREATE TRIGGER". +--echo # +--disable_warnings +drop tables if exists t1, t2, t3, t4, t5; +--enable_warnings +--echo # Let us simulate scenario in which we open some tables from extended +--echo # part of prelocking set but then encounter conflicting metadata lock, +--echo # so have to back-off and wait for it to go away. +connect (con1root,localhost,root,,test,,); +connect (con2root,localhost,root,,test,,); +connection default; +create table t1 (i int); +create table t2 (j int); +create table t3 (k int); +create table t4 (l int); +create trigger t1_bi before insert on t1 for each row + insert into t2 values (new.i); +create trigger t2_bi before insert on t2 for each row + insert into t3 values (new.j); +--echo # +--echo # Switching to connection 'con1root'. +connection con1root; +lock tables t4 read; +--echo # +--echo # Switching to connection 'con2root'. +connection con2root; +--echo # Send : +--send rename table t3 to t5, t4 to t3; +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Wait until the above RENAME TABLE adds pending requests for exclusive +--echo # metadata lock on its tables and blocks due to 't4' being used by LOCK +--echo # TABLES. +let $wait_condition= select count(*)= 1 from information_schema.processlist + where state= 'Waiting for table' and + info='rename table t3 to t5, t4 to t3'; +--source include/wait_condition.inc +--echo # Send : +--send insert into t1 values (1); +--echo # +--echo # Switching to connection 'con1root'. +connection con1root; +--echo # Wait until INSERT statement waits due to encountering pending +--echo # exclusive metadata lock on 't3'. +let $wait_condition= select count(*)= 1 from information_schema.processlist + where state= 'Waiting for table' and + info='insert into t1 values (1)'; +--source include/wait_condition.inc +unlock tables; +--echo # +--echo # Switching to connection 'con2root'. +connection con2root; +--echo # Reap RENAME TABLE. +--reap +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap INSERT. +--reap +--echo # Clean-up. +disconnect con1root; +disconnect con2root; +drop tables t1, t2, t3, t5; + + +# 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 -- cgit v1.2.1 From 20f19d147f8c7b6a825b7d85541d110ecb265421 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 13:15:35 +0100 Subject: Backport of revno: 2617.76.2 Bug #47107 assert in notify_shared_lock on incorrect CREATE TABLE , HANDLER Attempts to create a table (using CREATE TABLE, CREATE TABLE LIKE or CREATE TABLE SELECT statements) which already existed and was opened by the same connection through HANDLER statement, led to a stalled connection (for production builds of the server) or to the server being aborted due to an assertion failure (for debug builds of the server). This problem was introduced by the new implementation of a metadata locking subsystem and didn't affect earlier versions of the server. The cause of the problem was that the HANDLER was not closed by CREATE TABLE before CREATE tried to open and lock the table. Acquiring an exclusive MDL lock on the table to be created would therefore fail since HANDLER already had a shared MDL lock. This triggered an assert as the HANDLER and CREATE statements came from the same thread (self-deadlock). This patch resolves the issue by closing any open HANDLERs on tables to be created by CREATE TABLE, similar to what is already done for DROP and ALTER TABLE. Test case added to create.test. --- mysql-test/r/create.result | 18 ++++++++++++++++++ mysql-test/t/create.test | 30 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index cf424b8b058..90502f94f44 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -1961,3 +1961,21 @@ END ;| ERROR 42000: This version of MySQL doesn't yet support 'multiple triggers with the same action time and event for one table' DROP TABLE t1; DROP TABLE B; +# +# Bug #47107 assert in notify_shared_lock on incorrect +# CREATE TABLE , HANDLER +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(f1 integer); +# The following CREATE TABLEs before gave an assert. +HANDLER t1 OPEN AS A; +CREATE TABLE t1 SELECT 1 AS f2; +ERROR 42S01: Table 't1' already exists +HANDLER t1 OPEN AS A; +CREATE TABLE t1(f1 integer); +ERROR 42S01: Table 't1' already exists +CREATE TABLE t2(f1 integer); +HANDLER t1 OPEN AS A; +CREATE TABLE t1 LIKE t2; +ERROR 42S01: Table 't1' already exists +DROP TABLE t2; +DROP TABLE t1; diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index c07014bfc19..21778e00ab9 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -1639,3 +1639,33 @@ END ;| DROP TABLE t1; DROP TABLE B; + + +--echo # +--echo # Bug #47107 assert in notify_shared_lock on incorrect +--echo # CREATE TABLE , HANDLER +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1(f1 integer); + +--echo # The following CREATE TABLEs before gave an assert. + +HANDLER t1 OPEN AS A; +--error ER_TABLE_EXISTS_ERROR +CREATE TABLE t1 SELECT 1 AS f2; + +HANDLER t1 OPEN AS A; +--error ER_TABLE_EXISTS_ERROR +CREATE TABLE t1(f1 integer); + +CREATE TABLE t2(f1 integer); +HANDLER t1 OPEN AS A; +--error ER_TABLE_EXISTS_ERROR +CREATE TABLE t1 LIKE t2; + +DROP TABLE t2; +DROP TABLE t1; -- cgit v1.2.1 From 81813b1d72f9ab5c2a4355ff2aa01f99d374e1fb Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 13:27:24 +0100 Subject: Backport of revno: 2617.76.3 Bug#47107 Add missing line in previous change set. --- mysql-test/r/create.result | 1 + 1 file changed, 1 insertion(+) (limited to 'mysql-test') diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index 90502f94f44..66439593c7c 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -1964,6 +1964,7 @@ DROP TABLE B; # # Bug #47107 assert in notify_shared_lock on incorrect # CREATE TABLE , HANDLER +# DROP TABLE IF EXISTS t1; CREATE TABLE t1(f1 integer); # The following CREATE TABLEs before gave an assert. -- cgit v1.2.1 From cf1110cd4a807de8e5b4aa7c8655447358f6457c Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 14:03:37 +0100 Subject: Backport of revno: 3702 Bug #48248 assert in MDL_ticket::upgrade_shared_lock_to_exclusive The assert would happen if REPAIR TABLE was used on a table already locked by LOCK TABLES READ. REPAIR mistakenly tried to upgrade the read-lock to exclusive, thereby triggering the assert. The cause of the problem was that REPAIR TABLE ignored errors from opening and locking tables. This is by design, as REPAIR can be used to broken tables that cannot be opened. However, repair also ignored logical errors such as the inability to exclusivly lock a table due to conflicting LOCK TABLES. This patch fixes the problem by not ignoring errors from opening and locking tables if inside LOCK TABLES mode. In LOCK TABLES we already know that the table can be opened, so that the failure to open must be a logical error. Test added to repair.test. --- mysql-test/r/lock.result | 2 +- mysql-test/r/repair.result | 12 ++++++++++++ mysql-test/r/view.result | 6 +++--- mysql-test/t/repair.test | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 4 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index a542d70c5b9..8f680858fdc 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -41,7 +41,7 @@ lock tables t1 write; check table t2; Table Op Msg_type Msg_text test.t2 check Error Table 't2' was not locked with LOCK TABLES -test.t2 check error Corrupt +test.t2 check status Operation failed insert into t1 select index1,nr from t1; ERROR HY000: Table 't1' was not locked with LOCK TABLES unlock tables; diff --git a/mysql-test/r/repair.result b/mysql-test/r/repair.result index 5bb3dd76fed..77eb927a21f 100644 --- a/mysql-test/r/repair.result +++ b/mysql-test/r/repair.result @@ -157,3 +157,15 @@ REPAIR TABLE tt1 USE_FRM; Table Op Msg_type Msg_text tt1 repair error Cannot repair temporary table from .frm file DROP TABLE tt1; +# +# Bug #48248 assert in MDL_ticket::upgrade_shared_lock_to_exclusive +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a INT); +LOCK TABLES t1 READ; +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair Error Table 't1' was locked with a READ lock and can't be updated +test.t1 repair status Operation failed +UNLOCK TABLES; +DROP TABLE t1; diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 5f16d88a0dc..7e9739173df 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1955,15 +1955,15 @@ CHECK TABLE v1, v2, v3, v4, v5, v6; Table Op Msg_type Msg_text test.v1 check Error FUNCTION test.f1 does not exist test.v1 check Error View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them -test.v1 check error Corrupt +test.v1 check status Operation failed test.v2 check status OK test.v3 check Error FUNCTION test.f1 does not exist test.v3 check Error View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them -test.v3 check error Corrupt +test.v3 check status Operation failed test.v4 check status OK test.v5 check Error FUNCTION test.f1 does not exist test.v5 check Error View 'test.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them -test.v5 check error Corrupt +test.v5 check status Operation failed test.v6 check status OK create function f1 () returns int return (select max(col1) from t1); DROP TABLE t1; diff --git a/mysql-test/t/repair.test b/mysql-test/t/repair.test index eb2ca7992a6..ec4c9b3cae8 100644 --- a/mysql-test/t/repair.test +++ b/mysql-test/t/repair.test @@ -158,3 +158,18 @@ CREATE TEMPORARY TABLE tt1 (c1 INT); REPAIR TABLE tt1 USE_FRM; DROP TABLE tt1; + +--echo # +--echo # Bug #48248 assert in MDL_ticket::upgrade_shared_lock_to_exclusive +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1(a INT); +LOCK TABLES t1 READ; +REPAIR TABLE t1; + +UNLOCK TABLES; +DROP TABLE t1; -- cgit v1.2.1 From 1642f67b4019c1191ecd8fa5e53302171ef6aba4 Mon Sep 17 00:00:00 2001 From: "lars-erik.bjork@sun.com" <> Date: Wed, 9 Dec 2009 14:41:56 +0100 Subject: Backport of revno: 2617.68.36 --------------------------------------------- This is a patch for bug#47098 assert in MDL_context::destroy on HANDLER OPEN. The assert occurs in MDL_context::destroy when the connection is terminated, because all mdl_tickets have not been released. MERGE tables do not support being opened using the HANDLER ... OPEN command, and trying to do so will result in an error. In the event of an error, all tables that are opened, should be closed again. The fix for bug#45781 made sure that this also works for MERGE tables, which causes multiple tables to be opened. This fix extends the fix for bug#45781, by ensuring that also all locks are released, when MERGE tables are involved. --- mysql-test/r/merge.result | 30 ++++++++++++++++++++++++++++++ mysql-test/t/merge.test | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) (limited to 'mysql-test') diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 479645744ec..3a6cd4d3f5a 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -2502,4 +2502,34 @@ c1 DROP TRIGGER t2_au; DROP FUNCTION f1; DROP TABLE tm1, t1, t2, t3, t4, t5; +# +# Bug47098 assert in MDL_context::destroy on HANDLER +# OPEN +# +# Test that merge tables are closed correctly when opened using +# HANDLER ... OPEN. +# The general case. +DROP TABLE IF EXISTS t1, t2, t3; +# Connection con1. +CREATE TABLE t1 (c1 int); +CREATE TABLE t2 (c1 int); +CREATE TABLE t3 (c1 int) ENGINE = MERGE UNION (t1,t2); +START TRANSACTION; +HANDLER t3 OPEN; +ERROR HY000: Table storage engine for 't3' doesn't have this option +DROP TABLE t1, t2, t3; +# Connection default. +# Disconnecting con1, all mdl_tickets must have been released. +# The bug-specific case. +# Connection con1. +CREATE TABLE t1 (c1 int); +CREATE TABLE t2 (c1 int); +CREATE TABLE t3 (c1 int) ENGINE = MERGE UNION (t1,t2); +DROP TABLE t2; +START TRANSACTION; +HANDLER t3 OPEN; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +DROP TABLE t1, t3; +# Connection default. +# Disconnecting con1, all mdl_tickets must have been released. End of 6.0 tests diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index f7ce6ba700b..2738f79247f 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -1985,6 +1985,43 @@ DROP TRIGGER t2_au; DROP FUNCTION f1; DROP TABLE tm1, t1, t2, t3, t4, t5; - +--echo # +--echo # Bug47098 assert in MDL_context::destroy on HANDLER +--echo # OPEN +--echo # +--echo # Test that merge tables are closed correctly when opened using +--echo # HANDLER ... OPEN. +--echo # The general case. +--disable_warnings +DROP TABLE IF EXISTS t1, t2, t3; +--enable_warnings +--echo # Connection con1. +connect (con1,localhost,root,,); +CREATE TABLE t1 (c1 int); +CREATE TABLE t2 (c1 int); +CREATE TABLE t3 (c1 int) ENGINE = MERGE UNION (t1,t2); +START TRANSACTION; +--error ER_ILLEGAL_HA +HANDLER t3 OPEN; +DROP TABLE t1, t2, t3; +--echo # Connection default. +connection default; +--echo # Disconnecting con1, all mdl_tickets must have been released. +disconnect con1; +--echo # The bug-specific case. +--echo # Connection con1. +connect (con1,localhost,root,,); +CREATE TABLE t1 (c1 int); +CREATE TABLE t2 (c1 int); +CREATE TABLE t3 (c1 int) ENGINE = MERGE UNION (t1,t2); +DROP TABLE t2; +START TRANSACTION; +--error ER_WRONG_MRG_TABLE +HANDLER t3 OPEN; +DROP TABLE t1, t3; +--echo # Connection default. +connection default; +--echo # Disconnecting con1, all mdl_tickets must have been released. +disconnect con1; --echo End of 6.0 tests -- cgit v1.2.1 From 1b5f2b9030e0dc19eaac22de7db429b6a7ba31ae Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 9 Dec 2009 16:13:00 +0100 Subject: Backport of revno: 2617.68.18 Bug #42147 Concurrent DML and LOCK TABLE ... READ for InnoDB table cause warnings in errlog Concurrent execution of LOCK TABLES ... READ statement and DML statements affecting the same InnoDB table on debug builds of MySQL server might lead to "Found lock of type 6 that is write and read locked" warnings appearing in error log. The problem is that the table-level locking code allows a thread to acquire TL_READ_NO_INSERT lock on a table even if there is another thread which holds TL_WRITE_ALLOW_WRITE lock on the same table. At the same time, the locking code assumes that that such locks are incompatible (for example, see check_locks()). This doesn't lead to any problems other than warnings in error log for debug builds of server since for InnoDB tables TL_READ_NO_INSERT type of lock is only used for LOCK TABLES and for this statement InnoDB also performs its own table-level locking. Unfortunately, the table lock compatibility matrix cannot be updated to disallow TL_READ_NO_INSERT when another thread holds TL_WRITE_ALLOW_WRITE without causing starvation of LOCK TABLE READ in InnoDB under high write load. This patch therefore contains no code changes. The issue will be fixed later when LOCK TABLE READ has been updated to not use table locks. This bug will therefore be marked as "To be fixed later". Code comment in thr_lock.c expanded to clarify the issue and a test case based on the bug description added to innodb_mysql_lock.test. Note that a global suppression rule has been added to both MTR v1 and v2 for the "Found lock of type 6 that is write and read locked" warning. These suppression rules must be removed once this bug is properly fixed. --- mysql-test/include/mtr_warnings.sql | 8 +++++ mysql-test/lib/v1/mtr_report.pl | 3 ++ mysql-test/r/innodb_mysql_lock.result | 32 ++++++++++++++++++ mysql-test/t/innodb_mysql_lock.test | 63 +++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/include/mtr_warnings.sql b/mysql-test/include/mtr_warnings.sql index e03e83efac2..14f1dd97830 100644 --- a/mysql-test/include/mtr_warnings.sql +++ b/mysql-test/include/mtr_warnings.sql @@ -186,6 +186,14 @@ INSERT INTO global_suppressions VALUES (": The MySQL server is running with the --secure-backup-file-priv option so it cannot execute this statement"), ("Slave: Unknown table 't1' Error_code: 1051"), + /* + BUG#42147 - Concurrent DML and LOCK TABLE ... READ for InnoDB + table cause warnings in errlog + Note: This is a temporary suppression until Bug#42147 can be + fixed properly. See bug page for more information. + */ + ("Found lock of type 6 that is write and read locked"), + ("THE_LAST_SUPPRESSION")|| diff --git a/mysql-test/lib/v1/mtr_report.pl b/mysql-test/lib/v1/mtr_report.pl index 3c78c3ca064..36aba983c34 100644 --- a/mysql-test/lib/v1/mtr_report.pl +++ b/mysql-test/lib/v1/mtr_report.pl @@ -376,6 +376,9 @@ sub mtr_report_stats ($) { /Slave: Can't DROP 'c7'.* 1091/ or /Slave: Key column 'c6'.* 1072/ or + # Warnings generated until bug#42147 is properly resolved + /Found lock of type 6 that is write and read locked/ or + # rpl_idempotency.test produces warnings for the slave. ($testname eq 'rpl.rpl_idempotency' and (/Slave: Can\'t find record in \'t1\' Error_code: 1032/ or diff --git a/mysql-test/r/innodb_mysql_lock.result b/mysql-test/r/innodb_mysql_lock.result index 147267d5550..374f67358eb 100644 --- a/mysql-test/r/innodb_mysql_lock.result +++ b/mysql-test/r/innodb_mysql_lock.result @@ -21,4 +21,36 @@ INSERT INTO t1 VALUES (2); ERROR 40001: Deadlock found when trying to get lock; try restarting transaction # Cleanup commit; +set @@autocommit=1; commit; +set @@autocommit=1; +set @@autocommit=1; +# +# Bug #42147 Concurrent DML and LOCK TABLE ... READ for InnoDB +# table cause warnings in errlog +# +# +# Note that this test for now relies on a global suppression of +# the warning "Found lock of type 6 that is write and read locked" +# This suppression rule can be removed once Bug#42147 is properly +# fixed. See bug page for more info. +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (i INT) engine= innodb; +# Connection 2 +# Get user-level lock +SELECT get_lock('bug42147_lock', 60); +get_lock('bug42147_lock', 60) +1 +# Connection 1 +INSERT INTO t1 SELECT get_lock('bug42147_lock', 60); +# Connection 2 +LOCK TABLES t1 READ; +SELECT release_lock('bug42147_lock'); +release_lock('bug42147_lock') +1 +# Connection 1 +# Connection 2 +UNLOCK TABLES; +# Connection 1 +DROP TABLE t1; diff --git a/mysql-test/t/innodb_mysql_lock.test b/mysql-test/t/innodb_mysql_lock.test index daee94bedb5..c8c38cd1ab1 100644 --- a/mysql-test/t/innodb_mysql_lock.test +++ b/mysql-test/t/innodb_mysql_lock.test @@ -1,5 +1,8 @@ -- source include/have_innodb.inc +# Save the initial number of concurrent sessions. +--source include/count_sessions.inc + --echo # --echo # Bug #22876 Four-way deadlock --echo # @@ -51,8 +54,68 @@ INSERT INTO t1 VALUES (2); connection con2; --reap commit; +set @@autocommit=1; connection con1; commit; +set @@autocommit=1; connection con3; --reap +set @@autocommit=1; connection default; + +disconnect con1; +disconnect con3; + +--echo # +--echo # Bug #42147 Concurrent DML and LOCK TABLE ... READ for InnoDB +--echo # table cause warnings in errlog +--echo # + +--echo # +--echo # Note that this test for now relies on a global suppression of +--echo # the warning "Found lock of type 6 that is write and read locked" +--echo # This suppression rule can be removed once Bug#42147 is properly +--echo # fixed. See bug page for more info. +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (i INT) engine= innodb; + +--echo # Connection 2 +--echo # Get user-level lock +connection con2; +SELECT get_lock('bug42147_lock', 60); + +--echo # Connection 1 +connection default; +--send INSERT INTO t1 SELECT get_lock('bug42147_lock', 60) + +--echo # Connection 2 +connection con2; +let $wait_condition= + SELECT COUNT(*) > 0 FROM information_schema.processlist + WHERE state = 'User lock' + AND info = 'INSERT INTO t1 SELECT get_lock(\'bug42147_lock\', 60)'; +--source include/wait_condition.inc +LOCK TABLES t1 READ; +SELECT release_lock('bug42147_lock'); + +--echo # Connection 1 +connection default; +--reap + +--echo # Connection 2 +connection con2; +UNLOCK TABLES; + +--echo # Connection 1 +connection default; +disconnect con2; +DROP TABLE t1; + +# 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 -- cgit v1.2.1 From d8af2fe42ce9670e2a8e19ac66b4cd141f86d008 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 18:48:42 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2617.68.7 committer: Dmitry Lenev branch nick: mysql-next-bg46044 timestamp: Thu 2009-08-27 10:22:17 +0400 message: Fix for bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY FOR UPDATE". Deadlock occured when during execution of query to I_S we tried to open a table or its .FRM in order to get information about it and had to wait because we have encountered exclusive metadata lock on this table held by a DDL operation from another connection which in its turn waited for some resource currently owned by connection executing this I_S query. For example, this might have happened if one under LOCK TABLES executed I_S query targeted to particular table (which was not among locked) and also concurrently tried to create this table using CREATE TABLE SELECT which had to wait for one of tables locked by the first connection. Another situation in which deadlock might have occured is when I_S query, which was executed as part of transaction, tried to get information about table which just has been dropped by concurrent DROP TABLES executed under LOCK TABLES and this DROP TABLES for its completion also had to wait transaction from the first connection. This problem stemmed from the fact that opening of tables/.FRMs for I_S filling is happening outside of connection's main MDL_context so code which tries to detect deadlocks due to conflicting metadata locks doesn't work in this case. Indeed, this led to deadlocks when during I_S filling we tried to wait for conflicting metadata lock to go away, while its owner was waiting for some resource held by connection executing I_S query. This patch solves this problem by avoiding waiting in such situation. Instead we skip this table and produce warning that information about it was omitted from I_S due to concurrent DDL operation. We still wait for conflicting metadata lock to go away when it is known that deadlock is not possible (i.e. when connection executing I_S query does not hold any metadata or table-level locks). Basically, we apply our standard deadlock avoidance technique for metadata locks to the process of filling of I_S tables but replace ER_LOCK_DEADLOCK error with a warning. Note that this change is supposed to be safe for 'mysqldump' since the only its mode which is affected by this change is --single-transaction mode is not safe in the presence of concurrent DDL anyway (and this fact is documented). Other modes are unaffected because they either use SHOW TABLES/SELECT * FROM I_S.TABLE_NAMES which do not take any metadata locks in the process of I_S table filling and thus cannot skip tables or execute I_S queries for tables which were previously locked by LOCK TABLES (or in the presence of global read lock) which excludes possibility of encountering conflicting metadata lock. --- mysql-test/r/mdl_sync.result | 111 ++++++++++++++++++++++++ mysql-test/t/mdl_sync.test | 202 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 313 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index 36451985a86..f63179b893a 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -62,3 +62,114 @@ unlock tables; # Reap INSERT. # Clean-up. drop tables t1, t2, t3, t5; +# +# Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY +# FOR UPDATE" +# +drop tables if exists t1, t2; +create table t1 (i int); +# Let us check that we won't deadlock if during filling +# of I_S table we encounter conflicting metadata lock +# which owner is in its turn waiting for our connection. +lock tables t1 write; +# Switching to connection 'con46044'. +# Sending: +create table t2 select * from t1;; +# Switching to connection 'default'. +# Waiting until CREATE TABLE ... SELECT ... is blocked. +# First let us check that SHOW FIELDS/DESCRIBE doesn't +# gets blocked and emits and error. +show fields from t2; +ERROR HY000: Table 'test'.'t2' was skipped since its definition is being modified by concurrent DDL statement +# Now test for I_S query which reads only .FRMs. +# +# Query below should only emit a warning. +select column_name from information_schema.columns +where table_schema='test' and table_name='t2'; +column_name +Warnings: +Warning 1652 Table 'test'.'t2' was skipped since its definition is being modified by concurrent DDL statement +# Finally, test for I_S query which does full-blown table open. +# +# Query below should not be blocked. Warning message should be +# stored in the 'table_comment' column. +select table_name, table_type, auto_increment, table_comment +from information_schema.tables where table_schema='test' and table_name='t2'; +table_name table_type auto_increment table_comment +t2 BASE TABLE NULL Table 'test'.'t2' was skipped since its definition is being modified by concurre +# Switching to connection 'default'. +unlock tables; +# Switching to connection 'con46044'. +# Reaping CREATE TABLE ... SELECT ... . +drop table t2; +# +# Let us also check that queries to I_S wait for conflicting metadata +# locks to go away instead of skipping table with a warning in cases +# when deadlock is not possible. This is a nice thing from compatibility +# and ease of use points of view. +# +# We check same three queries to I_S in this new situation. +# Switching to connection 'con46044_2'. +lock tables t1 write; +# Switching to connection 'con46044'. +# Sending: +create table t2 select * from t1;; +# Switching to connection 'default'. +# Waiting until CREATE TABLE ... SELECT ... is blocked. +# Let us check that SHOW FIELDS/DESCRIBE gets blocked. +# Sending: +show fields from t2;; +# Switching to connection 'con46044_2'. +# Wait until SHOW FIELDS gets blocked. +unlock tables; +# Switching to connection 'con46044'. +# Reaping CREATE TABLE ... SELECT ... . +# Switching to connection 'default'. +# Reaping SHOW FIELDS ... +Field Type Null Key Default Extra +i int(11) YES NULL +drop table t2; +# Switching to connection 'con46044_2'. +lock tables t1 write; +# Switching to connection 'con46044'. +# Sending: +create table t2 select * from t1;; +# Switching to connection 'default'. +# Waiting until CREATE TABLE ... SELECT ... is blocked. +# Check that I_S query which reads only .FRMs gets blocked. +# Sending: +select column_name from information_schema.columns where table_schema='test' and table_name='t2';; +# Switching to connection 'con46044_2'. +# Wait until SELECT COLUMN_NAME FROM I_S.COLUMNS gets blocked. +unlock tables; +# Switching to connection 'con46044'. +# Reaping CREATE TABLE ... SELECT ... . +# Switching to connection 'default'. +# Reaping SELECT COLUMN_NAME FROM I_S.COLUMNS +column_name +i +drop table t2; +# Switching to connection 'con46044_2'. +lock tables t1 write; +# Switching to connection 'con46044'. +# Sending: +create table t2 select * from t1;; +# Switching to connection 'default'. +# Waiting until CREATE TABLE ... SELECT ... is blocked. +# Finally, check that I_S query which does full-blown table open +# also gets blocked. +# Sending: +select table_name, table_type, auto_increment, table_comment from information_schema.tables where table_schema='test' and table_name='t2';; +# Switching to connection 'con46044_2'. +# Wait until SELECT ... FROM I_S.TABLES gets blocked. +unlock tables; +# Switching to connection 'con46044'. +# Reaping CREATE TABLE ... SELECT ... . +# Switching to connection 'default'. +# Reaping SELECT ... FROM I_S.TABLES +table_name table_type auto_increment table_comment +t2 BASE TABLE NULL +drop table t2; +# Switching to connection 'default'. +# Clean-up. +drop table t1; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index 124e76bbc1f..cc03cc67f95 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -141,6 +141,208 @@ disconnect con2root; drop tables t1, t2, t3, t5; +--echo # +--echo # Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY +--echo # FOR UPDATE" +--echo # +--disable_warnings +drop tables if exists t1, t2; +--enable_warnings +connect (con46044, localhost, root,,); +connect (con46044_2, localhost, root,,); +connection default; +create table t1 (i int); + +--echo # Let us check that we won't deadlock if during filling +--echo # of I_S table we encounter conflicting metadata lock +--echo # which owner is in its turn waiting for our connection. +lock tables t1 write; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Sending: +--send create table t2 select * from t1; + +--echo # Switching to connection 'default'. +connection default; +--echo # Waiting until CREATE TABLE ... SELECT ... is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Table lock" and info = "create table t2 select * from t1"; +--source include/wait_condition.inc + +--echo # First let us check that SHOW FIELDS/DESCRIBE doesn't +--echo # gets blocked and emits and error. +--error ER_WARN_I_S_SKIPPED_TABLE +show fields from t2; + +--echo # Now test for I_S query which reads only .FRMs. +--echo # +--echo # Query below should only emit a warning. +select column_name from information_schema.columns + where table_schema='test' and table_name='t2'; + +--echo # Finally, test for I_S query which does full-blown table open. +--echo # +--echo # Query below should not be blocked. Warning message should be +--echo # stored in the 'table_comment' column. +select table_name, table_type, auto_increment, table_comment + from information_schema.tables where table_schema='test' and table_name='t2'; + +--echo # Switching to connection 'default'. +connection default; +unlock tables; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Reaping CREATE TABLE ... SELECT ... . +--reap +drop table t2; + +--echo # +--echo # Let us also check that queries to I_S wait for conflicting metadata +--echo # locks to go away instead of skipping table with a warning in cases +--echo # when deadlock is not possible. This is a nice thing from compatibility +--echo # and ease of use points of view. +--echo # +--echo # We check same three queries to I_S in this new situation. + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +lock tables t1 write; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Sending: +--send create table t2 select * from t1; + +--echo # Switching to connection 'default'. +connection default; +--echo # Waiting until CREATE TABLE ... SELECT ... is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Table lock" and info = "create table t2 select * from t1"; +--source include/wait_condition.inc + +--echo # Let us check that SHOW FIELDS/DESCRIBE gets blocked. +--echo # Sending: +--send show fields from t2; + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +--echo # Wait until SHOW FIELDS gets blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "show fields from t2"; +--source include/wait_condition.inc + +unlock tables; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Reaping CREATE TABLE ... SELECT ... . +--reap + +--echo # Switching to connection 'default'. +connection default; +--echo # Reaping SHOW FIELDS ... +--reap +drop table t2; + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +lock tables t1 write; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Sending: +--send create table t2 select * from t1; + +--echo # Switching to connection 'default'. +connection default; +--echo # Waiting until CREATE TABLE ... SELECT ... is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Table lock" and info = "create table t2 select * from t1"; +--source include/wait_condition.inc + +--echo # Check that I_S query which reads only .FRMs gets blocked. +--echo # Sending: +--send select column_name from information_schema.columns where table_schema='test' and table_name='t2'; + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +--echo # Wait until SELECT COLUMN_NAME FROM I_S.COLUMNS gets blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info like "select column_name from information_schema.columns%"; +--source include/wait_condition.inc + +unlock tables; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Reaping CREATE TABLE ... SELECT ... . +--reap + +--echo # Switching to connection 'default'. +connection default; +--echo # Reaping SELECT COLUMN_NAME FROM I_S.COLUMNS +--reap +drop table t2; + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +lock tables t1 write; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Sending: +--send create table t2 select * from t1; + +--echo # Switching to connection 'default'. +connection default; +--echo # Waiting until CREATE TABLE ... SELECT ... is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Table lock" and info = "create table t2 select * from t1"; +--source include/wait_condition.inc + +--echo # Finally, check that I_S query which does full-blown table open +--echo # also gets blocked. +--echo # Sending: +--send select table_name, table_type, auto_increment, table_comment from information_schema.tables where table_schema='test' and table_name='t2'; + +--echo # Switching to connection 'con46044_2'. +connection con46044_2; +--echo # Wait until SELECT ... FROM I_S.TABLES gets blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and + info like "select table_name, table_type, auto_increment, table_comment from information_schema.tables%"; +--source include/wait_condition.inc + +unlock tables; + +--echo # Switching to connection 'con46044'. +connection con46044; +--echo # Reaping CREATE TABLE ... SELECT ... . +--reap + +--echo # Switching to connection 'default'. +connection default; +--echo # Reaping SELECT ... FROM I_S.TABLES +--reap +drop table t2; + +--echo # Switching to connection 'default'. +connection default; +--echo # Clean-up. +disconnect con46044; +disconnect con46044_2; +drop table t1; + # 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 -- cgit v1.2.1 From 2c538778957b81ea70de57501eee61403c284e91 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 9 Dec 2009 18:56:34 +0300 Subject: Backport of: ------------------------------------------------------------ revno: 2617.68.10 committer: Dmitry Lenev branch nick: mysql-next-bg46673 timestamp: Tue 2009-09-01 19:57:05 +0400 message: Fix for bug #46673 "Deadlock between FLUSH TABLES WITH READ LOCK and DML". Deadlocks occured when one concurrently executed transactions with several statements modifying data and FLUSH TABLES WITH READ LOCK statement or SET READ_ONLY=1 statement. These deadlocks were introduced by the patch for WL 4284: "Transactional DDL locking"/Bug 989: "If DROP TABLE while there's an active transaction, wrong binlog order" which has changed FLUSH TABLES WITH READ LOCK/SET READ_ONLY=1 to wait for pending transactions. What happened was that FLUSH TABLES WITH READ LOCK blocked all further statements changing tables by setting global_read_lock global variable and has started waiting for all pending transactions to complete. Then one of those transactions tried to executed DML, detected that global_read_lock non-zero and tried to wait until global read lock will be released (i.e. global_read_lock becomes 0), indeed, this led to a deadlock. Proper solution for this problem should probably involve full integration of global read lock with metadata locking subsystem (which will allow to implement waiting for pending transactions without blocking DML in them). But since it requires significant changes another, short-term solution for the problem is implemented in this patch. Basically, this patch restores behavior of FLUSH TABLES WITH READ LOCK/ SET READ_ONLY=1 before the patch for WL 4284/bug 989. By ensuring that extra references to TABLE_SHARE are not stored for active metadata locks it changes these statements not to wait for pending transactions. As result deadlock is eliminated. Note that this does not change the fact that active FLUSH TABLES WITH READ LOCK lock or SET READ_ONLY=1 prevent modifications to tables as they also block transaction commits. --- mysql-test/r/flush_block_commit.result | 2 +- mysql-test/r/mdl_sync.result | 21 +++++++++++++++++++ mysql-test/r/read_only_innodb.result | 6 ++++-- mysql-test/t/flush_block_commit.test | 17 ++++++++-------- mysql-test/t/mdl_sync.test | 37 ++++++++++++++++++++++++++++++++++ mysql-test/t/read_only_innodb.test | 14 ++----------- 6 files changed, 73 insertions(+), 24 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/flush_block_commit.result b/mysql-test/r/flush_block_commit.result index da09d07b813..7062d05c2d7 100644 --- a/mysql-test/r/flush_block_commit.result +++ b/mysql-test/r/flush_block_commit.result @@ -17,9 +17,9 @@ COMMIT; # Verify that 'con1' was blocked and data did not move. SELECT * FROM t1; a -1 UNLOCK TABLES; # Switch to connection con1 +# Reaping COMMIT # Switch to connection con1 BEGIN; SELECT * FROM t1 FOR UPDATE; diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index f63179b893a..d409157a70b 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -173,3 +173,24 @@ drop table t2; # Switching to connection 'default'. # Clean-up. drop table t1; +# +# Test for bug #46673 "Deadlock between FLUSH TABLES WITH READ LOCK +# and DML". +# +drop tables if exists t1; +create table t1 (i int); +# Switching to connection 'con46673'. +begin; +insert into t1 values (1); +# Switching to connection 'default'. +# Statement below should not get blocked. And if after some +# changes to code it is there should not be a deadlock between +# it and transaction from connection 'con46673'. +flush tables with read lock; +unlock tables; +# Switching to connection 'con46673'. +delete from t1 where i = 1; +commit; +# Switching to connection 'default'. +# Clean-up +drop table t1; diff --git a/mysql-test/r/read_only_innodb.result b/mysql-test/r/read_only_innodb.result index 4cba98900a1..690de085bf9 100644 --- a/mysql-test/r/read_only_innodb.result +++ b/mysql-test/r/read_only_innodb.result @@ -7,10 +7,12 @@ insert into table_11733 values(11733); set global read_only=1; select @@global.read_only; @@global.read_only -0 +1 select * from table_11733 ; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +a +11733 COMMIT; +ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement set global read_only=0; drop table table_11733 ; drop user test@localhost; diff --git a/mysql-test/t/flush_block_commit.test b/mysql-test/t/flush_block_commit.test index 98bca8cdad7..0b3bede1684 100644 --- a/mysql-test/t/flush_block_commit.test +++ b/mysql-test/t/flush_block_commit.test @@ -29,26 +29,25 @@ BEGIN; INSERT INTO t1 VALUES(1); --echo # Switch to connection con2 connection con2; ---send FLUSH TABLES WITH READ LOCK +FLUSH TABLES WITH READ LOCK; --echo # Switch to connection con1 connection con1; --echo # Sending: -COMMIT; +--send COMMIT --echo # Switch to connection con2 connection con2; ---reap --echo # Wait until COMMIT gets blocked. -#let $wait_condition= -# select count(*) = 1 from information_schema.processlist -# where state = "Waiting for release of readlock" and info = "COMMIT"; -#--source include/wait_condition.inc +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for release of readlock" and info = "COMMIT"; +--source include/wait_condition.inc --echo # Verify that 'con1' was blocked and data did not move. SELECT * FROM t1; UNLOCK TABLES; --echo # Switch to connection con1 connection con1; -#--echo # Reaping COMMIT -#--reap +--echo # Reaping COMMIT +--reap # No deadlock ? diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index cc03cc67f95..d50c056fda3 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -343,6 +343,43 @@ disconnect con46044; disconnect con46044_2; drop table t1; + +--echo # +--echo # Test for bug #46673 "Deadlock between FLUSH TABLES WITH READ LOCK +--echo # and DML". +--echo # +--disable_warnings +drop tables if exists t1; +--enable_warnings +connect (con46673, localhost, root,,); +connection default; +create table t1 (i int); + +--echo # Switching to connection 'con46673'. +connection con46673; +begin; +insert into t1 values (1); + +--echo # Switching to connection 'default'. +connection default; +--echo # Statement below should not get blocked. And if after some +--echo # changes to code it is there should not be a deadlock between +--echo # it and transaction from connection 'con46673'. +flush tables with read lock; +unlock tables; + +--echo # Switching to connection 'con46673'. +connection con46673; +delete from t1 where i = 1; +commit; + +--echo # Switching to connection 'default'. +connection default; +--echo # Clean-up +disconnect con46673; +drop table t1; + + # 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/mysql-test/t/read_only_innodb.test b/mysql-test/t/read_only_innodb.test index 98e704e25c7..9e001f2b997 100644 --- a/mysql-test/t/read_only_innodb.test +++ b/mysql-test/t/read_only_innodb.test @@ -16,8 +16,6 @@ DROP TABLE IF EXISTS table_11733 ; grant CREATE, SELECT, DROP on *.* to test@localhost; connect (con1,localhost,test,,test); -connect (con2,localhost,root,,); - connection default; set global read_only=0; @@ -30,22 +28,15 @@ BEGIN; insert into table_11733 values(11733); connection default; -send set global read_only=1; - -connection con2; -let $wait_condition= - select count(*) = 1 from information_schema.processlist - where state = "Flushing tables" and info = "set global read_only=1"; ---source include/wait_condition.inc +set global read_only=1; connection con1; select @@global.read_only; ---error ER_LOCK_DEADLOCK select * from table_11733 ; +--error ER_OPTION_PREVENTS_STATEMENT COMMIT; connection default; -reap; set global read_only=0; drop table table_11733 ; drop user test@localhost; @@ -90,6 +81,5 @@ DROP TABLE t1; DROP USER test@localhost; disconnect con1; -disconnect con2; --echo echo End of 5.1 tests -- cgit v1.2.1 From b20a409c38107f2afe49c130b136828fce263336 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 11:53:20 +0100 Subject: Backport of revno: 2617.71.1 Bug#42546 Backup: RESTORE fails, thinking it finds an existing table The problem occured when a MDL locking conflict happened for a non-existent table between a CREATE and a INSERT statement. The code for CREATE interpreted this lock conflict to mean that the table existed, which meant that the statement failed when it should not have. The problem could occur for CREATE TABLE, CREATE TABLE LIKE and ALTER TABLE RENAME. This patch fixes the problem for CREATE TABLE and CREATE TABLE LIKE. It is based on code backported from the mysql-6.1-fk tree written by Dmitry Lenev. CREATE now uses normal open_and_lock_tables() code to acquire exclusive locks. This means that for the test case in the bug description, CREATE will wait until INSERT completes so that it can get the exclusive lock. This resolves the reported bug. The patch also prohibits CREATE TABLE and CREATE TABLE LIKE under LOCK TABLES. Note that this is an incompatible change and must be reflected in the documentation. Affected test cases have been updated. mdl_sync.test contains tests for CREATE TABLE and CREATE TABLE LIKE. Fixing the issue for ALTER TABLE RENAME is beyond the scope of this patch. ALTER TABLE cannot be prohibited from working under LOCK TABLES as this could seriously impact customers and a proper fix would require a significant rewrite. --- mysql-test/r/lock_multi.result | 3 +- mysql-test/r/mdl_sync.result | 60 ++++++++++++++++++++++++++ mysql-test/r/merge.result | 30 +++++++++---- mysql-test/r/ps_ddl.result | 17 ++++---- mysql-test/t/lock_multi.test | 3 +- mysql-test/t/mdl_sync.test | 95 ++++++++++++++++++++++++++++++++++++++++++ mysql-test/t/merge.test | 13 ++++-- mysql-test/t/ps_ddl.test | 11 ++--- 8 files changed, 203 insertions(+), 29 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index ef9292ad8c0..5d12e0efd64 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -72,9 +72,10 @@ CREATE TABLE t1 (c1 int); LOCK TABLE t1 WRITE; FLUSH TABLES WITH READ LOCK; CREATE TABLE t2 (c1 int); +ERROR HY000: Table 't2' was not locked with LOCK TABLES UNLOCK TABLES; UNLOCK TABLES; -DROP TABLE t1, t2; +DROP TABLE t1; CREATE TABLE t1 (c1 int); LOCK TABLE t1 WRITE; FLUSH TABLES WITH READ LOCK; diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index d409157a70b..e5447c32b7d 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -63,6 +63,66 @@ unlock tables; # Clean-up. drop tables t1, t2, t3, t5; # +# Bug#42546 - Backup: RESTORE fails, thinking it finds an existing table +# +DROP TABLE IF EXISTS t1; +set @save_log_output=@@global.log_output; +set global log_output=file; +# +# Test 1: CREATE TABLE +# +# Connection 2 +# Start insert on the not-yet existing table +# Wait after taking the MDL lock +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +INSERT INTO t1 VALUES(1,"def"); +# Connection 1 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Now INSERT has a MDL on the non-existent table t1. +# +# Continue the INSERT once CREATE waits for exclusive lock +SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish'; +# Try to create that table. +CREATE TABLE t1 (c1 INT, c2 VARCHAR(100), KEY(c1)); +# Connection 2 +# Insert fails +ERROR 42S02: Table 'test.t1' doesn't exist +# Connection 1 +SET DEBUG_SYNC= 'RESET'; +SHOW TABLES; +Tables_in_test +t1 +DROP TABLE IF EXISTS t1; +# +# Test 2: CREATE TABLE LIKE +# +CREATE TABLE t2 (c1 INT, c2 VARCHAR(100), KEY(c1)); +# Connection 2 +# Start insert on the not-yet existing table +# Wait after taking the MDL +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +INSERT INTO t1 VALUES(1,"def"); +# Connection 1 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Now INSERT has a MDL on the non-existent table t1. +# +# Continue the INSERT once CREATE waits for exclusive lock +SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish'; +# Try to create that table. +CREATE TABLE t1 LIKE t2; +# Connection 2 +# Insert fails +ERROR 42S02: Table 'test.t1' doesn't exist +# Connection 1 +SET DEBUG_SYNC= 'RESET'; +SHOW TABLES; +Tables_in_test +t1 +t2 +DROP TABLE t2; +DROP TABLE IF EXISTS t1; +set global log_output=@save_log_output; +# # Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY # FOR UPDATE" # diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 3a6cd4d3f5a..0417b91490e 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -1149,7 +1149,8 @@ SHOW CREATE TABLE t3; ERROR 42S02: Table 'test.t3' doesn't exist DROP TABLE t1, t2; # -# CREATE ... LIKE +# Bug#37371 "CREATE TABLE LIKE merge loses UNION parameter" +# Demonstrate that this is no longer the case. # # 1. Create like. CREATE TABLE t1 (c1 INT); @@ -1164,26 +1165,26 @@ SHOW CREATE TABLE t4; Table Create Table t4 CREATE TABLE `t4` ( `c1` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) INSERT INTO t4 VALUES (4); -ERROR HY000: Table 't4' is read only DROP TABLE t4; # # 1. Create like with locked tables. LOCK TABLES t3 WRITE, t2 WRITE, t1 WRITE; CREATE TABLE t4 LIKE t3; +ERROR HY000: Table 't4' was not locked with LOCK TABLES SHOW CREATE TABLE t4; ERROR HY000: Table 't4' was not locked with LOCK TABLES INSERT INTO t4 VALUES (4); ERROR HY000: Table 't4' was not locked with LOCK TABLES -UNLOCK TABLES; +CREATE TEMPORARY TABLE t4 LIKE t3; SHOW CREATE TABLE t4; -Table Create Table -t4 CREATE TABLE `t4` ( - `c1` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +INSERT INTO t4 VALUES (4); +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +UNLOCK TABLES; INSERT INTO t4 VALUES (4); -ERROR HY000: Table 't4' is read only +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist DROP TABLE t4; # # Rename child. @@ -1210,6 +1211,7 @@ c1 1 2 3 +4 RENAME TABLE t2 TO t5; SELECT * FROM t3 ORDER BY c1; ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist @@ -1219,6 +1221,7 @@ c1 1 2 3 +4 # # 3. Normal rename with locked tables. LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; @@ -1227,6 +1230,7 @@ c1 1 2 3 +4 RENAME TABLE t2 TO t5; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1234,6 +1238,7 @@ c1 1 2 3 +4 RENAME TABLE t5 TO t2; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1241,6 +1246,7 @@ c1 1 2 3 +4 UNLOCK TABLES; # # 4. Alter table rename. @@ -1253,6 +1259,7 @@ c1 1 2 3 +4 # # 5. Alter table rename with locked tables. LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; @@ -1268,6 +1275,7 @@ c1 1 2 3 +4 # # Rename parent. # @@ -1278,6 +1286,7 @@ c1 1 2 3 +4 RENAME TABLE t3 TO t5; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1285,6 +1294,7 @@ c1 1 2 3 +4 RENAME TABLE t5 TO t3; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1292,6 +1302,7 @@ c1 1 2 3 +4 # # 5. Alter table rename with locked tables. ALTER TABLE t3 RENAME TO t5; @@ -1306,6 +1317,7 @@ c1 1 2 3 +4 DROP TABLE t1, t2, t3; # # Drop locked tables. diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index f411328ed7c..3d57c8f7332 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -1755,21 +1755,21 @@ SUCCESS drop table t1; deallocate prepare stmt; -# XXX: no validation of the first table in case of -# CREATE TEMPORARY TABLE. This is a shortcoming of the current code, -# but since validation is not strictly necessary, nothing is done -# about it. -# Will be fixed as part of work on Bug#21431 "Incomplete support of -# temporary tables" create table t1 (a int); insert into t1 (a) values (1); prepare stmt from "create temporary table if not exists t2 as select * from t1"; execute stmt; drop table t2; execute stmt; +call p_verify_reprepare_count(0); +SUCCESS + execute stmt; Warnings: Note 1050 Table 't2' already exists +call p_verify_reprepare_count(1); +SUCCESS + select * from t2; a 1 @@ -1777,6 +1777,9 @@ a execute stmt; Warnings: Note 1050 Table 't2' already exists +call p_verify_reprepare_count(0); +SUCCESS + select * from t2; a 1 @@ -1790,7 +1793,7 @@ Note 1050 Table 't2' already exists select * from t2; a 1 -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); SUCCESS drop table t1; diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index cbb99c04967..31a10f89796 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -199,6 +199,7 @@ let $wait_condition= where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK"; --source include/wait_condition.inc # This must not block. +--error ER_TABLE_NOT_LOCKED CREATE TABLE t2 (c1 int); UNLOCK TABLES; # @@ -208,7 +209,7 @@ reap; UNLOCK TABLES; # connection default; -DROP TABLE t1, t2; +DROP TABLE t1; # # Test if CREATE TABLE SELECT with LOCK TABLE deadlocks. # diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index d50c056fda3..fd66f6d539d 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -141,6 +141,101 @@ disconnect con2root; drop tables t1, t2, t3, t5; +--echo # +--echo # Bug#42546 - Backup: RESTORE fails, thinking it finds an existing table +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +set @save_log_output=@@global.log_output; +set global log_output=file; + +connect(con2, localhost, root,,); + +--echo # +--echo # Test 1: CREATE TABLE +--echo # + +--echo # Connection 2 +connection con2; +--echo # Start insert on the not-yet existing table +--echo # Wait after taking the MDL lock +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +--send INSERT INTO t1 VALUES(1,"def") + +--echo # Connection 1 +connection default; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Now INSERT has a MDL on the non-existent table t1. + +--echo # +--echo # Continue the INSERT once CREATE waits for exclusive lock +SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish'; +--echo # Try to create that table. +--send CREATE TABLE t1 (c1 INT, c2 VARCHAR(100), KEY(c1)) + +--echo # Connection 2 +--echo # Insert fails +connection con2; +--error ER_NO_SUCH_TABLE +--reap + +--echo # Connection 1 +connection default; +--reap; +SET DEBUG_SYNC= 'RESET'; +SHOW TABLES; + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +--echo # +--echo # Test 2: CREATE TABLE LIKE +--echo # + +CREATE TABLE t2 (c1 INT, c2 VARCHAR(100), KEY(c1)); + +--echo # Connection 2 +connection con2; +--echo # Start insert on the not-yet existing table +--echo # Wait after taking the MDL +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +--send INSERT INTO t1 VALUES(1,"def") + +--echo # Connection 1 +connection default; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Now INSERT has a MDL on the non-existent table t1. + +--echo # +--echo # Continue the INSERT once CREATE waits for exclusive lock +SET DEBUG_SYNC= 'mdl_acquire_exclusive_locks_wait SIGNAL finish'; +--echo # Try to create that table. +--send CREATE TABLE t1 LIKE t2 + +--echo # Connection 2 +--echo # Insert fails +connection con2; +--error ER_NO_SUCH_TABLE +--reap + +--echo # Connection 1 +connection default; +--reap +SET DEBUG_SYNC= 'RESET'; +SHOW TABLES; + +DROP TABLE t2; +disconnect con2; +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +set global log_output=@save_log_output; + + --echo # --echo # Bug #46044 "MDL deadlock on LOCK TABLE + CREATE TABLE HIGH_PRIORITY --echo # FOR UPDATE" diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 2738f79247f..b9e6813a4df 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -846,7 +846,8 @@ SHOW CREATE TABLE t3; DROP TABLE t1, t2; # --echo # ---echo # CREATE ... LIKE +--echo # Bug#37371 "CREATE TABLE LIKE merge loses UNION parameter" +--echo # Demonstrate that this is no longer the case. --echo # --echo # 1. Create like. CREATE TABLE t1 (c1 INT); @@ -858,20 +859,24 @@ INSERT INTO t2 VALUES (2); INSERT INTO t3 VALUES (3); CREATE TABLE t4 LIKE t3; SHOW CREATE TABLE t4; ---error ER_OPEN_AS_READONLY INSERT INTO t4 VALUES (4); DROP TABLE t4; --echo # --echo # 1. Create like with locked tables. LOCK TABLES t3 WRITE, t2 WRITE, t1 WRITE; +--error ER_TABLE_NOT_LOCKED CREATE TABLE t4 LIKE t3; --error ER_TABLE_NOT_LOCKED SHOW CREATE TABLE t4; --error ER_TABLE_NOT_LOCKED INSERT INTO t4 VALUES (4); -UNLOCK TABLES; +CREATE TEMPORARY TABLE t4 LIKE t3; +--error ER_WRONG_MRG_TABLE SHOW CREATE TABLE t4; ---error ER_OPEN_AS_READONLY +--error ER_WRONG_MRG_TABLE +INSERT INTO t4 VALUES (4); +UNLOCK TABLES; +--error ER_WRONG_MRG_TABLE INSERT INTO t4 VALUES (4); DROP TABLE t4; # diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index 1ba193983b2..fe17bca1eba 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -1493,27 +1493,24 @@ execute stmt; call p_verify_reprepare_count(0); drop table t1; deallocate prepare stmt; ---echo # XXX: no validation of the first table in case of ---echo # CREATE TEMPORARY TABLE. This is a shortcoming of the current code, ---echo # but since validation is not strictly necessary, nothing is done ---echo # about it. ---echo # Will be fixed as part of work on Bug#21431 "Incomplete support of ---echo # temporary tables" create table t1 (a int); insert into t1 (a) values (1); prepare stmt from "create temporary table if not exists t2 as select * from t1"; execute stmt; drop table t2; execute stmt; +call p_verify_reprepare_count(0); execute stmt; +call p_verify_reprepare_count(1); select * from t2; execute stmt; +call p_verify_reprepare_count(0); select * from t2; drop table t2; create temporary table t2 (a varchar(10)); execute stmt; select * from t2; -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); drop table t1; create table t1 (x int); execute stmt; -- cgit v1.2.1 From 9e17ef84ccf4e00648eb7aab9d98dd4eab3b6e5a Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 12:46:16 +0100 Subject: Backport of revno: 2617.68.3 Followup to Bug#42546 Backup: RESTORE fails, thinking it finds an existing table This patch updates lowercase_table2.test with the changed error message CREATE TABLE produces if it fails because it finds an matching TABLE_SHARE in the TDC even if the .FRM/.MYD has been removed from disk. With the changes introduced in Bug#42546, CREATE TABLE uses open_tables() which will find the TDC entry and fail in open_table_from_share() with ER_FILE_NOT_FOUND. Before, CREATE TABLE would not use open_tables() and fail with ER_TABLE_EXISTS_ERROR upon finding the TDC entry in mysql_create_table_no_lock(). --- mysql-test/r/lowercase_table2.result | 16 +++++++++------- mysql-test/t/lowercase_table2.test | 14 +++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/lowercase_table2.result b/mysql-test/r/lowercase_table2.result index cf87fd1b5a4..b621a466a29 100644 --- a/mysql-test/r/lowercase_table2.result +++ b/mysql-test/r/lowercase_table2.result @@ -226,10 +226,9 @@ drop table t_bug44738_UPPERCASE; create table t_bug44738_UPPERCASE (i int); drop table t_bug44738_UPPERCASE; # Finally, let us check that another issue which was exposed by -# the original test case is solved. I.e. that fuse in CREATE TABLE -# which ensures that table is not created if there is an entry for -# it in TDC even though it was removed from disk uses normalized -# version of the table name. +# the original test case is solved. I.e. that the table is not +# created if there is an entry for it in TDC even though it was +# removed from disk. create table t_bug44738_UPPERCASE (i int) engine = myisam; # Load table definition in TDC. select table_schema, table_name, table_comment from information_schema.tables @@ -237,10 +236,13 @@ where table_schema = 'test' and table_name like 't_bug44738_%'; table_schema table_name table_comment test t_bug44738_UPPERCASE # Simulate manual removal of the table. -# After manual removal of table still there should be an entry for table -# in TDC so attempt to create table with the same name should fail. +# Check that still there is an entry for table in TDC. +show open tables like 't_bug44738_%'; +Database Table In_use Name_locked +test t_bug44738_uppercase 0 0 +# So attempt to create table with the same name should fail. create table t_bug44738_UPPERCASE (i int); -ERROR 42S01: Table 't_bug44738_uppercase' already exists +ERROR HY000: Can't find file: 't_bug44738_uppercase' (errno: 2) # And should succeed after FLUSH TABLES. flush tables; create table t_bug44738_UPPERCASE (i int); diff --git a/mysql-test/t/lowercase_table2.test b/mysql-test/t/lowercase_table2.test index 92add60616a..b8c7f532cde 100644 --- a/mysql-test/t/lowercase_table2.test +++ b/mysql-test/t/lowercase_table2.test @@ -201,10 +201,9 @@ create table t_bug44738_UPPERCASE (i int); drop table t_bug44738_UPPERCASE; --echo # Finally, let us check that another issue which was exposed by ---echo # the original test case is solved. I.e. that fuse in CREATE TABLE ---echo # which ensures that table is not created if there is an entry for ---echo # it in TDC even though it was removed from disk uses normalized ---echo # version of the table name. +--echo # the original test case is solved. I.e. that the table is not +--echo # created if there is an entry for it in TDC even though it was +--echo # removed from disk. create table t_bug44738_UPPERCASE (i int) engine = myisam; --echo # Load table definition in TDC. select table_schema, table_name, table_comment from information_schema.tables @@ -214,9 +213,10 @@ let $MYSQLD_DATADIR= `select @@datadir`; --remove_file $MYSQLD_DATADIR/test/t_bug44738_UPPERCASE.frm --remove_file $MYSQLD_DATADIR/test/t_bug44738_UPPERCASE.MYD --remove_file $MYSQLD_DATADIR/test/t_bug44738_UPPERCASE.MYI ---echo # After manual removal of table still there should be an entry for table ---echo # in TDC so attempt to create table with the same name should fail. ---error ER_TABLE_EXISTS_ERROR +--echo # Check that still there is an entry for table in TDC. +show open tables like 't_bug44738_%'; +--echo # So attempt to create table with the same name should fail. +--error ER_FILE_NOT_FOUND create table t_bug44738_UPPERCASE (i int); --echo # And should succeed after FLUSH TABLES. flush tables; -- cgit v1.2.1 From 3173cf335b1cca8bc2265e060c4e983c038bbd0e Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 13:02:37 +0100 Subject: Backport of revno: 2617.68.43 Bug #47335 assert in get_table_share The assert would happen if ALTER VIEW was used to alter a view (existing or non-existing) and a temporary table with the same name already existed. The assert is triggered if the current statement does not have a MDL lock on the view to be altered. This would happen because open_table() would open the temporary table instead and MDL locks are not taken for temporary tables (since they are local to one connection). The patch changes open_type for CREATE/ALTER VIEW to OT_BASE_ONLY. This prevents open_table() from trying to open a temporary table with the same name should one exist. Now the view will be altered if it exists or ER_NO_SUCH_TABLE will be reported if it does not. Test case added to view.test --- mysql-test/r/view.result | 17 +++++++++++++++++ mysql-test/t/view.test | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 7e9739173df..cb53bf462f5 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3959,3 +3959,20 @@ DROP TABLE t1; # ----------------------------------------------------------------- # -- End of 5.1 tests. # ----------------------------------------------------------------- +# +# Bug #47335 assert in get_table_share +# +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +CREATE TEMPORARY TABLE t1 (id INT); +ALTER VIEW t1 AS SELECT 1 AS f1; +ERROR 42S02: Table 'test.t1' doesn't exist +DROP TABLE t1; +CREATE VIEW v1 AS SELECT 1 AS f1; +CREATE TEMPORARY TABLE v1 (id INT); +ALTER VIEW v1 AS SELECT 2 AS f1; +DROP TABLE v1; +SELECT * FROM v1; +f1 +2 +DROP VIEW v1; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index c3ff58880c9..13a557dda60 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -3906,3 +3906,24 @@ DROP TABLE t1; --echo # ----------------------------------------------------------------- --echo # -- End of 5.1 tests. --echo # ----------------------------------------------------------------- + +--echo # +--echo # Bug #47335 assert in get_table_share +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +--enable_warnings + +CREATE TEMPORARY TABLE t1 (id INT); +--error ER_NO_SUCH_TABLE +ALTER VIEW t1 AS SELECT 1 AS f1; +DROP TABLE t1; + +CREATE VIEW v1 AS SELECT 1 AS f1; +CREATE TEMPORARY TABLE v1 (id INT); +ALTER VIEW v1 AS SELECT 2 AS f1; +DROP TABLE v1; +SELECT * FROM v1; +DROP VIEW v1; -- cgit v1.2.1 From b6fb4dbab23490eca5c181e3fb68ce61bdd36a87 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 13:15:20 +0100 Subject: Backport of revno: 2617.68.45 Bug #47635 assert in start_waiting_global_read_lock during CREATE VIEW The problem was that CREATE VIEW would trigger an assert if a temporary table with the same name already existed. This bug was fixed by the patch for Bug#47335. CREATE/ALTER VIEW will now ignore temporary tables. See Bug#47335 for more information. Test case added to view.test. --- mysql-test/r/view.result | 15 +++++++++++++++ mysql-test/t/view.test | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index cb53bf462f5..c097b70680f 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3976,3 +3976,18 @@ SELECT * FROM v1; f1 2 DROP VIEW v1; +# +# Bug #47635 assert in start_waiting_global_read_lock +# during CREATE VIEW +# +DROP TABLE IF EXISTS t1, t2; +DROP VIEW IF EXISTS t2; +CREATE TABLE t1 (f1 integer); +CREATE TEMPORARY TABLE IF NOT EXISTS t1 (f1 integer); +CREATE TEMPORARY TABLE t2 (f1 integer); +DROP TABLE t1; +FLUSH TABLES WITH READ LOCK; +CREATE VIEW t2 AS SELECT * FROM t1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +UNLOCK TABLES; +DROP TABLE t1, t2; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 13a557dda60..880b4b6a645 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1,3 +1,4 @@ + --disable_warnings drop table if exists t1,t2,t3,t4,t9,`t1a``b`,v1,v2,v3,v4,v5,v6; drop view if exists t1,t2,`t1a``b`,v1,v2,v3,v4,v5,v6; @@ -3927,3 +3928,25 @@ ALTER VIEW v1 AS SELECT 2 AS f1; DROP TABLE v1; SELECT * FROM v1; DROP VIEW v1; + + +--echo # +--echo # Bug #47635 assert in start_waiting_global_read_lock +--echo # during CREATE VIEW +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1, t2; +DROP VIEW IF EXISTS t2; +--enable_warnings + +CREATE TABLE t1 (f1 integer); +CREATE TEMPORARY TABLE IF NOT EXISTS t1 (f1 integer); +CREATE TEMPORARY TABLE t2 (f1 integer); +DROP TABLE t1; +FLUSH TABLES WITH READ LOCK; +--error ER_CANT_UPDATE_WITH_READLOCK +CREATE VIEW t2 AS SELECT * FROM t1; + +UNLOCK TABLES; +DROP TABLE t1, t2; -- cgit v1.2.1 From 1cfcd2d210b8a069496571d3d35bf3b8bd5ee67e Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 13:37:18 +0100 Subject: Backport of revno: 3673 Bug #47313 assert in check_key_in_view during CALL procedure View definitions are inlined in a stored procedure when the procedure is fist called. This means that if a temporary table is later added with the same name as the view, the stored procedure will still use the view. This happens even if temporary tables normally shadow base tables/views. The reason for the assert was that even if the stored procedure referenced the view, open_table() still tried to open the temporary table. This "half view/half temporary table" state caused the assert. The bug was not present in 5.1 as open_table() is not called for the view there. This code was changed with the introduction of MDL in order to properly lock the view and any objects it refers to. This patch fixes the problem by instructing open_table() to open base tables/views (using OT_BASE_ONLY) when reopening tables/views used by stored procedures. This also means that a prepared statement is no longer invalidated if a temporary table is created with the same name as a view used in the prepared statement. Test case added to sp.test. The test case also demonstrates the effect of sp cache invalidation between CALLs. --- mysql-test/r/ps_ddl.result | 61 +++++++++++++++++++++++++++++++++- mysql-test/r/sp.result | 59 +++++++++++++++++++++++++++++++++ mysql-test/t/ps_ddl.test | 57 +++++++++++++++++++++++++++++++- mysql-test/t/sp.test | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 256 insertions(+), 2 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index 3d57c8f7332..c72d129c8e4 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -707,6 +707,9 @@ deallocate prepare stmt; ===================================================================== Part 16: VIEW -> TEMPORARY TABLE transitions ===================================================================== +# +# Test 1: Merged view +# create table t2 (a int); insert into t2 (a) values (1); create view t1 as select * from t2; @@ -720,16 +723,72 @@ SUCCESS create temporary table t1 (a int); execute stmt; a -call p_verify_reprepare_count(1); +1 +call p_verify_reprepare_count(0); SUCCESS drop view t1; execute stmt; +ERROR 42S02: Table 'test.t1' doesn't exist +call p_verify_reprepare_count(0); +SUCCESS + +drop table t2; +drop temporary table t1; +deallocate prepare stmt; +# +# Test 2: Materialized view +# +create table t2 (a int); +insert into t2 (a) values (1); +create algorithm = temptable view t1 as select * from t2; +prepare stmt from "select * from t1"; +execute stmt; a +1 +call p_verify_reprepare_count(0); +SUCCESS + +create temporary table t1 (a int); +execute stmt; +a +1 +call p_verify_reprepare_count(0); +SUCCESS + +drop view t1; +execute stmt; +ERROR 42S02: Table 'test.t1' doesn't exist call p_verify_reprepare_count(0); SUCCESS drop table t2; +drop temporary table t1; +deallocate prepare stmt; +# +# Test 3: View referencing an Information schema table +# +create view t1 as select table_name from information_schema.views; +prepare stmt from "select * from t1"; +execute stmt; +table_name +t1 +call p_verify_reprepare_count(0); +SUCCESS + +create temporary table t1 (a int); +execute stmt; +table_name +t1 +call p_verify_reprepare_count(0); +SUCCESS + +drop view t1; +execute stmt; +table_name +call p_verify_reprepare_count(0); +SUCCESS + drop temporary table t1; deallocate prepare stmt; ===================================================================== diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index bcc738d695c..08c7831c955 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -7076,3 +7076,62 @@ SELECT routine_comment FROM information_schema.routines WHERE routine_name = "p1 routine_comment 12345678901234567890123456789012345678901234567890123456789012345678901234567890 DROP PROCEDURE p1; +# +# Bug #47313 assert in check_key_in_view during CALL procedure +# +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS t1, t2_unrelated; +DROP PROCEDURE IF EXISTS p1; +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE VIEW t1 AS SELECT 10 AS f1; +# t1 refers to the view +CALL p1(1); +ERROR HY000: The target table t1 of the INSERT is not insertable-into +CREATE TEMPORARY TABLE t1 (f1 INT); +# t1 still refers to the view since it was inlined +CALL p1(2); +ERROR HY000: The target table t1 of the INSERT is not insertable-into +DROP VIEW t1; +# t1 now refers to the temporary table +CALL p1(3); +# Check which values were inserted into the temp table. +SELECT * FROM t1; +f1 +3 +DROP TEMPORARY TABLE t1; +DROP PROCEDURE p1; +# Now test what happens if the sp cache is invalidated. +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE VIEW t1 AS SELECT 10 AS f1; +CREATE VIEW v2_unrelated AS SELECT 1 AS r1; +# Load the procedure into the sp cache +CALL p1(4); +ERROR HY000: The target table t1 of the INSERT is not insertable-into +CREATE TEMPORARY TABLE t1 (f1 int); +ALTER VIEW v2_unrelated AS SELECT 2 AS r1; +# Alter view causes the sp cache to be invalidated. +# Now t1 refers to the temporary table, not the view. +CALL p1(5); +# Check which values were inserted into the temp table. +SELECT * FROM t1; +f1 +5 +DROP TEMPORARY TABLE t1; +DROP VIEW t1, v2_unrelated; +DROP PROCEDURE p1; +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE TEMPORARY TABLE t1 (f1 INT); +# t1 refers to the temporary table +CALL p1(6); +CREATE VIEW t1 AS SELECT 10 AS f1; +# Create view causes the sp cache to be invalidated. +# t1 still refers to the temporary table since it shadows the view. +CALL p1(7); +DROP VIEW t1; +# Check which values were inserted into the temp table. +SELECT * FROM t1; +f1 +6 +7 +DROP TEMPORARY TABLE t1; +DROP PROCEDURE p1; diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index fe17bca1eba..e00d63aaedc 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -642,6 +642,9 @@ deallocate prepare stmt; --echo Part 16: VIEW -> TEMPORARY TABLE transitions --echo ===================================================================== +--echo # +--echo # Test 1: Merged view +--echo # create table t2 (a int); insert into t2 (a) values (1); create view t1 as select * from t2; @@ -651,9 +654,39 @@ execute stmt; call p_verify_reprepare_count(0); create temporary table t1 (a int); +# t1 still refers to the view - no reprepare has been done. execute stmt; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); + +drop view t1; +# t1 still refers to the, now deleted, view - no reprepare has been done. +--error ER_NO_SUCH_TABLE +execute stmt; +call p_verify_reprepare_count(0); + +drop table t2; +drop temporary table t1; +deallocate prepare stmt; + +--echo # +--echo # Test 2: Materialized view +--echo # +create table t2 (a int); +insert into t2 (a) values (1); +create algorithm = temptable view t1 as select * from t2; + +prepare stmt from "select * from t1"; +execute stmt; +call p_verify_reprepare_count(0); + +create temporary table t1 (a int); +# t1 still refers to the view - no reprepare has been done. +execute stmt; +call p_verify_reprepare_count(0); + drop view t1; +# t1 still refers to the, now deleted, view - no reprepare has been done. +--error ER_NO_SUCH_TABLE execute stmt; call p_verify_reprepare_count(0); @@ -661,6 +694,28 @@ drop table t2; drop temporary table t1; deallocate prepare stmt; +--echo # +--echo # Test 3: View referencing an Information schema table +--echo # +create view t1 as select table_name from information_schema.views; + +prepare stmt from "select * from t1"; +execute stmt; +call p_verify_reprepare_count(0); + +create temporary table t1 (a int); +# t1 has been substituted with a reference to the IS table +execute stmt; +call p_verify_reprepare_count(0); + +drop view t1; +# Since the IS table has been substituted in, the statement still works +execute stmt; +call p_verify_reprepare_count(0); + +drop temporary table t1; +deallocate prepare stmt; + --echo ===================================================================== --echo Part 17: VIEW -> VIEW (DDL) transitions --echo ===================================================================== diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 7cf2fcf9bb7..a29275eeda4 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -8433,3 +8433,84 @@ SELECT routine_comment FROM information_schema.routines WHERE routine_name = "p1 DROP PROCEDURE p1; + +--echo # +--echo # Bug #47313 assert in check_key_in_view during CALL procedure +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS t1, t2_unrelated; +DROP PROCEDURE IF EXISTS p1; +--enable_warnings + +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE VIEW t1 AS SELECT 10 AS f1; + +--echo # t1 refers to the view +--error ER_NON_INSERTABLE_TABLE +CALL p1(1); + +CREATE TEMPORARY TABLE t1 (f1 INT); + +--echo # t1 still refers to the view since it was inlined +--error ER_NON_INSERTABLE_TABLE +CALL p1(2); + +DROP VIEW t1; + +--echo # t1 now refers to the temporary table +CALL p1(3); + +--echo # Check which values were inserted into the temp table. +SELECT * FROM t1; + +DROP TEMPORARY TABLE t1; +DROP PROCEDURE p1; + +--echo # Now test what happens if the sp cache is invalidated. + +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE VIEW t1 AS SELECT 10 AS f1; +CREATE VIEW v2_unrelated AS SELECT 1 AS r1; + +--echo # Load the procedure into the sp cache +--error ER_NON_INSERTABLE_TABLE +CALL p1(4); + +CREATE TEMPORARY TABLE t1 (f1 int); + +ALTER VIEW v2_unrelated AS SELECT 2 AS r1; + +--echo # Alter view causes the sp cache to be invalidated. +--echo # Now t1 refers to the temporary table, not the view. +CALL p1(5); + +--echo # Check which values were inserted into the temp table. +SELECT * FROM t1; + +DROP TEMPORARY TABLE t1; +DROP VIEW t1, v2_unrelated; +DROP PROCEDURE p1; + +CREATE PROCEDURE p1(IN x INT) INSERT INTO t1 VALUES (x); +CREATE TEMPORARY TABLE t1 (f1 INT); + +--echo # t1 refers to the temporary table +CALL p1(6); + +CREATE VIEW t1 AS SELECT 10 AS f1; + +--echo # Create view causes the sp cache to be invalidated. +--echo # t1 still refers to the temporary table since it shadows the view. +CALL p1(7); + +DROP VIEW t1; + +--echo # Check which values were inserted into the temp table. +SELECT * FROM t1; + +DROP TEMPORARY TABLE t1; +DROP PROCEDURE p1; + + -- cgit v1.2.1 From 28b0eeff28f9ae8c834719bb852094f64f7157da Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 14:15:50 +0100 Subject: Backport of revno: 3514 Bug#40181 Made use of tdc_remove_table instead of just setting share->version to 0 to make sure all unused table instances go away as part of CREATE/ALTER TABLE. --- mysql-test/r/partition.result | 9 +++++++++ mysql-test/t/partition.test | 13 +++++++++++++ 2 files changed, 22 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 543a70f9a2a..e9bbc011f7b 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -65,6 +65,15 @@ show indexes from t1; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment t1 1 a 1 a A 1 NULL NULL YES BTREE drop table t1; +create table t1 (a int) +partition by hash (a); +create index i on t1 (a); +insert into t1 values (1); +insert into t1 select * from t1; +create index i on t1 (a); +ERROR 42000: Duplicate key name 'i' +create index i2 on t1 (a); +drop table t1; CREATE TABLE t1 (a INT, FOREIGN KEY (a) REFERENCES t0 (a)) ENGINE=MyISAM PARTITION BY HASH (a); diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 8ab91f23522..01e885a527c 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -74,6 +74,19 @@ analyze table t1; show indexes from t1; drop table t1; +# +# Bug#40181: hang if create index +# +create table t1 (a int) +partition by hash (a); +create index i on t1 (a); +insert into t1 values (1); +insert into t1 select * from t1; +--error ER_DUP_KEYNAME +create index i on t1 (a); +create index i2 on t1 (a); +drop table t1; + # # Bug#36001: Partitions: spelling and using some error messages # -- cgit v1.2.1 From 2945f773f21cfefa3f8c4805dffc459697dcc118 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 14:26:00 +0100 Subject: Backport of revno: 2617.68.37 Bug #46654 False deadlock on concurrent DML/DDL with partitions, inconsistent behavior The problem was that if one connection is running a multi-statement transaction which involves a single partitioned table, and another connection attempts to alter the table, the first connection gets ER_LOCK_DEADLOCK and cannot proceed anymore, even when the ALTER TABLE statement in another connection has timed out or failed. The reason for this was that the prepare phase for ALTER TABLE for partitioned tables removed all instances of the table from the table definition cache before it started waiting on the lock. The transaction running in the first connection would notice this and report ER_LOCK_DEADLOCK. This patch changes the prep_alter_part_table() ALTER TABLE code so that tdc_remove_table() is no longer called. Instead, only the TABLE instance changed by prep_alter_part_table() is marked as needing reopen. The patch also removes an unnecessary call to tdc_remove_table() from mysql_unpack_partition() as the changed TABLE object is destroyed by the caller at a later point. Test case added in partition_sync.test. --- mysql-test/r/partition_sync.result | 32 ++++++++++++++++++++++++ mysql-test/t/partition_sync.test | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/partition_sync.result b/mysql-test/r/partition_sync.result index 41ca19426fe..fbea2e0273a 100644 --- a/mysql-test/r/partition_sync.result +++ b/mysql-test/r/partition_sync.result @@ -1,2 +1,34 @@ # Disabled until Bug#46654 False deadlock on concurrent DML/DDL # with partitions, inconsistent behavior is backported +# +# Bug #46654 False deadlock on concurrent DML/DDL +# with partitions, inconsistent behavior +# +DROP TABLE IF EXISTS tbl_with_partitions; +CREATE TABLE tbl_with_partitions ( i INT ) +PARTITION BY HASH(i); +INSERT INTO tbl_with_partitions VALUES (1); +# Connection 3 +LOCK TABLE tbl_with_partitions READ; +# Connection 1 +# Access table with disabled autocommit +SET AUTOCOMMIT = 0; +SELECT * FROM tbl_with_partitions; +i +1 +# Connection 2 +# Alter table, abort after prepare +set session debug="+d,abort_copy_table"; +ALTER TABLE tbl_with_partitions ADD COLUMN f INT; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +# Connection 1 +# Try accessing the table after Alter aborted. +# This used to give ER_LOCK_DEADLOCK. +SELECT * FROM tbl_with_partitions; +i +1 +# Connection 3 +UNLOCK TABLES; +# Connection 1 +# Cleanup +DROP TABLE tbl_with_partitions; diff --git a/mysql-test/t/partition_sync.test b/mysql-test/t/partition_sync.test index 5d2b25e87f3..672e4cc0562 100644 --- a/mysql-test/t/partition_sync.test +++ b/mysql-test/t/partition_sync.test @@ -39,6 +39,57 @@ #DROP TABLE t1; +--echo # +--echo # Bug #46654 False deadlock on concurrent DML/DDL +--echo # with partitions, inconsistent behavior +--echo # + +--disable_warnings +DROP TABLE IF EXISTS tbl_with_partitions; +--enable_warnings + +CREATE TABLE tbl_with_partitions ( i INT ) + PARTITION BY HASH(i); +INSERT INTO tbl_with_partitions VALUES (1); + +connect(con2,localhost,root); +connect(con3,localhost,root); + +--echo # Connection 3 +connection con3; +LOCK TABLE tbl_with_partitions READ; + +--echo # Connection 1 +--echo # Access table with disabled autocommit +connection default; +SET AUTOCOMMIT = 0; +SELECT * FROM tbl_with_partitions; + +--echo # Connection 2 +--echo # Alter table, abort after prepare +connection con2; +set session debug="+d,abort_copy_table"; +--error ER_LOCK_WAIT_TIMEOUT +ALTER TABLE tbl_with_partitions ADD COLUMN f INT; + +--echo # Connection 1 +--echo # Try accessing the table after Alter aborted. +--echo # This used to give ER_LOCK_DEADLOCK. +connection default; +SELECT * FROM tbl_with_partitions; + +--echo # Connection 3 +connection con3; +UNLOCK TABLES; + +--echo # Connection 1 +--echo # Cleanup +connection default; +disconnect con2; +disconnect con3; +DROP TABLE tbl_with_partitions; + + # 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 -- cgit v1.2.1 From ff0001ed579693bb102244560b35ce14769b5ef2 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 14:41:41 +0100 Subject: Backport of revno: 2617.80.1 Also re-enables the test for Bug #43867 Followup to Bug#46654 False deadlock on concurrent DML/DDL with partitions, inconsistent behavior Partition_sync.test uses features only available in debug builds. Disabling the test for non-debug builds. --- mysql-test/r/partition_sync.result | 27 +++++++++++++-- mysql-test/t/partition_sync.test | 68 ++++++++++++++++++-------------------- 2 files changed, 58 insertions(+), 37 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/partition_sync.result b/mysql-test/r/partition_sync.result index fbea2e0273a..d48362cb57c 100644 --- a/mysql-test/r/partition_sync.result +++ b/mysql-test/r/partition_sync.result @@ -1,5 +1,28 @@ -# Disabled until Bug#46654 False deadlock on concurrent DML/DDL -# with partitions, inconsistent behavior is backported +# +# Bug #43867 ALTER TABLE on a partitioned table +# causes unnecessary deadlocks +# +CREATE TABLE t1 (a int) PARTITION BY RANGE (a) +(PARTITION p0 VALUES LESS THAN (1), +PARTITION p1 VALUES LESS THAN (2)); +INSERT INTO t1 VALUES (0),(1); +# Connection 2 +BEGIN; +SELECT * FROM t1; +a +0 +1 +# Connection 1 +ALTER TABLE t1 DROP PARTITION p3; +ERROR HY000: Error in list of partitions to DROP +# Connection 2 +# This failed with deadlock and should not do so. +SELECT * FROM t1; +a +0 +1 +# Connection 1 +DROP TABLE t1; # # Bug #46654 False deadlock on concurrent DML/DDL # with partitions, inconsistent behavior diff --git a/mysql-test/t/partition_sync.test b/mysql-test/t/partition_sync.test index 672e4cc0562..85eb33ebb6b 100644 --- a/mysql-test/t/partition_sync.test +++ b/mysql-test/t/partition_sync.test @@ -1,42 +1,40 @@ --source include/have_partition.inc +--source include/have_debug.inc # Save the initial number of concurrent sessions. --source include/count_sessions.inc ---echo # Disabled until Bug#46654 False deadlock on concurrent DML/DDL ---echo # with partitions, inconsistent behavior is backported - -#--echo # -#--echo # Bug #43867 ALTER TABLE on a partitioned table -#--echo # causes unnecessary deadlocks -#--echo # -# -#CREATE TABLE t1 (a int) PARTITION BY RANGE (a) -#(PARTITION p0 VALUES LESS THAN (1), -# PARTITION p1 VALUES LESS THAN (2)); -# -#INSERT INTO t1 VALUES (0),(1); -# -#connect(con1,localhost,root); -# -#--echo # Connection 2 -#connection con1; -#BEGIN; -#SELECT * FROM t1; -# -#--echo # Connection 1 -#connection default; -#--error ER_DROP_PARTITION_NON_EXISTENT -#ALTER TABLE t1 DROP PARTITION p3; -# -#--echo # Connection 2 -#connection con1; -#--echo # This failed with deadlock and should not do so. -#SELECT * FROM t1; -# -#--echo # Connection 1 -#connection default; -#disconnect con1; -#DROP TABLE t1; +--echo # +--echo # Bug #43867 ALTER TABLE on a partitioned table +--echo # causes unnecessary deadlocks +--echo # + +CREATE TABLE t1 (a int) PARTITION BY RANGE (a) +(PARTITION p0 VALUES LESS THAN (1), + PARTITION p1 VALUES LESS THAN (2)); + +INSERT INTO t1 VALUES (0),(1); + +connect(con1,localhost,root); + +--echo # Connection 2 +connection con1; +BEGIN; +SELECT * FROM t1; + +--echo # Connection 1 +connection default; +--error ER_DROP_PARTITION_NON_EXISTENT +ALTER TABLE t1 DROP PARTITION p3; + +--echo # Connection 2 +connection con1; +--echo # This failed with deadlock and should not do so. +SELECT * FROM t1; + +--echo # Connection 1 +connection default; +disconnect con1; +DROP TABLE t1; --echo # -- cgit v1.2.1 From 8724320989a53dd7f5ec13f169a6a722b9ec995d Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 15:09:00 +0100 Subject: Backport of revno: 3685 Bug #48210 FLUSH TABLES WITH READ LOCK deadlocks against concurrent CREATE PROCEDURE This deadlock occured between a) CREATE PROCEDURE (or other commands listed below) b) FLUSH TABLES WITH READ LOCK If the execution of them happened in the following order: - a) opens a table (e.g. mysql.proc) - b) locks the global read lock (or GRL) - a) sleeps inside wait_if_global_read_lock() - b) increases refresh_version and sleeps waiting for old tables to go away Note that a) must start waiting on the GRL before FLUSH increases refresh_version. Otherwise a) won't wait on the GRL and instead close its tables for reopen, allowing FLUSH to complete and thus avoid the deadlock. With this patch the deadlock is avoided by making CREATE PROCEDURE acquire a protection against global read locks before it starts executing. This means that FLUSH TABLES WITH READ LOCK will have to wait until CREATE PROCEDURE completes before acquiring the global read lock, thereby avoiding the deadlock. This is implemented by introducing a new SQL command flag called CF_PROTECT_AGAINST_GRL. Commands marked with this flag will acquire a GRL protection in the beginning of mysql_execute_command(). This patch adds the flag to CREATE, ALTER and DROP for PROCEDURE and FUNCTION, as well as CREATE USER, DROP USER, RENAME USER and REVOKE ALL. All these commands either call open_grant_tables() or open_system_table_for_updated() which make them susceptible for this deadlock. The patch also adds the CF_PROTECT_AGAINST_GRL flag to a number of commands that previously acquired GRL protection in their respective SQLCOM case in mysql_execute_command(). Test case that checks for GRL protection for CREATE PROCEDURE and CREATE USER added to mdl_sync.test. --- mysql-test/r/mdl_sync.result | 37 ++++++++++++++++++++++++ mysql-test/t/mdl_sync.test | 69 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index e5447c32b7d..130a8f22710 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -254,3 +254,40 @@ commit; # Switching to connection 'default'. # Clean-up drop table t1; +# +# Bug#48210 FLUSH TABLES WITH READ LOCK deadlocks +# against concurrent CREATE PROCEDURE +# +# Test 1: CREATE PROCEDURE +# Connection 1 +# Start CREATE PROCEDURE and open mysql.proc +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; +CREATE PROCEDURE p1() SELECT 1; +# Connection 2 +SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; +# Check that FLUSH must wait to get the GRL +# and let CREATE PROCEDURE continue +SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; +FLUSH TABLES WITH READ LOCK; +# Connection 1 +# Connection 2 +UNLOCK TABLES; +# Connection 1 +DROP PROCEDURE p1; +SET DEBUG_SYNC= 'RESET'; +# Test 2: CREATE USER +# Start CREATE USER and open the grant tables +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; +CREATE USER 'user_1@localhost'; +# Connection 2 +SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; +# Check that FLUSH must wait to get the GRL +# and let CREATE USER continue +SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; +FLUSH TABLES WITH READ LOCK; +# Connection 1 +# Connection 2 +UNLOCK TABLES; +# Connection 1 +DROP USER 'user_1@localhost'; +SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index fd66f6d539d..9422c67cb6f 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -475,6 +475,75 @@ disconnect con46673; drop table t1; +--echo # +--echo # Bug#48210 FLUSH TABLES WITH READ LOCK deadlocks +--echo # against concurrent CREATE PROCEDURE +--echo # + +connect (con2, localhost, root); + +--echo # Test 1: CREATE PROCEDURE + +--echo # Connection 1 +connection default; +--echo # Start CREATE PROCEDURE and open mysql.proc +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; +--send CREATE PROCEDURE p1() SELECT 1 + +--echo # Connection 2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; +--echo # Check that FLUSH must wait to get the GRL +--echo # and let CREATE PROCEDURE continue +SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; +--send FLUSH TABLES WITH READ LOCK + +--echo # Connection 1 +connection default; +--reap + +--echo # Connection 2 +connection con2; +--reap +UNLOCK TABLES; + +--echo # Connection 1 +connection default; +DROP PROCEDURE p1; +SET DEBUG_SYNC= 'RESET'; + +--echo # Test 2: CREATE USER + +connection default; +--echo # Start CREATE USER and open the grant tables +SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; +--send CREATE USER 'user_1@localhost' + +--echo # Connection 2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; +--echo # Check that FLUSH must wait to get the GRL +--echo # and let CREATE USER continue +SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; +--send FLUSH TABLES WITH READ LOCK + +--echo # Connection 1 +connection default; +--reap + +--echo # Connection 2 +connection con2; +--reap +UNLOCK TABLES; + +--echo # Connection 1 +connection default; +DROP USER 'user_1@localhost'; +SET DEBUG_SYNC= 'RESET'; + +disconnect con2; + + # 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 -- cgit v1.2.1 From d7f9583a9b3846b25402fce4510c3bc55e6cddc5 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 10 Dec 2009 15:09:56 +0100 Subject: Backport of revno: 3690 Postfix for Bug#48210 FLUSH TABLES WITH READ LOCK deadlocks against concurrent CREATE PROCEDURE Rewrote the second test to use DROP PROCEDURE instead of CREATE USER as CREATE USER does not work with embedded server. --- mysql-test/r/mdl_sync.result | 10 ++++------ mysql-test/t/mdl_sync.test | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index 130a8f22710..ec02f29b008 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -273,21 +273,19 @@ FLUSH TABLES WITH READ LOCK; # Connection 2 UNLOCK TABLES; # Connection 1 -DROP PROCEDURE p1; SET DEBUG_SYNC= 'RESET'; -# Test 2: CREATE USER -# Start CREATE USER and open the grant tables +# Test 2: DROP PROCEDURE +# Start DROP PROCEDURE and open tables SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; -CREATE USER 'user_1@localhost'; +DROP PROCEDURE p1; # Connection 2 SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; # Check that FLUSH must wait to get the GRL -# and let CREATE USER continue +# and let DROP PROCEDURE continue SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; FLUSH TABLES WITH READ LOCK; # Connection 1 # Connection 2 UNLOCK TABLES; # Connection 1 -DROP USER 'user_1@localhost'; SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index 9422c67cb6f..e3aceaa05fa 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -509,21 +509,20 @@ UNLOCK TABLES; --echo # Connection 1 connection default; -DROP PROCEDURE p1; SET DEBUG_SYNC= 'RESET'; ---echo # Test 2: CREATE USER +--echo # Test 2: DROP PROCEDURE connection default; ---echo # Start CREATE USER and open the grant tables +--echo # Start DROP PROCEDURE and open tables SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL table_opened WAIT_FOR grlwait'; ---send CREATE USER 'user_1@localhost' +--send DROP PROCEDURE p1 --echo # Connection 2 connection con2; SET DEBUG_SYNC= 'now WAIT_FOR table_opened'; --echo # Check that FLUSH must wait to get the GRL ---echo # and let CREATE USER continue +--echo # and let DROP PROCEDURE continue SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait'; --send FLUSH TABLES WITH READ LOCK @@ -538,7 +537,6 @@ UNLOCK TABLES; --echo # Connection 1 connection default; -DROP USER 'user_1@localhost'; SET DEBUG_SYNC= 'RESET'; disconnect con2; -- cgit v1.2.1 From 351f28d0cea345224f478307f014f5be62bb5e50 Mon Sep 17 00:00:00 2001 From: Magne Mahre Date: Thu, 10 Dec 2009 16:22:41 +0100 Subject: Bug#46374 crash, INSERT INTO t1 uses function, function modifies t1 An error occuring in the execution of a stored procedure, called from do_select is masked, since the error condition is not propagated back to the caller (join->conds->val_int() returns a result value, and not an error code) An explicit check was added to see if the thd error code has been set, and if so, the loop status is set to the error state. Backport from 6.0-codebase (revid: 2617.68.31) --- mysql-test/r/sp-error.result | 11 +++++++++++ mysql-test/t/sp-error.test | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 00ae7dd4eca..034923bbd4f 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -1687,6 +1687,17 @@ NULL SELECT non_existent (a) FROM t1 WHERE b = 999999; ERROR 42000: FUNCTION test.non_existent does not exist DROP TABLE t1; +CREATE TABLE t1 ( f2 INTEGER, f3 INTEGER ); +INSERT INTO t1 VALUES ( 1, 1 ); +CREATE FUNCTION func_1 () RETURNS INTEGER +BEGIN +INSERT INTO t1 SELECT * FROM t1 ; +RETURN 1 ; +END| +INSERT INTO t1 SELECT * FROM (SELECT 2 AS f1, 2 AS f2) AS A WHERE func_1() = 5; +ERROR HY000: Can't update table 't1' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. +DROP FUNCTION func_1; +DROP TABLE t1; # # Bug #47788: Crash in TABLE_LIST::hide_view_error on UPDATE + VIEW + # SP + MERGE + ALTER diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index e33adf56284..4df1118cd56 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -2490,6 +2490,35 @@ SELECT AVG (a) FROM t1 WHERE b = 999999; SELECT non_existent (a) FROM t1 WHERE b = 999999; DROP TABLE t1; + +# +# Bug #46374 crash, INSERT INTO t1 uses function, function modifies t1 +# +CREATE TABLE t1 ( f2 INTEGER, f3 INTEGER ); +INSERT INTO t1 VALUES ( 1, 1 ); + +delimiter |; + +CREATE FUNCTION func_1 () RETURNS INTEGER +BEGIN + INSERT INTO t1 SELECT * FROM t1 ; + RETURN 1 ; +END| + +delimiter ;| + +# The bug caused the following INSERT statement to trigger +# an assertion. Error 1442 is the correct response +# +--error 1442 +INSERT INTO t1 SELECT * FROM (SELECT 2 AS f1, 2 AS f2) AS A WHERE func_1() = 5; + +# Cleanup +DROP FUNCTION func_1; +DROP TABLE t1; + + + --echo # --echo # Bug #47788: Crash in TABLE_LIST::hide_view_error on UPDATE + VIEW + --echo # SP + MERGE + ALTER @@ -2513,3 +2542,4 @@ DROP VIEW v1; DROP TABLE t1; --echo End of 5.1 tests + -- cgit v1.2.1 From 700a361a6a6044f8ef49b6aca7db5810c2641e54 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 11 Dec 2009 15:24:23 +0300 Subject: Backport of: ------------------------------------------------------------ 2599.161.3 Ingo Struewing 2009-07-21 Bug#20667 - Truncate table fails for a write locked table TRUNCATE TABLE was not allowed under LOCK TABLES. The patch removes this restriction. mysql_truncate() does now handle that case. --- mysql-test/r/merge.result | 29 ++++--- mysql-test/r/truncate.result | 87 ++++++++++++++++++- mysql-test/r/truncate_coverage.result | 70 +++++++++++++++ mysql-test/t/merge.test | 16 +++- mysql-test/t/truncate.test | 73 +++++++++++++++- mysql-test/t/truncate_coverage.test | 155 ++++++++++++++++++++++++++++++++++ 6 files changed, 414 insertions(+), 16 deletions(-) create mode 100644 mysql-test/r/truncate_coverage.result create mode 100644 mysql-test/t/truncate_coverage.test (limited to 'mysql-test') diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 0417b91490e..a215c818b0f 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -1046,18 +1046,21 @@ c1 LOCK TABLE t1 WRITE, t2 WRITE, t3 WRITE; INSERT INTO t1 VALUES (1); TRUNCATE TABLE t3; -ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3; c1 -1 -2 +UNLOCK TABLES; +SELECT * FROM t1; +c1 +SELECT * FROM t2; +c1 # # Truncate child table under locked tables. +LOCK TABLE t1 WRITE, t2 WRITE, t3 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); TRUNCATE TABLE t1; -ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3; c1 -1 2 UNLOCK TABLES; DROP TABLE t1, t2, t3; @@ -1089,18 +1092,24 @@ INSERT INTO t1 VALUES (1); CREATE TABLE t4 (c1 INT, INDEX(c1)); LOCK TABLE t4 WRITE; TRUNCATE TABLE t3; -ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3; c1 -1 -2 +SELECT * FROM t1; +c1 +SELECT * FROM t2; +c1 # # Truncate temporary child table under locked tables. +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); TRUNCATE TABLE t1; -ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3; c1 -1 +2 +SELECT * FROM t1; +c1 +SELECT * FROM t2; +c1 2 UNLOCK TABLES; DROP TABLE t1, t2, t3, t4; diff --git a/mysql-test/r/truncate.result b/mysql-test/r/truncate.result index 8ce2ad9be21..8f237c81e75 100644 --- a/mysql-test/r/truncate.result +++ b/mysql-test/r/truncate.result @@ -1,4 +1,4 @@ -drop table if exists t1; +drop table if exists t1, t2; create table t1 (a integer, b integer,c1 CHAR(10)); insert into t1 (a) values (1),(2); truncate table t1; @@ -61,6 +61,91 @@ ERROR 42S02: Table 'test.v1' doesn't exist drop view v1; drop table t1; # +# Bug#20667 - Truncate table fails for a write locked table +# +CREATE TABLE t1 (c1 INT); +LOCK TABLE t1 WRITE; +INSERT INTO t1 VALUES (1); +SELECT * FROM t1; +c1 +1 +TRUNCATE TABLE t1; +SELECT * FROM t1; +c1 +UNLOCK TABLES; +LOCK TABLE t1 READ; +TRUNCATE TABLE t1; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +UNLOCK TABLES; +CREATE TABLE t2 (c1 INT); +LOCK TABLE t2 WRITE; +TRUNCATE TABLE t1; +ERROR HY000: Table 't1' was not locked with LOCK TABLES +UNLOCK TABLES; +CREATE VIEW v1 AS SELECT t1.c1 FROM t1,t2 WHERE t1.c1 = t2.c1; +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (1), (3), (4); +SELECT * FROM v1; +c1 +1 +3 +TRUNCATE v1; +ERROR 42S02: Table 'test.v1' doesn't exist +SELECT * FROM v1; +c1 +1 +3 +LOCK TABLE t1 WRITE; +SELECT * FROM v1; +ERROR HY000: Table 'v1' was not locked with LOCK TABLES +TRUNCATE v1; +ERROR 42S02: Table 'test.v1' doesn't exist +SELECT * FROM v1; +ERROR HY000: Table 'v1' was not locked with LOCK TABLES +UNLOCK TABLES; +LOCK TABLE t1 WRITE, t2 WRITE; +SELECT * FROM v1; +ERROR HY000: Table 'v1' was not locked with LOCK TABLES +TRUNCATE v1; +ERROR 42S02: Table 'test.v1' doesn't exist +SELECT * FROM v1; +ERROR HY000: Table 'v1' was not locked with LOCK TABLES +UNLOCK TABLES; +LOCK TABLE v1 WRITE; +SELECT * FROM v1; +c1 +1 +3 +TRUNCATE v1; +ERROR 42S02: Table 'test.v1' doesn't exist +SELECT * FROM v1; +c1 +1 +3 +UNLOCK TABLES; +LOCK TABLE t1 WRITE, t2 WRITE, v1 WRITE; +SELECT * FROM v1; +c1 +1 +3 +TRUNCATE v1; +ERROR 42S02: Table 'test.v1' doesn't exist +SELECT * FROM v1; +c1 +1 +3 +UNLOCK TABLES; +DROP VIEW v1; +DROP TABLE t1, t2; +CREATE PROCEDURE p1() SET @a = 5; +TRUNCATE p1; +ERROR 42S02: Table 'test.p1' doesn't exist +SHOW CREATE PROCEDURE p1; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +p1 CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`() +SET @a = 5 latin1 latin1_swedish_ci latin1_swedish_ci +DROP PROCEDURE p1; +# # Bug#46452 Crash in MDL, HANDLER OPEN + TRUNCATE TABLE # DROP TABLE IF EXISTS t1; diff --git a/mysql-test/r/truncate_coverage.result b/mysql-test/r/truncate_coverage.result new file mode 100644 index 00000000000..bb036329f6f --- /dev/null +++ b/mysql-test/r/truncate_coverage.result @@ -0,0 +1,70 @@ +SET DEBUG_SYNC='RESET'; +DROP TABLE IF EXISTS t1; +# +# Bug#20667 - Truncate table fails for a write locked table +# +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# connection con1 +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# connection default +LOCK TABLE t1 WRITE; +SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; +TRUNCATE TABLE t1; +# +# connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +KILL QUERY @id; +COMMIT; +# +# connection default +ERROR 70100: Query execution was interrupted +UNLOCK TABLES; +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# connection con1 +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# connection default +LOCK TABLE t1 WRITE; +SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; +TRUNCATE TABLE t1; +# +# connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +COMMIT; +# +# connection default +ERROR 42S02: Table 'test.t1' doesn't exist +UNLOCK TABLES; +DROP TABLE t1; +ERROR 42S02: Unknown table 't1' +SET DEBUG_SYNC='RESET'; +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# connection con1 +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# connection default +SET DEBUG_SYNC='mdl_acquire_exclusive_locks_wait SIGNAL waiting'; +TRUNCATE TABLE t1; +# +# connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +KILL QUERY @id; +COMMIT; +# +# connection default +ERROR 70100: Query execution was interrupted +UNLOCK TABLES; +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index b9e6813a4df..c3499037e97 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -675,12 +675,16 @@ SELECT * FROM t3; --echo # Truncate MERGE table under locked tables. LOCK TABLE t1 WRITE, t2 WRITE, t3 WRITE; INSERT INTO t1 VALUES (1); ---error ER_LOCK_OR_ACTIVE_TRANSACTION TRUNCATE TABLE t3; SELECT * FROM t3; +UNLOCK TABLES; +SELECT * FROM t1; +SELECT * FROM t2; --echo # --echo # Truncate child table under locked tables. ---error ER_LOCK_OR_ACTIVE_TRANSACTION +LOCK TABLE t1 WRITE, t2 WRITE, t3 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); TRUNCATE TABLE t1; SELECT * FROM t3; UNLOCK TABLES; @@ -706,14 +710,18 @@ SELECT * FROM t3; INSERT INTO t1 VALUES (1); CREATE TABLE t4 (c1 INT, INDEX(c1)); LOCK TABLE t4 WRITE; ---error ER_LOCK_OR_ACTIVE_TRANSACTION TRUNCATE TABLE t3; SELECT * FROM t3; +SELECT * FROM t1; +SELECT * FROM t2; --echo # --echo # Truncate temporary child table under locked tables. ---error ER_LOCK_OR_ACTIVE_TRANSACTION +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); TRUNCATE TABLE t1; SELECT * FROM t3; +SELECT * FROM t1; +SELECT * FROM t2; UNLOCK TABLES; DROP TABLE t1, t2, t3, t4; diff --git a/mysql-test/t/truncate.test b/mysql-test/t/truncate.test index feec4051e35..cdfa448f78a 100644 --- a/mysql-test/t/truncate.test +++ b/mysql-test/t/truncate.test @@ -2,7 +2,7 @@ # Test of truncate # --disable_warnings -drop table if exists t1; +drop table if exists t1, t2; --enable_warnings create table t1 (a integer, b integer,c1 CHAR(10)); @@ -69,6 +69,77 @@ drop table t1; # End of 5.0 tests +--echo # +--echo # Bug#20667 - Truncate table fails for a write locked table +--echo # +CREATE TABLE t1 (c1 INT); +LOCK TABLE t1 WRITE; +INSERT INTO t1 VALUES (1); +SELECT * FROM t1; +TRUNCATE TABLE t1; +SELECT * FROM t1; +UNLOCK TABLES; +# +LOCK TABLE t1 READ; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +TRUNCATE TABLE t1; +UNLOCK TABLES; +# +CREATE TABLE t2 (c1 INT); +LOCK TABLE t2 WRITE; +--error ER_TABLE_NOT_LOCKED +TRUNCATE TABLE t1; +UNLOCK TABLES; +# +CREATE VIEW v1 AS SELECT t1.c1 FROM t1,t2 WHERE t1.c1 = t2.c1; +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (1), (3), (4); +SELECT * FROM v1; +--error ER_NO_SUCH_TABLE +TRUNCATE v1; +SELECT * FROM v1; +# +LOCK TABLE t1 WRITE; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM v1; +--error ER_NO_SUCH_TABLE +TRUNCATE v1; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM v1; +UNLOCK TABLES; +# +LOCK TABLE t1 WRITE, t2 WRITE; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM v1; +--error ER_NO_SUCH_TABLE +TRUNCATE v1; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM v1; +UNLOCK TABLES; +# +LOCK TABLE v1 WRITE; +SELECT * FROM v1; +--error ER_NO_SUCH_TABLE +TRUNCATE v1; +SELECT * FROM v1; +UNLOCK TABLES; +# +LOCK TABLE t1 WRITE, t2 WRITE, v1 WRITE; +SELECT * FROM v1; +--error ER_NO_SUCH_TABLE +TRUNCATE v1; +SELECT * FROM v1; +UNLOCK TABLES; +# +DROP VIEW v1; +DROP TABLE t1, t2; +# +CREATE PROCEDURE p1() SET @a = 5; +--error ER_NO_SUCH_TABLE +TRUNCATE p1; +SHOW CREATE PROCEDURE p1; +DROP PROCEDURE p1; + --echo # --echo # Bug#46452 Crash in MDL, HANDLER OPEN + TRUNCATE TABLE --echo # diff --git a/mysql-test/t/truncate_coverage.test b/mysql-test/t/truncate_coverage.test new file mode 100644 index 00000000000..9870fbb5ebf --- /dev/null +++ b/mysql-test/t/truncate_coverage.test @@ -0,0 +1,155 @@ +# +# Code coverage testing of TRUNCATE TABLE. +# +# Ingo Struewing, 2009-07-20 +# + +--source include/have_debug_sync.inc +SET DEBUG_SYNC='RESET'; + +--let $MYSQLD_DATADIR= `SELECT @@datadir` + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +--echo # +--echo # Bug#20667 - Truncate table fails for a write locked table +--echo # +######## +# Attack wait_while_table_is_used(). Kill query while trying to +# upgrade MDL. +# +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# Start a transaction and execute a DML in it. Since 5.4.4 this leaves +# a shared meta data lock (MDL) behind. TRUNCATE shall block on it. +# +--echo # +--echo # connection con1 +--connect (con1, localhost, root,,) +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# Get connection id of default connection. +# Lock the table and start TRUNCATE, which will block on MDL upgrade. +# +--echo # +--echo # connection default +--connection default +let $ID= `SELECT @id := CONNECTION_ID()`; +LOCK TABLE t1 WRITE; +SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; +send TRUNCATE TABLE t1; +# +# Get the default connection ID into a variable in an invisible statement. +# Kill the TRUNCATE query. This shall result in an error return +# from wait_while_table_is_used(). +# +--echo # +--echo # connection con1 +--connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +let $invisible_assignment_in_select = `SELECT @id := $ID`; +KILL QUERY @id; +COMMIT; +--disconnect con1 +--echo # +--echo # connection default +--connection default +--error ER_QUERY_INTERRUPTED +reap; +UNLOCK TABLES; +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; +######## +# Attack reopen_tables(). Remove form file. +# +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# Start a transaction and execute a DML in it. Since 5.4.4 this leaves +# a shared meta data lock (MDL) behind. TRUNCATE shall block on it. +# +--echo # +--echo # connection con1 +--connect (con1, localhost, root,,) +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# Lock the table and start TRUNCATE, which will block on MDL upgrade. +# +--echo # +--echo # connection default +--connection default +LOCK TABLE t1 WRITE; +SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; +send TRUNCATE TABLE t1; +# +# Remove datafile. +# Commit to let TRUNCATE continue. +# +--echo # +--echo # connection con1 +--connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +--remove_file $MYSQLD_DATADIR/test/t1.frm +COMMIT; +--disconnect con1 +--echo # +--echo # connection default +--connection default +--error ER_NO_SUCH_TABLE +reap; +UNLOCK TABLES; +--error ER_BAD_TABLE_ERROR +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; +######## +# Attack acquire_exclusive_locks(). Hold a global read lock. +# Non-LOCK TABLE case. +# +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1); +# +# Start a transaction and execute a DML in it. Since 5.4.4 this leaves +# a shared meta data lock (MDL) behind. TRUNCATE shall block on it. +# +--echo # +--echo # connection con1 +--connect (con1, localhost, root,,) +START TRANSACTION; +INSERT INTO t1 VALUES (2); +# +# Get connection id of default connection. +# Start TRUNCATE, which will block on acquire_exclusive_locks(). +# +--echo # +--echo # connection default +--connection default +let $ID= `SELECT @id := CONNECTION_ID()`; +SET DEBUG_SYNC='mdl_acquire_exclusive_locks_wait SIGNAL waiting'; +send TRUNCATE TABLE t1; +# +# Get the default connection ID into a variable in an invisible statement. +# Kill the TRUNCATE query. This shall result in an error return +# from wait_while_table_is_used(). +# +--echo # +--echo # connection con1 +--connection con1 +SET DEBUG_SYNC='now WAIT_FOR waiting'; +let $invisible_assignment_in_select = `SELECT @id := $ID`; +KILL QUERY @id; +COMMIT; +--disconnect con1 +--echo # +--echo # connection default +--connection default +--error ER_QUERY_INTERRUPTED +reap; +UNLOCK TABLES; +DROP TABLE t1; +SET DEBUG_SYNC='RESET'; + -- cgit v1.2.1 From 3394cbf72cc09ca4735f88c2669ccd853134dfa8 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 15 Dec 2009 14:18:10 +0100 Subject: Bug #48940 MDL deadlocks against mysql_rm_db This deadlock would occur between two connections A and B if statements where executed in the following way: 1) Connection A executes a DML statement against table s1.t1 with autocommit off. This causes a shared metadata lock on s1.t1 to be acquired. (With autocommit on, the metadata lock will be dropped once the statment completes and the deadlock will not occour.) 2) Connection B tries to DROP DATABASE s1. This will block against the metadata lock connection A holds on s1.t1. While blocking, connection B will hold the LOCK_mysql_create_db mutex. 3) Connection A tries to ALTER DATABASE s1. This will block when trying to get LOCK_mysql_create_db mutex held by connection B. 4) Deadlock between DROP DATABASE and ALTER DATABASE (which has autocommit off). If Connection A used an explicitly started transaction rather than having autocommit off, this deadlock did not happen as ALTER DATABASE is disallowed inside transactions. This patch fixes the problem by changing ALTER DATABASE to cause an implicit commit before executing. This will cause the metadata lock on s1.t1 to be dropped, allowing DROP DATABASE to proceed. This will in turn cause the LOCK_mysql_create_db mutex to be unlocked, allowing ALTER DATABASE to proceed. Note that SQL commands other than ALTER DATABASE that also use LOCK_mysql_create_db, already cause an implicit commit. Incompatible change: ALTER DATABASE (and its synonym ALTER SCHEMA) now cause an implicit commit. This must be reflected in the documentation. Test case added to schema.test. --- mysql-test/r/schema.result | 17 ++++++++++++++++ mysql-test/t/schema.test | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/schema.result b/mysql-test/r/schema.result index 564fb3626df..33a2d4d9448 100644 --- a/mysql-test/r/schema.result +++ b/mysql-test/r/schema.result @@ -11,3 +11,20 @@ mtr mysql test drop schema foo; +# +# Bug #48940 MDL deadlocks against mysql_rm_db +# +DROP SCHEMA IF EXISTS schema1; +# Connection default +CREATE SCHEMA schema1; +CREATE TABLE schema1.t1 (a INT); +SET autocommit= FALSE; +INSERT INTO schema1.t1 VALUES (1); +# Connection 2 +DROP SCHEMA schema1; +# Connection default +ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8; +ERROR HY000: Can't create/write to file './schema1/db.opt' (Errcode: 2) +SET autocommit= TRUE; +# Connection 2 +# Connection default diff --git a/mysql-test/t/schema.test b/mysql-test/t/schema.test index a08d9b38935..a380a6241dd 100644 --- a/mysql-test/t/schema.test +++ b/mysql-test/t/schema.test @@ -4,6 +4,9 @@ # Drop mysqltest1 database, as it can left from the previous tests. # +# Save the initial number of concurrent sessions. +--source include/count_sessions.inc + --disable_warnings drop database if exists mysqltest1; --enable_warnings @@ -12,3 +15,50 @@ create schema foo; show create schema foo; show schemas; drop schema foo; + + +--echo # +--echo # Bug #48940 MDL deadlocks against mysql_rm_db +--echo # + +--disable_warnings +DROP SCHEMA IF EXISTS schema1; +--enable_warnings + +connect(con2, localhost, root); + +--echo # Connection default +connection default; + +CREATE SCHEMA schema1; +CREATE TABLE schema1.t1 (a INT); + +SET autocommit= FALSE; +INSERT INTO schema1.t1 VALUES (1); + +--echo # Connection 2 +connection con2; +--send DROP SCHEMA schema1 + +--echo # Connection default +connection default; +let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist + WHERE state= 'Waiting for table' + AND info='DROP SCHEMA schema1'; +--source include/wait_condition.inc +--error 1 +ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8; +SET autocommit= TRUE; + +--echo # Connection 2 +connection con2; +--reap + +--echo # Connection default +connection default; +disconnect con2; + + +# 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 -- cgit v1.2.1 From 39a1a50dfb52578224758ee1b240f1a89f95cc73 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 22 Dec 2009 19:09:15 +0300 Subject: A prerequisite patch for the fix for Bug#46224 "HANDLER statements within a transaction might lead to deadlocks". Introduce a notion of a sentinel to MDL_context. A sentinel is a ticket that separates all tickets in the context into two groups: before and after it. Currently we can have (and need) only one designated sentinel -- it separates all locks taken by LOCK TABLE or HANDLER statement, which must survive COMMIT and ROLLBACK and all other locks, which must be released at COMMIT or ROLLBACK. The tricky part is maintaining the sentinel up to date when someone release its corresponding ticket. This can happen, e.g. if someone issues DROP TABLE under LOCK TABLES (generally, see all calls to release_all_locks_for_name()). MDL_context::release_ticket() is modified to take care of it. ****** A fix and a test case for Bug#46224 "HANDLER statements within a transaction might lead to deadlocks". An attempt to mix HANDLER SQL statements, which are transaction- agnostic, an open multi-statement transaction, and DDL against the involved tables (in a concurrent connection) could lead to a deadlock. The deadlock would occur when HANDLER OPEN or HANDLER READ would have to wait on a conflicting metadata lock. If the connection that issued HANDLER statement also had other metadata locks (say, acquired in scope of a transaction), a classical deadlock situation of mutual wait could occur. Incompatible change: entering LOCK TABLES mode automatically closes all open HANDLERs in the current connection. Incompatible change: previously an attempt to wait on a lock in a connection that has an open HANDLER statement could wait indefinitely/deadlock. After this patch, an error ER_LOCK_DEADLOCK is produced. The idea of the fix is to merge thd->handler_mdl_context with the main mdl_context of the connection, used for transactional locks. This makes deadlock detection possible, since all waits with locks are "visible" and available to analysis in a single MDL context of the connection. Since HANDLER locks and transactional locks have a different life cycle -- HANDLERs are explicitly open and closed, and so are HANDLER locks, explicitly acquired and released, whereas transactional locks "accumulate" till the end of a transaction and are released only with COMMIT, ROLLBACK and ROLLBACK TO SAVEPOINT, a concept of "sentinel" was introduced to MDL_context. All locks, HANDLER and others, reside in the same linked list. However, a selected element of the list separates locks with different life cycle. HANDLER locks always reside at the end of the list, after the sentinel. Transactional locks are prepended to the beginning of the list, before the sentinel. Thus, ROLLBACK, COMMIT or ROLLBACK TO SAVEPOINT, only release those locks that reside before the sentinel. HANDLER locks must be released explicitly as part of HANDLER CLOSE statement, or an implicit close. The same approach with sentinel is also employed for LOCK TABLES locks. Since HANDLER and LOCK TABLES statement has never worked together, the implementation is made simple and only maintains one sentinel, which is used either for HANDLER locks, or for LOCK TABLES locks. --- mysql-test/include/handler.inc | 617 ++++++++++++++++++++++++++++++++++++- mysql-test/r/handler_innodb.result | 586 ++++++++++++++++++++++++++++++++++- mysql-test/r/handler_myisam.result | 585 ++++++++++++++++++++++++++++++++++- 3 files changed, 1776 insertions(+), 12 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc index 8ff38c7e7a1..a9965f97926 100644 --- a/mysql-test/include/handler.inc +++ b/mysql-test/include/handler.inc @@ -561,14 +561,29 @@ let $wait_condition= --source include/wait_condition.inc connection default; --echo connection: default +--echo # +--echo # RENAME placed two pending locks and waits. +--echo # When HANDLER t2 OPEN does open_tables(), it calls +--echo # mysql_ha_flush(), which in turn closes the open HANDLER for t1. +--echo # RENAME TABLE gets unblocked. If it gets scheduled quickly +--echo # and manages to complete before open_tables() +--echo # of HANDLER t2 OPEN, open_tables() and therefore the whole +--echo # HANDLER t2 OPEN succeeds. Otherwise open_tables() +--echo # notices a pending or active exclusive metadata lock on t2 +--echo # and the whole HANDLER t2 OPEN fails with ER_LOCK_DEADLOCK +--echo # error. +--echo # +--error 0, ER_LOCK_DEADLOCK handler t2 open; -handler t2 read first; ---error ER_NO_SUCH_TABLE -handler t1 read next; -handler t1 close; +--error 0, ER_UNKNOWN_TABLE handler t2 close; +--echo connection: flush connection flush; reap; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--error ER_UNKNOWN_TABLE +handler t1 close; connection default; drop table t2; connection flush; @@ -748,3 +763,597 @@ USE information_schema; --error ER_WRONG_USAGE HANDLER COLUMNS OPEN; USE test; + +--echo # +--echo # Add test coverage for HANDLER and LOCK TABLES, HANDLER and DDL. +--echo # +--disable_warnings +drop table if exists t1, t2, t3; +--enable_warnings +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)) select * from t1; +create temporary table t3 (a int, key a (a)) select * from t2; +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 # +--error ER_LOCK_OR_ACTIVE_TRANSACTION +handler t1 open; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +handler t1 read next; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +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. +unlock tables; +drop temporary table t3; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--error ER_UNKNOWN_TABLE +handler t2 close; +--error ER_UNKNOWN_TABLE +handler t3 read next; +--echo # +--echo # Other operations also implicitly close handler: +--echo # +--echo # TRUNCATE +--echo # +handler t1 open; +truncate table t1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +handler t1 open; +--echo # +--echo # CREATE TRIGGER +--echo # +create trigger t1_ai after insert on t1 for each row set @a=1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # DROP TRIGGER +--echo # +handler t1 open; +drop trigger t1_ai; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # ALTER TABLE +--echo # +handler t1 open; +alter table t1 add column b int; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # ANALYZE TABLE +--echo # +handler t1 open; +analyze table t1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # OPTIMIZE TABLE +--echo # +handler t1 open; +optimize table t1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # REPAIR TABLE +--echo # +handler t1 open; +repair table t1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # DROP TABLE, naturally. +--echo # +handler t1 open; +drop table t1; +--error ER_UNKNOWN_TABLE +handler t1 read next; +create table t1 (a int, b int, key a (a)) select a from t2; +--echo # +--echo # RENAME TABLE, naturally +--echo # +handler t1 open; +rename table t1 to t3; +--error ER_UNKNOWN_TABLE +handler t1 read next; +--echo # +--echo # CREATE TABLE (even with IF NOT EXISTS clause, +--echo # and the table exists). +--echo # +handler t2 open; +create table if not exists t2 (a int); +--error ER_UNKNOWN_TABLE +handler t2 read next; +rename table t3 to t1; +drop table t2; +--echo # +--echo # FLUSH TABLE doesn't close the table but loses the position +--echo # +handler t1 open; +handler t1 read a prev; +flush table t1; +handler t1 read a prev; +handler t1 close; +--echo # +--echo # FLUSH TABLES WITH READ LOCK behaves like FLUSH TABLE. +--echo # +handler t1 open; +handler t1 read a prev; +flush tables with read lock; +handler t1 read a prev; +handler t1 close; +unlock tables; +--echo # +--echo # Explore the effect of HANDLER locks on concurrent DDL +--echo # +handler t1 open; +--echo # Establishing auxiliary connections con1, con2, con3 +connect(con1, localhost, root,,); +connect(con2, localhost, root,,); +connect(con3, localhost, root,,); +--echo # --> connection con1; +connection con1; +--echo # Sending: +--send drop table t1 +--echo # We can't use connection 'default' as wait_condition will +--echo # autoclose handlers. +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop table t1' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t1'; +--source include/wait_condition.inc +--echo # --> connection default +connection default; +handler t1 read a prev; +handler t1 read a prev; +handler t1 close; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop table t1'... +--reap +--echo # --> connection default +connection default; +--echo # +--echo # Explore the effect of HANDLER locks in parallel with SELECT +--echo # +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +handler t1 open; +handler t1 read a prev; +handler t1 read a prev; +handler t1 close; +--echo # --> connection con1; +connection con1; +--echo # Sending: +--send drop table t1 +--echo # --> connection con2 +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' and info='drop table t1'; +--source include/wait_condition.inc +--echo # --> connection default +connection default; +--echo # We can still use the table, it's part of the transaction +select * from t1; +--echo # Such are the circumstances that t1 is a part of transaction, +--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. +commit; +handler t1 read a prev; +handler t1 read a prev; +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 +connection default; +--echo # +--echo # Demonstrate that HANDLER locks and transaction locks +--echo # reside in the same context, and we don't back-off +--echo # when have transaction or handler locks. +--echo # +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)); +insert into t2 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +--echo # --> connection con1 +connection con1; +lock table t2 read; +--echo # --> connection con2 +connection con2; +--echo # Sending: +--send drop table t2 +--echo # --> connection con1 +connection con1; +--echo # Waiting for 'drop table t2' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t2'; +--source include/wait_condition.inc +--echo # --> connection default +connection default; +--error ER_LOCK_DEADLOCK +handler t2 open; +--error ER_LOCK_DEADLOCK +select * from t2; +handler t1 open; +commit; +--error ER_LOCK_DEADLOCK +handler t2 open; +handler t1 close; +--echo # --> connection con1 +connection con1; +unlock tables; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'drop table t2'... +--reap +--echo # --> connection default +connection default; +handler t1 open; +handler t1 read a prev; +handler t1 close; +--echo # +--echo # Likewise, this doesn't require a multi-statement transaction. +--echo # ER_LOCK_DEADLOCK is also produced when we have an open +--echo # HANDLER and try to acquire locks for a single statement. +--echo # +create table t2 (a int, key a (a)); +handler t1 open; +--echo # --> connection con1 +connection con1; +lock tables t2 read; +--echo # --> connection con2 +connection con2; +--echo # Sending 'drop table t2'... +--send drop table t2 +--echo # --> connection con1 +connection con1; +--echo # Waiting for 'drop table t2' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t2'; +--source include/wait_condition.inc +--echo # --> connection default +connection default; +--error ER_LOCK_DEADLOCK +select * from t2; +--echo # --> connection con1 +connection con1; +unlock tables; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'drop table t2'... +--reap +--echo # --> connection default +connection default; +handler t1 close; + +--echo # +--echo # ROLLBACK TO SAVEPOINT releases transactional locks, +--echo # but has no effect on open HANDLERs +--echo # +create table t2 like t1; +create table t3 like t1; +begin; +--echo # Have something before the savepoint +select * from t3; +savepoint sv; +handler t1 open; +handler t1 read a first; +handler t1 read a next; +select * from t2; +--echo # --> connection con1 +connection con1; +--echo # Sending: +--send drop table t1 +--echo # --> connection con2 +connection con2; +--echo # Sending: +--send drop table t2 +--echo # --> connection default +connection default; +--echo # Let DROP TABLE statements sync in. We must use +--echo # a separate connection for that, because otherwise SELECT +--echo # will auto-close the HANDLERs, becaues there are pending +--echo # exclusive locks against them. +--echo # --> connection con3 +connection con3; +--echo # Waiting for 'drop table t1' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t1'; +--source include/wait_condition.inc +--echo # Waiting for 'drop table t2' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t2'; +--source include/wait_condition.inc +--echo # Demonstrate that t2 lock was released and t2 was dropped +--echo # after ROLLBACK TO SAVEPOINT +--echo # --> connection default +connection default; +rollback to savepoint sv; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'drop table t2'... +--reap +--echo # Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +--echo # lock. +--echo # --> connection default +connection default; +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; +--echo # Reaping 'drop table t1'... +--reap +--echo # --> connection default +connection default; +commit; +drop table t3; +--echo # +--echo # A few special cases when using SAVEPOINT/ROLLBACK TO +--echo # SAVEPOINT and HANDLER. +--echo # +--echo # Show that rollback to the savepoint taken in the beginning +--echo # of the transaction doesn't release mdl lock on +--echo # the HANDLER that was opened later. +--echo # +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +begin; +savepoint sv; +handler t1 open; +handler t1 read a first; +handler t1 read a next; +select * from t2; +--echo # --> connection con1 +connection con1; +--echo # Sending: +--send drop table t1 +--echo # --> connection con2 +connection con2; +--echo # Sending: +--send drop table t2 +--echo # --> connection default +connection default; +--echo # Let DROP TABLE statements sync in. We must use +--echo # a separate connection for that, because otherwise SELECT +--echo # will auto-close the HANDLERs, becaues there are pending +--echo # exclusive locks against them. +--echo # --> connection con3 +connection con3; +--echo # Waiting for 'drop table t1' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t1'; +--source include/wait_condition.inc +--echo # Waiting for 'drop table t2' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t2'; +--source include/wait_condition.inc +--echo # Demonstrate that t2 lock was released and t2 was dropped +--echo # after ROLLBACK TO SAVEPOINT +--echo # --> connection default +connection default; +rollback to savepoint sv; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'drop table t2'... +--reap +--echo # Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +--echo # lock. +--echo # --> connection default +connection default; +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; +--echo # Reaping 'drop table t1'... +--reap +--echo # --> connection default +connection default; +commit; +--echo # +--echo # Show that rollback to the savepoint taken in the beginning +--echo # of the transaction works properly (no valgrind warnins, etc), +--echo # even though it's done after the HANDLER mdl lock that was there +--echo # at the beginning is released and added again. +--echo # +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +create table t3 like t1; +insert into t3 (a) select a from t1; +begin; +handler t1 open; +savepoint sv; +handler t1 read a first; +select * from t2; +handler t1 close; +handler t3 open; +handler t3 read a first; +rollback to savepoint sv; +--echo # --> connection con1 +connection con1; +drop table t1, t2; +--echo # Sending: +--send drop table t3 +--echo # Let DROP TABLE statement sync in. +--echo # --> connection con2 +connection con2; +--echo # Waiting for 'drop table t3' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t3'; +--source include/wait_condition.inc +--echo # Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +--echo # lock. +--echo # --> connection default +connection default; +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; +--echo # Reaping 'drop table t3'... +--reap +--echo # --> connection default +connection default; +commit; + +--echo # +--echo # If we have to wait on an exclusive locks while having +--echo # an open HANDLER, ER_LOCK_DEADLOCK is reported. +--echo # +create table t1 (a int, key a(a)); +create table t2 like t1; +handler t1 open; +--echo # --> connection con1 +connection con1; +lock table t2 read; +--echo # --> connection default +connection default; +--error ER_LOCK_DEADLOCK +drop table t2; +--error ER_LOCK_DEADLOCK +rename table t2 to t3; +--echo # Demonstrate that there is no deadlock with FLUSH TABLE, +--echo # even though it is waiting for the other table to go away +--echo # Sending: +--send flush table t2 +--echo # --> connection con2 +connection con2; +drop table t1; +--echo # --> connection con1 +connection con1; +unlock tables; +--echo # --> connection default +connection default; +--echo # Reaping 'flush table t2'... +--reap +drop table t2; + +--echo # +--echo # Bug #46224 HANDLER statements within a transaction might +--echo # lead to deadlocks +--echo # +create table t1 (a int, key a(a)); + +--echo # --> connection default +connection default; +begin; +select * from t1; +handler t1 open; + +--echo # --> connection con1 +connection con1; +lock tables t1 write; + +--echo # --> connection default +connection default; +--echo # Sending: +--send handler t1 read a next + +--echo # --> connection con1 +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 = "Table lock" and info = "handler t1 read a next"; +--source include/wait_condition.inc +--echo # Sending: +--send drop table t1 + +--echo # --> connection con2 +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" and info = "drop table t1"; +--source include/wait_condition.inc + +--echo # --> connection default +connection default; +--echo # Reaping 'handler t1 read a next'... +--error ER_LOCK_DEADLOCK +--reap +handler t1 close; +commit; + +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop table t1'... +--reap + +--echo # --> connection con1 +connection con1; +disconnect con1; +--source include/wait_until_disconnected.inc +--echo # --> connection con2 +connection con2; +disconnect con2; +--source include/wait_until_disconnected.inc +--echo # --> connection con3 +connection con3; +disconnect con3; +--source include/wait_until_disconnected.inc +connection default; + +--echo # +--echo # A temporary table test. +--echo # Check that we don't loose positions of HANDLER opened +--echo # against a temporary table. +--echo # +create table t1 (a int, b int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create temporary table t2 (a int, b int, key a (a)); +insert into t2 (a) select a from t1; +handler t1 open; +handler t1 read a next; +handler t2 open; +handler t2 read a next; +flush table t1; +handler t2 read a next; +--echo # Sic: the position is lost +handler t1 read a next; +select * from t1; +--echo # Sic: the position is not lost +handler t2 read a next; +--error ER_CANT_REOPEN_TABLE +select * from t2; +handler t2 read a next; +drop table t1; +drop temporary table t2; + +--echo # +--echo # A test for lock_table_names()/unlock_table_names() function. +--echo # It should work properly in presence of open HANDLER. +--echo # +create table t1 (a int, b int, key a (a)); +create table t2 like t1; +create table t3 like t1; +create table t4 like t1; +handler t1 open; +handler t2 open; +rename table t4 to t5, t3 to t4, t5 to t3; +handler t1 read first; +handler t2 read first; +drop table t1, t2, t3, t4; diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result index 5990b19062b..df948f3d0b6 100644 --- a/mysql-test/r/handler_innodb.result +++ b/mysql-test/r/handler_innodb.result @@ -570,13 +570,25 @@ connection: flush rename table t1 to t2;; connection: waiter connection: default +# +# RENAME placed two pending locks and waits. +# When HANDLER t2 OPEN does open_tables(), it calls +# mysql_ha_flush(), which in turn closes the open HANDLER for t1. +# RENAME TABLE gets unblocked. If it gets scheduled quickly +# and manages to complete before open_tables() +# of HANDLER t2 OPEN, open_tables() and therefore the whole +# HANDLER t2 OPEN succeeds. Otherwise open_tables() +# notices a pending or active exclusive metadata lock on t2 +# and the whole HANDLER t2 OPEN fails with ER_LOCK_DEADLOCK +# error. +# handler t2 open; -handler t2 read first; -c1 +handler t2 close; +connection: flush handler t1 read next; -ERROR 42S02: Table 'test.t1' doesn't exist +ERROR 42S02: Unknown table 't1' in HANDLER handler t1 close; -handler t2 close; +ERROR 42S02: Unknown table 't1' in HANDLER drop table t2; drop table if exists t1; create temporary table t1 (a int, b char(1), key a(a), key b(a,b)); @@ -745,3 +757,569 @@ USE information_schema; HANDLER COLUMNS OPEN; ERROR HY000: Incorrect usage of HANDLER OPEN and information_schema USE test; +# +# Add test coverage for HANDLER and LOCK TABLES, HANDLER and DDL. +# +drop table if exists t1, t2, t3; +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)) select * from t1; +create temporary table t3 (a int, key a (a)) select * from t2; +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. +# +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; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +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. +unlock tables; +drop temporary table t3; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t2 close; +ERROR 42S02: Unknown table 't2' in HANDLER +handler t3 read next; +ERROR 42S02: Unknown table 't3' in HANDLER +# +# Other operations also implicitly close handler: +# +# TRUNCATE +# +handler t1 open; +truncate table t1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t1 open; +# +# CREATE TRIGGER +# +create trigger t1_ai after insert on t1 for each row set @a=1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TRIGGER +# +handler t1 open; +drop trigger t1_ai; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ALTER TABLE +# +handler t1 open; +alter table t1 add column b int; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ANALYZE TABLE +# +handler t1 open; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# OPTIMIZE TABLE +# +handler t1 open; +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 +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# REPAIR TABLE +# +handler t1 open; +repair table t1; +Table Op Msg_type Msg_text +test.t1 repair note The storage engine for the table doesn't support repair +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TABLE, naturally. +# +handler t1 open; +drop table t1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +create table t1 (a int, b int, key a (a)) select a from t2; +# +# RENAME TABLE, naturally +# +handler t1 open; +rename table t1 to t3; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# CREATE TABLE (even with IF NOT EXISTS clause, +# and the table exists). +# +handler t2 open; +create table if not exists t2 (a int); +Warnings: +Note 1050 Table 't2' already exists +handler t2 read next; +ERROR 42S02: Unknown table 't2' in HANDLER +rename table t3 to t1; +drop table t2; +# +# FLUSH TABLE doesn't close the table but loses the position +# +handler t1 open; +handler t1 read a prev; +b a +NULL 5 +flush table t1; +handler t1 read a prev; +b a +NULL 5 +handler t1 close; +# +# FLUSH TABLES WITH READ LOCK behaves like FLUSH TABLE. +# +handler t1 open; +handler t1 read a prev; +b a +NULL 5 +flush tables with read lock; +handler t1 read a prev; +b a +NULL 5 +handler t1 close; +unlock tables; +# +# Explore the effect of HANDLER locks on concurrent DDL +# +handler t1 open; +# Establishing auxiliary connections con1, con2, con3 +# --> connection con1; +# Sending: +drop table t1 ; +# We can't use connection 'default' as wait_condition will +# autoclose handlers. +# --> connection con2 +# Waitng for 'drop table t1' to get blocked... +# --> connection default +handler t1 read a prev; +b a +NULL 5 +handler t1 read a prev; +b a +NULL 4 +handler t1 close; +# --> connection con1 +# Reaping 'drop table t1'... +# --> connection default +# +# Explore the effect of HANDLER locks in parallel with SELECT +# +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +a +1 +2 +3 +4 +5 +handler t1 open; +handler t1 read a prev; +a +5 +handler t1 read a prev; +a +4 +handler t1 close; +# --> connection con1; +# Sending: +drop table t1 ; +# --> connection con2 +# Waiting for 'drop table t1' to get blocked... +# --> connection default +# We can still use the table, it's part of the transaction +select * from t1; +a +1 +2 +3 +4 +5 +# Such are the circumstances that t1 is a part of transaction, +# 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. +commit; +handler t1 read a prev; +a +5 +handler t1 read a prev; +a +4 +handler t1 read a prev; +a +3 +handler t1 close; +# --> connection con1 +# Now drop can proceed +# Reaping 'drop table t1'... +# --> connection default +# +# Demonstrate that HANDLER locks and transaction locks +# reside in the same context, and we don't back-off +# when have transaction or handler locks. +# +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)); +insert into t2 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +a +1 +2 +3 +4 +5 +# --> connection con1 +lock table t2 read; +# --> connection con2 +# Sending: +drop table t2; +# --> connection con1 +# Waiting for 'drop table t2' to get blocked... +# --> connection default +handler t2 open; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 open; +commit; +handler t2 open; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 close; +# --> connection con1 +unlock tables; +# --> connection con2 +# Reaping 'drop table t2'... +# --> connection default +handler t1 open; +handler t1 read a prev; +a +5 +handler t1 close; +# +# Likewise, this doesn't require a multi-statement transaction. +# ER_LOCK_DEADLOCK is also produced when we have an open +# HANDLER and try to acquire locks for a single statement. +# +create table t2 (a int, key a (a)); +handler t1 open; +# --> connection con1 +lock tables t2 read; +# --> connection con2 +# Sending 'drop table t2'... +drop table t2; +# --> connection con1 +# Waiting for 'drop table t2' to get blocked... +# --> connection default +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# --> connection con1 +unlock tables; +# --> connection con2 +# Reaping 'drop table t2'... +# --> connection default +handler t1 close; +# +# ROLLBACK TO SAVEPOINT releases transactional locks, +# but has no effect on open HANDLERs +# +create table t2 like t1; +create table t3 like t1; +begin; +# Have something before the savepoint +select * from t3; +a +savepoint sv; +handler t1 open; +handler t1 read a first; +a +1 +handler t1 read a next; +a +2 +select * from t2; +a +# --> connection con1 +# Sending: +drop table t1; +# --> connection con2 +# Sending: +drop table t2; +# --> connection default +# Let DROP TABLE statements sync in. We must use +# a separate connection for that, because otherwise SELECT +# will auto-close the HANDLERs, becaues there are pending +# exclusive locks against them. +# --> connection con3 +# Waiting for 'drop table t1' to get blocked... +# Waiting for 'drop table t2' to get blocked... +# Demonstrate that t2 lock was released and t2 was dropped +# after ROLLBACK TO SAVEPOINT +# --> connection default +rollback to savepoint sv; +# --> connection con2 +# Reaping 'drop table t2'... +# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +# lock. +# --> connection default +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 +handler t1 close; +# connection con1 +# Reaping 'drop table t1'... +# --> connection default +commit; +drop table t3; +# +# A few special cases when using SAVEPOINT/ROLLBACK TO +# SAVEPOINT and HANDLER. +# +# Show that rollback to the savepoint taken in the beginning +# of the transaction doesn't release mdl lock on +# the HANDLER that was opened later. +# +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +begin; +savepoint sv; +handler t1 open; +handler t1 read a first; +a +1 +handler t1 read a next; +a +2 +select * from t2; +a +# --> connection con1 +# Sending: +drop table t1; +# --> connection con2 +# Sending: +drop table t2; +# --> connection default +# Let DROP TABLE statements sync in. We must use +# a separate connection for that, because otherwise SELECT +# will auto-close the HANDLERs, becaues there are pending +# exclusive locks against them. +# --> connection con3 +# Waiting for 'drop table t1' to get blocked... +# Waiting for 'drop table t2' to get blocked... +# Demonstrate that t2 lock was released and t2 was dropped +# after ROLLBACK TO SAVEPOINT +# --> connection default +rollback to savepoint sv; +# --> connection con2 +# Reaping 'drop table t2'... +# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +# lock. +# --> connection default +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 +handler t1 close; +# connection con1 +# Reaping 'drop table t1'... +# --> connection default +commit; +# +# Show that rollback to the savepoint taken in the beginning +# of the transaction works properly (no valgrind warnins, etc), +# even though it's done after the HANDLER mdl lock that was there +# at the beginning is released and added again. +# +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +create table t3 like t1; +insert into t3 (a) select a from t1; +begin; +handler t1 open; +savepoint sv; +handler t1 read a first; +a +1 +select * from t2; +a +handler t1 close; +handler t3 open; +handler t3 read a first; +a +1 +rollback to savepoint sv; +# --> connection con1 +drop table t1, t2; +# Sending: +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. +# --> connection default +handler t3 read a next; +a +2 +# Demonstrate that the drop will go through as soon as we close the +# HANDLER +handler t3 close; +# connection con1 +# Reaping 'drop table t3'... +# --> connection default +commit; +# +# If we have to wait on an exclusive locks while having +# an open HANDLER, ER_LOCK_DEADLOCK is reported. +# +create table t1 (a int, key a(a)); +create table t2 like t1; +handler t1 open; +# --> connection con1 +lock table t2 read; +# --> connection default +drop table t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +rename table t2 to t3; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Demonstrate that there is no deadlock with FLUSH TABLE, +# even though it is waiting for the other table to go away +# Sending: +flush table t2; +# --> connection con2 +drop table t1; +# --> connection con1 +unlock tables; +# --> connection default +# Reaping 'flush table t2'... +drop table t2; +# +# Bug #46224 HANDLER statements within a transaction might +# lead to deadlocks +# +create table t1 (a int, key a(a)); +# --> connection default +begin; +select * from t1; +a +handler t1 open; +# --> connection con1 +lock tables t1 write; +# --> connection default +# Sending: +handler t1 read a next; +# --> connection con1 +# Waiting for 'handler t1 read a next' to get blocked... +# Sending: +drop table t1; +# --> connection con2 +# Waiting for 'drop table t1' to get blocked... +# --> connection default +# Reaping 'handler t1 read a next'... +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 close; +commit; +# --> connection con1 +# Reaping 'drop table t1'... +# --> connection con1 +# --> connection con2 +# --> connection con3 +# +# A temporary table test. +# Check that we don't loose positions of HANDLER opened +# against a temporary table. +# +create table t1 (a int, b int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create temporary table t2 (a int, b int, key a (a)); +insert into t2 (a) select a from t1; +handler t1 open; +handler t1 read a next; +a b +1 NULL +handler t2 open; +handler t2 read a next; +a b +1 NULL +flush table t1; +handler t2 read a next; +a b +2 NULL +# Sic: the position is lost +handler t1 read a next; +a b +1 NULL +select * from t1; +a b +1 NULL +2 NULL +3 NULL +4 NULL +5 NULL +# Sic: the position is not lost +handler t2 read a next; +a b +3 NULL +select * from t2; +ERROR HY000: Can't reopen table: 't2' +handler t2 read a next; +a b +4 NULL +drop table t1; +drop temporary table t2; +# +# A test for lock_table_names()/unlock_table_names() function. +# It should work properly in presence of open HANDLER. +# +create table t1 (a int, b int, key a (a)); +create table t2 like t1; +create table t3 like t1; +create table t4 like t1; +handler t1 open; +handler t2 open; +rename table t4 to t5, t3 to t4, t5 to t3; +handler t1 read first; +a b +handler t2 read first; +a b +drop table t1, t2, t3, t4; diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result index f7b0ff2e04e..4b287e6560b 100644 --- a/mysql-test/r/handler_myisam.result +++ b/mysql-test/r/handler_myisam.result @@ -569,13 +569,25 @@ connection: flush rename table t1 to t2;; connection: waiter connection: default +# +# RENAME placed two pending locks and waits. +# When HANDLER t2 OPEN does open_tables(), it calls +# mysql_ha_flush(), which in turn closes the open HANDLER for t1. +# RENAME TABLE gets unblocked. If it gets scheduled quickly +# and manages to complete before open_tables() +# of HANDLER t2 OPEN, open_tables() and therefore the whole +# HANDLER t2 OPEN succeeds. Otherwise open_tables() +# notices a pending or active exclusive metadata lock on t2 +# and the whole HANDLER t2 OPEN fails with ER_LOCK_DEADLOCK +# error. +# handler t2 open; -handler t2 read first; -c1 +handler t2 close; +connection: flush handler t1 read next; -ERROR 42S02: Table 'test.t1' doesn't exist +ERROR 42S02: Unknown table 't1' in HANDLER handler t1 close; -handler t2 close; +ERROR 42S02: Unknown table 't1' in HANDLER drop table t2; drop table if exists t1; create temporary table t1 (a int, b char(1), key a(a), key b(a,b)); @@ -744,6 +756,571 @@ HANDLER COLUMNS OPEN; ERROR HY000: Incorrect usage of HANDLER OPEN and information_schema USE test; # +# Add test coverage for HANDLER and LOCK TABLES, HANDLER and DDL. +# +drop table if exists t1, t2, t3; +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)) select * from t1; +create temporary table t3 (a int, key a (a)) select * from t2; +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. +# +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; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +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. +unlock tables; +drop temporary table t3; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t2 close; +ERROR 42S02: Unknown table 't2' in HANDLER +handler t3 read next; +ERROR 42S02: Unknown table 't3' in HANDLER +# +# Other operations also implicitly close handler: +# +# TRUNCATE +# +handler t1 open; +truncate table t1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +handler t1 open; +# +# CREATE TRIGGER +# +create trigger t1_ai after insert on t1 for each row set @a=1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TRIGGER +# +handler t1 open; +drop trigger t1_ai; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ALTER TABLE +# +handler t1 open; +alter table t1 add column b int; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# ANALYZE TABLE +# +handler t1 open; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Table is already up to date +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# OPTIMIZE TABLE +# +handler t1 open; +optimize table t1; +Table Op Msg_type Msg_text +test.t1 optimize status OK +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# REPAIR TABLE +# +handler t1 open; +repair table t1; +Table Op Msg_type Msg_text +test.t1 repair status OK +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# DROP TABLE, naturally. +# +handler t1 open; +drop table t1; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +create table t1 (a int, b int, key a (a)) select a from t2; +# +# RENAME TABLE, naturally +# +handler t1 open; +rename table t1 to t3; +handler t1 read next; +ERROR 42S02: Unknown table 't1' in HANDLER +# +# CREATE TABLE (even with IF NOT EXISTS clause, +# and the table exists). +# +handler t2 open; +create table if not exists t2 (a int); +Warnings: +Note 1050 Table 't2' already exists +handler t2 read next; +ERROR 42S02: Unknown table 't2' in HANDLER +rename table t3 to t1; +drop table t2; +# +# FLUSH TABLE doesn't close the table but loses the position +# +handler t1 open; +handler t1 read a prev; +b a +NULL 5 +flush table t1; +handler t1 read a prev; +b a +NULL 5 +handler t1 close; +# +# FLUSH TABLES WITH READ LOCK behaves like FLUSH TABLE. +# +handler t1 open; +handler t1 read a prev; +b a +NULL 5 +flush tables with read lock; +handler t1 read a prev; +b a +NULL 5 +handler t1 close; +unlock tables; +# +# Explore the effect of HANDLER locks on concurrent DDL +# +handler t1 open; +# Establishing auxiliary connections con1, con2, con3 +# --> connection con1; +# Sending: +drop table t1 ; +# We can't use connection 'default' as wait_condition will +# autoclose handlers. +# --> connection con2 +# Waitng for 'drop table t1' to get blocked... +# --> connection default +handler t1 read a prev; +b a +NULL 5 +handler t1 read a prev; +b a +NULL 4 +handler t1 close; +# --> connection con1 +# Reaping 'drop table t1'... +# --> connection default +# +# Explore the effect of HANDLER locks in parallel with SELECT +# +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +a +1 +2 +3 +4 +5 +handler t1 open; +handler t1 read a prev; +a +5 +handler t1 read a prev; +a +4 +handler t1 close; +# --> connection con1; +# Sending: +drop table t1 ; +# --> connection con2 +# Waiting for 'drop table t1' to get blocked... +# --> connection default +# We can still use the table, it's part of the transaction +select * from t1; +a +1 +2 +3 +4 +5 +# Such are the circumstances that t1 is a part of transaction, +# 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. +commit; +handler t1 read a prev; +a +5 +handler t1 read a prev; +a +4 +handler t1 read a prev; +a +3 +handler t1 close; +# --> connection con1 +# Now drop can proceed +# Reaping 'drop table t1'... +# --> connection default +# +# Demonstrate that HANDLER locks and transaction locks +# reside in the same context, and we don't back-off +# when have transaction or handler locks. +# +create table t1 (a int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 (a int, key a (a)); +insert into t2 (a) values (1), (2), (3), (4), (5); +begin; +select * from t1; +a +1 +2 +3 +4 +5 +# --> connection con1 +lock table t2 read; +# --> connection con2 +# Sending: +drop table t2; +# --> connection con1 +# Waiting for 'drop table t2' to get blocked... +# --> connection default +handler t2 open; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 open; +commit; +handler t2 open; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 close; +# --> connection con1 +unlock tables; +# --> connection con2 +# Reaping 'drop table t2'... +# --> connection default +handler t1 open; +handler t1 read a prev; +a +5 +handler t1 close; +# +# Likewise, this doesn't require a multi-statement transaction. +# ER_LOCK_DEADLOCK is also produced when we have an open +# HANDLER and try to acquire locks for a single statement. +# +create table t2 (a int, key a (a)); +handler t1 open; +# --> connection con1 +lock tables t2 read; +# --> connection con2 +# Sending 'drop table t2'... +drop table t2; +# --> connection con1 +# Waiting for 'drop table t2' to get blocked... +# --> connection default +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# --> connection con1 +unlock tables; +# --> connection con2 +# Reaping 'drop table t2'... +# --> connection default +handler t1 close; +# +# ROLLBACK TO SAVEPOINT releases transactional locks, +# but has no effect on open HANDLERs +# +create table t2 like t1; +create table t3 like t1; +begin; +# Have something before the savepoint +select * from t3; +a +savepoint sv; +handler t1 open; +handler t1 read a first; +a +1 +handler t1 read a next; +a +2 +select * from t2; +a +# --> connection con1 +# Sending: +drop table t1; +# --> connection con2 +# Sending: +drop table t2; +# --> connection default +# Let DROP TABLE statements sync in. We must use +# a separate connection for that, because otherwise SELECT +# will auto-close the HANDLERs, becaues there are pending +# exclusive locks against them. +# --> connection con3 +# Waiting for 'drop table t1' to get blocked... +# Waiting for 'drop table t2' to get blocked... +# Demonstrate that t2 lock was released and t2 was dropped +# after ROLLBACK TO SAVEPOINT +# --> connection default +rollback to savepoint sv; +# --> connection con2 +# Reaping 'drop table t2'... +# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +# lock. +# --> connection default +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 +handler t1 close; +# connection con1 +# Reaping 'drop table t1'... +# --> connection default +commit; +drop table t3; +# +# A few special cases when using SAVEPOINT/ROLLBACK TO +# SAVEPOINT and HANDLER. +# +# Show that rollback to the savepoint taken in the beginning +# of the transaction doesn't release mdl lock on +# the HANDLER that was opened later. +# +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +begin; +savepoint sv; +handler t1 open; +handler t1 read a first; +a +1 +handler t1 read a next; +a +2 +select * from t2; +a +# --> connection con1 +# Sending: +drop table t1; +# --> connection con2 +# Sending: +drop table t2; +# --> connection default +# Let DROP TABLE statements sync in. We must use +# a separate connection for that, because otherwise SELECT +# will auto-close the HANDLERs, becaues there are pending +# exclusive locks against them. +# --> connection con3 +# Waiting for 'drop table t1' to get blocked... +# Waiting for 'drop table t2' to get blocked... +# Demonstrate that t2 lock was released and t2 was dropped +# after ROLLBACK TO SAVEPOINT +# --> connection default +rollback to savepoint sv; +# --> connection con2 +# Reaping 'drop table t2'... +# Demonstrate that ROLLBACK TO SAVEPOINT didn't release the handler +# lock. +# --> connection default +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 +handler t1 close; +# connection con1 +# Reaping 'drop table t1'... +# --> connection default +commit; +# +# Show that rollback to the savepoint taken in the beginning +# of the transaction works properly (no valgrind warnins, etc), +# even though it's done after the HANDLER mdl lock that was there +# at the beginning is released and added again. +# +create table t1 (a int, key a(a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create table t2 like t1; +create table t3 like t1; +insert into t3 (a) select a from t1; +begin; +handler t1 open; +savepoint sv; +handler t1 read a first; +a +1 +select * from t2; +a +handler t1 close; +handler t3 open; +handler t3 read a first; +a +1 +rollback to savepoint sv; +# --> connection con1 +drop table t1, t2; +# Sending: +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. +# --> connection default +handler t3 read a next; +a +2 +# Demonstrate that the drop will go through as soon as we close the +# HANDLER +handler t3 close; +# connection con1 +# Reaping 'drop table t3'... +# --> connection default +commit; +# +# If we have to wait on an exclusive locks while having +# an open HANDLER, ER_LOCK_DEADLOCK is reported. +# +create table t1 (a int, key a(a)); +create table t2 like t1; +handler t1 open; +# --> connection con1 +lock table t2 read; +# --> connection default +drop table t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +rename table t2 to t3; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Demonstrate that there is no deadlock with FLUSH TABLE, +# even though it is waiting for the other table to go away +# Sending: +flush table t2; +# --> connection con2 +drop table t1; +# --> connection con1 +unlock tables; +# --> connection default +# Reaping 'flush table t2'... +drop table t2; +# +# Bug #46224 HANDLER statements within a transaction might +# lead to deadlocks +# +create table t1 (a int, key a(a)); +# --> connection default +begin; +select * from t1; +a +handler t1 open; +# --> connection con1 +lock tables t1 write; +# --> connection default +# Sending: +handler t1 read a next; +# --> connection con1 +# Waiting for 'handler t1 read a next' to get blocked... +# Sending: +drop table t1; +# --> connection con2 +# Waiting for 'drop table t1' to get blocked... +# --> connection default +# Reaping 'handler t1 read a next'... +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +handler t1 close; +commit; +# --> connection con1 +# Reaping 'drop table t1'... +# --> connection con1 +# --> connection con2 +# --> connection con3 +# +# A temporary table test. +# Check that we don't loose positions of HANDLER opened +# against a temporary table. +# +create table t1 (a int, b int, key a (a)); +insert into t1 (a) values (1), (2), (3), (4), (5); +create temporary table t2 (a int, b int, key a (a)); +insert into t2 (a) select a from t1; +handler t1 open; +handler t1 read a next; +a b +1 NULL +handler t2 open; +handler t2 read a next; +a b +1 NULL +flush table t1; +handler t2 read a next; +a b +2 NULL +# Sic: the position is lost +handler t1 read a next; +a b +1 NULL +select * from t1; +a b +1 NULL +2 NULL +3 NULL +4 NULL +5 NULL +# Sic: the position is not lost +handler t2 read a next; +a b +3 NULL +select * from t2; +ERROR HY000: Can't reopen table: 't2' +handler t2 read a next; +a b +4 NULL +drop table t1; +drop temporary table t2; +# +# A test for lock_table_names()/unlock_table_names() function. +# It should work properly in presence of open HANDLER. +# +create table t1 (a int, b int, key a (a)); +create table t2 like t1; +create table t3 like t1; +create table t4 like t1; +handler t1 open; +handler t2 open; +rename table t4 to t5, t3 to t4, t5 to t3; +handler t1 read first; +a b +handler t2 read first; +a b +drop table t1, t2, t3, t4; +# # BUG #46456: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash # CREATE TABLE t1 AS SELECT 1 AS f1; -- cgit v1.2.1 From 3b311f399d996cd28b263d246ae27e9b1dc56840 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 29 Dec 2009 15:19:05 +0300 Subject: Apply and review: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3655 Jon Olav Hauglid 2009-10-19 Bug #30977 Concurrent statement using stored function and DROP FUNCTION breaks SBR Bug #48246 assert in close_thread_table Implement a fix for: Bug #41804 purge stored procedure cache causes mysterious hang for many minutes Bug #49972 Crash in prepared statements The problem was that concurrent execution of DML statements that use stored functions and DDL statements that drop/modify the same function might result in incorrect binary log in statement (and mixed) mode and therefore break replication. This patch fixes the problem by introducing metadata locking for stored procedures and functions. This is similar to what is done in Bug#25144 for views. Procedures and functions now are locked using metadata locks until the transaction is either committed or rolled back. This prevents other statements from modifying the procedure/function while it is being executed. This provides commit ordering - guaranteeing serializability across multiple transactions and thus fixes the reported binlog problem. Note that we do not take locks for top-level CALLs. This means that procedures called directly are not protected from changes by simultaneous DDL operations so they are executed at the state they had at the time of the CALL. By not taking locks for top-level CALLs, we still allow transactions to be started inside procedures. This patch also changes stored procedure cache invalidation. Upon a change of cache version, we no longer invalidate the entire cache, but only those routines which we use, only when a statement is executed that uses them. This patch also changes the logic of prepared statement validation. A stored procedure used by a prepared statement is now validated only once a metadata lock has been acquired. A version mismatch causes a flush of the obsolete routine from the cache and statement reprepare. Incompatible changes: 1) ER_LOCK_DEADLOCK is reported for a transaction trying to access a procedure/function that is locked by a DDL operation in another connection. 2) Procedure/function DDL operations are now prohibited in LOCK TABLES mode as exclusive locks must be taken all at once and LOCK TABLES provides no way to specifiy procedures/functions to be locked. Test cases have been added to sp-lock.test and rpl_sp.test. Work on this bug has very much been a team effort and this patch includes and is based on contributions from Davi Arnaut, Dmitry Lenev, Magne Mæhre and Konstantin Osipov. --- mysql-test/r/ps_ddl.result | 13 +- mysql-test/r/ps_ddl1.result | 2 +- mysql-test/r/sp-error.result | 2 +- mysql-test/r/sp-lock.result | 697 ++++++++++++++++++++++++++++ mysql-test/suite/rpl/r/rpl_sp.result | 45 +- mysql-test/suite/rpl/t/rpl_sp.test | 59 ++- mysql-test/t/ps_ddl.test | 11 +- mysql-test/t/ps_ddl1.test | 2 +- mysql-test/t/sp-error.test | 2 +- mysql-test/t/sp-lock.test | 876 +++++++++++++++++++++++++++++++++++ 10 files changed, 1691 insertions(+), 18 deletions(-) create mode 100644 mysql-test/r/sp-lock.result create mode 100644 mysql-test/t/sp-lock.test (limited to 'mysql-test') diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index c72d129c8e4..2ce43d363c3 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -269,8 +269,6 @@ Part 7: TABLE -> TABLE (TRIGGER dependencies) transitions ===================================================================== # Test 7-a: dependent PROCEDURE has changed # -# Note, this scenario is not supported, subject of Bug#12093 -# create table t1 (a int); create trigger t1_ai after insert on t1 for each row call p1(new.a); @@ -282,10 +280,9 @@ drop procedure p1; create procedure p1 (a int) begin end; set @var= 2; execute stmt using @var; -ERROR 42000: PROCEDURE test.p1 does not exist # Cleanup drop procedure p1; -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); SUCCESS # Test 7-b: dependent FUNCTION has changed @@ -361,11 +358,13 @@ set @var=8; # XXX: bug, the SQL statement in the trigger is still # pointing at table 't3', since the view was expanded # at first statement execution. +# Since the view definition is inlined in the statement +# at prepare, changing the view definition does not cause +# repreparation. # Repreparation of the main statement doesn't cause repreparation # of trigger statements. execute stmt using @var; -ERROR 42S02: Table 'test.t3' doesn't exist -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); SUCCESS # @@ -382,6 +381,7 @@ select * from t3; a 6 7 +8 flush table t1; set @var=9; execute stmt using @var; @@ -396,6 +396,7 @@ select * from t3; a 6 7 +8 drop view v1; drop table t1,t2,t3; # Test 7-d: dependent TABLE has changed diff --git a/mysql-test/r/ps_ddl1.result b/mysql-test/r/ps_ddl1.result index e41a72ceb96..87abcd90590 100644 --- a/mysql-test/r/ps_ddl1.result +++ b/mysql-test/r/ps_ddl1.result @@ -460,7 +460,7 @@ create schema mysqltest; end| execute stmt; ERROR 42000: PROCEDURE test.p1 does not exist -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); SUCCESS execute stmt; diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 034923bbd4f..4cefee95903 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -512,7 +512,7 @@ select * from t1; end| lock table t1 read| alter procedure bug9566 comment 'Some comment'| -ERROR HY000: Table 'proc' was not locked with LOCK TABLES +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction unlock tables| drop procedure bug9566| drop procedure if exists bug7299| diff --git a/mysql-test/r/sp-lock.result b/mysql-test/r/sp-lock.result new file mode 100644 index 00000000000..65524d02d08 --- /dev/null +++ b/mysql-test/r/sp-lock.result @@ -0,0 +1,697 @@ +# +# Test coverage for changes performed by the fix +# for Bug#30977 "Concurrent statement using stored function +# and DROP FUNCTION breaks SBR. +# +# +# 1) Verify that the preceding transaction is +# (implicitly) committed before CREATE/ALTER/DROP +# PROCEDURE. Note, that this is already tested +# in implicit_commit.test, but here we use an alternative +# approach. +# +# Start a transaction, create a savepoint, +# then call a DDL operation on a procedure, and then check +# that the savepoint is no longer present. +drop table if exists t1; +drop procedure if exists p1; +drop procedure if exists p2; +drop procedure if exists p3; +drop procedure if exists p4; +drop function if exists f1; +create table t1 (a int); +# +# Test 'CREATE PROCEDURE'. +# +begin; +savepoint sv; +create procedure p1() begin end; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# Test 'ALTER PROCEDURE'. +# +begin; +savepoint sv; +alter procedure p1 comment 'changed comment'; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# Test 'DROP PROCEDURE'. +# +begin; +savepoint sv; +drop procedure p1; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# Test 'CREATE FUNCTION'. +# +begin; +savepoint sv; +create function f1() returns int return 1; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# Test 'ALTER FUNCTION'. +# +begin; +savepoint sv; +alter function f1 comment 'new comment'; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# Test 'DROP FUNCTION'. +# +begin; +savepoint sv; +drop function f1; +rollback to savepoint sv; +ERROR 42000: SAVEPOINT sv does not exist +# +# 2) Verify that procedure DDL operations fail +# under lock tables. +# +# Auxiliary routines to test ALTER. +create procedure p1() begin end; +create function f1() returns int return 1; +lock table t1 write; +create procedure p2() begin end; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter procedure p1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +drop procedure p1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +create function f2() returns int return 1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter function f1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +lock table t1 read; +create procedure p2() begin end; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter procedure p1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +drop procedure p1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +create function f2() returns int return 1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter function f1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +unlock tables; +# +# Even if we locked a temporary table. +# Todo: this is a restriction we could possibly lift. +# +drop table t1; +create temporary table t1 (a int); +lock table t1 read; +create procedure p2() begin end; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter procedure p1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +drop procedure p1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +create function f2() returns int return 1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +alter function f1 comment 'changed comment'; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +unlock tables; +drop function f1; +drop procedure p1; +drop temporary table t1; +# +# 3) Verify that CREATE/ALTER/DROP routine grab an +# exclusive lock. +# +# For that, start a transaction, use a routine. In a concurrent +# connection, try to drop or alter the routine. It should place +# a pending or exlusive lock and block. In a concurrnet +# connection, try to use the routine under LOCK TABLES. +# That should yield ER_LOCK_DEADLOCK. +# +# Establish helper connections. +# +# Test DROP PROCEDURE. +# +# --> connection default +create table t1 (a int); +create procedure p1() begin end; +create function f1() returns int +begin +call p1(); +return 1; +end| +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'drop procedure p1'... +drop procedure p1; +# --> connection con2 +# Waitng for 'drop procedure t1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'drop procedure p1'... +# --> connection default +# +# Test CREATE PROCEDURE. +# +create procedure p1() begin end; +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'create procedure p1'... +create procedure p1() begin end; +# --> connection con2 +# Waitng for 'create procedure t1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'create procedure p1'... +ERROR 42000: PROCEDURE p1 already exists +# +# Test ALTER PROCEDURE. +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'alter procedure p1'... +alter procedure p1 contains sql; +# --> connection con2 +# Waitng for 'alter procedure t1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'alter procedure p1'... +# --> connection default +# +# Test DROP FUNCTION. +# +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'drop function f1'... +drop function f1; +# --> connection con2 +# Waitng for 'drop function f1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'drop function f1'... +# --> connection default +# +# Test CREATE FUNCTION. +# +create function f1() returns int return 1; +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'create function f1'... +create function f1() returns int return 2; +# --> connection con2 +# Waitng for 'create function f1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'create function f1'... +ERROR 42000: FUNCTION f1 already exists +# --> connection default +# +# Test ALTER FUNCTION. +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'alter function f1'... +alter function f1 contains sql; +# --> connection con2 +# Waitng for 'alter function f1' to get blocked on MDL lock... +# Demonstrate that there is a pending exclusive lock. +lock table t1 read; +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +unlock tables; +# --> connection default +commit; +# --> connection con1 +# Reaping 'alter function f1'... +# --> connection default +drop function f1; +drop procedure p1; +# +# 4) MDL lock should not be taken for +# unrolled CALL statements. +# The primary goal of metadata locks is a consistent binary log. +# When a call statement is unrolled, it doesn't get to the +# binary log, instead the statements that are contained +# in the procedure body do. This can nest to any level. +# +create procedure p1() begin end; +create procedure p2() begin end; +create procedure p3() +begin +call p1(); +call p1(); +call p2(); +end| +create procedure p4() +begin +call p1(); +call p1(); +call p2(); +call p2(); +call p3(); +end| +begin; +select * from t1; +a +savepoint sv; +call p4(); +# Prepared statement should not add any locks either. +prepare stmt from "call p4()"; +execute stmt; +execute stmt; +# --> connection con1 +drop procedure p1; +drop procedure p2; +drop procedure p3; +drop procedure p4; +# --> connection default +# This is to verify there was no implicit commit. +rollback to savepoint sv; +call p4(); +ERROR 42000: PROCEDURE test.p4 does not exist +commit; +drop table t1; +# +# 5) Locks should be taken on routines +# used indirectly by views or triggers. +# +# +# A function is used from a trigger. +# +create function f1() returns int return 1; +create table t1 (a int); +create table t2 (a int, b int); +create trigger t1_ai after insert on t1 for each row +insert into t2 (a, b) values (new.a, f1()); +begin; +insert into t1 (a) values (1); +# --> connection con1 +# Sending 'drop function f1' +drop function f1; +# --> connection con2 +# Waitng for 'drop function f1' to get blocked on MDL lock... +# --> connnection default +commit; +# --> connection con1 +# Reaping 'drop function f1'... +# --> connection default +# +# A function is used from a view. +# +create function f1() returns int return 1; +create view v1 as select f1() as a; +begin; +select * from v1; +a +1 +# --> connection con1 +# Sending 'drop function f1' +drop function f1; +# --> connection con2 +# Waitng for 'drop function f1' to get blocked on MDL lock... +# --> connnection default +commit; +# --> connection con1 +# Reaping 'drop function f1'... +# --> connection default +# +# A procedure is used from a function. +# +create function f1() returns int +begin +declare v_out int; +call p1(v_out); +return v_out; +end| +create procedure p1(out v_out int) set v_out=3; +begin; +select * from v1; +a +3 +# --> connection con1 +# Sending 'drop procedure p1' +drop procedure p1; +# --> connection con2 +# Waitng for 'drop procedure p1' to get blocked on MDL lock... +# --> connnection default +commit; +# --> connection con1 +# Reaping 'drop procedure p1'... +# --> connection default +# +# Deep nesting: a function is used from a procedure used +# from a function used from a view used in a trigger. +# +create function f2() returns int return 4; +create procedure p1(out v_out int) set v_out=f2(); +drop trigger t1_ai; +create trigger t1_ai after insert on t1 for each row +insert into t2 (a, b) values (new.a, (select max(a) from v1)); +begin; +insert into t1 (a) values (3); +# --> connection con1 +# Sending 'drop function f2' +drop function f2; +# --> connection con2 +# Waitng for 'drop function f2' to get blocked on MDL lock... +# --> connnection default +commit; +# --> connection con1 +# Reaping 'drop function f2'... +# --> connection default +drop view v1; +drop function f1; +drop procedure p1; +drop table t1, t2; +# +# 6) Check that ER_LOCK_DEADLOCK is reported if +# acquisition of a shared lock fails during a transaction or +# we need to back off to flush the sp cache. +# +# a) A back off due to a lock conflict. +# +create table t1 (a int); +create function f1() returns int return 6; +begin; +select f1(); +f1() +6 +# --> connection con1 +# Sending 'drop function f1'... +drop function f1; +# --> connection con2 +# Waitng for 'drop function f1' to get blocked on MDL lock... +begin; +select * from t1; +a +select f1(); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +commit; +# --> connection default +commit; +# --> connection con1 +# Reaping 'drop function f1'... +# --> connection default +# +# b) A back off to flush the cache. +# Sic: now this situation does not require a back off since we +# flush the cache on the fly. +# +create function f1() returns int return 7; +begin; +select * from t1; +a +select f1(); +f1() +7 +commit; +drop table t1; +drop function f1; +# +# 7) Demonstrate that under LOCK TABLES we accumulate locks +# on stored routines, and release metadata locks in +# ROLLBACK TO SAVEPOINT. That is done only for those stored +# routines that are not part of LOCK TABLES prelocking list. +# Those stored routines that are part of LOCK TABLES +# prelocking list are implicitly locked when entering +# LOCK TABLES, and ROLLBACK TO SAVEPOINT has no effect on +# them. +# +create function f1() returns varchar(20) return "f1()"; +create function f2() returns varchar(20) return "f2()"; +create view v1 as select f1() as a; +set @@session.autocommit=0; +lock table v1 read; +select * from v1; +a +f1() +savepoint sv; +select f2(); +f2() +f2() +# --> connection con1 +# Sending 'drop function f1'... +drop function f1; +# --> connection con2 +# Waitng for 'drop function f1' to get blocked on MDL lock... +# Sending 'drop function f2'... +drop function f2; +# --> connection default +# Waitng for 'drop function f2' to get blocked on MDL lock... +rollback to savepoint sv; +# --> connection con2 +# Reaping 'drop function f2'... +# --> connection default +unlock tables; +# --> connection con1 +# Reaping 'drop function f1'... +# --> connection default +drop function f1; +ERROR 42000: FUNCTION test.f1 does not exist +drop function f2; +ERROR 42000: FUNCTION test.f2 does not exist +drop view v1; +set @@session.autocommit=default; +# +# 8) Check the situation when we're preparing or executing a +# prepared statement, and as part of that try to flush the +# session sp cache. However, one of the procedures that +# needs a flush is in use. Verify that there is no infinite +# reprepare loop and no crash. +# +create function f1() returns int return 1; +# +# We just mention p1() in the body of f2() to make +# sure that p1() metadata is validated when validating +# 'select f2()'. +# Recursion is not allowed in stored functions, so +# an attempt to just invoke p1() from f2() which is in turn +# called from p1() would have given a run-time error. +# +create function f2() returns int +begin +if @var is null then +call p1(); +end if; +return 1; +end| +create procedure p1() +begin +select f1() into @var; +execute stmt; +end| +# --> connection con2 +prepare stmt from "select f2()"; +# --> connection default +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'alter function f1 ...'... +alter function f1 comment "comment"; +# --> connection con2 +# Waitng for 'alter function f1 ...' to get blocked on MDL lock... +# Sending 'call p1()'... +call p1(); +# Waitng for 'call p1()' to get blocked on MDL lock on f1... +# Let 'alter function f1 ...' go through... +commit; +# --> connection con1 +# Reaping 'alter function f1 ...' +# --> connection con2 +# Reaping 'call p1()'... +f2() +1 +deallocate prepare stmt; +# --> connection default +drop function f1; +drop function f2; +drop procedure p1; +# +# 9) Check the situation when a stored function is invoked +# from a stored procedure, and recursively invokes the +# stored procedure that is in use. But for the second +# invocation, a cache flush is requested. We can't +# flush the procedure that's in use, and are forced +# to use an old version. It is not a violation of +# consistency, since we unroll top-level calls. +# Just verify the code works. +# +create function f1() returns int return 1; +begin; +select f1(); +f1() +1 +# --> connection con1 +# Sending 'alter function f1 ...'... +alter function f1 comment "comment"; +# --> connection con2 +# Waitng for 'alter function f1 ...' to get blocked on MDL lock... +# +# We just mention p1() in the body of f2() to make +# sure that p1() is prelocked for f2(). +# Recursion is not allowed in stored functions, so +# an attempt to just invoke p1() from f2() which is in turn +# called from p1() would have given a run-time error. +# +create function f2() returns int +begin +if @var is null then +call p1(); +end if; +return 1; +end| +create procedure p1() +begin +select f1() into @var; +select f2() into @var; +end| +# Sending 'call p1()'... +call p1(); +# Waitng for 'call p1()' to get blocked on MDL lock on f1... +# Let 'alter function f1 ...' go through... +commit; +# --> connection con1 +# Reaping 'alter function f1 ...' +# --> connection con2 +# Reaping 'call p1()'... +# --> connection default +drop function f1; +drop function f2; +drop procedure p1; +# +# 10) A select from information_schema.routines now +# flushes the stored routines caches. Test that this +# does not remove from the cache a stored routine +# that is already prelocked. +# +create function f1() returns int return get_lock("30977", 100000); +create function f2() returns int return 2; +create function f3() returns varchar(255) +begin +declare res varchar(255); +declare c cursor for select routine_name from +information_schema.routines where routine_name='f1'; +select f1() into @var; +open c; +fetch c into res; +close c; +select f2() into @var; +return res; +end| +# --> connection con1 +select get_lock("30977", 0); +get_lock("30977", 0) +1 +# --> connection default +# Sending 'select f3()'... +select f3(); +# --> connection con1 +# Waitng for 'select f3()' to get blocked on the user level lock... +# Do something to change the cache version. +create function f4() returns int return 4; +drop function f4; +select release_lock("30977"); +release_lock("30977") +1 +# --> connection default +# Reaping 'select f3()'... +# Routine 'f2()' should exist and get executed successfully. +f3() +f1 +select @var; +@var +2 +drop function f1; +drop function f2; +drop function f3; +# 11) Check the situation when the connection is flushing the +# SP cache which contains a procedure that is being executed. +# +# Function f1() calls p1(). Procedure p1() has a DROP +# VIEW statement, which, we know, invalidates the routines cache. +# During cache flush p1() must not be flushed since it's in +# use. +# +create function f1() returns int +begin +call p1(); +return 1; +end| +create procedure p1() +begin +create view v1 as select 1; +drop view v1; +select f1() into @var; +set @exec_count=@exec_count+1; +end| +set @exec_count=0; +call p1(); +ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1 +select @exec_count; +@exec_count +0 +set @@session.max_sp_recursion_depth=5; +set @exec_count=0; +call p1(); +ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger. +select @exec_count; +@exec_count +0 +drop procedure p1; +drop function f1; +set @@session.max_sp_recursion_depth=default; +# --> connection con1 +# --> connection con2 +# --> connection default +# +# End of 5.5 tests +# diff --git a/mysql-test/suite/rpl/r/rpl_sp.result b/mysql-test/suite/rpl/r/rpl_sp.result index 33d8267ad91..ba748049ac1 100644 --- a/mysql-test/suite/rpl/r/rpl_sp.result +++ b/mysql-test/suite/rpl/r/rpl_sp.result @@ -977,4 +977,47 @@ drop procedure mysqltestbug36570_p1; drop procedure ` mysqltestbug36570_p2`; drop function mysqltestbug36570_f1; End of 5.0 tests -End of 5.1 tests +# End of 5.1 tests +# +# Test Bug#30977 Concurrent statement using stored +# function and DROP FUNCTION breaks SBR. +# +# Demonstrate that stored function DDL can not go through, +# or, worse yet, make its way into the binary log, while +# the stored function is in use. +# For that, try to insert a result of a stored function +# into a table. Block the insert in the beginning, waiting +# on a table lock. While insert is blocked, attempt to +# drop the routine. Verify that this attempt +# blocks and waits for INSERT to complete. Commit and +# reap the chain of events. Master and slave must contain +# identical data. Statements in the binrary log must be +# consistent with data in the table. +# +# --> connection default +drop table if exists t1, t2; +drop function if exists t1; +create table t1 (a int); +create table t2 (a int) as select 1 as a; +create function f1() returns int deterministic return (select max(a) from t2); +lock table t2 write; +# --> connection master +# Sending 'insert into t1 (a) values (f1())'... +insert into t1 (a) values (f1()); +# Waitng for 'insert into t1 ...' to get blocked on table lock... +# Sending 'drop function f1'. It will abort the table lock wait. +drop function f1; +# --> connection default +# Now let's let 'insert' go through... +unlock tables; +# --> connection con1 +# Reaping 'insert into t1 (a) values (f1())'... +ERROR 42000: FUNCTION test.f1 does not exist +select * from t1; +a +select * from t1; +a +drop table t1, t2; +drop function f1; +ERROR 42000: FUNCTION test.f1 does not exist +# End of 5.5 tests. diff --git a/mysql-test/suite/rpl/t/rpl_sp.test b/mysql-test/suite/rpl/t/rpl_sp.test index 96a41d9a9ad..231f0c6bcc0 100644 --- a/mysql-test/suite/rpl/t/rpl_sp.test +++ b/mysql-test/suite/rpl/t/rpl_sp.test @@ -621,7 +621,64 @@ drop procedure mysqltestbug36570_p1; drop procedure ` mysqltestbug36570_p2`; drop function mysqltestbug36570_f1; --echo End of 5.0 tests ---echo End of 5.1 tests +--echo # End of 5.1 tests +--echo # +--echo # Test Bug#30977 Concurrent statement using stored +--echo # function and DROP FUNCTION breaks SBR. +--echo # +--echo # Demonstrate that stored function DDL can not go through, +--echo # or, worse yet, make its way into the binary log, while +--echo # the stored function is in use. +--echo # For that, try to insert a result of a stored function +--echo # into a table. Block the insert in the beginning, waiting +--echo # on a table lock. While insert is blocked, attempt to +--echo # drop the routine. Verify that this attempt +--echo # blocks and waits for INSERT to complete. Commit and +--echo # reap the chain of events. Master and slave must contain +--echo # identical data. Statements in the binrary log must be +--echo # consistent with data in the table. +--echo # +--echo # --> connection default +connection default; +--disable_warnings +drop table if exists t1, t2; +drop function if exists t1; +--enable_warnings +create table t1 (a int); +create table t2 (a int) as select 1 as a; +create function f1() returns int deterministic return (select max(a) from t2); +lock table t2 write; +--echo # --> connection master +connection master; +--echo # Sending 'insert into t1 (a) values (f1())'... +--send insert into t1 (a) values (f1()) +connection master1; +--echo # Waitng for 'insert into t1 ...' to get blocked on table lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Table lock' and info='insert into t1 (a) values (f1())'; +--source include/wait_condition.inc +--echo # Sending 'drop function f1'. It will abort the table lock wait. +drop function f1; +--echo # --> connection default +connection default; +--echo # Now let's let 'insert' go through... +unlock tables; +--echo # --> connection con1 +connection master; +--echo # Reaping 'insert into t1 (a) values (f1())'... +--error ER_SP_DOES_NOT_EXIST +--reap +connection master1; +select * from t1; +connection slave; +select * from t1; +connection master; +drop table t1, t2; +--error ER_SP_DOES_NOT_EXIST +drop function f1; + +--echo # End of 5.5 tests. + # Cleanup sync_slave_with_master; diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index e00d63aaedc..6a2b49ac9b7 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -278,8 +278,6 @@ deallocate prepare stmt; --echo # Test 7-a: dependent PROCEDURE has changed --echo # ---echo # Note, this scenario is not supported, subject of Bug#12093 ---echo # create table t1 (a int); create trigger t1_ai after insert on t1 for each row @@ -291,11 +289,10 @@ execute stmt using @var; drop procedure p1; create procedure p1 (a int) begin end; set @var= 2; ---error ER_SP_DOES_NOT_EXIST execute stmt using @var; --echo # Cleanup drop procedure p1; -call p_verify_reprepare_count(0); +call p_verify_reprepare_count(1); --echo # Test 7-b: dependent FUNCTION has changed --echo # @@ -355,11 +352,13 @@ set @var=8; --echo # XXX: bug, the SQL statement in the trigger is still --echo # pointing at table 't3', since the view was expanded --echo # at first statement execution. +--echo # Since the view definition is inlined in the statement +--echo # at prepare, changing the view definition does not cause +--echo # repreparation. --echo # Repreparation of the main statement doesn't cause repreparation --echo # of trigger statements. ---error ER_NO_SUCH_TABLE execute stmt using @var; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); --echo # --echo # Sic: the insert went into t3, even though the view now --echo # points at t2. This is because neither the merged view diff --git a/mysql-test/t/ps_ddl1.test b/mysql-test/t/ps_ddl1.test index 379ed576b5f..0145d445a14 100644 --- a/mysql-test/t/ps_ddl1.test +++ b/mysql-test/t/ps_ddl1.test @@ -363,7 +363,7 @@ end| delimiter ;| --error ER_SP_DOES_NOT_EXIST execute stmt; -call p_verify_reprepare_count(1); +call p_verify_reprepare_count(0); --error ER_SP_DOES_NOT_EXIST execute stmt; call p_verify_reprepare_count(0); diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 4df1118cd56..c8b2595e23d 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -723,7 +723,7 @@ lock table t1 read| # This should fail since we forgot to lock mysql.proc for writing # explicitly, and we can't open mysql.proc for _writing_ if there # are locked tables. ---error 1100 +--error ER_LOCK_OR_ACTIVE_TRANSACTION alter procedure bug9566 comment 'Some comment'| unlock tables| # This should succeed diff --git a/mysql-test/t/sp-lock.test b/mysql-test/t/sp-lock.test new file mode 100644 index 00000000000..a90b85a59be --- /dev/null +++ b/mysql-test/t/sp-lock.test @@ -0,0 +1,876 @@ +# Copyright (C) 2009 Sun Microsystems, Inc +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# +# Metadata lock handling for stored procedures and +# functions. +# +--echo # +--echo # Test coverage for changes performed by the fix +--echo # for Bug#30977 "Concurrent statement using stored function +--echo # and DROP FUNCTION breaks SBR. +--echo # + +--echo # +--echo # 1) Verify that the preceding transaction is +--echo # (implicitly) committed before CREATE/ALTER/DROP +--echo # PROCEDURE. Note, that this is already tested +--echo # in implicit_commit.test, but here we use an alternative +--echo # approach. +--echo # + +--echo # Start a transaction, create a savepoint, +--echo # then call a DDL operation on a procedure, and then check +--echo # that the savepoint is no longer present. + +--disable_warnings +drop table if exists t1; +drop procedure if exists p1; +drop procedure if exists p2; +drop procedure if exists p3; +drop procedure if exists p4; +drop function if exists f1; +--enable_warnings +create table t1 (a int); +--echo # +--echo # Test 'CREATE PROCEDURE'. +--echo # +begin; +savepoint sv; +create procedure p1() begin end; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; +--echo # +--echo # Test 'ALTER PROCEDURE'. +--echo # +begin; +savepoint sv; +alter procedure p1 comment 'changed comment'; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; +--echo # +--echo # Test 'DROP PROCEDURE'. +--echo # +begin; +savepoint sv; +drop procedure p1; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; +--echo # +--echo # Test 'CREATE FUNCTION'. +--echo # +begin; +savepoint sv; +create function f1() returns int return 1; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; +--echo # +--echo # Test 'ALTER FUNCTION'. +--echo # +begin; +savepoint sv; +alter function f1 comment 'new comment'; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; +--echo # +--echo # Test 'DROP FUNCTION'. +--echo # +begin; +savepoint sv; +drop function f1; +--error ER_SP_DOES_NOT_EXIST +rollback to savepoint sv; + +--echo # +--echo # 2) Verify that procedure DDL operations fail +--echo # under lock tables. +--echo # +--echo # Auxiliary routines to test ALTER. +create procedure p1() begin end; +create function f1() returns int return 1; + +lock table t1 write; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create procedure p2() begin end; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter procedure p1 comment 'changed comment'; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +drop procedure p1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create function f2() returns int return 1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter function f1 comment 'changed comment'; +lock table t1 read; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create procedure p2() begin end; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter procedure p1 comment 'changed comment'; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +drop procedure p1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create function f2() returns int return 1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter function f1 comment 'changed comment'; +unlock tables; +--echo # +--echo # Even if we locked a temporary table. +--echo # Todo: this is a restriction we could possibly lift. +--echo # +drop table t1; +create temporary table t1 (a int); +lock table t1 read; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create procedure p2() begin end; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter procedure p1 comment 'changed comment'; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +drop procedure p1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +create function f2() returns int return 1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +alter function f1 comment 'changed comment'; +unlock tables; + +drop function f1; +drop procedure p1; +drop temporary table t1; + +--echo # +--echo # 3) Verify that CREATE/ALTER/DROP routine grab an +--echo # exclusive lock. +--echo # +--echo # For that, start a transaction, use a routine. In a concurrent +--echo # connection, try to drop or alter the routine. It should place +--echo # a pending or exlusive lock and block. In a concurrnet +--echo # connection, try to use the routine under LOCK TABLES. +--echo # That should yield ER_LOCK_DEADLOCK. +--echo # +--echo # Establish helper connections. +connect(con1, localhost, root,,); +connect(con2, localhost, root,,); + +--echo # +--echo # Test DROP PROCEDURE. +--echo # +--echo # --> connection default +connection default; +create table t1 (a int); +create procedure p1() begin end; +delimiter |; +create function f1() returns int +begin + call p1(); + return 1; +end| +delimiter ;| +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop procedure p1'... +--send drop procedure p1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop procedure t1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop procedure p1'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop procedure p1'... +--reap +--echo # --> connection default +connection default; + +--echo # +--echo # Test CREATE PROCEDURE. +--echo # +create procedure p1() begin end; +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'create procedure p1'... +--send create procedure p1() begin end +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'create procedure t1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='create procedure p1() begin end'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'create procedure p1'... +--error ER_SP_ALREADY_EXISTS +--reap +connection default; +--echo # +--echo # Test ALTER PROCEDURE. +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'alter procedure p1'... +--send alter procedure p1 contains sql +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'alter procedure t1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='alter procedure p1 contains sql'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'alter procedure p1'... +--reap +--echo # --> connection default +connection default; + +--echo # +--echo # Test DROP FUNCTION. +--echo # +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f1'... +--send drop function f1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f1'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f1'... +--reap +--echo # --> connection default +connection default; + +--echo # +--echo # Test CREATE FUNCTION. +--echo # +create function f1() returns int return 1; +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'create function f1'... +--send create function f1() returns int return 2 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'create function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='create function f1() returns int return 2'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'create function f1'... +--error ER_SP_ALREADY_EXISTS +--reap +--echo # --> connection default +connection default; +--echo # +--echo # Test ALTER FUNCTION. +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'alter function f1'... +--send alter function f1 contains sql +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'alter function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='alter function f1 contains sql'; +--source include/wait_condition.inc +--echo # Demonstrate that there is a pending exclusive lock. +lock table t1 read; +--error ER_LOCK_DEADLOCK +select f1(); +unlock tables; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'alter function f1'... +--reap +--echo # --> connection default +connection default; +drop function f1; +drop procedure p1; + +--echo # +--echo # 4) MDL lock should not be taken for +--echo # unrolled CALL statements. +--echo # The primary goal of metadata locks is a consistent binary log. +--echo # When a call statement is unrolled, it doesn't get to the +--echo # binary log, instead the statements that are contained +--echo # in the procedure body do. This can nest to any level. +--echo # +create procedure p1() begin end; +create procedure p2() begin end; +delimiter |; +create procedure p3() +begin + call p1(); + call p1(); + call p2(); +end| +create procedure p4() +begin + call p1(); + call p1(); + call p2(); + call p2(); + call p3(); +end| +delimiter ;| +begin; +select * from t1; +savepoint sv; +call p4(); +--echo # Prepared statement should not add any locks either. +prepare stmt from "call p4()"; +execute stmt; +execute stmt; +--echo # --> connection con1 +connection con1; +drop procedure p1; +drop procedure p2; +drop procedure p3; +drop procedure p4; +--echo # --> connection default +connection default; +--echo # This is to verify there was no implicit commit. +rollback to savepoint sv; +--error ER_SP_DOES_NOT_EXIST +call p4(); +commit; +drop table t1; + +--echo # +--echo # 5) Locks should be taken on routines +--echo # used indirectly by views or triggers. +--echo # +--echo # +--echo # A function is used from a trigger. +--echo # +create function f1() returns int return 1; +create table t1 (a int); +create table t2 (a int, b int); +create trigger t1_ai after insert on t1 for each row + insert into t2 (a, b) values (new.a, f1()); +begin; +insert into t1 (a) values (1); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f1' +--send drop function f1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f1'; +--source include/wait_condition.inc +--echo # --> connnection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f1'... +--reap +--echo # --> connection default +connection default; +--echo # +--echo # A function is used from a view. +--echo # +create function f1() returns int return 1; +create view v1 as select f1() as a; +begin; +select * from v1; +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f1' +--send drop function f1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f1'; +--source include/wait_condition.inc +--echo # --> connnection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f1'... +--reap +--echo # --> connection default +connection default; +--echo # +--echo # A procedure is used from a function. +--echo # +delimiter |; +create function f1() returns int +begin + declare v_out int; + call p1(v_out); + return v_out; +end| +delimiter ;| +create procedure p1(out v_out int) set v_out=3; +begin; +select * from v1; +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop procedure p1' +--send drop procedure p1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop procedure p1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop procedure p1'; +--source include/wait_condition.inc +--echo # --> connnection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop procedure p1'... +--reap +--echo # --> connection default +connection default; + +--echo # +--echo # Deep nesting: a function is used from a procedure used +--echo # from a function used from a view used in a trigger. +--echo # +create function f2() returns int return 4; +create procedure p1(out v_out int) set v_out=f2(); +drop trigger t1_ai; +create trigger t1_ai after insert on t1 for each row + insert into t2 (a, b) values (new.a, (select max(a) from v1)); +begin; +insert into t1 (a) values (3); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f2' +--send drop function f2 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f2' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f2'; +--source include/wait_condition.inc +--echo # --> connnection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f2'... +--reap +--echo # --> connection default +connection default; + +drop view v1; +drop function f1; +drop procedure p1; +drop table t1, t2; + +--echo # +--echo # 6) Check that ER_LOCK_DEADLOCK is reported if +--echo # acquisition of a shared lock fails during a transaction or +--echo # we need to back off to flush the sp cache. +--echo # +--echo # a) A back off due to a lock conflict. +--echo # +create table t1 (a int); +create function f1() returns int return 6; +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f1'... +--send drop function f1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f1'; +--source include/wait_condition.inc +begin; +select * from t1; +--error ER_LOCK_DEADLOCK +select f1(); +commit; +--echo # --> connection default +connection default; +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f1'... +--reap +--echo # --> connection default +connection default; +--echo # +--echo # b) A back off to flush the cache. +--echo # Sic: now this situation does not require a back off since we +--echo # flush the cache on the fly. +--echo # +create function f1() returns int return 7; +begin; +select * from t1; +# Used to have a back-off here, with optional ER_LOCK_DEADLOCK +#--error ER_LOCK_DEADLOCK +select f1(); +commit; +drop table t1; +drop function f1; + +--echo # +--echo # 7) Demonstrate that under LOCK TABLES we accumulate locks +--echo # on stored routines, and release metadata locks in +--echo # ROLLBACK TO SAVEPOINT. That is done only for those stored +--echo # routines that are not part of LOCK TABLES prelocking list. +--echo # Those stored routines that are part of LOCK TABLES +--echo # prelocking list are implicitly locked when entering +--echo # LOCK TABLES, and ROLLBACK TO SAVEPOINT has no effect on +--echo # them. +--echo # +create function f1() returns varchar(20) return "f1()"; +create function f2() returns varchar(20) return "f2()"; +create view v1 as select f1() as a; +set @@session.autocommit=0; +lock table v1 read; +select * from v1; +savepoint sv; +select f2(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'drop function f1'... +--send drop function f1 +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'drop function f1' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f1'; +--source include/wait_condition.inc +--echo # Sending 'drop function f2'... +--send drop function f2 +--echo # --> connection default +connection default; +--echo # Waitng for 'drop function f2' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='drop function f2'; +--source include/wait_condition.inc +rollback to savepoint sv; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'drop function f2'... +--reap +--echo # --> connection default +connection default; +unlock tables; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'drop function f1'... +--reap +--echo # --> connection default +connection default; +--error ER_SP_DOES_NOT_EXIST +drop function f1; +--error ER_SP_DOES_NOT_EXIST +drop function f2; +drop view v1; +set @@session.autocommit=default; + +--echo # +--echo # 8) Check the situation when we're preparing or executing a +--echo # prepared statement, and as part of that try to flush the +--echo # session sp cache. However, one of the procedures that +--echo # needs a flush is in use. Verify that there is no infinite +--echo # reprepare loop and no crash. +--echo # +create function f1() returns int return 1; +delimiter |; +--echo # +--echo # We just mention p1() in the body of f2() to make +--echo # sure that p1() metadata is validated when validating +--echo # 'select f2()'. +--echo # Recursion is not allowed in stored functions, so +--echo # an attempt to just invoke p1() from f2() which is in turn +--echo # called from p1() would have given a run-time error. +--echo # +create function f2() returns int +begin + if @var is null then + call p1(); + end if; + return 1; +end| +create procedure p1() +begin + select f1() into @var; + execute stmt; +end| +delimiter ;| +--echo # --> connection con2 +connection con2; +prepare stmt from "select f2()"; +--echo # --> connection default +connection default; +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'alter function f1 ...'... +--send alter function f1 comment "comment" +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'alter function f1 ...' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info like 'alter function f1 comment%'; +--source include/wait_condition.inc +--echo # Sending 'call p1()'... +--send call p1() +connection default; +--echo # Waitng for 'call p1()' to get blocked on MDL lock on f1... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1() into @var'; +--source include/wait_condition.inc +--echo # Let 'alter function f1 ...' go through... +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'alter function f1 ...' +--reap +--echo # --> connection con2 +connection con2; +--echo # Reaping 'call p1()'... +--reap +deallocate prepare stmt; +--echo # --> connection default +connection default; +drop function f1; +drop function f2; +drop procedure p1; + +--echo # +--echo # 9) Check the situation when a stored function is invoked +--echo # from a stored procedure, and recursively invokes the +--echo # stored procedure that is in use. But for the second +--echo # invocation, a cache flush is requested. We can't +--echo # flush the procedure that's in use, and are forced +--echo # to use an old version. It is not a violation of +--echo # consistency, since we unroll top-level calls. +--echo # Just verify the code works. +--echo # +create function f1() returns int return 1; +begin; +select f1(); +--echo # --> connection con1 +connection con1; +--echo # Sending 'alter function f1 ...'... +--send alter function f1 comment "comment" +--echo # --> connection con2 +connection con2; +--echo # Waitng for 'alter function f1 ...' to get blocked on MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info like 'alter function f1 comment%'; +--source include/wait_condition.inc +delimiter |; +--echo # +--echo # We just mention p1() in the body of f2() to make +--echo # sure that p1() is prelocked for f2(). +--echo # Recursion is not allowed in stored functions, so +--echo # an attempt to just invoke p1() from f2() which is in turn +--echo # called from p1() would have given a run-time error. +--echo # +create function f2() returns int +begin + if @var is null then + call p1(); + end if; + return 1; +end| +create procedure p1() +begin + select f1() into @var; + select f2() into @var; +end| +delimiter ;| +--echo # Sending 'call p1()'... +--send call p1() +connection default; +--echo # Waitng for 'call p1()' to get blocked on MDL lock on f1... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1() into @var'; +--source include/wait_condition.inc +--echo # Let 'alter function f1 ...' go through... +commit; +--echo # --> connection con1 +connection con1; +--echo # Reaping 'alter function f1 ...' +--reap +--echo # --> connection con2 +connection con2; +--echo # Reaping 'call p1()'... +--reap +--echo # --> connection default +connection default; +drop function f1; +drop function f2; +drop procedure p1; + +--echo # +--echo # 10) A select from information_schema.routines now +--echo # flushes the stored routines caches. Test that this +--echo # does not remove from the cache a stored routine +--echo # that is already prelocked. +--echo # +create function f1() returns int return get_lock("30977", 100000); +create function f2() returns int return 2; +delimiter |; +create function f3() returns varchar(255) +begin + declare res varchar(255); + declare c cursor for select routine_name from + information_schema.routines where routine_name='f1'; + select f1() into @var; + open c; + fetch c into res; + close c; + select f2() into @var; + return res; +end| +delimiter ;| +--echo # --> connection con1 +connection con1; +select get_lock("30977", 0); +--echo # --> connection default +connection default; +--echo # Sending 'select f3()'... +--send select f3() +--echo # --> connection con1 +connection con1; +--echo # Waitng for 'select f3()' to get blocked on the user level lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='User lock' and info='select f1() into @var'; +--source include/wait_condition.inc +--echo # Do something to change the cache version. +create function f4() returns int return 4; +drop function f4; +select release_lock("30977"); +--echo # --> connection default +connection default; +--echo # Reaping 'select f3()'... +--echo # Routine 'f2()' should exist and get executed successfully. +--reap +select @var; +drop function f1; +drop function f2; +drop function f3; + + +--echo # 11) Check the situation when the connection is flushing the +--echo # SP cache which contains a procedure that is being executed. +--echo # +--echo # Function f1() calls p1(). Procedure p1() has a DROP +--echo # VIEW statement, which, we know, invalidates the routines cache. +--echo # During cache flush p1() must not be flushed since it's in +--echo # use. +--echo # +delimiter |; +create function f1() returns int +begin + call p1(); + return 1; +end| +create procedure p1() +begin + create view v1 as select 1; + drop view v1; + select f1() into @var; + set @exec_count=@exec_count+1; +end| +delimiter ;| +set @exec_count=0; +--error ER_SP_RECURSION_LIMIT +call p1(); +select @exec_count; +set @@session.max_sp_recursion_depth=5; +set @exec_count=0; +--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG +call p1(); +select @exec_count; +drop procedure p1; +drop function f1; +set @@session.max_sp_recursion_depth=default; + +--echo # --> connection con1 +connection con1; +disconnect con1; +--source include/wait_until_disconnected.inc +--echo # --> connection con2 +connection con2; +disconnect con2; +--source include/wait_until_disconnected.inc +--echo # --> connection default +connection default; +--echo # +--echo # End of 5.5 tests +--echo # -- cgit v1.2.1 From 236539b47160a384901533fc41c6793bb5241e0b Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Wed, 30 Dec 2009 20:53:30 +0300 Subject: Implementation of simple deadlock detection for metadata locks. This change is supposed to reduce number of ER_LOCK_DEADLOCK errors which occur when multi-statement transaction encounters conflicting metadata lock in cases when waiting is possible. The idea is not to fail ER_LOCK_DEADLOCK error immediately when we encounter conflicting metadata lock. Instead we release all metadata locks acquired by current statement and start to wait until conflicting lock go away. To avoid deadlocks we use simple empiric which aborts waiting with ER_LOCK_DEADLOCK error if it turns out that somebody is waiting for metadata locks owned by this transaction. This patch also fixes bug #46273 "MySQL 5.4.4 new MDL: Bug#989 is not fully fixed in case of ALTER". The bug was that concurrent execution of UPDATE or MULTI-UPDATE statement as a part of multi-statement transaction that already has used table being updated and ALTER TABLE statement might have resulted of loss of isolation between this transaction and ALTER TABLE statement, which manifested itself as changes performed by ALTER TABLE becoming visible in transaction and wrong binary log order as a consequence. This problem occurred when UPDATE or MULTI-UPDATE's wait in mysql_lock_tables() call was aborted due to metadata lock upgrade performed by concurrent ALTER TABLE. After such abort all metadata locks held by transaction were released but transaction silently continued to be executed as if nothing has happened. We solve this problem by changing our code not to release all locks in such case. Instead we release only locks which were acquired by current statement and then try to reacquire them by restarting open/lock tables process. We piggyback on simple deadlock detector implementation since this change has to be done anyway for it. --- mysql-test/include/handler.inc | 32 +-- mysql-test/r/handler_innodb.result | 27 ++- mysql-test/r/handler_myisam.result | 27 ++- mysql-test/r/mdl_sync.result | 251 ++++++++++++++++++++++ mysql-test/r/sp-lock.result | 96 ++++----- mysql-test/t/mdl_sync.test | 420 +++++++++++++++++++++++++++++++++++++ mysql-test/t/sp-lock.test | 210 ++++++++++--------- 7 files changed, 886 insertions(+), 177 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc index a9965f97926..5562f7b2558 100644 --- a/mysql-test/include/handler.inc +++ b/mysql-test/include/handler.inc @@ -732,7 +732,7 @@ connection default; --disable_warnings drop table if exists t1; --enable_warnings -create table t1 (a int); +create table t1 (a int, key a (a)); insert into t1 values (1); handler t1 open; connection con1; @@ -743,7 +743,6 @@ let $wait_condition= where state = "Waiting for table" and info = "alter table t1 engine=memory"; --source include/wait_condition.inc connection default; ---error ER_ILLEGAL_HA handler t1 read a next; handler t1 close; connection con1; @@ -983,11 +982,12 @@ lock table t2 read; --echo # --> connection con2 connection con2; --echo # Sending: ---send drop table t2 +send rename table t2 to t3, t1 to t2, t3 to t1; --echo # --> connection con1 connection con1; ---echo # Waiting for 'drop table t2' to get blocked... -let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop table t2'; +--echo # Waiting for 'rename table ...' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='rename table t2 to t3, t1 to t2, t3 to t1'; --source include/wait_condition.inc --echo # --> connection default connection default; @@ -997,25 +997,26 @@ handler t2 open; select * from t2; handler t1 open; commit; ---error ER_LOCK_DEADLOCK -handler t2 open; handler t1 close; --echo # --> connection con1 connection con1; unlock tables; --echo # --> connection con2 connection con2; ---echo # Reaping 'drop table t2'... +--echo # Reaping 'rename table ...'... --reap --echo # --> connection default connection default; handler t1 open; handler t1 read a prev; handler t1 close; +drop table t2; --echo # ---echo # Likewise, this doesn't require a multi-statement transaction. ---echo # ER_LOCK_DEADLOCK is also produced when we have an open ---echo # HANDLER and try to acquire locks for a single statement. +--echo # Originally there was a deadlock error in this test. +--echo # With implementation of deadlock detector +--echo # we no longer deadlock, but block and wait on a lock. +--echo # The HANDLER is auto-closed as soon as the connection +--echo # sees a pending conflicting lock against it. --echo # create table t2 (a int, key a (a)); handler t1 open; @@ -1033,10 +1034,12 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where --source include/wait_condition.inc --echo # --> connection default connection default; ---error ER_LOCK_DEADLOCK -select * from t2; +--echo # Sending 'select * from t2' +send select * from t2; --echo # --> connection con1 connection con1; +--echo # Waiting for 'select * from t2' to get blocked... +let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='select * from t2'; unlock tables; --echo # --> connection con2 connection con2; @@ -1044,6 +1047,9 @@ connection con2; --reap --echo # --> connection default connection default; +--echo # Reaping 'select * from t2' +--error ER_NO_SUCH_TABLE +reap; handler t1 close; --echo # diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result index df948f3d0b6..807e8becea8 100644 --- a/mysql-test/r/handler_innodb.result +++ b/mysql-test/r/handler_innodb.result @@ -745,12 +745,13 @@ drop table t1; handler t1 read a next; ERROR 42S02: Unknown table 't1' in HANDLER drop table if exists t1; -create table t1 (a int); +create table t1 (a int, key a (a)); insert into t1 values (1); handler t1 open; alter table t1 engine=memory; handler t1 read a next; -ERROR HY000: Table storage engine for 't1' doesn't have this option +a +1 handler t1 close; drop table t1; USE information_schema; @@ -1002,9 +1003,9 @@ a lock table t2 read; # --> connection con2 # Sending: -drop table t2; +rename table t2 to t3, t1 to t2, t3 to t1; # --> connection con1 -# Waiting for 'drop table t2' to get blocked... +# Waiting for 'rename table ...' to get blocked... # --> connection default handler t2 open; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction @@ -1012,23 +1013,24 @@ select * from t2; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction handler t1 open; commit; -handler t2 open; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction handler t1 close; # --> connection con1 unlock tables; # --> connection con2 -# Reaping 'drop table t2'... +# Reaping 'rename table ...'... # --> connection default handler t1 open; handler t1 read a prev; a 5 handler t1 close; +drop table t2; # -# Likewise, this doesn't require a multi-statement transaction. -# ER_LOCK_DEADLOCK is also produced when we have an open -# HANDLER and try to acquire locks for a single statement. +# Originally there was a deadlock error in this test. +# With implementation of deadlock detector +# we no longer deadlock, but block and wait on a lock. +# The HANDLER is auto-closed as soon as the connection +# sees a pending conflicting lock against it. # create table t2 (a int, key a (a)); handler t1 open; @@ -1040,13 +1042,16 @@ drop table t2; # --> connection con1 # Waiting for 'drop table t2' to get blocked... # --> connection default +# Sending 'select * from t2' select * from t2; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction # --> connection con1 +# Waiting for 'select * from t2' to get blocked... unlock tables; # --> connection con2 # Reaping 'drop table t2'... # --> connection default +# Reaping 'select * from t2' +ERROR 42S02: Table 'test.t2' doesn't exist handler t1 close; # # ROLLBACK TO SAVEPOINT releases transactional locks, diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result index 4b287e6560b..adcbf068b97 100644 --- a/mysql-test/r/handler_myisam.result +++ b/mysql-test/r/handler_myisam.result @@ -743,12 +743,13 @@ drop table t1; handler t1 read a next; ERROR 42S02: Unknown table 't1' in HANDLER drop table if exists t1; -create table t1 (a int); +create table t1 (a int, key a (a)); insert into t1 values (1); handler t1 open; alter table t1 engine=memory; handler t1 read a next; -ERROR HY000: Table storage engine for 't1' doesn't have this option +a +1 handler t1 close; drop table t1; USE information_schema; @@ -999,9 +1000,9 @@ a lock table t2 read; # --> connection con2 # Sending: -drop table t2; +rename table t2 to t3, t1 to t2, t3 to t1; # --> connection con1 -# Waiting for 'drop table t2' to get blocked... +# Waiting for 'rename table ...' to get blocked... # --> connection default handler t2 open; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction @@ -1009,23 +1010,24 @@ select * from t2; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction handler t1 open; commit; -handler t2 open; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction handler t1 close; # --> connection con1 unlock tables; # --> connection con2 -# Reaping 'drop table t2'... +# Reaping 'rename table ...'... # --> connection default handler t1 open; handler t1 read a prev; a 5 handler t1 close; +drop table t2; # -# Likewise, this doesn't require a multi-statement transaction. -# ER_LOCK_DEADLOCK is also produced when we have an open -# HANDLER and try to acquire locks for a single statement. +# Originally there was a deadlock error in this test. +# With implementation of deadlock detector +# we no longer deadlock, but block and wait on a lock. +# The HANDLER is auto-closed as soon as the connection +# sees a pending conflicting lock against it. # create table t2 (a int, key a (a)); handler t1 open; @@ -1037,13 +1039,16 @@ drop table t2; # --> connection con1 # Waiting for 'drop table t2' to get blocked... # --> connection default +# Sending 'select * from t2' select * from t2; -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction # --> connection con1 +# Waiting for 'select * from t2' to get blocked... unlock tables; # --> connection con2 # Reaping 'drop table t2'... # --> connection default +# Reaping 'select * from t2' +ERROR 42S02: Table 'test.t2' doesn't exist handler t1 close; # # ROLLBACK TO SAVEPOINT releases transactional locks, diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index ec02f29b008..0c9b6432e95 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -20,6 +20,220 @@ ERROR 42S02: Unknown table 't1' drop table t3; SET DEBUG_SYNC= 'RESET'; # +# Test coverage for basic deadlock detection in metadata +# locking subsystem. +# +drop tables if exists t1, t2, t3, t4; +create table t1 (i int); +create table t2 (j int); +create table t3 (k int); +create table t4 (k int); +# +# Test for the case in which no deadlock occurs. +# +# +# Switching to connection 'deadlock_con1'. +begin; +insert into t1 values (1); +# +# Switching to connection 'deadlock_con2'. +begin; +insert into t2 values (1); +# +# Switching to connection 'default'. +# Send: +rename table t2 to t0, t3 to t2, t0 to t3;; +# +# Switching to connection 'deadlock_con1'. +# Wait until the above RENAME TABLE is blocked because it has to wait +# for 'deadlock_con2' which holds shared metadata lock on 't2'. +# The below statement should wait for exclusive metadata lock +# on 't2' to go away and should not produce ER_LOCK_DEADLOCK +# as no deadlock is possible in this situation. +# Send: +select * from t2;; +# +# Switching to connection 'deadlock_con2'. +# Wait until the above SELECT * FROM t2 is starts waiting +# for an exclusive metadata lock to go away. +# +# Unblock RENAME TABLE by releasing shared metadata lock on t2. +commit; +# +# Switching to connection 'default'. +# Reap RENAME TABLE. +# +# Switching to connection 'deadlock_con1'. +# Reap SELECT. +k +# +# Switching to connection 'default'. +# +# Let us check that in the process of waiting for conflicting lock +# on table 't2' to go away transaction in connection 'deadlock_con1' +# has not released metadata lock on table 't1'. +# Send: +rename table t1 to t0, t3 to t1, t0 to t3;; +# +# Switching to connection 'deadlock_con1'. +# Wait until the above RENAME TABLE is blocked because it has to wait +# for 'deadlock_con1' which should still hold shared metadata lock on +# table 't1'. +# Commit transaction to unblock RENAME TABLE. +commit; +# +# Switching to connection 'default'. +# Reap RENAME TABLE. +# +# Test for case when deadlock occurs and should be detected immediately. +# +# +# Switching to connection 'deadlock_con1'. +begin; +insert into t1 values (2); +# +# Switching to connection 'default'. +# Send: +rename table t2 to t0, t1 to t2, t0 to t1;; +# +# Switching to connection 'deadlock_con1'. +# Wait until the above RENAME TABLE is blocked because it has to wait +# for 'deadlock_con1' which holds shared metadata lock on 't1'. +# +# The below statement should not wait as doing so will cause deadlock. +# Instead it should fail and emit ER_LOCK_DEADLOCK statement. +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# +# Let us check that failure of the above statement has not released +# metadata lock on table 't1', i.e. that RENAME TABLE is still blocked. +# Commit transaction to unblock RENAME TABLE. +commit; +# +# Switching to connection 'default'. +# Reap RENAME TABLE. +# +# Test for the case in which deadlock also occurs but not immediately. +# +# +# Switching to connection 'deadlock_con1'. +begin; +insert into t1 values (1); +# +# Switching to connection 'deadlock_con2'. +begin; +insert into t3 values (1); +# +# Switching to connection 'default'. +# Send: +rename table t2 to t0, t3 to t2, t0 to t3;; +# +# Switching to connection 'deadlock_con1'. +# Wait until the above RENAME TABLE is blocked because it has to wait +# for 'deadlock_con2' which holds shared metadata lock on 't3'. +# The below SELECT statement should wait for metadata lock +# on table 't2' and should not produce ER_LOCK_DEADLOCK +# immediately as no deadlock is possible at the moment. +select * from t2;; +# +# Switching to connection 'deadlock_con3'. +# Wait until the above SELECT * FROM t2 is starts waiting +# for an exclusive metadata lock to go away. +# Send RENAME TABLE statement that will deadlock with the +# SELECT statement and thus should abort the latter. +rename table t1 to t0, t2 to t1, t0 to t2;; +# +# Switching to connection 'deadlock_con1'. +# Since the latest RENAME TABLE entered in deadlock with SELECT +# statement the latter should be aborted and emit ER_LOCK_DEADLOCK +# error. +# Reap SELECT * FROM t2. +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# +# Again let us check that failure of the SELECT statement has not +# released metadata lock on table 't1', i.e. that the latest RENAME +# is blocked. +# Commit transaction to unblock this RENAME TABLE. +commit; +# +# Switching to connection 'deadlock_con3'. +# Reap RENAME TABLE t1 TO t0 ... . +# +# Switching to connection 'deadlock_con2'. +# Commit transaction to unblock the first RENAME TABLE. +commit; +# +# Switching to connection 'default'. +# Reap RENAME TABLE t2 TO t0 ... . +drop tables t1, t2, t3, t4; +# +# Now, test case which shows that deadlock detection empiric +# also takes into account requests for metadata lock upgrade. +# +create table t1 (i int); +# +# Switching to connection 'deadlock_con1'. +begin; +insert into t1 values (1); +# +# Switching to connection 'default'. +# Send: +alter table t1 add column j int, rename to t2;; +# +# Switching to connection 'deadlock_con1'. +# Wait until the above ALTER TABLE ... RENAME acquires exclusive +# metadata lock on 't2' and starts waiting for connection +# 'deadlock_con1' which holds shared lock on 't1'. +# The below statement should not wait as it will cause deadlock. +# An appropriate error should be reported instead. +select * from t2; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Again let us check that failure of the above statement has not +# released all metadata locks in connection 'deadlock_con1' and +# so ALTER TABLE ... RENAME is still blocked. +# Commit transaction to unblock ALTER TABLE ... RENAME. +commit; +# +# Switching to connection 'default'. +# Reap ALTER TABLE ... RENAME. +drop table t2; +# +# Finally, test case in which deadlock (or potentially livelock) occurs +# between metadata locking subsystem and table definition cache/table +# locks, but which should still be detected by our empiric. +# +create table t1 (i int); +# +# Switching to connection 'deadlock_con1'. +begin; +insert into t1 values (1); +# +# Switching to connection 'default'. +lock tables t1 write; +# +# Switching to connection 'deadlock_con1'. +# Send: +insert into t1 values (2);; +# +# Switching to connection 'default'. +# Wait until INSERT in connection 'deadlock_con1' is blocked on +# table-level lock. +# Send: +alter table t1 add column j int;; +# +# Switching to connection 'deadlock_con1'. +# The above ALTER TABLE statement should cause INSERT statement in +# this connection to be aborted and emit ER_LOCK_DEADLOCK error. +# Reap INSERT +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# Commit transaction to unblock ALTER TABLE. +commit; +# +# Switching to connection 'default'. +# Reap ALTER TABLE. +unlock tables; +drop table t1; +# # Test for bug #46748 "Assertion in MDL_context::wait_for_locks() # on INSERT + CREATE TRIGGER". # @@ -234,6 +448,43 @@ drop table t2; # Clean-up. drop table t1; # +# Test for bug #46273 "MySQL 5.4.4 new MDL: Bug#989 is not fully fixed +# in case of ALTER". +# +drop table if exists t1; +set debug_sync= 'RESET'; +create table t1 (c1 int primary key, c2 int, c3 int); +insert into t1 values (1,1,0),(2,2,0),(3,3,0),(4,4,0),(5,5,0); +begin; +update t1 set c3=c3+1 where c2=3; +# +# Switching to connection 'con46273'. +set debug_sync='after_lock_tables_takes_lock SIGNAL alter_table_locked WAIT_FOR alter_go'; +alter table t1 add column e int, rename to t2;; +# +# Switching to connection 'default'. +set debug_sync='now WAIT_FOR alter_table_locked'; +set debug_sync='wait_for_lock SIGNAL alter_go'; +# The below statement should get ER_LOCK_DEADLOCK error +# (i.e. it should not allow ALTER to proceed, and then +# fail due to 't1' changing its name to 't2'). +update t1 set c3=c3+1 where c2=4; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# +# Let us check that failure of the above statement has not released +# metadata lock on table 't1', i.e. that ALTER TABLE is still blocked. +# Unblock ALTER TABLE by commiting transaction and thus releasing +# metadata lock on 't1'. +commit; +# +# Switching to connection 'con46273'. +# Reap ALTER TABLE. +# +# Switching to connection 'default'. +# Clean-up. +set debug_sync= 'RESET'; +drop table t2; +# # Test for bug #46673 "Deadlock between FLUSH TABLES WITH READ LOCK # and DML". # diff --git a/mysql-test/r/sp-lock.result b/mysql-test/r/sp-lock.result index 65524d02d08..e9087f61807 100644 --- a/mysql-test/r/sp-lock.result +++ b/mysql-test/r/sp-lock.result @@ -125,16 +125,15 @@ drop temporary table t1; # # For that, start a transaction, use a routine. In a concurrent # connection, try to drop or alter the routine. It should place -# a pending or exlusive lock and block. In a concurrnet -# connection, try to use the routine under LOCK TABLES. -# That should yield ER_LOCK_DEADLOCK. +# a pending or exclusive lock and block. In another concurrnet +# connection, try to use the routine. +# That should block on the pending exclusive lock. # # Establish helper connections. # # Test DROP PROCEDURE. # # --> connection default -create table t1 (a int); create procedure p1() begin end; create function f1() returns int begin @@ -151,14 +150,17 @@ drop procedure p1; # --> connection con2 # Waitng for 'drop procedure t1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'drop procedure p1'... +# --> connection con2 +# Reaping 'select f1()' +ERROR 42000: PROCEDURE test.p1 does not exist # --> connection default # # Test CREATE PROCEDURE. @@ -174,17 +176,22 @@ create procedure p1() begin end; # --> connection con2 # Waitng for 'create procedure t1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'create procedure p1'... ERROR 42000: PROCEDURE p1 already exists +# --> connection con2 +# Reaping 'select f1()' +f1() +1 # # Test ALTER PROCEDURE. +# begin; select f1(); f1() @@ -195,14 +202,18 @@ alter procedure p1 contains sql; # --> connection con2 # Waitng for 'alter procedure t1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'alter procedure p1'... +# --> connection con2 +# Reaping 'select f1()' +f1() +1 # --> connection default # # Test DROP FUNCTION. @@ -217,14 +228,17 @@ drop function f1; # --> connection con2 # Waitng for 'drop function f1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'drop function f1'... +# --> connection con2 +# Reaping 'select f1()' +ERROR 42000: FUNCTION test.f1 does not exist # --> connection default # # Test CREATE FUNCTION. @@ -240,18 +254,23 @@ create function f1() returns int return 2; # --> connection con2 # Waitng for 'create function f1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'create function f1'... ERROR 42000: FUNCTION f1 already exists +# --> connection con2 +# Reaping 'select f1()' +f1() +1 # --> connection default # # Test ALTER FUNCTION. +# begin; select f1(); f1() @@ -262,14 +281,18 @@ alter function f1 contains sql; # --> connection con2 # Waitng for 'alter function f1' to get blocked on MDL lock... # Demonstrate that there is a pending exclusive lock. -lock table t1 read; +# Sending 'select f1()'... select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -unlock tables; +# --> connection con3 +# Waitng for 'select f1()' to get blocked by a pending MDL lock... # --> connection default commit; # --> connection con1 # Reaping 'alter function f1'... +# --> connection con2 +# Reaping 'select f1()' +f1() +1 # --> connection default drop function f1; drop procedure p1; @@ -283,6 +306,7 @@ drop procedure p1; # create procedure p1() begin end; create procedure p2() begin end; +create table t1 (a int); create procedure p3() begin call p1(); @@ -415,36 +439,11 @@ drop table t1, t2; # acquisition of a shared lock fails during a transaction or # we need to back off to flush the sp cache. # -# a) A back off due to a lock conflict. -# -create table t1 (a int); -create function f1() returns int return 6; -begin; -select f1(); -f1() -6 -# --> connection con1 -# Sending 'drop function f1'... -drop function f1; -# --> connection con2 -# Waitng for 'drop function f1' to get blocked on MDL lock... -begin; -select * from t1; -a -select f1(); -ERROR 40001: Deadlock found when trying to get lock; try restarting transaction -commit; -# --> connection default -commit; -# --> connection con1 -# Reaping 'drop function f1'... -# --> connection default -# -# b) A back off to flush the cache. # Sic: now this situation does not require a back off since we # flush the cache on the fly. # create function f1() returns int return 7; +create table t1 (a int); begin; select * from t1; a @@ -691,6 +690,7 @@ drop function f1; set @@session.max_sp_recursion_depth=default; # --> connection con1 # --> connection con2 +# --> connection con3 # --> connection default # # End of 5.5 tests diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index e3aceaa05fa..c817012fb2f 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -73,6 +73,366 @@ SET DEBUG_SYNC= 'RESET'; --enable_warnings +--echo # +--echo # Test coverage for basic deadlock detection in metadata +--echo # locking subsystem. +--echo # +--disable_warnings +drop tables if exists t1, t2, t3, t4; +--enable_warnings + +connect(deadlock_con1,localhost,root,,); +connect(deadlock_con2,localhost,root,,); +connect(deadlock_con3,localhost,root,,); +connection default; +create table t1 (i int); +create table t2 (j int); +create table t3 (k int); +create table t4 (k int); + +--echo # +--echo # Test for the case in which no deadlock occurs. +--echo # + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +begin; +insert into t1 values (1); + +--echo # +--echo # Switching to connection 'deadlock_con2'. +connection deadlock_con2; +begin; +insert into t2 values (1); + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Send: +--send rename table t2 to t0, t3 to t2, t0 to t3; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Wait until the above RENAME TABLE is blocked because it has to wait +--echo # for 'deadlock_con2' which holds shared metadata lock on 't2'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t2 to t0, t3 to t2, t0 to t3"; +--source include/wait_condition.inc +--echo # The below statement should wait for exclusive metadata lock +--echo # on 't2' to go away and should not produce ER_LOCK_DEADLOCK +--echo # as no deadlock is possible in this situation. +--echo # Send: +--send select * from t2; + +--echo # +--echo # Switching to connection 'deadlock_con2'. +connection deadlock_con2; +--echo # Wait until the above SELECT * FROM t2 is starts waiting +--echo # for an exclusive metadata lock to go away. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "select * from t2"; +--source include/wait_condition.inc +--echo # +--echo # Unblock RENAME TABLE by releasing shared metadata lock on t2. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap RENAME TABLE. +--reap + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Reap SELECT. +--reap + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # +--echo # Let us check that in the process of waiting for conflicting lock +--echo # on table 't2' to go away transaction in connection 'deadlock_con1' +--echo # has not released metadata lock on table 't1'. +--echo # Send: +--send rename table t1 to t0, t3 to t1, t0 to t3; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Wait until the above RENAME TABLE is blocked because it has to wait +--echo # for 'deadlock_con1' which should still hold shared metadata lock on +--echo # table 't1'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t1 to t0, t3 to t1, t0 to t3"; +--source include/wait_condition.inc +--echo # Commit transaction to unblock RENAME TABLE. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap RENAME TABLE. +--reap + +--echo # +--echo # Test for case when deadlock occurs and should be detected immediately. +--echo # + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +begin; +insert into t1 values (2); + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Send: +--send rename table t2 to t0, t1 to t2, t0 to t1; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Wait until the above RENAME TABLE is blocked because it has to wait +--echo # for 'deadlock_con1' which holds shared metadata lock on 't1'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t2 to t0, t1 to t2, t0 to t1"; +--source include/wait_condition.inc +--echo # +--echo # The below statement should not wait as doing so will cause deadlock. +--echo # Instead it should fail and emit ER_LOCK_DEADLOCK statement. +--error ER_LOCK_DEADLOCK +select * from t2; + +--echo # +--echo # Let us check that failure of the above statement has not released +--echo # metadata lock on table 't1', i.e. that RENAME TABLE is still blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t2 to t0, t1 to t2, t0 to t1"; +--source include/wait_condition.inc +--echo # Commit transaction to unblock RENAME TABLE. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap RENAME TABLE. +--reap + +--echo # +--echo # Test for the case in which deadlock also occurs but not immediately. +--echo # + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +begin; +insert into t1 values (1); + +--echo # +--echo # Switching to connection 'deadlock_con2'. +connection deadlock_con2; +begin; +insert into t3 values (1); + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Send: +--send rename table t2 to t0, t3 to t2, t0 to t3; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Wait until the above RENAME TABLE is blocked because it has to wait +--echo # for 'deadlock_con2' which holds shared metadata lock on 't3'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t2 to t0, t3 to t2, t0 to t3"; +--source include/wait_condition.inc +--echo # The below SELECT statement should wait for metadata lock +--echo # on table 't2' and should not produce ER_LOCK_DEADLOCK +--echo # immediately as no deadlock is possible at the moment. +--send select * from t2; + +--echo # +--echo # Switching to connection 'deadlock_con3'. +connection deadlock_con3; +--echo # Wait until the above SELECT * FROM t2 is starts waiting +--echo # for an exclusive metadata lock to go away. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "select * from t2"; +--source include/wait_condition.inc + +--echo # Send RENAME TABLE statement that will deadlock with the +--echo # SELECT statement and thus should abort the latter. +--send rename table t1 to t0, t2 to t1, t0 to t2; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Since the latest RENAME TABLE entered in deadlock with SELECT +--echo # statement the latter should be aborted and emit ER_LOCK_DEADLOCK +--echo # error. +--echo # Reap SELECT * FROM t2. +--error ER_LOCK_DEADLOCK +--reap + +--echo # +--echo # Again let us check that failure of the SELECT statement has not +--echo # released metadata lock on table 't1', i.e. that the latest RENAME +--echo # is blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "rename table t1 to t0, t2 to t1, t0 to t2"; +--source include/wait_condition.inc +--echo # Commit transaction to unblock this RENAME TABLE. +commit; + +--echo # +--echo # Switching to connection 'deadlock_con3'. +connection deadlock_con3; +--echo # Reap RENAME TABLE t1 TO t0 ... . +--reap; + +--echo # +--echo # Switching to connection 'deadlock_con2'. +connection deadlock_con2; +--echo # Commit transaction to unblock the first RENAME TABLE. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap RENAME TABLE t2 TO t0 ... . +--reap + +drop tables t1, t2, t3, t4; + +--echo # +--echo # Now, test case which shows that deadlock detection empiric +--echo # also takes into account requests for metadata lock upgrade. +--echo # +create table t1 (i int); + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +begin; +insert into t1 values (1); + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Send: +--send alter table t1 add column j int, rename to t2; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Wait until the above ALTER TABLE ... RENAME acquires exclusive +--echo # metadata lock on 't2' and starts waiting for connection +--echo # 'deadlock_con1' which holds shared lock on 't1'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "alter table t1 add column j int, rename to t2"; +--source include/wait_condition.inc + +--echo # The below statement should not wait as it will cause deadlock. +--echo # An appropriate error should be reported instead. +--error ER_LOCK_DEADLOCK +select * from t2; + +--echo # Again let us check that failure of the above statement has not +--echo # released all metadata locks in connection 'deadlock_con1' and +--echo # so ALTER TABLE ... RENAME is still blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "alter table t1 add column j int, rename to t2"; +--source include/wait_condition.inc + +--echo # Commit transaction to unblock ALTER TABLE ... RENAME. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap ALTER TABLE ... RENAME. +--reap + +drop table t2; + +--echo # +--echo # Finally, test case in which deadlock (or potentially livelock) occurs +--echo # between metadata locking subsystem and table definition cache/table +--echo # locks, but which should still be detected by our empiric. +--echo # +create table t1 (i int); + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +begin; +insert into t1 values (1); + +--echo # +--echo # Switching to connection 'default'. +connection default; +lock tables t1 write; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # Send: +--send insert into t1 values (2); + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Wait until INSERT in connection 'deadlock_con1' is blocked on +--echo # table-level lock. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Table lock" and info = "insert into t1 values (2)"; +--source include/wait_condition.inc + +--echo # Send: +--send alter table t1 add column j int; + +--echo # +--echo # Switching to connection 'deadlock_con1'. +connection deadlock_con1; +--echo # The above ALTER TABLE statement should cause INSERT statement in +--echo # this connection to be aborted and emit ER_LOCK_DEADLOCK error. +--echo # Reap INSERT +--error ER_LOCK_DEADLOCK +--reap +--echo # Commit transaction to unblock ALTER TABLE. +commit; + +--echo # +--echo # Switching to connection 'default'. +connection default; +--echo # Reap ALTER TABLE. +--reap +unlock tables; + +drop table t1; +disconnect deadlock_con1; +disconnect deadlock_con2; +disconnect deadlock_con3; + + --echo # --echo # Test for bug #46748 "Assertion in MDL_context::wait_for_locks() --echo # on INSERT + CREATE TRIGGER". @@ -439,6 +799,66 @@ disconnect con46044_2; drop table t1; +--echo # +--echo # Test for bug #46273 "MySQL 5.4.4 new MDL: Bug#989 is not fully fixed +--echo # in case of ALTER". +--echo # +--disable_warnings +drop table if exists t1; +--enable_warnings +set debug_sync= 'RESET'; +connect (con46273,localhost,root,,test,,); +connection default; +create table t1 (c1 int primary key, c2 int, c3 int); +insert into t1 values (1,1,0),(2,2,0),(3,3,0),(4,4,0),(5,5,0); + +begin; +update t1 set c3=c3+1 where c2=3; + +--echo # +--echo # Switching to connection 'con46273'. +connection con46273; +set debug_sync='after_lock_tables_takes_lock SIGNAL alter_table_locked WAIT_FOR alter_go'; +--send alter table t1 add column e int, rename to t2; + +--echo # +--echo # Switching to connection 'default'. +connection default; +set debug_sync='now WAIT_FOR alter_table_locked'; +set debug_sync='wait_for_lock SIGNAL alter_go'; +--echo # The below statement should get ER_LOCK_DEADLOCK error +--echo # (i.e. it should not allow ALTER to proceed, and then +--echo # fail due to 't1' changing its name to 't2'). +--error ER_LOCK_DEADLOCK +update t1 set c3=c3+1 where c2=4; + +--echo # +--echo # Let us check that failure of the above statement has not released +--echo # metadata lock on table 't1', i.e. that ALTER TABLE is still blocked. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table" and info = "alter table t1 add column e int, rename to t2"; +--source include/wait_condition.inc + +--echo # Unblock ALTER TABLE by commiting transaction and thus releasing +--echo # metadata lock on 't1'. +commit; + +--echo # +--echo # Switching to connection 'con46273'. +connection con46273; +--echo # Reap ALTER TABLE. +--reap + +--echo # +--echo # Switching to connection 'default'. +connection default; +disconnect con46273; +--echo # Clean-up. +set debug_sync= 'RESET'; +drop table t2; + + --echo # --echo # Test for bug #46673 "Deadlock between FLUSH TABLES WITH READ LOCK --echo # and DML". diff --git a/mysql-test/t/sp-lock.test b/mysql-test/t/sp-lock.test index a90b85a59be..0b31eabb0f1 100644 --- a/mysql-test/t/sp-lock.test +++ b/mysql-test/t/sp-lock.test @@ -153,20 +153,20 @@ drop temporary table t1; --echo # --echo # For that, start a transaction, use a routine. In a concurrent --echo # connection, try to drop or alter the routine. It should place ---echo # a pending or exlusive lock and block. In a concurrnet ---echo # connection, try to use the routine under LOCK TABLES. ---echo # That should yield ER_LOCK_DEADLOCK. +--echo # a pending or exclusive lock and block. In another concurrnet +--echo # connection, try to use the routine. +--echo # That should block on the pending exclusive lock. --echo # --echo # Establish helper connections. connect(con1, localhost, root,,); connect(con2, localhost, root,,); +connect(con3, localhost, root,,); --echo # --echo # Test DROP PROCEDURE. --echo # --echo # --> connection default connection default; -create table t1 (a int); create procedure p1() begin end; delimiter |; create function f1() returns int @@ -180,7 +180,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'drop procedure p1'... ---send drop procedure p1 +send drop procedure p1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop procedure t1' to get blocked on MDL lock... @@ -188,17 +188,25 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop procedure p1'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop procedure p1'... ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +--error ER_SP_DOES_NOT_EXIST +reap; --echo # --> connection default connection default; @@ -211,7 +219,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'create procedure p1'... ---send create procedure p1() begin end +send create procedure p1() begin end; --echo # --> connection con2 connection con2; --echo # Waitng for 'create procedure t1' to get blocked on MDL lock... @@ -219,10 +227,13 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='create procedure p1() begin end'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; @@ -230,16 +241,22 @@ commit; connection con1; --echo # Reaping 'create procedure p1'... --error ER_SP_ALREADY_EXISTS ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +reap; connection default; + --echo # --echo # Test ALTER PROCEDURE. +--echo # begin; select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'alter procedure p1'... ---send alter procedure p1 contains sql +send alter procedure p1 contains sql; --echo # --> connection con2 connection con2; --echo # Waitng for 'alter procedure t1' to get blocked on MDL lock... @@ -247,17 +264,24 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='alter procedure p1 contains sql'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; --echo # --> connection con1 connection con1; --echo # Reaping 'alter procedure p1'... ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +reap; --echo # --> connection default connection default; @@ -269,7 +293,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'drop function f1'... ---send drop function f1 +send drop function f1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop function f1' to get blocked on MDL lock... @@ -277,17 +301,25 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop function f1'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop function f1'... ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +--error ER_SP_DOES_NOT_EXIST +reap; --echo # --> connection default connection default; @@ -300,7 +332,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'create function f1'... ---send create function f1() returns int return 2 +send create function f1() returns int return 2; --echo # --> connection con2 connection con2; --echo # Waitng for 'create function f1' to get blocked on MDL lock... @@ -308,10 +340,13 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='create function f1() returns int return 2'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; @@ -319,17 +354,23 @@ commit; connection con1; --echo # Reaping 'create function f1'... --error ER_SP_ALREADY_EXISTS ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +reap; --echo # --> connection default connection default; + --echo # --echo # Test ALTER FUNCTION. +--echo # begin; select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'alter function f1'... ---send alter function f1 contains sql +send alter function f1 contains sql; --echo # --> connection con2 connection con2; --echo # Waitng for 'alter function f1' to get blocked on MDL lock... @@ -337,17 +378,24 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='alter function f1 contains sql'; --source include/wait_condition.inc --echo # Demonstrate that there is a pending exclusive lock. -lock table t1 read; ---error ER_LOCK_DEADLOCK -select f1(); -unlock tables; +--echo # Sending 'select f1()'... +send select f1(); +--echo # --> connection con3 +connection con3; +--echo # Waitng for 'select f1()' to get blocked by a pending MDL lock... +let $wait_condition=select count(*)=1 from information_schema.processlist +where state='Waiting for table' and info='select f1()'; --echo # --> connection default connection default; commit; --echo # --> connection con1 connection con1; --echo # Reaping 'alter function f1'... ---reap +reap; +--echo # --> connection con2 +connection con2; +--echo # Reaping 'select f1()' +reap; --echo # --> connection default connection default; drop function f1; @@ -363,6 +411,7 @@ drop procedure p1; --echo # create procedure p1() begin end; create procedure p2() begin end; +create table t1 (a int); delimiter |; create procedure p3() begin @@ -419,7 +468,7 @@ insert into t1 (a) values (1); --echo # --> connection con1 connection con1; --echo # Sending 'drop function f1' ---send drop function f1 +send drop function f1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop function f1' to get blocked on MDL lock... @@ -432,7 +481,7 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop function f1'... ---reap +reap; --echo # --> connection default connection default; --echo # @@ -445,7 +494,7 @@ select * from v1; --echo # --> connection con1 connection con1; --echo # Sending 'drop function f1' ---send drop function f1 +send drop function f1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop function f1' to get blocked on MDL lock... @@ -458,7 +507,7 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop function f1'... ---reap +reap; --echo # --> connection default connection default; --echo # @@ -478,7 +527,7 @@ select * from v1; --echo # --> connection con1 connection con1; --echo # Sending 'drop procedure p1' ---send drop procedure p1 +send drop procedure p1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop procedure p1' to get blocked on MDL lock... @@ -491,7 +540,7 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop procedure p1'... ---reap +reap; --echo # --> connection default connection default; @@ -509,7 +558,7 @@ insert into t1 (a) values (3); --echo # --> connection con1 connection con1; --echo # Sending 'drop function f2' ---send drop function f2 +send drop function f2; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop function f2' to get blocked on MDL lock... @@ -522,7 +571,7 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'drop function f2'... ---reap +reap; --echo # --> connection default connection default; @@ -536,42 +585,11 @@ drop table t1, t2; --echo # acquisition of a shared lock fails during a transaction or --echo # we need to back off to flush the sp cache. --echo # ---echo # a) A back off due to a lock conflict. ---echo # -create table t1 (a int); -create function f1() returns int return 6; -begin; -select f1(); ---echo # --> connection con1 -connection con1; ---echo # Sending 'drop function f1'... ---send drop function f1 ---echo # --> connection con2 -connection con2; ---echo # Waitng for 'drop function f1' to get blocked on MDL lock... -let $wait_condition=select count(*)=1 from information_schema.processlist -where state='Waiting for table' and info='drop function f1'; ---source include/wait_condition.inc -begin; -select * from t1; ---error ER_LOCK_DEADLOCK -select f1(); -commit; ---echo # --> connection default -connection default; -commit; ---echo # --> connection con1 -connection con1; ---echo # Reaping 'drop function f1'... ---reap ---echo # --> connection default -connection default; ---echo # ---echo # b) A back off to flush the cache. --echo # Sic: now this situation does not require a back off since we --echo # flush the cache on the fly. --echo # create function f1() returns int return 7; +create table t1 (a int); begin; select * from t1; # Used to have a back-off here, with optional ER_LOCK_DEADLOCK @@ -602,7 +620,7 @@ select f2(); --echo # --> connection con1 connection con1; --echo # Sending 'drop function f1'... ---send drop function f1 +send drop function f1; --echo # --> connection con2 connection con2; --echo # Waitng for 'drop function f1' to get blocked on MDL lock... @@ -610,7 +628,7 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info='drop function f1'; --source include/wait_condition.inc --echo # Sending 'drop function f2'... ---send drop function f2 +send drop function f2; --echo # --> connection default connection default; --echo # Waitng for 'drop function f2' to get blocked on MDL lock... @@ -621,14 +639,14 @@ rollback to savepoint sv; --echo # --> connection con2 connection con2; --echo # Reaping 'drop function f2'... ---reap +reap; --echo # --> connection default connection default; unlock tables; --echo # --> connection con1 connection con1; --echo # Reaping 'drop function f1'... ---reap +reap; --echo # --> connection default connection default; --error ER_SP_DOES_NOT_EXIST @@ -678,7 +696,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'alter function f1 ...'... ---send alter function f1 comment "comment" +send alter function f1 comment "comment"; --echo # --> connection con2 connection con2; --echo # Waitng for 'alter function f1 ...' to get blocked on MDL lock... @@ -686,7 +704,7 @@ let $wait_condition=select count(*)=1 from information_schema.processlist where state='Waiting for table' and info like 'alter function f1 comment%'; --source include/wait_condition.inc --echo # Sending 'call p1()'... ---send call p1() +send call p1(); connection default; --echo # Waitng for 'call p1()' to get blocked on MDL lock on f1... let $wait_condition=select count(*)=1 from information_schema.processlist @@ -697,11 +715,11 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'alter function f1 ...' ---reap +reap; --echo # --> connection con2 connection con2; --echo # Reaping 'call p1()'... ---reap +reap; deallocate prepare stmt; --echo # --> connection default connection default; @@ -725,7 +743,7 @@ select f1(); --echo # --> connection con1 connection con1; --echo # Sending 'alter function f1 ...'... ---send alter function f1 comment "comment" +send alter function f1 comment "comment"; --echo # --> connection con2 connection con2; --echo # Waitng for 'alter function f1 ...' to get blocked on MDL lock... @@ -754,7 +772,7 @@ begin end| delimiter ;| --echo # Sending 'call p1()'... ---send call p1() +send call p1(); connection default; --echo # Waitng for 'call p1()' to get blocked on MDL lock on f1... let $wait_condition=select count(*)=1 from information_schema.processlist @@ -765,11 +783,11 @@ commit; --echo # --> connection con1 connection con1; --echo # Reaping 'alter function f1 ...' ---reap +reap; --echo # --> connection con2 connection con2; --echo # Reaping 'call p1()'... ---reap +reap; --echo # --> connection default connection default; drop function f1; @@ -804,7 +822,7 @@ select get_lock("30977", 0); --echo # --> connection default connection default; --echo # Sending 'select f3()'... ---send select f3() +send select f3(); --echo # --> connection con1 connection con1; --echo # Waitng for 'select f3()' to get blocked on the user level lock... @@ -819,7 +837,7 @@ select release_lock("30977"); connection default; --echo # Reaping 'select f3()'... --echo # Routine 'f2()' should exist and get executed successfully. ---reap +reap; select @var; drop function f1; drop function f2; @@ -869,6 +887,10 @@ disconnect con1; connection con2; disconnect con2; --source include/wait_until_disconnected.inc +--echo # --> connection con3 +connection con3; +disconnect con3; +--source include/wait_until_disconnected.inc --echo # --> connection default connection default; --echo # -- cgit v1.2.1 From 0bce0c9041c5faf5a64855f21680f6898113d550 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Fri, 8 Jan 2010 11:26:32 +0100 Subject: Fix for bug #48538 "Assertion in thr_lock() on LOAD DATA CONCURRENT INFILE". Attempts to execute an INSERT statement for a MEMORY table which invoked a trigger or called a stored function which tried to perform LOW_PRIORITY update on the table being inserted into, resulted in debug servers aborting due to an assertion failure. On non-debug servers such INSERTs failed with "Can't update table t1 in stored function/trigger because it is already used by statement which invoked this stored function/trigger" as expected. The problem was that in the above scenario TL_WRITE_CONCURRENT_INSERT is converted to TL_WRITE inside the thr_lock() function since the MEMORY engine does not support concurrent inserts. This triggered an assertion which assumed that for the same table, one thread always requests locks with higher thr_lock_type value first. When TL_WRITE_CONCURRENT_INSERT is upgraded to TL_WRITE after the locks have been sorted, this is no longer true. In this case, TL_WRITE was requested after acquiring a TL_WRITE_LOW_PRIORITY lock on the table, triggering the assert. This fix solves the problem by adjusting this assert to take this scenario into account. An alternative approach to change handler::store_locks() methods for all engines which do not support concurrent inserts in such way that TL_WRITE_CONCURRENT_INSERT is upgraded to TL_WRITE there instead, was considered too intrusive. Commit on behalf of Dmitry Lenev. --- mysql-test/r/lock.result | 13 +++++++++++++ mysql-test/t/lock.test | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'mysql-test') diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 8f680858fdc..348412ea441 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -356,5 +356,18 @@ ERROR HY000: Can't execute the given command because you have active locked tabl UNLOCK TABLES; DROP TABLE t1; # +# Simplified test for bug #48538 "Assertion in thr_lock() on LOAD DATA +# CONCURRENT INFILE". +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (f1 INT, f2 INT) ENGINE = MEMORY; +CREATE TRIGGER t1_ai AFTER INSERT ON t1 FOR EACH ROW +UPDATE LOW_PRIORITY t1 SET f2 = 7; +# Statement below should fail with ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG +# error instead of failing on assertion in table-level locking subsystem. +INSERT INTO t1(f1) VALUES(0); +ERROR HY000: Can't update table 't1' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. +DROP TABLE t1; +# # End of 6.0 tests. # diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 64003c9d861..49e98abdc76 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -441,6 +441,28 @@ FLUSH TABLES WITH READ LOCK; UNLOCK TABLES; DROP TABLE t1; + +--echo # +--echo # Simplified test for bug #48538 "Assertion in thr_lock() on LOAD DATA +--echo # CONCURRENT INFILE". +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (f1 INT, f2 INT) ENGINE = MEMORY; +CREATE TRIGGER t1_ai AFTER INSERT ON t1 FOR EACH ROW + UPDATE LOW_PRIORITY t1 SET f2 = 7; + +--echo # Statement below should fail with ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG +--echo # error instead of failing on assertion in table-level locking subsystem. +--error ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG +INSERT INTO t1(f1) VALUES(0); + +DROP TABLE t1; + + --echo # --echo # End of 6.0 tests. --echo # -- cgit v1.2.1 From db1888b53cbad8b6def0cb07024c573b40597941 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 12 Jan 2010 16:15:21 +0100 Subject: Bug #49988 MDL deadlocks with mysql_create_db, reload_acl_and_cache This was a deadlock between LOCK TABLES/CREATE DATABASE in one connection and DROP DATABASE in another. It only happened if the table locked by LOCK TABLES was in the database to be dropped. The deadlock is similar to the one in Bug#48940, but with LOCK TABLES instead of an active transaction. The order of events needed to trigger the deadlock was: 1) Connection 1 locks table db1.t1 using LOCK TABLES. It will now have a metadata lock on the table name. 2) Connection 2 issues DROP DATABASE db1. This will wait inside the MDL subsystem for the lock on db1.t1 to go away. While waiting, it will hold the LOCK_mysql_create_db mutex. 3) Connection 1 issues CREATE DATABASE (database name irrelevant). This will hang trying to lock the same mutex. Since this is the connection holding the metadata lock blocking Connection 2, we have a deadlock. This deadlock would also happen for earlier trees without MDL, but there DROP DATABASE would wait for a table to be removed from the table definition cache. This patch fixes the problem by prohibiting CREATE DATABASE in LOCK TABLES mode. In the example above, this prevents Connection 1 from hanging trying to get the LOCK_mysql_create_db mutex. Note that other commands that use LOCK_mysql_create_db (ALTER/DROP DATABASE) are already prohibited in LOCK TABLES mode. Incompatible change: CREATE DATABASE is now disallowed in LOCK TABLES mode. Test case added to schema.test. --- mysql-test/r/drop.result | 2 +- mysql-test/r/schema.result | 21 ++++++++++++++++++++- mysql-test/t/drop.test | 2 +- mysql-test/t/schema.test | 45 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 4 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/r/drop.result b/mysql-test/r/drop.result index 54bd05e526f..8c6bd582232 100644 --- a/mysql-test/r/drop.result +++ b/mysql-test/r/drop.result @@ -77,8 +77,8 @@ drop table t1; drop database if exists mysqltest; drop table if exists t1; create table t1 (i int); -lock tables t1 read; create database mysqltest; +lock tables t1 read; drop table t1; show open tables; drop database mysqltest; diff --git a/mysql-test/r/schema.result b/mysql-test/r/schema.result index 33a2d4d9448..b43f601caef 100644 --- a/mysql-test/r/schema.result +++ b/mysql-test/r/schema.result @@ -24,7 +24,26 @@ INSERT INTO schema1.t1 VALUES (1); DROP SCHEMA schema1; # Connection default ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8; -ERROR HY000: Can't create/write to file './schema1/db.opt' (Errcode: 2) +Got one of the listed errors SET autocommit= TRUE; # Connection 2 # Connection default +# +# Bug #49988 MDL deadlocks with mysql_create_db, reload_acl_and_cache +# +DROP SCHEMA IF EXISTS schema1; +# Connection default +CREATE SCHEMA schema1; +CREATE TABLE schema1.t1 (id INT); +LOCK TABLE schema1.t1 WRITE; +# Connection con2 +DROP SCHEMA schema1; +# Connection default +# CREATE SCHEMA used to give a deadlock. +# Now we prohibit CREATE SCHEMA in LOCK TABLES mode. +CREATE SCHEMA IF NOT EXISTS schema1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +# UNLOCK TABLES so DROP SCHEMA can continue. +UNLOCK TABLES; +# Connection con2 +# Connection default diff --git a/mysql-test/t/drop.test b/mysql-test/t/drop.test index 4aeb7165bcb..5ef4a28b202 100644 --- a/mysql-test/t/drop.test +++ b/mysql-test/t/drop.test @@ -100,8 +100,8 @@ drop database if exists mysqltest; drop table if exists t1; --enable_warnings create table t1 (i int); -lock tables t1 read; create database mysqltest; +lock tables t1 read; connect (addconroot1, localhost, root,,); --send drop table t1 connect (addconroot2, localhost, root,,); diff --git a/mysql-test/t/schema.test b/mysql-test/t/schema.test index a380a6241dd..f106b9e4865 100644 --- a/mysql-test/t/schema.test +++ b/mysql-test/t/schema.test @@ -46,7 +46,8 @@ let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist WHERE state= 'Waiting for table' AND info='DROP SCHEMA schema1'; --source include/wait_condition.inc ---error 1 +# Listing the error twice to prevent result diffences based on filename +--error 1,1 ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8; SET autocommit= TRUE; @@ -59,6 +60,48 @@ connection default; disconnect con2; +--echo # +--echo # Bug #49988 MDL deadlocks with mysql_create_db, reload_acl_and_cache +--echo # + +--disable_warnings +DROP SCHEMA IF EXISTS schema1; +--enable_warnings + +connect (con2, localhost, root); + +--echo # Connection default +connection default; +CREATE SCHEMA schema1; +CREATE TABLE schema1.t1 (id INT); +LOCK TABLE schema1.t1 WRITE; + +--echo # Connection con2 +connection con2; +--send DROP SCHEMA schema1 + +--echo # Connection default +connection default; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' and info='DROP SCHEMA schema1'; +--source include/wait_condition.inc + +--echo # CREATE SCHEMA used to give a deadlock. +--echo # Now we prohibit CREATE SCHEMA in LOCK TABLES mode. +--error ER_LOCK_OR_ACTIVE_TRANSACTION +CREATE SCHEMA IF NOT EXISTS schema1; + +--echo # UNLOCK TABLES so DROP SCHEMA can continue. +UNLOCK TABLES; + +--echo # Connection con2 +connection con2; +--reap + +--echo # Connection default +connection default; +disconnect con2; + # 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 -- cgit v1.2.1 From 5045ad38de456124d899bf22e45af92334cda64c Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 14 Jan 2010 14:03:24 +0100 Subject: Partial backport of: revno: 2762 [merge] committer: Matthias Leich branch nick: mysql-6.0-bugteam-push timestamp: Wed 2008-08-13 22:05:34 +0200 message: Upmerge 5.1 -> 6.0 ------------------------------------------------------------ revno: 2497.374.2 committer: Matthias Leich branch nick: mysql-5.1-bugteam-push timestamp: Wed 2008-08-13 21:44:54 +0200 message: Fix for Bug#37853 Test "funcs_1.processlist_val_ps" fails in various ways + corrections of logic in poll routines + minor improvements --- mysql-test/suite/funcs_1/datadict/processlist_val.inc | 10 +++++----- mysql-test/suite/funcs_1/r/processlist_val_no_prot.result | 6 +++--- mysql-test/suite/funcs_1/r/processlist_val_ps.result | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'mysql-test') diff --git a/mysql-test/suite/funcs_1/datadict/processlist_val.inc b/mysql-test/suite/funcs_1/datadict/processlist_val.inc index c34fb626bcd..8b10cfc5e97 100644 --- a/mysql-test/suite/funcs_1/datadict/processlist_val.inc +++ b/mysql-test/suite/funcs_1/datadict/processlist_val.inc @@ -367,13 +367,13 @@ echo ; connection default; echo -# Poll till INFO is no more NULL and State = 'Locked'. +# Poll till INFO is no more NULL and State = 'Table Lock'. ; let $wait_condition= SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST - WHERE INFO IS NOT NULL AND STATE = 'Locked'; + WHERE INFO IS NOT NULL AND STATE = 'Table Lock'; --source include/wait_condition.inc # -# Expect to see the state 'Locked' for the third connection because the SELECT +# Expect to see the state 'Table Lock' for the third connection because the SELECT # collides with the WRITE TABLE LOCK. --replace_column 1 3 6