diff options
44 files changed, 1008 insertions, 315 deletions
diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index 7d0379c08a0..c3fcbcf6acd 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] post_commit_to = "commits@lists.mysql.com" post_push_to = "commits@lists.mysql.com" -tree_name = "mysql-5.5-bugfixing" +tree_name = "mysql-5.5-runtime" diff --git a/mysql-test/r/check.result b/mysql-test/r/check.result index 0bff34dba20..ac9c7bca9d7 100644 --- a/mysql-test/r/check.result +++ b/mysql-test/r/check.result @@ -23,3 +23,19 @@ REPAIR TABLE t1; Table Op Msg_type Msg_text test.t1 repair status OK DROP TABLE t1; +# +# Bug#56422 CHECK TABLE run when the table is locked reports corruption +# along with timeout +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a INT); +LOCK TABLE t1 WRITE; +# Connection con1 +SET lock_wait_timeout= 1; +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check Error Lock wait timeout exceeded; try restarting transaction +test.t1 check status Operation failed +# Connection default +UNLOCK TABLES; +DROP TABLE t1; diff --git a/mysql-test/r/delayed.result b/mysql-test/r/delayed.result index 4cb97719ff2..77aa0d49407 100644 --- a/mysql-test/r/delayed.result +++ b/mysql-test/r/delayed.result @@ -422,3 +422,31 @@ UNLOCK TABLES; # Connection con1 # Connection default DROP TABLE t1, t2, t3; +# +# Test for bug #56251 "Deadlock with INSERT DELAYED and MERGE tables". +# +drop table if exists t1, t2, tm; +create table t1(a int); +create table t2(a int); +create table tm(a int) engine=merge union=(t1, t2); +begin; +select * from t1; +a +# Connection 'con1'. +# Sending: +alter table t1 comment 'test'; +# Connection 'default'. +# Wait until ALTER TABLE blocks and starts waiting +# for connection 'default'. It should wait with a +# pending SNW lock on 't1'. +# Attempt to perform delayed insert into 'tm' should not lead +# to a deadlock. Instead error ER_DELAYED_NOT_SUPPORTED should +# be emitted. +insert delayed into tm values (1); +ERROR HY000: DELAYED option not supported for table 'tm' +# Unblock ALTER TABLE. +commit; +# Connection 'con1'. +# Reaping ALTER TABLE: +# Connection 'default'. +drop tables tm, t1, t2; diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result index aee18d91edf..ced8306c3ab 100644 --- a/mysql-test/r/flush.result +++ b/mysql-test/r/flush.result @@ -373,3 +373,53 @@ commit; # --> connection con2 # --> connection default drop table t1; +# +# Test for bug #55273 "FLUSH TABLE tm WITH READ LOCK for Merge table +# causes assert failure". +# +drop table if exists t1, t2, tm; +create table t1 (i int); +create table t2 (i int); +create table tm (i int) engine=merge union=(t1, t2); +insert into t1 values (1), (2); +insert into t2 values (3), (4); +# The below statement should succeed and lock merge +# table for read. Only merge table gets flushed and +# not underlying tables. +flush tables tm with read lock; +select * from tm; +i +1 +2 +3 +4 +# Check that underlying tables are locked. +select * from t1; +i +1 +2 +select * from t2; +i +3 +4 +unlock tables; +# This statement should succeed as well and flush +# all tables in the list. +flush tables tm, t1, t2 with read lock; +select * from tm; +i +1 +2 +3 +4 +# Naturally, underlying tables should be locked in this case too. +select * from t1; +i +1 +2 +select * from t2; +i +3 +4 +unlock tables; +drop tables tm, t1, t2; diff --git a/mysql-test/r/log_tables_upgrade.result b/mysql-test/r/log_tables_upgrade.result index 5d9be85a48a..814ebd7d5dc 100644 --- a/mysql-test/r/log_tables_upgrade.result +++ b/mysql-test/r/log_tables_upgrade.result @@ -17,9 +17,7 @@ mysql.columns_priv OK mysql.db OK mysql.event OK mysql.func OK -mysql.general_log -Error : You can't use locks with log tables. -status : OK +mysql.general_log OK mysql.help_category OK mysql.help_keyword OK mysql.help_relation OK @@ -31,9 +29,7 @@ mysql.proc OK mysql.procs_priv OK mysql.renamed_general_log OK mysql.servers OK -mysql.slow_log -Error : You can't use locks with log tables. -status : OK +mysql.slow_log OK mysql.tables_priv OK mysql.time_zone OK mysql.time_zone_leap_second OK diff --git a/mysql-test/r/lowercase_fs_off.result b/mysql-test/r/lowercase_fs_off.result index 30f835a8ea3..c3284b225dd 100644 --- a/mysql-test/r/lowercase_fs_off.result +++ b/mysql-test/r/lowercase_fs_off.result @@ -55,3 +55,11 @@ DROP USER user_1@localhost; DROP USER USER_1@localhost; DROP DATABASE db1; use test; +# +# Extra test coverage for Bug#56595 RENAME TABLE causes assert on OS X +# +CREATE TABLE t1(a INT); +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW SET new.a= 1; +RENAME TABLE t1 TO T1; +ALTER TABLE T1 RENAME t1; +DROP TABLE t1; diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index d484ab77701..de57e3859b7 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -2913,3 +2913,41 @@ UNLOCK TABLES; # Connection default UNLOCK TABLES; DROP DATABASE db1; +# +# Bug#56292 Deadlock with ALTER TABLE and MERGE tables +# +DROP TABLE IF EXISTS t1, t2, m1; +CREATE TABLE t1(a INT) engine=MyISAM; +CREATE TABLE t2(a INT) engine=MyISAM; +CREATE TABLE m1(a INT) engine=MERGE UNION=(t1, t2); +INSERT INTO t1 VALUES (1), (2); +INSERT INTO t2 VALUES (3), (4); +# Connection con1 +SET DEBUG_SYNC= 'mdl_upgrade_shared_lock_to_exclusive SIGNAL upgrade WAIT_FOR continue'; +# Sending: +ALTER TABLE m1 engine=MERGE UNION=(t2, t1); +# Connection con2 +# Waiting for ALTER TABLE to try lock upgrade +SET DEBUG_SYNC= 'now WAIT_FOR upgrade'; +# Sending: +DELETE FROM t2 WHERE a = 3; +# Connection default +# Check that DELETE is waiting on a metadata lock and not a table lock. +# Now that DELETE blocks on a metadata lock, we should be able to do +# SELECT * FROM m1 here. SELECT used to be blocked by a DELETE table +# lock request. +SELECT * FROM m1; +a +1 +2 +3 +4 +# Resuming ALTER TABLE +SET DEBUG_SYNC= 'now SIGNAL continue'; +# Connection con1 +# Reaping: ALTER TABLE m1 engine=MERGE UNION=(t2, t1) +# Connection con2 +# Reaping: DELETE FROM t2 WHERE a = 3 +# Connection default +DROP TABLE m1, t1, t2; +SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index c7e54288f8b..bb6af084f38 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -2358,6 +2358,48 @@ t2 WHERE b SOUNDS LIKE e AND d = 1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables DROP TABLE t2, t1; +# +# Bug#46339 - crash on REPAIR TABLE merge table USE_FRM +# +DROP TABLE IF EXISTS m1, t1; +CREATE TABLE t1 (c1 INT) ENGINE=MYISAM; +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1) INSERT_METHOD=LAST; +LOCK TABLE m1 READ; +REPAIR TABLE m1 USE_FRM; +Table Op Msg_type Msg_text +test.m1 repair Error Table 'm1' was locked with a READ lock and can't be updated +test.m1 repair status Operation failed +UNLOCK TABLES; +REPAIR TABLE m1 USE_FRM; +Table Op Msg_type Msg_text +test.m1 repair note The storage engine for the table doesn't support repair +DROP TABLE m1,t1; +CREATE TABLE m1 (f1 BIGINT) ENGINE=MRG_MyISAM UNION(t1); +REPAIR TABLE m1 USE_FRM; +Table Op Msg_type Msg_text +test.m1 repair Warning Can't open table +test.m1 repair error Corrupt +CREATE TABLE t1 (f1 BIGINT) ENGINE = MyISAM; +REPAIR TABLE m1 USE_FRM; +Table Op Msg_type Msg_text +test.m1 repair note The storage engine for the table doesn't support repair +REPAIR TABLE m1; +Table Op Msg_type Msg_text +test.m1 repair note The storage engine for the table doesn't support repair +DROP TABLE m1, t1; +CREATE TEMPORARY TABLE m1 (f1 BIGINT) ENGINE=MRG_MyISAM UNION(t1); +REPAIR TABLE m1 USE_FRM; +Table Op Msg_type Msg_text +test.m1 repair Error Table 'test.m1' doesn't exist +test.m1 repair error Corrupt +CREATE TEMPORARY TABLE t1 (f1 BIGINT) ENGINE=MyISAM; +REPAIR TABLE m1 USE_FRM; +Table Op Msg_type Msg_text +m1 repair error Cannot repair temporary table from .frm file +REPAIR TABLE m1; +Table Op Msg_type Msg_text +test.m1 repair note The storage engine for the table doesn't support repair +DROP TABLE m1, t1; End of 5.1 tests # # An additional test case for Bug#27430 Crash in subquery code @@ -2677,7 +2719,7 @@ OPTIMIZE TABLE t1; Table Op Msg_type Msg_text test.t1 optimize Error Table 'test.t_not_exists' doesn't exist test.t1 optimize Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist -test.t1 optimize note The storage engine for the table doesn't support optimize +test.t1 optimize error Corrupt DROP TABLE t1; # # Bug#36171 - CREATE TEMPORARY TABLE and MERGE engine @@ -3444,13 +3486,12 @@ ALTER TABLE m1 ADD INDEX (c1); UNLOCK TABLES; DROP TABLE m1, t1; # -# If children are to be altered, they need an explicit lock. +# Locking the merge table will implicitly lock children. # CREATE TABLE t1 (c1 INT); CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1); LOCK TABLE m1 WRITE; ALTER TABLE t1 ADD INDEX (c1); -ERROR HY000: Table 't1' was locked with a READ lock and can't be updated LOCK TABLE m1 WRITE, t1 WRITE; ALTER TABLE t1 ADD INDEX (c1); UNLOCK TABLES; @@ -3576,4 +3617,48 @@ ERROR HY000: The definition of table 'v1' prevents operation DELETE on table 'm1 drop view v1; drop temporary table tmp; drop table t1, t2, t3, m1, m2; +# +# Bug#56494 Segfault in upgrade_shared_lock_to_exclusive() for +# REPAIR of merge table +# +DROP TABLE IF EXISTS t1, t2, t_not_exists; +CREATE TABLE t1(a INT); +ALTER TABLE t1 engine= MERGE UNION (t_not_exists); +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze Error Table 'test.t_not_exists' doesn't exist +test.t1 analyze Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +test.t1 analyze error Corrupt +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check Error Table 'test.t_not_exists' doesn't exist +test.t1 check Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +test.t1 check error Corrupt +CHECKSUM TABLE t1; +Table Checksum +test.t1 NULL +Warnings: +Error 1146 Table 'test.t_not_exists' doesn't exist +Error 1168 Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize Error Table 'test.t_not_exists' doesn't exist +test.t1 optimize Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +test.t1 optimize error Corrupt +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair Error Table 'test.t_not_exists' doesn't exist +test.t1 repair Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +test.t1 repair error Corrupt +REPAIR TABLE t1 USE_FRM; +Table Op Msg_type Msg_text +test.t1 repair Warning Can't open table +test.t1 repair error Corrupt +DROP TABLE t1; +CREATE TABLE t1(a INT); +CREATE TABLE t2(a INT) engine= MERGE UNION (t1); +REPAIR TABLE t2 USE_FRM; +Table Op Msg_type Msg_text +test.t2 repair note The storage engine for the table doesn't support repair +DROP TABLE t1, t2; End of 6.0 tests diff --git a/mysql-test/r/myisampack.result b/mysql-test/r/myisampack.result index dd14c31f32e..91700701139 100644 --- a/mysql-test/r/myisampack.result +++ b/mysql-test/r/myisampack.result @@ -46,14 +46,12 @@ insert into t1 select * from t1; flush tables; optimize table t1; Table Op Msg_type Msg_text -test.t1 optimize error Table 'test.t1' is read only -Warnings: -Error 1036 Table 't1' is read only +test.t1 optimize Error Table 't1' is read only +test.t1 optimize status Operation failed repair table t1; Table Op Msg_type Msg_text -test.t1 repair error Table 'test.t1' is read only -Warnings: -Error 1036 Table 't1' is read only +test.t1 repair Error Table 't1' is read only +test.t1 repair status Operation failed drop table t1; # # BUG#41541 - Valgrind warnings on packed MyISAM table diff --git a/mysql-test/r/mysql_upgrade.result b/mysql-test/r/mysql_upgrade.result index 58f6ffd4040..5252d5f0c17 100644 --- a/mysql-test/r/mysql_upgrade.result +++ b/mysql-test/r/mysql_upgrade.result @@ -5,9 +5,7 @@ mysql.columns_priv OK mysql.db OK mysql.event OK mysql.func OK -mysql.general_log -Error : You can't use locks with log tables. -status : OK +mysql.general_log OK mysql.help_category OK mysql.help_keyword OK mysql.help_relation OK @@ -18,9 +16,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.servers OK -mysql.slow_log -Error : You can't use locks with log tables. -status : OK +mysql.slow_log OK mysql.tables_priv OK mysql.time_zone OK mysql.time_zone_leap_second OK @@ -37,9 +33,7 @@ mysql.columns_priv OK mysql.db OK mysql.event OK mysql.func OK -mysql.general_log -Error : You can't use locks with log tables. -status : OK +mysql.general_log OK mysql.help_category OK mysql.help_keyword OK mysql.help_relation OK @@ -50,9 +44,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.servers OK -mysql.slow_log -Error : You can't use locks with log tables. -status : OK +mysql.slow_log OK mysql.tables_priv OK mysql.time_zone OK mysql.time_zone_leap_second OK @@ -69,9 +61,7 @@ mysql.columns_priv OK mysql.db OK mysql.event OK mysql.func OK -mysql.general_log -Error : You can't use locks with log tables. -status : OK +mysql.general_log OK mysql.help_category OK mysql.help_keyword OK mysql.help_relation OK @@ -82,9 +72,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.servers OK -mysql.slow_log -Error : You can't use locks with log tables. -status : OK +mysql.slow_log OK mysql.tables_priv OK mysql.time_zone OK mysql.time_zone_leap_second OK @@ -103,9 +91,7 @@ mysql.columns_priv OK mysql.db OK mysql.event OK mysql.func OK -mysql.general_log -Error : You can't use locks with log tables. -status : OK +mysql.general_log OK mysql.help_category OK mysql.help_keyword OK mysql.help_relation OK @@ -116,9 +102,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.servers OK -mysql.slow_log -Error : You can't use locks with log tables. -status : OK +mysql.slow_log OK mysql.tables_priv OK mysql.time_zone OK mysql.time_zone_leap_second OK @@ -141,9 +125,7 @@ mysql.columns_priv OK mysql.db OK mysql.event OK mysql.func OK -mysql.general_log -Error : You can't use locks with log tables. -status : OK +mysql.general_log OK mysql.help_category OK mysql.help_keyword OK mysql.help_relation OK @@ -154,9 +136,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.servers OK -mysql.slow_log -Error : You can't use locks with log tables. -status : OK +mysql.slow_log OK mysql.tables_priv OK mysql.time_zone OK mysql.time_zone_leap_second OK @@ -182,9 +162,7 @@ mysql.columns_priv OK mysql.db OK mysql.event OK mysql.func OK -mysql.general_log -Error : You can't use locks with log tables. -status : OK +mysql.general_log OK mysql.help_category OK mysql.help_keyword OK mysql.help_relation OK @@ -195,9 +173,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.servers OK -mysql.slow_log -Error : You can't use locks with log tables. -status : OK +mysql.slow_log OK mysql.tables_priv OK mysql.time_zone OK mysql.time_zone_leap_second OK diff --git a/mysql-test/r/mysql_upgrade_ssl.result b/mysql-test/r/mysql_upgrade_ssl.result index f1229c4a405..b89862966b5 100644 --- a/mysql-test/r/mysql_upgrade_ssl.result +++ b/mysql-test/r/mysql_upgrade_ssl.result @@ -7,9 +7,7 @@ mysql.columns_priv OK mysql.db OK mysql.event OK mysql.func OK -mysql.general_log -Error : You can't use locks with log tables. -status : OK +mysql.general_log OK mysql.help_category OK mysql.help_keyword OK mysql.help_relation OK @@ -20,9 +18,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.servers OK -mysql.slow_log -Error : You can't use locks with log tables. -status : OK +mysql.slow_log OK mysql.tables_priv OK mysql.time_zone OK mysql.time_zone_leap_second OK diff --git a/mysql-test/r/sp-destruct.result b/mysql-test/r/sp-destruct.result index a4933ee9800..32d0e39c5af 100644 --- a/mysql-test/r/sp-destruct.result +++ b/mysql-test/r/sp-destruct.result @@ -134,3 +134,19 @@ Warning 1405 Failed to revoke all privileges to dropped routine # Restore the procs_priv table RENAME TABLE procs_priv_backup TO mysql.procs_priv; FLUSH TABLE mysql.procs_priv; +# +# Bug #56137 "Assertion `thd->lock == 0' failed on upgrading from +# 5.1.50 to 5.5.6". +# +drop database if exists mysqltest; +# Backup mysql.proc. +flush table mysql.proc; +create database mysqltest; +# Corrupt mysql.proc to make it unusable by current version of server. +alter table mysql.proc drop column type; +# The below statement should not cause assertion failure. +drop database mysqltest; +Warnings: +Error 1547 Column count of mysql.proc is wrong. Expected 20, found 19. The table is probably corrupted +# Restore mysql.proc. +drop table mysql.proc; diff --git a/mysql-test/r/xa.result b/mysql-test/r/xa.result index fedbb43ea2a..6ef3bf392d9 100644 --- a/mysql-test/r/xa.result +++ b/mysql-test/r/xa.result @@ -131,3 +131,14 @@ XA START 'xid1'; XA END 'xid1'; XA ROLLBACK 'xid1'; DROP TABLE t1; +# +# Bug#56448 Assertion failed: ! is_set() with second xa end +# +XA START 'x'; +XA END 'x'; +XA END 'x'; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state +XA PREPARE 'x'; +XA PREPARE 'x'; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +XA ROLLBACK 'x'; diff --git a/mysql-test/suite/parts/r/partition_special_innodb.result b/mysql-test/suite/parts/r/partition_special_innodb.result index b8dffd61fbe..0738f45b012 100644 --- a/mysql-test/suite/parts/r/partition_special_innodb.result +++ b/mysql-test/suite/parts/r/partition_special_innodb.result @@ -242,7 +242,7 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction CHECK TABLE t1; Table Op Msg_type Msg_text test.t1 check Error Lock wait timeout exceeded; try restarting transaction -test.t1 check error Corrupt +test.t1 check status Operation failed UNLOCK TABLES; DROP TABLE t1; CREATE TABLE t2 ( i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, f INT ) diff --git a/mysql-test/t/check.test b/mysql-test/t/check.test index ff23b352b5a..2234d2c680f 100644 --- a/mysql-test/t/check.test +++ b/mysql-test/t/check.test @@ -53,5 +53,29 @@ REPAIR TABLE t1; DROP TABLE t1; +--echo # +--echo # Bug#56422 CHECK TABLE run when the table is locked reports corruption +--echo # along with timeout +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1(a INT); +LOCK TABLE t1 WRITE; + +--echo # Connection con1 +connect(con1, localhost, root); +SET lock_wait_timeout= 1; +CHECK TABLE t1; + +--echo # Connection default +connection default; +UNLOCK TABLES; +DROP TABLE t1; +disconnect con1; + + # Wait till we reached the initial number of concurrent sessions --source include/wait_until_count_sessions.inc diff --git a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test index 5f56cdf54e1..3a2bc982ad3 100644 --- a/mysql-test/t/delayed.test +++ b/mysql-test/t/delayed.test @@ -552,3 +552,52 @@ disconnect con1; connection default; DROP TABLE t1, t2, t3; --enable_ps_protocol + + +--echo # +--echo # Test for bug #56251 "Deadlock with INSERT DELAYED and MERGE tables". +--echo # +connect (con1,localhost,root,,); +connection default; +--disable_warnings +drop table if exists t1, t2, tm; +--enable_warnings +create table t1(a int); +create table t2(a int); +create table tm(a int) engine=merge union=(t1, t2); +begin; +select * from t1; + +--echo # Connection 'con1'. +connection con1; +--echo # Sending: +--send alter table t1 comment 'test' + +--echo # Connection 'default'. +connection default; +--echo # Wait until ALTER TABLE blocks and starts waiting +--echo # for connection 'default'. It should wait with a +--echo # pending SNW lock on 't1'. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = "Waiting for table metadata lock" and + info = "alter table t1 comment 'test'"; +--source include/wait_condition.inc +--echo # Attempt to perform delayed insert into 'tm' should not lead +--echo # to a deadlock. Instead error ER_DELAYED_NOT_SUPPORTED should +--echo # be emitted. +--error ER_DELAYED_NOT_SUPPORTED +insert delayed into tm values (1); +--echo # Unblock ALTER TABLE. +commit; + +--echo # Connection 'con1'. +connection con1; +--echo # Reaping ALTER TABLE: +--reap + +disconnect con1; +--source include/wait_until_disconnected.inc +--echo # Connection 'default'. +connection default; +drop tables tm, t1, t2; diff --git a/mysql-test/t/flush.test b/mysql-test/t/flush.test index d4c3533847d..1cafbe3e6bd 100644 --- a/mysql-test/t/flush.test +++ b/mysql-test/t/flush.test @@ -546,3 +546,34 @@ disconnect con2; connection default; drop table t1; + +--echo # +--echo # Test for bug #55273 "FLUSH TABLE tm WITH READ LOCK for Merge table +--echo # causes assert failure". +--echo # +--disable_warnings +drop table if exists t1, t2, tm; +--enable_warnings +create table t1 (i int); +create table t2 (i int); +create table tm (i int) engine=merge union=(t1, t2); +insert into t1 values (1), (2); +insert into t2 values (3), (4); +--echo # The below statement should succeed and lock merge +--echo # table for read. Only merge table gets flushed and +--echo # not underlying tables. +flush tables tm with read lock; +select * from tm; +--echo # Check that underlying tables are locked. +select * from t1; +select * from t2; +unlock tables; +--echo # This statement should succeed as well and flush +--echo # all tables in the list. +flush tables tm, t1, t2 with read lock; +select * from tm; +--echo # Naturally, underlying tables should be locked in this case too. +select * from t1; +select * from t2; +unlock tables; +drop tables tm, t1, t2; diff --git a/mysql-test/t/lowercase_fs_off.test b/mysql-test/t/lowercase_fs_off.test index 86d1e084c29..1be0351f9bc 100644 --- a/mysql-test/t/lowercase_fs_off.test +++ b/mysql-test/t/lowercase_fs_off.test @@ -91,3 +91,14 @@ DROP DATABASE db1; use test; # End of 5.0 tests + + +--echo # +--echo # Extra test coverage for Bug#56595 RENAME TABLE causes assert on OS X +--echo # + +CREATE TABLE t1(a INT); +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW SET new.a= 1; +RENAME TABLE t1 TO T1; +ALTER TABLE T1 RENAME t1; +DROP TABLE t1; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index 55c04d7870b..f7780d2003a 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -4532,6 +4532,68 @@ disconnect con2; disconnect con3; +--echo # +--echo # Bug#56292 Deadlock with ALTER TABLE and MERGE tables +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1, t2, m1; +--enable_warnings + +CREATE TABLE t1(a INT) engine=MyISAM; +CREATE TABLE t2(a INT) engine=MyISAM; +CREATE TABLE m1(a INT) engine=MERGE UNION=(t1, t2); + +INSERT INTO t1 VALUES (1), (2); +INSERT INTO t2 VALUES (3), (4); + +connect(con1, localhost, root); +connect(con2, localhost, root); + +--echo # Connection con1 +connection con1; +SET DEBUG_SYNC= 'mdl_upgrade_shared_lock_to_exclusive SIGNAL upgrade WAIT_FOR continue'; +--echo # Sending: +--send ALTER TABLE m1 engine=MERGE UNION=(t2, t1) + +--echo # Connection con2 +connection con2; +--echo # Waiting for ALTER TABLE to try lock upgrade +SET DEBUG_SYNC= 'now WAIT_FOR upgrade'; +--echo # Sending: +--send DELETE FROM t2 WHERE a = 3 + +--echo # Connection default +connection default; +--echo # Check that DELETE is waiting on a metadata lock and not a table lock. +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE state = "Waiting for table metadata lock" AND + info = "DELETE FROM t2 WHERE a = 3"; +--source include/wait_condition.inc +--echo # Now that DELETE blocks on a metadata lock, we should be able to do +--echo # SELECT * FROM m1 here. SELECT used to be blocked by a DELETE table +--echo # lock request. +SELECT * FROM m1; +--echo # Resuming ALTER TABLE +SET DEBUG_SYNC= 'now SIGNAL continue'; + +--echo # Connection con1 +connection con1; +--echo # Reaping: ALTER TABLE m1 engine=MERGE UNION=(t2, t1) +--reap +--echo # Connection con2 +connection con2; +--echo # Reaping: DELETE FROM t2 WHERE a = 3 +--reap +--echo # Connection default +connection default; +DROP TABLE m1, t1, t2; +SET DEBUG_SYNC= 'RESET'; +disconnect con1; +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 diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index b6ad3324d19..34d0b5bf237 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -2,6 +2,9 @@ # Test of MERGE TABLES # +# Save the initial number of concurrent sessions. +--source include/count_sessions.inc + # MERGE tables require MyISAM tables let $default=`select @@global.storage_engine`; set global storage_engine=myisam; @@ -1731,6 +1734,84 @@ t2 WHERE b SOUNDS LIKE e AND d = 1; DROP TABLE t2, t1; +--echo # +--echo # Bug#46339 - crash on REPAIR TABLE merge table USE_FRM +--echo # +--disable_warnings +DROP TABLE IF EXISTS m1, t1; +--enable_warnings +# +# Test derived from a proposal of Shane Bester. +# +CREATE TABLE t1 (c1 INT) ENGINE=MYISAM; +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1) INSERT_METHOD=LAST; +# +# REPAIR ... USE_FRM with LOCK TABLES. +# +LOCK TABLE m1 READ; +REPAIR TABLE m1 USE_FRM; +UNLOCK TABLES; +# +# REPAIR ... USE_FRM without LOCK TABLES. +# +# This statement crashed the server (Bug#46339). +# +REPAIR TABLE m1 USE_FRM; +# +DROP TABLE m1,t1; +# +# Test derived from a proposal of Matthias Leich. +# +# Base table is missing. +# +CREATE TABLE m1 (f1 BIGINT) ENGINE=MRG_MyISAM UNION(t1); +# +# This statement crashed the server (Bug#46339). +# +REPAIR TABLE m1 USE_FRM; +# +# Create base table. +# +CREATE TABLE t1 (f1 BIGINT) ENGINE = MyISAM; +# +# This statement crashed the server (Bug#46339). +# +REPAIR TABLE m1 USE_FRM; +# +# Normal repair as reference. +# +REPAIR TABLE m1; +# +# Cleanup. +# +DROP TABLE m1, t1; +# +# Same with temporary tables. +# +# Base table is missing. +# +CREATE TEMPORARY TABLE m1 (f1 BIGINT) ENGINE=MRG_MyISAM UNION(t1); +# +# This statement crashed the server (Bug#46339). +# +REPAIR TABLE m1 USE_FRM; +# +# Create base table. +# +CREATE TEMPORARY TABLE t1 (f1 BIGINT) ENGINE=MyISAM; +# +# This statement crashed the server (Bug#46339). +# +REPAIR TABLE m1 USE_FRM; +# +# Normal repair as reference. +# +REPAIR TABLE m1; +# +# Cleanup. +# +DROP TABLE m1, t1; + --echo End of 5.1 tests --echo # @@ -2519,12 +2600,11 @@ UNLOCK TABLES; DROP TABLE m1, t1; --echo # ---echo # If children are to be altered, they need an explicit lock. +--echo # Locking the merge table will implicitly lock children. --echo # CREATE TABLE t1 (c1 INT); CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1); LOCK TABLE m1 WRITE; ---error ER_TABLE_NOT_LOCKED_FOR_WRITE ALTER TABLE t1 ADD INDEX (c1); LOCK TABLE m1 WRITE, t1 WRITE; ALTER TABLE t1 ADD INDEX (c1); @@ -2665,6 +2745,37 @@ drop view v1; drop temporary table tmp; drop table t1, t2, t3, m1, m2; + +--echo # +--echo # Bug#56494 Segfault in upgrade_shared_lock_to_exclusive() for +--echo # REPAIR of merge table +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1, t2, t_not_exists; +--enable_warnings + +CREATE TABLE t1(a INT); +ALTER TABLE t1 engine= MERGE UNION (t_not_exists); +# This caused the segfault +ANALYZE TABLE t1; +CHECK TABLE t1; +CHECKSUM TABLE t1; +OPTIMIZE TABLE t1; +REPAIR TABLE t1; + +# This caused an assert +REPAIR TABLE t1 USE_FRM; + +DROP TABLE t1; +CREATE TABLE t1(a INT); +CREATE TABLE t2(a INT) engine= MERGE UNION (t1); +# This caused an assert +REPAIR TABLE t2 USE_FRM; + +DROP TABLE t1, t2; + + --echo End of 6.0 tests --disable_result_log @@ -2672,3 +2783,7 @@ drop table t1, t2, t3, m1, m2; eval set global storage_engine=$default; --enable_result_log --enable_query_log + +# 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/sp-destruct.test b/mysql-test/t/sp-destruct.test index b7090c01f1e..a5c287e44a8 100644 --- a/mysql-test/t/sp-destruct.test +++ b/mysql-test/t/sp-destruct.test @@ -222,3 +222,33 @@ SHOW WARNINGS; --echo # Restore the procs_priv table RENAME TABLE procs_priv_backup TO mysql.procs_priv; FLUSH TABLE mysql.procs_priv; + + +--echo # +--echo # Bug #56137 "Assertion `thd->lock == 0' failed on upgrading from +--echo # 5.1.50 to 5.5.6". +--echo # +--disable_warnings +drop database if exists mysqltest; +--enable_warnings +--echo # Backup mysql.proc. +flush table mysql.proc; +let $MYSQLD_DATADIR= `select @@datadir`; +--copy_file $MYSQLD_DATADIR/mysql/proc.frm $MYSQLTEST_VARDIR/tmp/proc.frm +--copy_file $MYSQLD_DATADIR/mysql/proc.MYD $MYSQLTEST_VARDIR/tmp/proc.MYD +--copy_file $MYSQLD_DATADIR/mysql/proc.MYI $MYSQLTEST_VARDIR/tmp/proc.MYI + +create database mysqltest; +--echo # Corrupt mysql.proc to make it unusable by current version of server. +alter table mysql.proc drop column type; +--echo # The below statement should not cause assertion failure. +drop database mysqltest; + +--echo # Restore mysql.proc. +drop table mysql.proc; +--copy_file $MYSQLTEST_VARDIR/tmp/proc.frm $MYSQLD_DATADIR/mysql/proc.frm +--copy_file $MYSQLTEST_VARDIR/tmp/proc.MYD $MYSQLD_DATADIR/mysql/proc.MYD +--copy_file $MYSQLTEST_VARDIR/tmp/proc.MYI $MYSQLD_DATADIR/mysql/proc.MYI +--remove_file $MYSQLTEST_VARDIR/tmp/proc.frm +--remove_file $MYSQLTEST_VARDIR/tmp/proc.MYD +--remove_file $MYSQLTEST_VARDIR/tmp/proc.MYI diff --git a/mysql-test/t/xa.test b/mysql-test/t/xa.test index fe36af60c27..3fd243398a9 100644 --- a/mysql-test/t/xa.test +++ b/mysql-test/t/xa.test @@ -228,6 +228,23 @@ XA ROLLBACK 'xid1'; disconnect con1; DROP TABLE t1; + +--echo # +--echo # Bug#56448 Assertion failed: ! is_set() with second xa end +--echo # + +XA START 'x'; +XA END 'x'; +# Second XA END caused an assertion. +--error ER_XAER_RMFAIL +XA END 'x'; +XA PREPARE 'x'; +# Second XA PREPARE also caused an assertion. +--error ER_XAER_RMFAIL +XA PREPARE 'x'; +XA ROLLBACK 'x'; + + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/sql/events.cc b/sql/events.cc index 10a7535425f..e7e47801586 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -485,10 +485,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, ret= TRUE; // OOM else if ((ret= db_repository->load_named_event(thd, dbname, name, new_element))) - { - DBUG_ASSERT(ret == OP_LOAD_ERROR); delete new_element; - } else { /* diff --git a/sql/events.h b/sql/events.h index 1482e736d29..f3ebc6da4ad 100644 --- a/sql/events.h +++ b/sql/events.h @@ -44,19 +44,6 @@ class THD; typedef class Item COND; typedef struct charset_info_st CHARSET_INFO; -/* Return codes */ -enum enum_events_error_code -{ - OP_OK= 0, - OP_NOT_RUNNING, - OP_CANT_KILL, - OP_CANT_INIT, - OP_DISABLED_EVENT, - OP_LOAD_ERROR, - OP_ALREADY_EXISTS -}; - - int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); diff --git a/sql/sp.cc b/sql/sp.cc index 0265ef45a2a..8821dc9365d 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -440,6 +440,7 @@ static TABLE *open_proc_table_for_update(THD *thd) { TABLE_LIST table_list; TABLE *table; + MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_proc_table_for_update"); table_list.init_one_table("mysql", 5, "proc", 4, "proc", TL_WRITE); @@ -450,6 +451,9 @@ static TABLE *open_proc_table_for_update(THD *thd) if (!proc_table_intact.check(table, &proc_table_def)) DBUG_RETURN(table); + close_thread_tables(thd); + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + DBUG_RETURN(NULL); } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 19373507955..41e4ad7a522 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -4035,7 +4035,8 @@ end: bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, bool any_combination_will_do, uint number, bool no_errors) { - TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table(); + TABLE_LIST *tl; + TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); Security_context *sctx= thd->security_ctx; uint i; ulong orig_want_access= want_access; @@ -4052,34 +4053,32 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, the given table list refers to the list for prelocking (contains tables of other queries). For simple queries first_not_own_table is 0. */ - for (i= 0, table= tables; - i < number && table != first_not_own_table; - table= table->next_global, i++) + for (i= 0, tl= tables; + i < number && tl != first_not_own_table; + tl= tl->next_global, i++) { /* Save a copy of the privileges without the SHOW_VIEW_ACL attribute. It will be checked during making view. */ - table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); + tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); } mysql_rwlock_rdlock(&LOCK_grant); - for (table= tables; - table && number-- && table != first_not_own_table; - table= table->next_global) + for (tl= tables; + tl && number-- && tl != first_not_own_table; + tl= tl->next_global) { - GRANT_TABLE *grant_table; - sctx = test(table->security_ctx) ? - table->security_ctx : thd->security_ctx; + sctx = test(tl->security_ctx) ? tl->security_ctx : thd->security_ctx; - const ACL_internal_table_access *access; - access= get_cached_table_access(&table->grant.m_internal, - table->get_db_name(), - table->get_table_name()); + const ACL_internal_table_access *access= + get_cached_table_access(&tl->grant.m_internal, + tl->get_db_name(), + tl->get_table_name()); if (access) { - switch(access->check(orig_want_access, &table->grant.privilege)) + switch(access->check(orig_want_access, &tl->grant.privilege)) { case ACL_INTERNAL_ACCESS_GRANTED: /* @@ -4103,29 +4102,33 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, if (!want_access) continue; // ok - if (!(~table->grant.privilege & want_access) || - table->is_anonymous_derived_table() || table->schema_table) + if (!(~tl->grant.privilege & want_access) || + tl->is_anonymous_derived_table() || tl->schema_table) { /* - It is subquery in the FROM clause. VIEW set table->derived after + It is subquery in the FROM clause. VIEW set tl->derived after table opening, but this function always called before table opening. */ - if (!table->referencing_view) + if (!tl->referencing_view) { /* If it's a temporary table created for a subquery in the FROM clause, or an INFORMATION_SCHEMA table, drop the request for a privilege. */ - table->grant.want_privilege= 0; + tl->grant.want_privilege= 0; } continue; } - if (!(grant_table= table_hash_search(sctx->host, sctx->ip, - table->get_db_name(), sctx->priv_user, - table->get_table_name(), FALSE))) + GRANT_TABLE *grant_table= table_hash_search(sctx->host, sctx->ip, + tl->get_db_name(), + sctx->priv_user, + tl->get_table_name(), + FALSE); + + if (!grant_table) { - want_access &= ~table->grant.privilege; + want_access &= ~tl->grant.privilege; goto err; // No grants } @@ -4136,18 +4139,17 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, if (any_combination_will_do) continue; - table->grant.grant_table=grant_table; // Remember for column test - table->grant.version=grant_version; - table->grant.privilege|= grant_table->privs; - table->grant.want_privilege= ((want_access & COL_ACLS) - & ~table->grant.privilege); + tl->grant.grant_table= grant_table; // Remember for column test + tl->grant.version= grant_version; + tl->grant.privilege|= grant_table->privs; + tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege); - if (!(~table->grant.privilege & want_access)) + if (!(~tl->grant.privilege & want_access)) continue; - if (want_access & ~(grant_table->cols | table->grant.privilege)) + if (want_access & ~(grant_table->cols | tl->grant.privilege)) { - want_access &= ~(grant_table->cols | table->grant.privilege); + want_access &= ~(grant_table->cols | tl->grant.privilege); goto err; // impossible } } @@ -4164,7 +4166,7 @@ err: command, sctx->priv_user, sctx->host_or_ip, - table ? table->get_table_name() : "unknown"); + tl ? tl->get_table_name() : "unknown"); } DBUG_RETURN(TRUE); } diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 1f96f1cf0e4..21a05a5baca 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -111,9 +111,6 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, table= &tmp_table; } - /* A MERGE table must not come here. */ - DBUG_ASSERT(table->file->ht->db_type != DB_TYPE_MRG_MYISAM); - /* REPAIR TABLE ... USE_FRM for temporary tables makes little sense. */ @@ -151,6 +148,9 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, if (!ext[0] || !ext[1]) goto end; // No data file + /* A MERGE table must not come here. */ + DBUG_ASSERT(table->file->ht->db_type != DB_TYPE_MRG_MYISAM); + // Name of data file strxmov(from, table->s->normalized_path.str, ext[1], NullS); if (!mysql_file_stat(key_file_misc, from, &stat_info, MYF(0))) @@ -231,6 +231,26 @@ end: } +/** + Check if a given error is something that could occur during + open_and_lock_tables() that does not indicate table corruption. + + @param sql_errno Error number to check. + + @retval TRUE Error does not indicate table corruption. + @retval FALSE Error could indicate table corruption. +*/ + +static inline bool table_not_corrupt_error(uint sql_errno) +{ + return (sql_errno == ER_NO_SUCH_TABLE || + sql_errno == ER_FILE_NOT_FOUND || + sql_errno == ER_LOCK_WAIT_TIMEOUT || + sql_errno == ER_LOCK_DEADLOCK || + sql_errno == ER_CANT_LOCK_LOG_TABLE || + sql_errno == ER_OPEN_AS_READONLY); +} + /* RETURN VALUES @@ -311,7 +331,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, lex->query_tables= table; lex->query_tables_last= &table->next_global; lex->query_tables_own_last= 0; - thd->no_warnings_for_error= no_warnings_for_error; + /* + Under locked tables, we know that the table can be opened, + so any errors opening the table are logical errors. + In these cases it makes sense to report them. + */ + if (!thd->locked_tables_mode) + thd->no_warnings_for_error= no_warnings_for_error; if (view_operator_func == NULL) table->required_type=FRMTYPE_TABLE; @@ -320,6 +346,14 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, table->next_global= save_next_global; table->next_local= save_next_local; thd->open_options&= ~extra_open_options; + + /* + If open_and_lock_tables() failed, close_thread_tables() will close + the table and table->table can therefore be invalid. + */ + if (open_error) + table->table= NULL; + /* Under locked tables, we know that the table can be opened, so any errors opening the table are logical errors. @@ -418,9 +452,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM)); if (thd->stmt_da->is_error() && - (thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE || - thd->stmt_da->sql_errno() == ER_FILE_NOT_FOUND)) - /* A missing table is just issued as a failed command */ + table_not_corrupt_error(thd->stmt_da->sql_errno())) result_code= HA_ADMIN_FAILED; else /* Default failure code is corrupt table */ diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 046e09b20a3..5af01523aa7 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -13,8 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "sql_parse.h" // check_access, - // check_merge_table_access +#include "sql_parse.h" // check_access #include "sql_table.h" // mysql_alter_table, // mysql_exchange_partition #include "sql_alter.h" @@ -60,11 +59,15 @@ bool Alter_table_statement::execute(THD *thd) check_access(thd, INSERT_ACL | CREATE_ACL, select_lex->db, &priv, NULL, /* Don't use first_tab->grant with sel_lex->db */ - 0, 0) || - check_merge_table_access(thd, first_table->db, - create_info.merge_list.first)) + 0, 0)) DBUG_RETURN(TRUE); /* purecov: inspected */ + /* If it is a merge table, check privileges for merge children. */ + if (create_info.merge_list.first && + check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, + create_info.merge_list.first, FALSE, UINT_MAX, FALSE)) + DBUG_RETURN(TRUE); + if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE)) DBUG_RETURN(TRUE); /* purecov: inspected */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 01ab0b6dec5..8305283cd17 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -247,7 +247,8 @@ static void check_unused(void) Length of key */ -uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list, +uint create_table_def_key(THD *thd, char *key, + const TABLE_LIST *table_list, bool tmp_table) { uint key_length= (uint) (strmov(strmov(key, table_list->db)+1, @@ -1987,39 +1988,60 @@ void update_non_unique_table_error(TABLE_LIST *update, } +/** + Find temporary table specified by database and table names in the + THD::temporary_tables list. + + @return TABLE instance if a temporary table has been found; NULL otherwise. +*/ + TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name) { - TABLE_LIST table_list; + TABLE_LIST tl; - table_list.db= (char*) db; - table_list.table_name= (char*) table_name; - return find_temporary_table(thd, &table_list); + tl.db= (char*) db; + tl.table_name= (char*) table_name; + + return find_temporary_table(thd, &tl); } -TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list) +/** + Find a temporary table specified by TABLE_LIST instance in the + THD::temporary_tables list. + + @return TABLE instance if a temporary table has been found; NULL otherwise. +*/ + +TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl) { - char key[MAX_DBKEY_LENGTH]; - uint key_length; - TABLE *table; - DBUG_ENTER("find_temporary_table"); - DBUG_PRINT("enter", ("table: '%s'.'%s'", - table_list->db, table_list->table_name)); + char key[MAX_DBKEY_LENGTH]; + uint key_length= create_table_def_key(thd, key, tl, 1); + + return find_temporary_table(thd, key, key_length); +} - key_length= create_table_def_key(thd, key, table_list, 1); - for (table=thd->temporary_tables ; table ; table= table->next) + +/** + Find a temporary table specified by a key in the THD::temporary_tables list. + + @return TABLE instance if a temporary table has been found; NULL otherwise. +*/ + +TABLE *find_temporary_table(THD *thd, + const char *table_key, + uint table_key_length) +{ + for (TABLE *table= thd->temporary_tables; table; table= table->next) { - if (table->s->table_cache_key.length == key_length && - !memcmp(table->s->table_cache_key.str, key, key_length)) + if (table->s->table_cache_key.length == table_key_length && + !memcmp(table->s->table_cache_key.str, table_key, table_key_length)) { - DBUG_PRINT("info", - ("Found table. server_id: %u pseudo_thread_id: %lu", - (uint) thd->server_id, - (ulong) thd->variables.pseudo_thread_id)); - DBUG_RETURN(table); + return table; } } - DBUG_RETURN(0); // Not a temporary table + + return NULL; } @@ -3023,41 +3045,11 @@ retry_share: table->reginfo.lock_type=TL_READ; /* Assume read */ reset: - DBUG_ASSERT(table->s->ref_count > 0 || table->s->tmp_table != NO_TMP_TABLE); - - if (thd->lex->need_correct_ident()) - table->alias_name_used= my_strcasecmp(table_alias_charset, - table->s->table_name.str, alias); - /* Fix alias if table name changes */ - if (strcmp(table->alias, alias)) - { - uint length=(uint) strlen(alias)+1; - table->alias= (char*) my_realloc((char*) table->alias, length, - MYF(MY_WME)); - memcpy((char*) table->alias, alias, length); - } - table->tablenr=thd->current_tablenr++; - table->used_fields=0; - table->const_table=0; - table->null_row= table->maybe_null= 0; - table->force_index= table->force_index_order= table->force_index_group= 0; - table->status=STATUS_NO_RECORD; - table->insert_values= 0; - table->fulltext_searched= 0; - table->file->ft_handler= 0; - table->reginfo.impossible_range= 0; - /* Catch wrong handling of the auto_increment_field_not_null. */ - DBUG_ASSERT(!table->auto_increment_field_not_null); - table->auto_increment_field_not_null= FALSE; - if (table->timestamp_field) - table->timestamp_field_type= table->timestamp_field->get_auto_set_type(); - table->pos_in_table_list= table_list; table_list->updatable= 1; // It is not derived table nor non-updatable VIEW - table->clear_column_bitmaps(); table_list->table= table; - DBUG_ASSERT(table->key_read == 0); - /* Tables may be reused in a sub statement. */ - DBUG_ASSERT(! table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)); + + table->init(thd, table_list); + DBUG_RETURN(FALSE); err_lock: @@ -4530,13 +4522,15 @@ lock_table_names(THD *thd, ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) && find_temporary_table(thd, table)))) { - if (schema_set.insert(table)) + if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) && + schema_set.insert(table)) return TRUE; mdl_requests.push_front(&table->mdl_request); } } - if (! mdl_requests.is_empty()) + if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) && + ! mdl_requests.is_empty()) { /* Scoped locks: Take intention exclusive locks on all involved @@ -5171,6 +5165,8 @@ static bool check_lock_and_start_stmt(THD *thd, @param[in] lock_type lock to use for table @param[in] flags options to be used while opening and locking table (see open_table(), mysql_lock_tables()) + @param[in] prelocking_strategy Strategy which specifies how prelocking + algorithm should work for this statement. @return table @retval != NULL OK, opened table returned @@ -5196,7 +5192,8 @@ static bool check_lock_and_start_stmt(THD *thd, */ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, - thr_lock_type lock_type, uint flags) + thr_lock_type lock_type, uint flags, + Prelocking_strategy *prelocking_strategy) { TABLE_LIST *save_next_global; DBUG_ENTER("open_n_lock_single_table"); @@ -5212,7 +5209,8 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, table_l->required_type= FRMTYPE_TABLE; /* Open the table. */ - if (open_and_lock_tables(thd, table_l, FALSE, flags)) + if (open_and_lock_tables(thd, table_l, FALSE, flags, + prelocking_strategy)) table_l->table= NULL; /* Just to be sure. */ /* Restore list. */ @@ -5704,35 +5702,37 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, } -/* - Open a single table without table caching and don't set it in open_list - - SYNPOSIS - open_temporary_table() - thd Thread object - path Path (without .frm) - db database - table_name Table name - link_in_list 1 if table should be linked into thd->temporary_tables - - NOTES: - Used by alter_table to open a temporary table and when creating - a temporary table with CREATE TEMPORARY ... - - RETURN - 0 Error - # TABLE object +/** + Open a single table without table caching and don't add it to + THD::open_tables. Depending on the 'add_to_temporary_tables_list' value, + the opened TABLE instance will be addded to THD::temporary_tables list. + + @param thd Thread context. + @param path Path (without .frm) + @param db Database name. + @param table_name Table name. + @param add_to_temporary_tables_list Specifies if the opened TABLE + instance should be linked into + THD::temporary_tables list. + + @note This function is used: + - by alter_table() to open a temporary table; + - when creating a temporary table with CREATE TEMPORARY TABLE. + + @return TABLE instance for opened table. + @retval NULL on error. */ -TABLE *open_temporary_table(THD *thd, const char *path, const char *db, - const char *table_name, bool link_in_list) +TABLE *open_table_uncached(THD *thd, const char *path, const char *db, + const char *table_name, + bool add_to_temporary_tables_list) { TABLE *tmp_table; TABLE_SHARE *share; char cache_key[MAX_DBKEY_LENGTH], *saved_cache_key, *tmp_path; uint key_length; TABLE_LIST table_list; - DBUG_ENTER("open_temporary_table"); + DBUG_ENTER("open_table_uncached"); DBUG_PRINT("enter", ("table: '%s'.'%s' path: '%s' server_id: %u " "pseudo_thread_id: %lu", @@ -5775,7 +5775,7 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, share->tmp_table= (tmp_table->file->has_transactions() ? TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE); - if (link_in_list) + if (add_to_temporary_tables_list) { /* growing temp list at the head */ tmp_table->next= thd->temporary_tables; diff --git a/sql/sql_base.h b/sql/sql_base.h index ff935c3fc09..7ae3971942b 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -17,7 +17,6 @@ #define SQL_BASE_INCLUDED #include "unireg.h" // REQUIRED: for other includes -#include "table.h" /* open_table_mode */ #include "sql_trigger.h" /* trg_event_type */ #include "sql_class.h" /* enum_mark_columns */ #include "mysqld.h" /* key_map */ @@ -79,7 +78,8 @@ void table_def_start_shutdown(void); void assign_new_table_id(TABLE_SHARE *share); uint cached_open_tables(void); uint cached_table_definitions(void); -uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list, +uint create_table_def_key(THD *thd, char *key, + const TABLE_LIST *table_list, bool tmp_table); TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, uint key_length, uint db_flags, int *error, @@ -122,6 +122,11 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, (LONG_TIMEOUT = 1 year) rather than the user-supplied timeout value. */ #define MYSQL_LOCK_IGNORE_TIMEOUT 0x0800 +/** + When acquiring "strong" (SNW, SNRW, X) metadata locks on tables to + be open do not acquire global and schema-scope IX locks. +*/ +#define MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK 0x1000 /** Please refer to the internals manual. */ #define MYSQL_OPEN_REOPEN (MYSQL_OPEN_IGNORE_FLUSH |\ @@ -141,8 +146,9 @@ bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, bool get_key_map_from_key_list(key_map *map, TABLE *table, List<String> *index_list); -TABLE *open_temporary_table(THD *thd, const char *path, const char *db, - const char *table_name, bool link_in_list); +TABLE *open_table_uncached(THD *thd, const char *path, const char *db, + const char *table_name, + bool add_to_temporary_tables_list); TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name); TABLE *find_write_locked_table(TABLE *list, const char *db, const char *table_name); @@ -159,7 +165,9 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, const char *db_name, const char *table_name); TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name); -TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list); +TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl); +TABLE *find_temporary_table(THD *thd, const char *table_key, + uint table_key_length); void close_thread_tables(THD *thd); bool fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields, List<Item> &values, @@ -238,7 +246,8 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived); /* simple open_and_lock_tables without derived handling for single table */ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, - thr_lock_type lock_type, uint flags); + thr_lock_type lock_type, uint flags, + Prelocking_strategy *prelocking_strategy); bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags); bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags); int decide_logging_format(THD *thd, TABLE_LIST *tables); @@ -447,6 +456,16 @@ open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags) } +inline TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, + thr_lock_type lock_type, uint flags) +{ + DML_prelocking_strategy prelocking_strategy; + + return open_n_lock_single_table(thd, table_l, lock_type, flags, + &prelocking_strategy); +} + + /* open_and_lock_tables with derived handling */ inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, bool derived, uint flags) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index adbdc1ffbc8..ad324fed4fe 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2496,6 +2496,65 @@ void kill_delayed_threads(void) /** + A strategy for the prelocking algorithm which prevents the + delayed insert thread from opening tables with engines which + do not support delayed inserts. + + Particularly it allows to abort open_tables() as soon as we + discover that we have opened a MERGE table, without acquiring + metadata locks on underlying tables. +*/ + +class Delayed_prelocking_strategy : public Prelocking_strategy +{ +public: + virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx, + Sroutine_hash_entry *rt, sp_head *sp, + bool *need_prelocking); + virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking); + virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking); +}; + + +bool Delayed_prelocking_strategy:: +handle_table(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking) +{ + DBUG_ASSERT(table_list->lock_type == TL_WRITE_DELAYED); + + if (!(table_list->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED)) + { + my_error(ER_DELAYED_NOT_SUPPORTED, MYF(0), table_list->table_name); + return TRUE; + } + return FALSE; +} + + +bool Delayed_prelocking_strategy:: +handle_routine(THD *thd, Query_tables_list *prelocking_ctx, + Sroutine_hash_entry *rt, sp_head *sp, + bool *need_prelocking) +{ + /* LEX used by the delayed insert thread has no routines. */ + DBUG_ASSERT(0); + return FALSE; +} + + +bool Delayed_prelocking_strategy:: +handle_view(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *table_list, bool *need_prelocking) +{ + /* We don't open views in the delayed insert thread. */ + DBUG_ASSERT(0); + return FALSE; +} + + +/** Open and lock table for use by delayed thread and check that this table is suitable for delayed inserts. @@ -2505,21 +2564,21 @@ void kill_delayed_threads(void) bool Delayed_insert::open_and_lock_table() { + Delayed_prelocking_strategy prelocking_strategy; + + /* + Use special prelocking strategy to get ER_DELAYED_NOT_SUPPORTED + error for tables with engines which don't support delayed inserts. + */ if (!(table= open_n_lock_single_table(&thd, &table_list, TL_WRITE_DELAYED, - MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK))) + MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK, + &prelocking_strategy))) { thd.fatal_error(); // Abort waiting inserts return TRUE; } - if (!(table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED)) - { - /* To rollback InnoDB statement transaction. */ - trans_rollback_stmt(&thd); - my_error(ER_DELAYED_NOT_SUPPORTED, MYF(ME_FATALERROR), - table_list.table_name); - return TRUE; - } + if (table->triggers) { /* diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b78815f0e52..23a5f35d967 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -177,8 +177,7 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables) for (TABLE_LIST *table= tables; table; table= table->next_global) { DBUG_ASSERT(table->db && table->table_name); - if (table->updating && - !find_temporary_table(thd, table->db, table->table_name)) + if (table->updating && !find_temporary_table(thd, table)) return 1; } return 0; @@ -402,6 +401,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_OPTIMIZE]|= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CHECK]= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_CREATE_USER]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_USER]|= CF_AUTO_COMMIT_TRANS; @@ -415,7 +415,6 @@ void init_update_queries(void) sql_command_flags[SQLCOM_FLUSH]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_RESET]= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_CHECK]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_CREATE_SERVER]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ALTER_SERVER]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_SERVER]= CF_AUTO_COMMIT_TRANS; @@ -643,45 +642,6 @@ end: } -/** - @brief Check access privs for a MERGE table and fix children lock types. - - @param[in] thd thread handle - @param[in] db database name - @param[in,out] table_list list of child tables (merge_list) - lock_type and optionally db set per table - - @return status - @retval 0 OK - @retval != 0 Error - - @detail - This function is used for write access to MERGE tables only - (CREATE TABLE, ALTER TABLE ... UNION=(...)). Set TL_WRITE for - every child. Set 'db' for every child if not present. -*/ -#ifndef NO_EMBEDDED_ACCESS_CHECKS -bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list) -{ - int error= 0; - - if (table_list) - { - /* Check that all tables use the current database */ - TABLE_LIST *tlist; - - for (tlist= table_list; tlist; tlist= tlist->next_local) - { - if (!tlist->db || !tlist->db[0]) - tlist->db= db; /* purecov: inspected */ - } - error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, - table_list, FALSE, UINT_MAX, FALSE); - } - return error; -} -#endif - /* This works because items are allocated with sql_alloc() */ void free_items(Item *item) @@ -6964,10 +6924,16 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, if (check_access(thd, want_priv, create_table->db, &create_table->grant.privilege, &create_table->grant.m_internal, - 0, 0) || - check_merge_table_access(thd, create_table->db, - lex->create_info.merge_list.first)) + 0, 0)) + goto err; + + /* If it is a merge table, check privileges for merge children. */ + if (lex->create_info.merge_list.first && + check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, + lex->create_info.merge_list.first, + FALSE, UINT_MAX, FALSE)) goto err; + if (want_priv != CREATE_TMP_ACL && check_grant(thd, want_priv, create_table, FALSE, 1, FALSE)) goto err; diff --git a/sql/sql_parse.h b/sql/sql_parse.h index ad620544d29..c9b0f981d19 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -153,7 +153,6 @@ bool check_single_table_access(THD *thd, ulong privilege, bool check_routine_access(THD *thd,ulong want_access,char *db,char *name, bool is_proc, bool no_errors); bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); -bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list); bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc); bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, GRANT_INTERNAL_INFO *grant_internal_info, @@ -176,8 +175,6 @@ inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) table->grant.privilege= want_access; return false; } -inline bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list) -{ return false; } inline bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc) { return false; } diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index bf38af78536..35f27408247 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -328,7 +328,6 @@ bool reload_acl_and_cache(THD *thd, unsigned long options, ------------------------------------- - you can't flush WITH READ LOCK a non-existent table - you can't flush WITH READ LOCK under LOCK TABLES - - currently incompatible with the GRL (@todo: fix) Effect on views and temporary tables. ------------------------------------ @@ -338,6 +337,13 @@ bool reload_acl_and_cache(THD *thd, unsigned long options, if there is a base table, it's used, otherwise ER_NO_SUCH_TABLE is returned. + Handling of MERGE tables + ------------------------ + For MERGE table this statement will open and lock child tables + for read (it is impossible to lock parent table without it). + Child tables won't be flushed unless they are explicitly present + in the statement's table list. + Implicit commit --------------- This statement causes an implicit commit before and @@ -354,7 +360,6 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) { Lock_tables_prelocking_strategy lock_tables_prelocking_strategy; TABLE_LIST *table_list; - MDL_request_list mdl_requests; /* This is called from SQLCOM_FLUSH, the transaction has @@ -368,17 +373,13 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) } /* - Acquire SNW locks on tables to be flushed. We can't use - lock_table_names() here as this call will also acquire global IX - and database-scope IX locks on the tables, and this will make + Acquire SNW locks on tables to be flushed. Don't acquire global + IX and database-scope IX locks on the tables as this will make this statement incompatible with FLUSH TABLES WITH READ LOCK. */ - for (table_list= all_tables; table_list; - table_list= table_list->next_global) - mdl_requests.push_front(&table_list->mdl_request); - - if (thd->mdl_context.acquire_locks(&mdl_requests, - thd->variables.lock_wait_timeout)) + if (lock_table_names(thd, all_tables, NULL, + thd->variables.lock_wait_timeout, + MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) goto error; DEBUG_SYNC(thd,"flush_tables_with_read_lock_after_acquire_locks"); @@ -390,21 +391,24 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table_list->db, table_list->table_name, FALSE); - - /* Skip views and temporary tables. */ - table_list->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ - table_list->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ + /* Reset ticket to satisfy asserts in open_tables(). */ + table_list->mdl_request.ticket= NULL; } /* Before opening and locking tables the below call also waits for old shares to go away, so the fact that we don't pass MYSQL_LOCK_IGNORE_FLUSH flag to it is important. + Also we don't pass MYSQL_OPEN_HAS_MDL_LOCK flag as we want + to open underlying tables if merge table is flushed. + For underlying tables of the merge the below call has to + acquire SNW locks to ensure that they can be locked for + read without further waiting. */ - if (open_and_lock_tables(thd, all_tables, FALSE, - MYSQL_OPEN_HAS_MDL_LOCK, - &lock_tables_prelocking_strategy) || - thd->locked_tables_list.init_locked_tables(thd)) + if (open_and_lock_tables(thd, all_tables, FALSE, + MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK, + &lock_tables_prelocking_strategy) || + thd->locked_tables_list.init_locked_tables(thd)) { goto error; } diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 97f8e46d052..ac15239b040 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -285,6 +285,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, { if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db, old_alias, + ren_table->table_name, new_db, new_alias))) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 3f0a0326c84..d8ef6d7030a 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -22,7 +22,7 @@ #include "sql_rename.h" // do_rename #include "sql_parse.h" // test_if_data_home_dir #include "sql_cache.h" // query_cache_* -#include "sql_base.h" // open_temporary_table, lock_table_names +#include "sql_base.h" // open_table_uncached, lock_table_names #include "lock.h" // wait_if_global_read_lock // start_waiting_global_read_lock, // mysql_unlock_tables @@ -2008,7 +2008,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, { for (table= tables; table; table= table->next_local) if (table->open_type != OT_BASE_ONLY && - find_temporary_table(thd, table->db, table->table_name)) + find_temporary_table(thd, table)) { /* A temporary table. @@ -4224,9 +4224,14 @@ bool mysql_create_table_no_lock(THD *thd, if (create_info->options & HA_LEX_CREATE_TMP_TABLE) { - TABLE *table= NULL; - /* Open table and put in temporary table list */ - if (!(table= open_temporary_table(thd, path, db, table_name, 1))) + /* + Open a table (skipping table cache) and add it into + THD::temporary_tables list. + */ + + TABLE *table= open_table_uncached(thd, path, db, table_name, TRUE); + + if (!table) { (void) rm_temporary_table(create_info->db_type, path); goto err; @@ -5913,7 +5918,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, *fn_ext(new_name)=0; if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0)) error= -1; - else if (Table_triggers_list::change_table_name(thd, db, table_name, + else if (Table_triggers_list::change_table_name(thd, db, + alias, table_name, new_db, new_alias)) { (void) mysql_rename_table(old_db_type, new_db, new_alias, db, @@ -6301,8 +6307,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, /* table is a normal table: Create temporary table in same directory */ build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "", FN_IS_TMP); - /* Open our intermediate table */ - new_table= open_temporary_table(thd, path, new_db, tmp_name, 1); + /* Open our intermediate table. */ + new_table= open_table_uncached(thd, path, new_db, tmp_name, TRUE); } if (!new_table) goto err_new_table_cleanup; @@ -6550,7 +6556,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, (need_copy_table != ALTER_TABLE_METADATA_ONLY || mysql_rename_table(save_old_db_type, db, table_name, new_db, new_alias, NO_FRM_RENAME)) && - Table_triggers_list::change_table_name(thd, db, table_name, + Table_triggers_list::change_table_name(thd, db, alias, table_name, new_db, new_alias))) { /* Try to get everything back. */ @@ -6642,7 +6648,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, char path[FN_REFLEN]; TABLE *t_table; build_table_filename(path + 1, sizeof(path) - 1, new_db, table_name, "", 0); - t_table= open_temporary_table(thd, path, new_db, tmp_name, 0); + t_table= open_table_uncached(thd, path, new_db, tmp_name, FALSE); if (t_table) { intern_close_table(t_table); diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index b81461e8371..392343eb9a3 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -458,7 +458,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) DBUG_ASSERT(tables->next_global == 0); /* We do not allow creation of triggers on temporary tables. */ - if (create && find_temporary_table(thd, tables->db, tables->table_name)) + if (create && find_temporary_table(thd, tables)) { my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias); goto end; @@ -1874,6 +1874,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *old_db_name, @param[in,out] thd Thread context @param[in] db Old database of subject table + @param[in] old_alias Old alias of subject table @param[in] old_table Old name of subject table @param[in] new_db New database for subject table @param[in] new_table New name of subject table @@ -1890,6 +1891,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *old_db_name, */ bool Table_triggers_list::change_table_name(THD *thd, const char *db, + const char *old_alias, const char *old_table, const char *new_db, const char *new_table) @@ -1911,7 +1913,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, MDL_EXCLUSIVE)); DBUG_ASSERT(my_strcasecmp(table_alias_charset, db, new_db) || - my_strcasecmp(table_alias_charset, old_table, new_table)); + my_strcasecmp(table_alias_charset, old_alias, new_table)); if (Table_triggers_list::check_n_load(thd, db, old_table, &table, TRUE)) { @@ -1920,7 +1922,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, } if (table.triggers) { - LEX_STRING old_table_name= { (char *) old_table, strlen(old_table) }; + LEX_STRING old_table_name= { (char *) old_alias, strlen(old_alias) }; LEX_STRING new_table_name= { (char *) new_table, strlen(new_table) }; /* Since triggers should be in the same schema as their subject tables diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 28bf0a60877..dd954957f07 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -157,6 +157,7 @@ public: TABLE *table, bool names_only); static bool drop_all_triggers(THD *thd, char *db, char *table_name); static bool change_table_name(THD *thd, const char *db, + const char *old_alias, const char *old_table, const char *new_db, const char *new_table); diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index a61abdafe3e..c0bc726a188 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -208,8 +208,8 @@ static bool recreate_temporary_table(THD *thd, TABLE *table) ha_create_table(thd, share->normalized_path.str, share->db.str, share->table_name.str, &create_info, 1); - if (open_temporary_table(thd, share->path.str, share->db.str, - share->table_name.str, 1)) + if (open_table_uncached(thd, share->path.str, share->db.str, + share->table_name.str, TRUE)) { error= FALSE; thd->thread_specific_used= TRUE; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 83e9078f5cb..2ee2d83f9d2 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11278,7 +11278,11 @@ opt_with_read_lock: TABLE_LIST *tables= Lex->query_tables; Lex->type|= REFRESH_READ_LOCK; for (; tables; tables= tables->next_global) + { tables->mdl_request.set_type(MDL_SHARED_NO_WRITE); + tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ + tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ + } } ; diff --git a/sql/table.cc b/sql/table.cc index f08a0aa84ca..e84991912a4 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3223,6 +3223,65 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime, } +/** + Initialize TABLE instance (newly created, or coming either from table + cache or THD::temporary_tables list) and prepare it for further use + during statement execution. Set the 'alias' attribute from the specified + TABLE_LIST element. Remember the TABLE_LIST element in the + TABLE::pos_in_table_list member. + + @param thd Thread context. + @param tl TABLE_LIST element. +*/ + +void TABLE::init(THD *thd, TABLE_LIST *tl) +{ + DBUG_ASSERT(s->ref_count > 0 || s->tmp_table != NO_TMP_TABLE); + + if (thd->lex->need_correct_ident()) + alias_name_used= my_strcasecmp(table_alias_charset, + s->table_name.str, + tl->alias); + /* Fix alias if table name changes. */ + if (strcmp(alias, tl->alias)) + { + uint length= (uint) strlen(tl->alias)+1; + alias= (char*) my_realloc((char*) alias, length, MYF(MY_WME)); + memcpy((char*) alias, tl->alias, length); + } + + tablenr= thd->current_tablenr++; + used_fields= 0; + const_table= 0; + null_row= 0; + maybe_null= 0; + force_index= 0; + force_index_order= 0; + force_index_group= 0; + status= STATUS_NO_RECORD; + insert_values= 0; + fulltext_searched= 0; + file->ft_handler= 0; + reginfo.impossible_range= 0; + + /* Catch wrong handling of the auto_increment_field_not_null. */ + DBUG_ASSERT(!auto_increment_field_not_null); + auto_increment_field_not_null= FALSE; + + if (timestamp_field) + timestamp_field_type= timestamp_field->get_auto_set_type(); + + pos_in_table_list= tl; + + clear_column_bitmaps(); + + DBUG_ASSERT(key_read == 0); + + /* Tables may be reused in a sub statement. */ + DBUG_ASSERT(!file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)); +} + + /* Create Item_field for each column in the table. diff --git a/sql/table.h b/sql/table.h index 45b9aa3e699..6723293c1ec 100644 --- a/sql/table.h +++ b/sql/table.h @@ -83,18 +83,6 @@ enum enum_table_ref_type }; -/** - Opening modes for open_temporary_table and open_table_from_share -*/ - -enum open_table_mode -{ - OTM_OPEN= 0, - OTM_CREATE= 1, - OTM_ALTER= 2 -}; - - /*************************************************************************/ /** @@ -1096,6 +1084,7 @@ public: #endif MDL_ticket *mdl_ticket; + void init(THD *thd, TABLE_LIST *tl); bool fill_item_list(List<Item> *item_list) const; void reset_item_list(List<Item> *item_list) const; void clear_column_bitmaps(void); diff --git a/sql/transaction.cc b/sql/transaction.cc index a28fba8805d..d3e3ba142b9 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -565,7 +565,8 @@ bool trans_xa_end(THD *thd) else if (!xa_trans_rolled_back(&thd->transaction.xid_state)) thd->transaction.xid_state.xa_state= XA_IDLE; - DBUG_RETURN(thd->transaction.xid_state.xa_state != XA_IDLE); + DBUG_RETURN(thd->is_error() || + thd->transaction.xid_state.xa_state != XA_IDLE); } @@ -596,7 +597,8 @@ bool trans_xa_prepare(THD *thd) else thd->transaction.xid_state.xa_state= XA_PREPARED; - DBUG_RETURN(thd->transaction.xid_state.xa_state != XA_PREPARED); + DBUG_RETURN(thd->is_error() || + thd->transaction.xid_state.xa_state != XA_PREPARED); } diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index f62aff4e383..466c66ec769 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -478,6 +478,8 @@ int ha_myisammrg::add_children_list(void) /* Set the expected table version, to not cause spurious re-prepare. */ child_l->set_table_ref_id(mrg_child_def->get_child_table_ref_type(), mrg_child_def->get_child_def_version()); + /* Use the same metadata lock type for children. */ + child_l->mdl_request.set_type(parent_l->mdl_request.type); /* Link TABLE_LIST object into the children list. */ if (this->children_last_l) child_l->prev_global= this->children_last_l; |