From: Alexander Nozdrin Date: December 23 2010 4:18pm Subject: bzr commit into mysql-trunk-bugfixing branch (alexander.nozdrin:3471) Bug#27480 List-Archive: http://lists.mysql.com/commits/127565 X-Bug: 27480 Message-Id: <201012231618.oBNGIMJj020345@rcsinet13.oracle.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============1032043632838004218==" --===============1032043632838004218== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///home/alik/MySQL/bzr/00/bug27480/mysql-trunk-bugfixing/ based on revid:tor.didriksen@stripped 3471 Alexander Nozdrin 2010-12-23 Prerequisite patch for Bug#27480 (Extend CREATE TEMPORARY TABLES privilege to allow temp table operations): - move opening of temporary tables out of open_table(); - make open_table() to work with base tables and views only. It will be renamed to open_base_table_or_view() in a follow-up patch. - introduce open_temporary_table() to open temporary tables (similar to open_table()); - introduce open_temporary_tables() to open temporary tables corresponding to table list elements; - introduce a new "command flag" (CF_PREOPEN_TMP_TABLES) to mark statements that work with temporary tables, thus temporary tables should be opened for those statements; - open temporary tables in a unified way in the beginning of the statements marked with CF_PREOPEN_TMP_TABLES flag; - introduce a new "command flag" (CF_HA_CLOSE) to mark statements for which open handlers (by HANDLER OPEN) should be closed; - close open handlers in a unified way in the beginning of the statements marked with CF_HA_CLOSE flag. modified: mysql-test/r/alter_table.result mysql-test/r/grant4.result mysql-test/r/handler_myisam.result mysql-test/r/merge.result mysql-test/r/temp_table.result mysql-test/suite/innodb/r/innodb_mysql.result mysql-test/suite/innodb/t/innodb_mysql.test mysql-test/suite/rpl/r/rpl_create_if_not_exists.result mysql-test/suite/rpl/t/rpl_create_if_not_exists.test mysql-test/t/alter_table.test mysql-test/t/grant4.test mysql-test/t/handler_myisam.test mysql-test/t/merge.test mysql-test/t/temp_table.test sql/log_event.cc sql/sp_head.cc sql/sql_admin.cc sql/sql_base.cc sql/sql_base.h sql/sql_class.h sql/sql_db.cc sql/sql_handler.cc sql/sql_insert.cc sql/sql_parse.cc sql/sql_partition_admin.cc sql/sql_prepare.cc sql/sql_rename.cc sql/sql_show.cc sql/sql_table.cc sql/sql_truncate.cc sql/sql_view.cc storage/myisammrg/ha_myisammrg.cc === modified file 'mysql-test/r/alter_table.result' --- a/mysql-test/r/alter_table.result 2010-08-30 06:38:09 +0000 +++ b/mysql-test/r/alter_table.result 2010-12-23 16:18:19 +0000 @@ -1383,3 +1383,14 @@ ALTER TABLE t1 CHANGE a id INT; affected rows: 0 info: Records: 0 Duplicates: 0 Warnings: 0 DROP TABLE t1; +# +# Additional coverage for refactoring which is made as part +# of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege +# to allow temp table operations". +# +# At some point the below test case failed on assertion. +DROP TABLE IF EXISTS t1; +CREATE TEMPORARY TABLE t1 (i int) ENGINE=MyISAM; +ALTER TABLE t1 DISCARD TABLESPACE; +ERROR HY000: Table storage engine for 't1' doesn't have this option +DROP TABLE t1; === modified file 'mysql-test/r/grant4.result' --- a/mysql-test/r/grant4.result 2010-02-20 10:07:32 +0000 +++ b/mysql-test/r/grant4.result 2010-12-23 16:18:19 +0000 @@ -121,3 +121,60 @@ View Create View character_set_client co v3 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v3` AS select `t_select_priv`.`a` AS `a`,`t_select_priv`.`b` AS `b` from `t_select_priv` latin1 latin1_swedish_ci drop database mysqltest_db1; drop user mysqltest_u1@localhost; +# +# Additional coverage for refactoring which is made as part +# of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege +# to allow temp table operations". +# +# Check that for statements like CHECK/REPAIR and OPTIMIZE TABLE +# privileges for all tables involved are checked before processing +# any tables. Doing otherwise, i.e. checking privileges for table +# right before processing it might result in lost results for tables +# which were processed by the time when table for which privileges +# are insufficient are discovered. +# +call mtr.add_suppression("Got an error from thread_id=.*ha_myisam.cc:"); +call mtr.add_suppression("MySQL thread id .*, query id .* localhost.*mysqltest_u1 Checking table"); +drop database if exists mysqltest_db1; +create database mysqltest_db1; +# Create tables which we are going to CHECK/REPAIR. +create table mysqltest_db1.t1 (a int, key(a)) engine=myisam; +create table mysqltest_db1.t2 (b int); +insert into mysqltest_db1.t1 values (1), (2); +insert into mysqltest_db1.t2 values (1); +# Create user which will try to do this. +create user mysqltest_u1@localhost; +grant insert, select on mysqltest_db1.t1 to mysqltest_u1@localhost; +# Corrupt t1 by replacing t1.MYI with a corrupt + unclosed one created +# by doing: 'create table t1 (a int key(a))' +# head -c1024 t1.MYI > corrupt_t1.MYI +flush table mysqltest_db1.t1; +# Switching to connection 'con1'. +check table mysqltest_db1.t1; +Table Op Msg_type Msg_text +mysqltest_db1.t1 check warning 1 client is using or hasn't closed the table properly +mysqltest_db1.t1 check error Size of indexfile is: 1024 Should be: 2048 +mysqltest_db1.t1 check warning Size of datafile is: 14 Should be: 7 +mysqltest_db1.t1 check error Corrupt +# The below statement should fail before repairing t1. +# Otherwise info about such repair will be missing from its result-set. +repair table mysqltest_db1.t1, mysqltest_db1.t2; +ERROR 42000: SELECT, INSERT command denied to user 'mysqltest_u1'@'localhost' for table 't2' +# The same is true for CHECK TABLE statement. +check table mysqltest_db1.t1, mysqltest_db1.t2; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't2' +check table mysqltest_db1.t1; +Table Op Msg_type Msg_text +mysqltest_db1.t1 check warning Table is marked as crashed +mysqltest_db1.t1 check warning 1 client is using or hasn't closed the table properly +mysqltest_db1.t1 check error Size of indexfile is: 1024 Should be: 2048 +mysqltest_db1.t1 check warning Size of datafile is: 14 Should be: 7 +mysqltest_db1.t1 check error Corrupt +repair table mysqltest_db1.t1; +Table Op Msg_type Msg_text +mysqltest_db1.t1 repair warning Number of rows changed from 1 to 2 +mysqltest_db1.t1 repair status OK +# Clean-up. +# Switching to connection 'default'. +drop database mysqltest_db1; +drop user mysqltest_u1@localhost; === modified file 'mysql-test/r/handler_myisam.result' --- a/mysql-test/r/handler_myisam.result 2010-11-18 16:34:56 +0000 +++ b/mysql-test/r/handler_myisam.result 2010-12-23 16:18:19 +0000 @@ -1858,4 +1858,90 @@ a b 4 40 HANDLER t1 CLOSE; DROP TABLE t1; +# +# Additional coverage for refactoring which is made as part +# of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege +# to allow temp table operations". +# +# Check that DDL on temporary table properly closes HANDLER cursors +# for this table belonging to the same connection. +CREATE TEMPORARY TABLE t1 AS SELECT 1 AS f1; +# -- CREATE TABLE +HANDLER t1 OPEN; +CREATE TEMPORARY TABLE IF NOT EXISTS t1 SELECT 1 AS f1; +Warnings: +Note 1050 Table 't1' already exists +HANDLER t1 READ FIRST; +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 FIRST; +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 FIRST; +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 FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +# -- CHECK TABLE +HANDLER t1 OPEN; +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +# -- ALTER TABLE +HANDLER t1 OPEN; +ALTER TABLE t1 ADD COLUMN b INT; +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +# -- CREATE INDEX +HANDLER t1 OPEN; +CREATE INDEX b ON t1 (b); +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +# -- DROP INDEX +HANDLER t1 OPEN; +DROP INDEX b ON t1; +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +# -- TRUNCATE TABLE +HANDLER t1 OPEN; +TRUNCATE TABLE t1; +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +# -- DROP TABLE +HANDLER t1 OPEN; +DROP TABLE t1; +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +CREATE TEMPORARY TABLE t1(a INT, b INT, INDEX i(a)); +set global keycache1.key_cache_block_size=2048; +set global keycache1.key_buffer_size=1*1024*1024; +set global keycache1.key_buffer_size=1024*1024; +# -- CACHE INDEX +HANDLER t1 OPEN; +CACHE INDEX t1 IN keycache1; +Table Op Msg_type Msg_text +test.t1 assign_to_keycache status OK +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +# -- LOAD INDEX +HANDLER t1 OPEN; +LOAD INDEX INTO CACHE t1; +Table Op Msg_type Msg_text +test.t1 preload_keys status OK +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER End of 5.1 tests === modified file 'mysql-test/r/merge.result' --- a/mysql-test/r/merge.result 2010-12-06 13:12:51 +0000 +++ b/mysql-test/r/merge.result 2010-12-23 16:18:19 +0000 @@ -3676,4 +3676,78 @@ ALTER TABLE t1 engine=myisam; ERROR HY000: Table 't1' was locked with a READ lock and can't be updated UNLOCK TABLES; DROP TABLE m1, t1; +# +# Additional coverage for refactoring which is made as part +# of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege +# to allow temp table operations". +# +# Check that prelocking works correctly for various variants of +# merge tables. +drop table if exists t1, t2, m1; +drop function if exists f1; +create table t1 (j int); +insert into t1 values (1); +create function f1() returns int return (select count(*) from m1); +create temporary table t2 (a int) engine=myisam; +insert into t2 values (1); +create temporary table m1 (a int) engine=merge union=(t2); +select f1() from t1; +f1() +1 +drop tables t2, m1; +create table t2 (a int) engine=myisam; +insert into t2 values (1); +create table m1 (a int) engine=merge union=(t2); +select f1() from t1; +f1() +1 +drop table m1; +create temporary table m1 (a int) engine=merge union=(t2); +select f1() from t1; +f1() +1 +drop tables t1, t2, m1; +drop function f1; +# +# Check that REPAIR/CHECK and CHECKSUM statements work correctly +# for various variants of merge tables. +create table t1 (a int) engine=myisam; +insert into t1 values (1); +create table m1 (a int) engine=merge union=(t1); +check table m1; +Table Op Msg_type Msg_text +test.m1 check status OK +repair table m1; +Table Op Msg_type Msg_text +test.m1 repair note The storage engine for the table doesn't support repair +checksum table m1; +Table Checksum +test.m1 3459908756 +drop tables t1, m1; +create temporary table t1 (a int) engine=myisam; +insert into t1 values (1); +create temporary table m1 (a int) engine=merge union=(t1); +check table m1; +Table Op Msg_type Msg_text +test.m1 check status OK +repair table m1; +Table Op Msg_type Msg_text +test.m1 repair note The storage engine for the table doesn't support repair +checksum table m1; +Table Checksum +test.m1 3459908756 +drop tables t1, m1; +create table t1 (a int) engine=myisam; +insert into t1 values (1); +create temporary table m1 (a int) engine=merge union=(t1); +check table m1; +Table Op Msg_type Msg_text +test.m1 check status OK +repair table m1; +Table Op Msg_type Msg_text +test.m1 repair note The storage engine for the table doesn't support repair +checksum table m1; +Table Checksum +test.m1 3459908756 +drop tables t1, m1; End of 6.0 tests === modified file 'mysql-test/r/temp_table.result' --- a/mysql-test/r/temp_table.result 2010-08-30 06:38:09 +0000 +++ b/mysql-test/r/temp_table.result 2010-12-23 16:18:19 +0000 @@ -223,3 +223,51 @@ CREATE TEMPORARY TABLE bug48067.t1 (c1 i DROP DATABASE bug48067; DROP TEMPORARY table bug48067.t1; End of 5.1 tests +# +# Test that admin statements work for temporary tables. +# +DROP TABLE IF EXISTS t1,t2; +CREATE TEMPORARY TABLE t1(a INT); +CREATE TEMPORARY TABLE t2(b INT); +CREATE TEMPORARY TABLE t3(c INT); +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (11), (12), (13); +INSERT INTO t3 VALUES (101), (102), (103); +ANALYZE TABLE t1, t2, t3; +Table Op Msg_type Msg_text +test.t1 analyze status OK +test.t2 analyze status OK +test.t3 analyze status OK +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (11), (12), (13); +INSERT INTO t3 VALUES (101), (102), (103); +CHECK TABLE t1, t2, t3; +Table Op Msg_type Msg_text +test.t1 check status OK +test.t2 check status OK +test.t3 check status OK +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (11), (12), (13); +INSERT INTO t3 VALUES (101), (102), (103); +CHECKSUM TABLE t1, t2, t3; +Table Checksum +test.t1 xxx +test.t2 xxx +test.t3 xxx +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (11), (12), (13); +INSERT INTO t3 VALUES (101), (102), (103); +OPTIMIZE TABLE t1, t2, t3; +Table Op Msg_type Msg_text +test.t1 optimize status OK +test.t2 optimize status OK +test.t3 optimize status OK +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (11), (12), (13); +INSERT INTO t3 VALUES (101), (102), (103); +REPAIR TABLE t1, t2, t3; +Table Op Msg_type Msg_text +test.t1 repair status OK +test.t2 repair status OK +test.t3 repair status OK +DROP TABLES t1, t2, t3; === modified file 'mysql-test/suite/innodb/r/innodb_mysql.result' --- a/mysql-test/suite/innodb/r/innodb_mysql.result 2010-12-20 13:24:37 +0000 +++ b/mysql-test/suite/innodb/r/innodb_mysql.result 2010-12-23 16:18:19 +0000 @@ -2830,4 +2830,17 @@ PACK_KEYS=0; CREATE INDEX a ON t1 (a); CREATE INDEX c on t1 (c); DROP TABLE t1; +# +# Additional coverage for refactoring which is made as part +# of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege +# to allow temp table operations". +# +# Check that OPTIMIZE table works for temporary InnoDB tables. +DROP TABLE IF EXISTS t1; +CREATE TEMPORARY TABLE t1 (a INT) ENGINE=InnoDB; +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 +DROP TABLE t1; End of 6.0 tests === modified file 'mysql-test/suite/innodb/t/innodb_mysql.test' --- a/mysql-test/suite/innodb/t/innodb_mysql.test 2010-12-20 12:43:48 +0000 +++ b/mysql-test/suite/innodb/t/innodb_mysql.test 2010-12-23 16:18:19 +0000 @@ -992,4 +992,19 @@ CREATE INDEX c on t1 (c); DROP TABLE t1; + +--echo # +--echo # Additional coverage for refactoring which is made as part +--echo # of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege +--echo # to allow temp table operations". +--echo # +--echo # Check that OPTIMIZE table works for temporary InnoDB tables. +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +CREATE TEMPORARY TABLE t1 (a INT) ENGINE=InnoDB; +OPTIMIZE TABLE t1; +DROP TABLE t1; + + --echo End of 6.0 tests === modified file 'mysql-test/suite/rpl/r/rpl_create_if_not_exists.result' --- a/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result 2010-12-19 17:22:30 +0000 +++ b/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result 2010-12-23 16:18:19 +0000 @@ -117,4 +117,18 @@ show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info DROP VIEW v1; DROP TABLE t1, t2; +# +# Test case which has failed on assertion after refactoring which was +# made as part of fix for bug #27480 "Extend CREATE TEMPORARY TABLES +# privilege to allow temp table operations". +# +CREATE TEMPORARY TABLE t1 (id int); +CREATE TABLE IF NOT EXISTS t2 LIKE t1; +# The below statement should succeed with warning and +# should not crash due to failing assertion. +CREATE TABLE IF NOT EXISTS t2 LIKE t1; +Warnings: +Note 1050 Table 't2' already exists +# Clean-up. +DROP TABLE t1, t2; include/rpl_end.inc === modified file 'mysql-test/suite/rpl/t/rpl_create_if_not_exists.test' --- a/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test 2010-12-19 17:22:30 +0000 +++ b/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test 2010-12-23 16:18:19 +0000 @@ -173,4 +173,21 @@ DROP VIEW v1; DROP TABLE t1, t2; + +--echo # +--echo # Test case which has failed on assertion after refactoring which was +--echo # made as part of fix for bug #27480 "Extend CREATE TEMPORARY TABLES +--echo # privilege to allow temp table operations". +--echo # +CREATE TEMPORARY TABLE t1 (id int); +CREATE TABLE IF NOT EXISTS t2 LIKE t1; +--echo # The below statement should succeed with warning and +--echo # should not crash due to failing assertion. +CREATE TABLE IF NOT EXISTS t2 LIKE t1; +--echo # Clean-up. +DROP TABLE t1, t2; +sync_slave_with_master; +connection master; + + --source include/rpl_end.inc === modified file 'mysql-test/t/alter_table.test' --- a/mysql-test/t/alter_table.test 2010-07-26 09:22:38 +0000 +++ b/mysql-test/t/alter_table.test 2010-12-23 16:18:19 +0000 @@ -1144,3 +1144,18 @@ INSERT INTO t1 VALUES (1, 1), (2, 2); ALTER TABLE t1 CHANGE a id INT; --disable_info DROP TABLE t1; + + +--echo # +--echo # Additional coverage for refactoring which is made as part +--echo # of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege +--echo # to allow temp table operations". +--echo # +--echo # At some point the below test case failed on assertion. +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +CREATE TEMPORARY TABLE t1 (i int) ENGINE=MyISAM; +--error ER_ILLEGAL_HA +ALTER TABLE t1 DISCARD TABLESPACE; +DROP TABLE t1; === modified file 'mysql-test/t/grant4.test' --- a/mysql-test/t/grant4.test 2009-10-19 12:58:13 +0000 +++ b/mysql-test/t/grant4.test 2010-12-23 16:18:19 +0000 @@ -144,3 +144,62 @@ connection default; disconnect con1; drop database mysqltest_db1; drop user mysqltest_u1@localhost; + + +--echo # +--echo # Additional coverage for refactoring which is made as part +--echo # of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege +--echo # to allow temp table operations". +--echo # +--echo # Check that for statements like CHECK/REPAIR and OPTIMIZE TABLE +--echo # privileges for all tables involved are checked before processing +--echo # any tables. Doing otherwise, i.e. checking privileges for table +--echo # right before processing it might result in lost results for tables +--echo # which were processed by the time when table for which privileges +--echo # are insufficient are discovered. +--echo # +call mtr.add_suppression("Got an error from thread_id=.*ha_myisam.cc:"); +call mtr.add_suppression("MySQL thread id .*, query id .* localhost.*mysqltest_u1 Checking table"); +--disable_warnings +drop database if exists mysqltest_db1; +--enable_warnings +let $MYSQLD_DATADIR = `SELECT @@datadir`; +create database mysqltest_db1; +--echo # Create tables which we are going to CHECK/REPAIR. +create table mysqltest_db1.t1 (a int, key(a)) engine=myisam; +create table mysqltest_db1.t2 (b int); +insert into mysqltest_db1.t1 values (1), (2); +insert into mysqltest_db1.t2 values (1); +--echo # Create user which will try to do this. +create user mysqltest_u1@localhost; +grant insert, select on mysqltest_db1.t1 to mysqltest_u1@localhost; +connect (con1,localhost,mysqltest_u1,,); +connection default; + +--echo # Corrupt t1 by replacing t1.MYI with a corrupt + unclosed one created +--echo # by doing: 'create table t1 (a int key(a))' +--echo # head -c1024 t1.MYI > corrupt_t1.MYI +flush table mysqltest_db1.t1; +--remove_file $MYSQLD_DATADIR/mysqltest_db1/t1.MYI +--copy_file std_data/corrupt_t1.MYI $MYSQLD_DATADIR/mysqltest_db1/t1.MYI + +--echo # Switching to connection 'con1'. +connection con1; +check table mysqltest_db1.t1; +--echo # The below statement should fail before repairing t1. +--echo # Otherwise info about such repair will be missing from its result-set. +--error ER_TABLEACCESS_DENIED_ERROR +repair table mysqltest_db1.t1, mysqltest_db1.t2; +--echo # The same is true for CHECK TABLE statement. +--error ER_TABLEACCESS_DENIED_ERROR +check table mysqltest_db1.t1, mysqltest_db1.t2; +check table mysqltest_db1.t1; +repair table mysqltest_db1.t1; + +--echo # Clean-up. +disconnect con1; +--source include/wait_until_disconnected.inc +--echo # Switching to connection 'default'. +connection default; +drop database mysqltest_db1; +drop user mysqltest_u1@localhost; === modified file 'mysql-test/t/handler_myisam.test' --- a/mysql-test/t/handler_myisam.test 2010-06-09 10:45:04 +0000 +++ b/mysql-test/t/handler_myisam.test 2010-12-23 16:18:19 +0000 @@ -97,4 +97,93 @@ HANDLER t1 CLOSE; DROP TABLE t1; +--echo # +--echo # Additional coverage for refactoring which is made as part +--echo # of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege +--echo # to allow temp table operations". +--echo # +--echo # Check that DDL on temporary table properly closes HANDLER cursors +--echo # for this table belonging to the same connection. + +CREATE TEMPORARY TABLE t1 AS SELECT 1 AS f1; + +--echo # -- CREATE TABLE +HANDLER t1 OPEN; +CREATE TEMPORARY TABLE IF NOT EXISTS t1 SELECT 1 AS f1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +--echo # -- REPAIR TABLE +HANDLER t1 OPEN; +REPAIR TABLE t1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +--echo # -- ANALYZE TABLE +HANDLER t1 OPEN; +ANALYZE TABLE t1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +--echo # -- OPTIMIZE TABLE +HANDLER t1 OPEN; +OPTIMIZE TABLE t1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +--echo # -- CHECK TABLE +HANDLER t1 OPEN; +CHECK TABLE t1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +--echo # -- ALTER TABLE +HANDLER t1 OPEN; +ALTER TABLE t1 ADD COLUMN b INT; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +--echo # -- CREATE INDEX +HANDLER t1 OPEN; +CREATE INDEX b ON t1 (b); +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +--echo # -- DROP INDEX +HANDLER t1 OPEN; +DROP INDEX b ON t1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +--echo # -- TRUNCATE TABLE +HANDLER t1 OPEN; +TRUNCATE TABLE t1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +--echo # -- DROP TABLE +HANDLER t1 OPEN; +DROP TABLE t1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +CREATE TEMPORARY TABLE t1(a INT, b INT, INDEX i(a)); + +set global keycache1.key_cache_block_size=2048; +set global keycache1.key_buffer_size=1*1024*1024; +set global keycache1.key_buffer_size=1024*1024; + +--echo # -- CACHE INDEX +HANDLER t1 OPEN; +CACHE INDEX t1 IN keycache1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + +--echo # -- LOAD INDEX +HANDLER t1 OPEN; +LOAD INDEX INTO CACHE t1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; + + --echo End of 5.1 tests === modified file 'mysql-test/t/merge.test' --- a/mysql-test/t/merge.test 2010-10-26 09:10:59 +0000 +++ b/mysql-test/t/merge.test 2010-12-23 16:18:19 +0000 @@ -2798,6 +2798,60 @@ UNLOCK TABLES; DROP TABLE m1, t1; +--echo # +--echo # Additional coverage for refactoring which is made as part +--echo # of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege +--echo # to allow temp table operations". +--echo # +--echo # Check that prelocking works correctly for various variants of +--echo # merge tables. +--disable_warnings +drop table if exists t1, t2, m1; +drop function if exists f1; +--enable_warnings +create table t1 (j int); +insert into t1 values (1); +create function f1() returns int return (select count(*) from m1); +create temporary table t2 (a int) engine=myisam; +insert into t2 values (1); +create temporary table m1 (a int) engine=merge union=(t2); +select f1() from t1; +drop tables t2, m1; +create table t2 (a int) engine=myisam; +insert into t2 values (1); +create table m1 (a int) engine=merge union=(t2); +select f1() from t1; +drop table m1; +create temporary table m1 (a int) engine=merge union=(t2); +select f1() from t1; +drop tables t1, t2, m1; +drop function f1; +--echo # +--echo # Check that REPAIR/CHECK and CHECKSUM statements work correctly +--echo # for various variants of merge tables. +create table t1 (a int) engine=myisam; +insert into t1 values (1); +create table m1 (a int) engine=merge union=(t1); +check table m1; +repair table m1; +checksum table m1; +drop tables t1, m1; +create temporary table t1 (a int) engine=myisam; +insert into t1 values (1); +create temporary table m1 (a int) engine=merge union=(t1); +check table m1; +repair table m1; +checksum table m1; +drop tables t1, m1; +create table t1 (a int) engine=myisam; +insert into t1 values (1); +create temporary table m1 (a int) engine=merge union=(t1); +check table m1; +repair table m1; +checksum table m1; +drop tables t1, m1; + + --echo End of 6.0 tests --disable_result_log === modified file 'mysql-test/t/temp_table.test' --- a/mysql-test/t/temp_table.test 2010-06-23 11:34:40 +0000 +++ b/mysql-test/t/temp_table.test 2010-12-23 16:18:19 +0000 @@ -251,3 +251,46 @@ DROP DATABASE bug48067; DROP TEMPORARY table bug48067.t1; --echo End of 5.1 tests + +--echo # +--echo # Test that admin statements work for temporary tables. +--echo # +--disable_warnings +DROP TABLE IF EXISTS t1,t2; +--enable_warnings +CREATE TEMPORARY TABLE t1(a INT); +CREATE TEMPORARY TABLE t2(b INT); +CREATE TEMPORARY TABLE t3(c INT); + +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (11), (12), (13); +INSERT INTO t3 VALUES (101), (102), (103); + +ANALYZE TABLE t1, t2, t3; + +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (11), (12), (13); +INSERT INTO t3 VALUES (101), (102), (103); + +CHECK TABLE t1, t2, t3; + +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (11), (12), (13); +INSERT INTO t3 VALUES (101), (102), (103); + +--replace_column 2 xxx +CHECKSUM TABLE t1, t2, t3; + +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (11), (12), (13); +INSERT INTO t3 VALUES (101), (102), (103); + +OPTIMIZE TABLE t1, t2, t3; + +INSERT INTO t1 VALUES (1), (2), (3); +INSERT INTO t2 VALUES (11), (12), (13); +INSERT INTO t3 VALUES (101), (102), (103); + +REPAIR TABLE t1, t2, t3; + +DROP TABLES t1, t2, t3; === modified file 'sql/log_event.cc' --- a/sql/log_event.cc 2010-12-21 10:39:20 +0000 +++ b/sql/log_event.cc 2010-12-23 16:18:19 +0000 @@ -5321,7 +5321,8 @@ int Load_log_event::do_apply_event(NET* update it inside mysql_load(). */ List tmp_list; - if (mysql_load(thd, &ex, &tables, field_list, tmp_list, tmp_list, + if (open_temporary_tables(thd, &tables) || + mysql_load(thd, &ex, &tables, field_list, tmp_list, tmp_list, handle_dup, ignore, net != 0)) thd->is_slave_error= 1; if (thd->cuted_fields) === modified file 'sql/sp_head.cc' --- a/sql/sp_head.cc 2010-12-17 11:28:59 +0000 +++ b/sql/sp_head.cc 2010-12-23 16:18:19 +0000 @@ -3041,8 +3041,9 @@ int sp_instr::exec_open_and_lock_tables( Check whenever we have access to tables for this statement and open and lock them before executing instructions core function. */ - if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE) - || open_and_lock_tables(thd, tables, TRUE, 0)) + if (open_temporary_tables(thd, tables) || + check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE) || + open_and_lock_tables(thd, tables, TRUE, 0)) result= -1; else result= 0; === modified file 'sql/sql_admin.cc' --- a/sql/sql_admin.cc 2010-11-18 16:34:56 +0000 +++ b/sql/sql_admin.cc 2010-12-23 16:18:19 +0000 @@ -17,7 +17,6 @@ #include "keycaches.h" // get_key_cache #include "sql_base.h" // Open_table_context #include "lock.h" // MYSQL_OPEN_* -#include "sql_handler.h" // mysql_ha_rm_tables #include "partition_element.h" // PART_ADMIN #include "sql_partition.h" // set_part_state #include "transaction.h" // trans_rollback_stmt @@ -88,8 +87,7 @@ static int prepare_for_repair(THD *thd, MDL_EXCLUSIVE, MDL_TRANSACTION); if (lock_table_names(thd, table_list, table_list->next_global, - thd->variables.lock_wait_timeout, - MYSQL_OPEN_SKIP_TEMPORARY)) + thd->variables.lock_wait_timeout, 0)) DBUG_RETURN(0); has_mdl_lock= TRUE; @@ -292,7 +290,13 @@ static bool mysql_admin_table(THD* thd, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); - mysql_ha_rm_tables(thd, tables); + /* + Close all temporary tables which were pre-open to simplify + privilege checking. Clear all references to closed tables. + */ + close_thread_tables(thd); + for (table= tables; table; table= table->next_local) + table->table= NULL; for (table= tables; table; table= table->next_local) { @@ -341,7 +345,11 @@ static bool mysql_admin_table(THD* thd, if (view_operator_func == NULL) table->required_type=FRMTYPE_TABLE; - open_error= open_and_lock_tables(thd, table, TRUE, 0); + open_error= open_temporary_tables(thd, table); + + if (! open_error) + open_error= open_and_lock_tables(thd, table, TRUE, 0); + thd->no_warnings_for_error= 0; table->next_global= save_next_global; table->next_local= save_next_local; @@ -556,10 +564,26 @@ static bool mysql_admin_table(THD* thd, HA_ADMIN_NEEDS_ALTER)) { DBUG_PRINT("admin", ("recreating table")); + /* + Temporary table are always created by current server so they never + require upgrade. So we don't need to pre-open them before calling + mysql_recreate_table(). + */ + DBUG_ASSERT(! table->table->s->tmp_table); + trans_rollback_stmt(thd); trans_rollback(thd); close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); + + /* + table_list->table has been closed and freed. Do not reference + uninitialized data. open_tables() could fail. + */ + table->table= NULL; + /* Same applies to MDL ticket. */ + table->mdl_request.ticket= NULL; + tmp_disable_binlog(thd); // binlogging is done by caller if wanted result_code= mysql_recreate_table(thd, table); reenable_binlog(thd); @@ -684,6 +708,15 @@ send_result_message: trans_commit(thd); close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); + + /* + table_list->table has been closed and freed. Do not reference + uninitialized data. open_tables() could fail. + */ + table->table= NULL; + /* Same applies to MDL ticket. */ + table->mdl_request.ticket= NULL; + DEBUG_SYNC(thd, "ha_admin_try_alter"); protocol->store(STRING_WITH_LEN("note"), system_charset_info); protocol->store(STRING_WITH_LEN( @@ -696,7 +729,9 @@ send_result_message: *save_next_global= table->next_global; table->next_local= table->next_global= 0; tmp_disable_binlog(thd); // binlogging is done by caller if wanted - result_code= mysql_recreate_table(thd, table); + /* Don't forget to pre-open temporary tables. */ + result_code= (open_temporary_tables(thd, table) || + mysql_recreate_table(thd, table)); reenable_binlog(thd); /* mysql_recreate_table() can push OK or ERROR. @@ -710,14 +745,15 @@ send_result_message: trans_commit(thd); close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); + /* Clear references to TABLE and MDL_ticket after releasing them. */ table->table= NULL; + table->mdl_request.ticket= NULL; if (!result_code) // recreation went ok { - /* Clear the ticket released above. */ - table->mdl_request.ticket= NULL; DEBUG_SYNC(thd, "ha_admin_open_ltable"); table->mdl_request.set_type(MDL_SHARED_WRITE); - if ((table->table= open_ltable(thd, table, lock_type, 0))) + if (!open_temporary_tables(thd, table) && + (table->table= open_n_lock_single_table(thd, table, lock_type, 0))) { result_code= table->table->file->ha_analyze(thd, check_opt); if (result_code == HA_ADMIN_ALREADY_DONE) @@ -853,8 +889,6 @@ err: trans_rollback(thd); close_thread_tables(thd); // Shouldn't be needed thd->mdl_context.release_transactional_locks(); - if (table) - table->table=0; DBUG_RETURN(TRUE); } @@ -890,8 +924,8 @@ bool mysql_assign_to_keycache(THD* thd, mysql_mutex_unlock(&LOCK_global_system_variables); check_opt.key_cache= key_cache; DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt, - "assign_to_keycache", TL_READ_NO_INSERT, 0, 0, - 0, 0, &handler::assign_to_keycache, 0)); + "assign_to_keycache", TL_READ_NO_INSERT, 0, 0, + 0, 0, &handler::assign_to_keycache, 0)); } @@ -917,8 +951,8 @@ bool mysql_preload_keys(THD* thd, TABLE_ outdated information if parallel inserts into cache blocks happen. */ DBUG_RETURN(mysql_admin_table(thd, tables, 0, - "preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0, - &handler::preload_keys, 0)); + "preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0, + &handler::preload_keys, 0)); } === modified file 'sql/sql_base.cc' --- a/sql/sql_base.cc 2010-12-16 10:09:53 +0000 +++ b/sql/sql_base.cc 2010-12-23 16:18:19 +0000 @@ -2557,46 +2557,41 @@ tdc_wait_for_old_version(THD *thd, const } -/* - Open a table. - - SYNOPSIS - open_table() - thd Thread context. - table_list Open first table in list. - action INOUT Pointer to variable of enum_open_table_action type - which will be set according to action which is - required to remedy problem appeared during attempt - to open table. - flags Bitmap of flags to modify how open works: - MYSQL_OPEN_IGNORE_FLUSH - Open table even if - someone has done a flush or there is a pending - exclusive metadata lock requests against it - (i.e. request high priority metadata lock). - No version number checking is done. - MYSQL_OPEN_TEMPORARY_ONLY - Open only temporary - table not the base table or view. - MYSQL_OPEN_TAKE_UPGRADABLE_MDL - Obtain upgradable - metadata lock for tables on which we are going to - take some kind of write table-level lock. - - IMPLEMENTATION - Uses a cache of open tables to find a table not in use. - - If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is opened - only if it exists. If the open strategy is OPEN_STUB, the underlying table - is never opened. In both cases, metadata locks are always taken according - to the lock strategy. +/** + Open a base table. - RETURN - TRUE Open failed. "action" parameter may contain type of action - needed to remedy problem before retrying again. - FALSE Success. Members of TABLE_LIST structure are filled properly (e.g. - TABLE_LIST::table is set for real tables and TABLE_LIST::view is - set for views). + @param thd Thread context. + @param table_list Open first table in list. + @param mem_root Temporary MEM_ROOT to be used for + parsing .FRMs for views. + @param ot_ctx Context with flags which modify how open works + and which is used to recover from a failed + open_table() attempt. + Some examples of flags: + MYSQL_OPEN_IGNORE_FLUSH - Open table even if + someone has done a flush. No version number + checking is done. + MYSQL_OPEN_HAS_MDL_LOCK - instead of acquiring + metadata locks rely on that caller already has + appropriate ones. + + Uses a cache of open tables to find a TABLE instance not in use. + + If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is + opened only if it exists. If the open strategy is OPEN_STUB, the + underlying table is never opened. In both cases, metadata locks are + always taken according to the lock strategy. + + The function used to open temporary tables, but now it opens base tables + only. + + @retval TRUE Open failed. "action" parameter may contain type of action + needed to remedy problem before retrying again. + @retval FALSE Success. Members of TABLE_LIST structure are filled properly + (e.g. TABLE_LIST::table is set for real tables and + TABLE_LIST::view is set for views). */ - bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, Open_table_context *ot_ctx) { @@ -2611,6 +2606,14 @@ bool open_table(THD *thd, TABLE_LIST *ta my_hash_value_type hash_value; DBUG_ENTER("open_table"); + /* + The table must not be opened already. The table can be pre-opened for + some statements if it is a temporary table. + + open_temporary_table() must be used to open temporary tables. + */ + DBUG_ASSERT(!table_list->table); + /* an open table operation needs a lot of the stack space */ if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias)) DBUG_RETURN(TRUE); @@ -2618,66 +2621,13 @@ bool open_table(THD *thd, TABLE_LIST *ta if (thd->killed) DBUG_RETURN(TRUE); - key_length= (create_table_def_key(thd, key, table_list, 1) - - TMP_TABLE_KEY_EXTRA); - - /* - Unless requested otherwise, try to resolve this table in the list - of temporary tables of this thread. In MySQL temporary tables - are always thread-local and "shadow" possible base tables with the - same name. This block implements the behaviour. - TODO: move this block into a separate function. - */ - if (table_list->open_type != OT_BASE_ONLY && - ! (flags & MYSQL_OPEN_SKIP_TEMPORARY)) - { - for (table= thd->temporary_tables; table ; table=table->next) - { - if (table->s->table_cache_key.length == key_length + - TMP_TABLE_KEY_EXTRA && - !memcmp(table->s->table_cache_key.str, key, - key_length + TMP_TABLE_KEY_EXTRA)) - { - /* - We're trying to use the same temporary table twice in a query. - Right now we don't support this because a temporary table - is always represented by only one TABLE object in THD, and - it can not be cloned. Emit an error for an unsupported behaviour. - */ - if (table->query_id) - { - DBUG_PRINT("error", - ("query_id: %lu server_id: %u pseudo_thread_id: %lu", - (ulong) table->query_id, (uint) thd->server_id, - (ulong) thd->variables.pseudo_thread_id)); - my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); - DBUG_RETURN(TRUE); - } - table->query_id= thd->query_id; - thd->thread_specific_used= TRUE; - DBUG_PRINT("info",("Using temporary table")); - goto reset; - } - } - } - - if (table_list->open_type == OT_TEMPORARY_ONLY || - (flags & MYSQL_OPEN_TEMPORARY_ONLY)) - { - if (table_list->open_strategy == TABLE_LIST::OPEN_NORMAL) - { - my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name); - DBUG_RETURN(TRUE); - } - else - DBUG_RETURN(FALSE); - } + key_length= create_table_def_key(thd, key, table_list, 0); /* - The table is not temporary - if we're in pre-locked or LOCK TABLES - mode, let's try to find the requested table in the list of pre-opened - and locked tables. If the table is not there, return an error - we can't - open not pre-opened tables in pre-locked/LOCK TABLES mode. + If we're in pre-locked or LOCK TABLES mode, let's try to find the + requested table in the list of pre-opened and locked tables. If the + table is not there, return an error - we can't open not pre-opened + tables in pre-locked/LOCK TABLES mode. TODO: move this block into a separate function. */ if (thd->locked_tables_mode && @@ -2773,7 +2723,7 @@ bool open_table(THD *thd, TABLE_LIST *ta } /* No table in the locked tables list. In case of explicit LOCK TABLES - this can happen if a user did not include the able into the list. + this can happen if a user did not include the table into the list. In case of pre-locked mode locked tables list is generated automatically, so we may only end up here if the table did not exist when locked tables list was created. @@ -2785,10 +2735,7 @@ bool open_table(THD *thd, TABLE_LIST *ta DBUG_RETURN(TRUE); } - /* - Non pre-locked/LOCK TABLES mode, and the table is not temporary. - This is the normal use case. - */ + /* Non pre-locked/LOCK TABLES mode. This is the normal use case. */ if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) { @@ -3997,8 +3944,7 @@ recover_from_failed_open(THD *thd) case OT_DISCOVER: { if ((result= lock_table_names(thd, m_failed_table, NULL, - get_timeout(), - MYSQL_OPEN_SKIP_TEMPORARY))) + get_timeout(), 0))) break; tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db, @@ -4014,8 +3960,7 @@ recover_from_failed_open(THD *thd) case OT_REPAIR: { if ((result= lock_table_names(thd, m_failed_table, NULL, - get_timeout(), - MYSQL_OPEN_SKIP_TEMPORARY))) + get_timeout(), 0))) break; tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db, @@ -4348,10 +4293,32 @@ open_and_process_table(THD *thd, LEX *le tables->db, tables->table_name, tables)); //psergey: invalid read of size 1 here (*counter)++; - /* Not a placeholder: must be a base table or a view. Let us open it. */ - DBUG_ASSERT(!tables->table); + /* Not a placeholder: must be a base/temporary table or a view. Let us open it. */ - if (tables->prelocking_placeholder) + if (tables->table) + { + /* + If this TABLE_LIST object has an associated open TABLE object + (TABLE_LIST::table is not NULL), that TABLE object must be a pre-opened + temporary table. + */ + DBUG_ASSERT(is_temporary_table(tables)); + } + else if (tables->open_type == OT_TEMPORARY_ONLY) + { + /* + OT_TEMPORARY_ONLY means that we are in CREATE TEMPORARY TABLE statement. + Also such table list element can't correspond to prelocking placeholder + or to underlying table of merge table. + So existing temporary table should have been preopened by this moment + and we can simply continue without trying to open temporary or base + table. + */ + DBUG_ASSERT(tables->open_strategy); + DBUG_ASSERT(!tables->prelocking_placeholder); + DBUG_ASSERT(!tables->parent_l); + } + else if (tables->prelocking_placeholder) { /* For the tables added by the pre-locking code, attempt to open @@ -4361,12 +4328,53 @@ open_and_process_table(THD *thd, LEX *le */ No_such_table_error_handler no_such_table_handler; thd->push_internal_handler(&no_such_table_handler); - error= open_table(thd, tables, new_frm_mem, ot_ctx); + + /* + We're opening a table from the prelocking list. + + Since this table list element might have been added after pre-opening + of temporary tables we have to try to open temporary table for it. + + We can't simply skip this table list element and postpone opening of + temporary tabletill the execution of substatement for several reasons: + - Temporary table can be a MERGE table with base underlying tables, + so its underlying tables has to be properly open and locked at + prelocking stage. + - Temporary table can be a MERGE table and we might be in PREPARE + phase for a prepared statement. In this case it is important to call + HA_ATTACH_CHILDREN for all merge children. + This is necessary because merge children remember "TABLE_SHARE ref type" + and "TABLE_SHARE def version" in the HA_ATTACH_CHILDREN operation. + If HA_ATTACH_CHILDREN is not called, these attributes are not set. + Then, during the first EXECUTE, those attributes need to be updated. + That would cause statement re-preparing (because changing those + attributes during EXECUTE is caught by THD::m_reprepare_observer). + The problem is that since those attributes are not set in merge + children, another round of PREPARE will not help. + */ + error= open_temporary_table(thd, tables); + + if (!error && !tables->table) + error= open_table(thd, tables, new_frm_mem, ot_ctx); + thd->pop_internal_handler(); safe_to_ignore_table= no_such_table_handler.safely_trapped_errors(); } else - error= open_table(thd, tables, new_frm_mem, ot_ctx); + { + if (tables->parent_l) + { + /* + Even if we are opening table not from the prelocking list we + still might need to look for a temporary table if this table + list element corresponds to underlying table of a merge table. + */ + error= open_temporary_table(thd, tables); + } + + if (!error && !tables->table) + error= open_table(thd, tables, new_frm_mem, ot_ctx); + } free_root(new_frm_mem, MYF(MY_KEEP_PREALLOC)); @@ -4454,6 +4462,7 @@ open_and_process_table(THD *thd, LEX *le goto end; } + /* Set appropriate TABLE::lock_type. */ if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode) { if (tables->lock_type == TL_WRITE_DEFAULT) @@ -4464,6 +4473,8 @@ open_and_process_table(THD *thd, LEX *le else tables->table->reginfo.lock_type= tables->lock_type; } + + /* Copy grant information from TABLE_LIST instance to TABLE one. */ tables->table->grant= tables->grant; /* Check and update metadata version of a base table. */ @@ -4551,18 +4562,17 @@ lock_table_names(THD *thd, for (table= tables_start; table && table != tables_end; table= table->next_global) { - if (table->mdl_request.type >= MDL_SHARED_NO_WRITE && - !(table->open_type == OT_TEMPORARY_ONLY || - (flags & MYSQL_OPEN_TEMPORARY_ONLY) || - (table->open_type != OT_BASE_ONLY && - ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) && - find_temporary_table(thd, table)))) + if (table->mdl_request.type < MDL_SHARED_NO_WRITE || + table->open_type == OT_TEMPORARY_ONLY || + (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table))) { - if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) && - schema_set.insert(table)) - return TRUE; - mdl_requests.push_front(&table->mdl_request); + continue; } + + if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) && schema_set.insert(table)) + return TRUE; + + mdl_requests.push_front(&table->mdl_request); } if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) && @@ -4630,35 +4640,34 @@ open_tables_check_upgradable_mdl(THD *th for (table= tables_start; table && table != tables_end; table= table->next_global) { - if (table->mdl_request.type >= MDL_SHARED_NO_WRITE && - !(table->open_type == OT_TEMPORARY_ONLY || - (flags & MYSQL_OPEN_TEMPORARY_ONLY) || - (table->open_type != OT_BASE_ONLY && - ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) && - find_temporary_table(thd, table)))) - { - /* - We don't need to do anything about the found TABLE instance as it - will be handled later in open_tables(), we only need to check that - an upgradable lock is already acquired. When we enter LOCK TABLES - mode, SNRW locks are acquired before all other locks. So if under - LOCK TABLES we find that there is TABLE instance with upgradeable - lock, all other instances of TABLE for the same table will have the - same ticket. - - Note that this works OK even for CREATE TABLE statements which - request X type of metadata lock. This is because under LOCK TABLES - such statements don't create the table but only check if it exists - or, in most complex case, only insert into it. - Thus SNRW lock should be enough. - - Note that find_table_for_mdl_upgrade() will report an error if - no suitable ticket is found. - */ - if (!find_table_for_mdl_upgrade(thd->open_tables, table->db, - table->table_name, FALSE)) - return TRUE; + if (table->mdl_request.type < MDL_SHARED_NO_WRITE || + table->open_type == OT_TEMPORARY_ONLY || + (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table))) + { + continue; } + + /* + We don't need to do anything about the found TABLE instance as it + will be handled later in open_tables(), we only need to check that + an upgradable lock is already acquired. When we enter LOCK TABLES + mode, SNRW locks are acquired before all other locks. So if under + LOCK TABLES we find that there is TABLE instance with upgradeable + lock, all other instances of TABLE for the same table will have the + same ticket. + + Note that this works OK even for CREATE TABLE statements which + request X type of metadata lock. This is because under LOCK TABLES + such statements don't create the table but only check if it exists + or, in most complex case, only insert into it. + Thus SNRW lock should be enough. + + Note that find_table_for_mdl_upgrade() will report an error if + no suitable ticket is found. + */ + if (!find_table_for_mdl_upgrade(thd->open_tables, table->db, + table->table_name, FALSE)) + return TRUE; } return FALSE; @@ -4837,6 +4846,10 @@ restart: if (ot_ctx.recover_from_failed_open(thd)) goto err; + /* Re-open temporary tables after close_tables_for_reopen(). */ + if (open_temporary_tables(thd, *start)) + goto err; + error= FALSE; goto restart; } @@ -4883,6 +4896,10 @@ restart: if (ot_ctx.recover_from_failed_open(thd)) goto err; + /* Re-open temporary tables after close_tables_for_reopen(). */ + if (open_temporary_tables(thd, *start)) + goto err; + error= FALSE; goto restart; } @@ -4913,6 +4930,11 @@ restart: { TABLE *tbl= tables->table; + /* + NOTE: temporary merge tables should be processed here too, because + a temporary merge table can be based on non-temporary tables. + */ + /* Schema tables may not have a TABLE object here. */ if (tbl && tbl->file->ht->db_type == DB_TYPE_MRG_MYISAM) { @@ -5950,6 +5972,134 @@ static void update_field_dependencies(TH } +/** + Find a temporary table specified by TABLE_LIST instance in the cache and + prepare its TABLE instance for use. + + This function tries to resolve this table in the list of temporary tables + of this thread. Temporary tables are thread-local and "shadow" base + tables with the same name. + + @note In most cases one should use open_temporary_tables() instead + of this call. + + @note One should finalize process of opening temporary table for table + list element by calling open_and_process_table(). This function + is responsible for table version checking and handling of merge + tables. + + @note We used to check global_read_lock before opening temporary tables. + However, that limitation was artificial and is removed now. + + @return Error status. + @retval FALSE On success. If a temporary table exists for the given + key, tl->table is set. + @retval TRUE On error. my_error() has been called. +*/ + +bool open_temporary_table(THD *thd, TABLE_LIST *tl) +{ + DBUG_ENTER("open_temporary_table"); + DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db, tl->table_name)); + + /* + Code in open_table() assumes that TABLE_LIST::table can + be non-zero only for pre-opened temporary tables. + */ + DBUG_ASSERT(tl->table == NULL); + + /* + This function should not be called for cases when derived or I_S + tables can be met since table list elements for such tables can + have invalid db or table name. + Instead open_temporary_tables() should be used. + */ + DBUG_ASSERT(!tl->derived && !tl->schema_table); + + if (tl->open_type == OT_BASE_ONLY) + { + DBUG_PRINT("info", ("skip_temporary is set")); + DBUG_RETURN(FALSE); + } + + TABLE *table= find_temporary_table(thd, tl); + + if (!table) + { + if (tl->open_type == OT_TEMPORARY_ONLY && + tl->open_strategy == TABLE_LIST::OPEN_NORMAL) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db, tl->table_name); + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); + } + + if (table->query_id) + { + /* + We're trying to use the same temporary table twice in a query. + Right now we don't support this because a temporary table is always + represented by only one TABLE object in THD, and it can not be + cloned. Emit an error for an unsupported behaviour. + */ + + DBUG_PRINT("error", + ("query_id: %lu server_id: %u pseudo_thread_id: %lu", + (ulong) table->query_id, (uint) thd->server_id, + (ulong) thd->variables.pseudo_thread_id)); + my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); + DBUG_RETURN(TRUE); + } + + table->query_id= thd->query_id; + thd->thread_specific_used= TRUE; + + tl->updatable= 1; // It is not derived table nor non-updatable VIEW. + tl->table= table; + + table->init(thd, tl); + + DBUG_PRINT("info", ("Using temporary table")); + DBUG_RETURN(FALSE); +} + + +/** + Pre-open temporary tables corresponding to table list elements. + + @note One should finalize process of opening temporary tables + by calling open_tables(). This function is responsible + for table version checking and handling of merge tables. + + @return Error status. + @retval FALSE On success. If a temporary tables exists for the + given element, tl->table is set. + @retval TRUE On error. my_error() has been called. +*/ + +bool open_temporary_tables(THD *thd, TABLE_LIST *tl_list) +{ + DBUG_ENTER("open_temporary_tables"); + + for (TABLE_LIST *tl= tl_list; tl; tl= tl->next_global) + { + if (tl->derived || tl->schema_table) + { + /* + Derived and I_S tables will be handled by a later call to open_tables(). + */ + continue; + } + + if (open_temporary_table(thd, tl)) + DBUG_RETURN(TRUE); + } + + DBUG_RETURN(FALSE); +} + + /* Find a field by name in a view that uses merge algorithm. === modified file 'sql/sql_base.h' --- a/sql/sql_base.h 2010-11-23 22:37:59 +0000 +++ b/sql/sql_base.h 2010-12-23 16:18:19 +0000 @@ -93,7 +93,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST /* mysql_lock_tables() and open_table() flags bits */ #define MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK 0x0001 #define MYSQL_OPEN_IGNORE_FLUSH 0x0002 -#define MYSQL_OPEN_TEMPORARY_ONLY 0x0004 +/* MYSQL_OPEN_TEMPORARY_ONLY (0x0004) is not used anymore. */ #define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008 #define MYSQL_LOCK_LOG_TABLE 0x0010 /** @@ -106,8 +106,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST a new instance of the table. */ #define MYSQL_OPEN_GET_NEW_TABLE 0x0040 -/** Don't look up the table in the list of temporary tables. */ -#define MYSQL_OPEN_SKIP_TEMPORARY 0x0080 +/* 0x0080 used to be MYSQL_OPEN_SKIP_TEMPORARY */ /** Fail instead of waiting when conficting metadata lock is discovered. */ #define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT 0x0100 /** Open tables using MDL_SHARED lock instead of one specified in parser. */ @@ -139,7 +138,6 @@ TABLE *open_ltable(THD *thd, TABLE_LIST MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |\ MYSQL_LOCK_IGNORE_TIMEOUT |\ MYSQL_OPEN_GET_NEW_TABLE |\ - MYSQL_OPEN_SKIP_TEMPORARY |\ MYSQL_OPEN_HAS_MDL_LOCK) bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, @@ -269,6 +267,8 @@ void close_temporary_table(THD *thd, TAB void close_temporary(TABLE *table, bool free_share, bool delete_table); bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db, const char *table_name); +bool open_temporary_tables(THD *thd, TABLE_LIST *tl_list); +bool open_temporary_table(THD *thd, TABLE_LIST *tl); bool is_equal(const LEX_STRING *a, const LEX_STRING *b); /* Functions to work with system tables. */ @@ -572,6 +572,16 @@ private: /** + Check if a TABLE_LIST instance represents a pre-opened temporary table. +*/ + +inline bool is_temporary_table(TABLE_LIST *tl) +{ + return tl->table ? (tl->table->s->tmp_table != NO_TMP_TABLE) : FALSE; +} + + +/** This internal handler is used to trap ER_NO_SUCH_TABLE. */ === modified file 'sql/sql_class.h' --- a/sql/sql_class.h 2010-12-20 10:28:06 +0000 +++ b/sql/sql_class.h 2010-12-23 16:18:19 +0000 @@ -3727,6 +3727,18 @@ public: #define CF_CAN_GENERATE_ROW_EVENTS (1U << 9) /** + Identifies statements which may deal with temporary tables and for which + temporary tables should be pre-opened to simplify privilege checks. +*/ +#define CF_PREOPEN_TMP_TABLES (1U << 10) + +/** + Identifies statements for which open handlers should be closed in the + beginning of the statement. +*/ +#define CF_HA_CLOSE (1U << 11) + +/** Identifies statements that can directly update a rpl info table. */ #define CF_WRITE_RPL_INFO_COMMAND (1U << 12) === modified file 'sql/sql_db.cc' --- a/sql/sql_db.cc 2010-12-10 12:52:55 +0000 +++ b/sql/sql_db.cc 2010-12-23 16:18:19 +0000 @@ -817,8 +817,7 @@ bool mysql_rm_db(THD *thd,char *db,bool } /* Lock all tables and stored routines about to be dropped. */ - if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout, - MYSQL_OPEN_SKIP_TEMPORARY) || + if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout, 0) || lock_db_routines(thd, db)) goto exit; === modified file 'sql/sql_handler.cc' --- a/sql/sql_handler.cc 2010-11-18 16:34:56 +0000 +++ b/sql/sql_handler.cc 2010-12-23 16:18:19 +0000 @@ -288,11 +288,13 @@ bool mysql_ha_open(THD *thd, TABLE_LIST DBUG_ASSERT(! hash_tables->table); /* - We use open_tables() here, rather than, say, - open_ltable() or open_table() because we would like to be able - to open a temporary table. + TODO/FIXME: In the upcoming patch we somehow should handle + situation with privilege check for temporary table. */ - error= open_tables(thd, &hash_tables, &counter, 0); + error= open_temporary_tables(thd, hash_tables); + + if (!error) + error= open_tables(thd, &hash_tables, &counter, 0); if (! error && ! (hash_tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER)) === modified file 'sql/sql_insert.cc' --- a/sql/sql_insert.cc 2010-12-22 13:23:59 +0000 +++ b/sql/sql_insert.cc 2010-12-23 16:18:19 +0000 @@ -3785,8 +3785,7 @@ static TABLE *create_table_from_items(TH } else { - Open_table_context ot_ctx(thd, MYSQL_OPEN_TEMPORARY_ONLY); - if (open_table(thd, create_table, thd->mem_root, &ot_ctx)) + if (open_temporary_table(thd, create_table)) { /* This shouldn't happen as creation of temporary table should make @@ -3796,7 +3795,9 @@ static TABLE *create_table_from_items(TH drop_temporary_table(thd, create_table, NULL); } else + { table= create_table->table; + } } } if (!table) // open failed === modified file 'sql/sql_parse.cc' --- a/sql/sql_parse.cc 2010-12-17 11:28:59 +0000 +++ b/sql/sql_parse.cc 2010-12-23 16:18:19 +0000 @@ -23,7 +23,7 @@ // set_handler_table_locks, // lock_global_read_lock, // make_global_read_lock_block_commit -#include "sql_base.h" // find_temporary_tablesx +#include "sql_base.h" // find_temporary_table #include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE, query_cache_* #include "sql_show.h" // mysqld_list_*, mysqld_show_*, // calc_sum_of_all_status @@ -43,7 +43,6 @@ #include "sql_table.h" // mysql_create_like_table, // mysql_create_table, // mysql_alter_table, - // mysql_recreate_table, // mysql_backup_table, // mysql_restore_table #include "sql_reload.h" // reload_acl_and_cache @@ -446,6 +445,60 @@ void init_update_queries(void) 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; + + /* + The following statements can deal with temporary tables, + so temporary tables should be pre-opened for those statements to + simplify privilege checking. + + There are other statements that deal with temporary tables and open + them, but which are not listed here. The thing is that the order of + pre-opening temporary tables for those statements is somewhat custom. + */ + sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_TRUNCATE]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_LOAD]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_DROP_INDEX]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_CREATE_VIEW]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_UPDATE]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_UPDATE_MULTI]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_INSERT]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_INSERT_SELECT]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_DELETE]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_DELETE_MULTI]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_REPLACE]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_REPLACE_SELECT]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_SELECT]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_SET_OPTION]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_DO]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_CALL]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_CHECKSUM]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_ANALYZE]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_CHECK]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_OPTIMIZE]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_REPAIR]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_PRELOAD_KEYS]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]|= CF_PREOPEN_TMP_TABLES; + + /* + DDL statements that should start with closing opened handlers. + + We use this flag only for statements for which open HANDLERs + have to be closed before emporary tables are pre-opened. + */ + sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_TRUNCATE]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_REPAIR]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_OPTIMIZE]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_ANALYZE]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_CHECK]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_DROP_INDEX]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_PRELOAD_KEYS]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]|= CF_HA_CLOSE; } bool sqlcom_can_generate_row_events(const THD *thd) @@ -1203,6 +1256,9 @@ bool dispatch_command(enum enum_server_c thd->set_query(fields, query_length); general_log_print(thd, command, "%s %s", table_list.table_name, fields); + if (open_temporary_tables(thd, &table_list)) + break; + if (check_table_access(thd, SELECT_ACL, &table_list, TRUE, UINT_MAX, FALSE)) break; @@ -2024,6 +2080,31 @@ mysql_execute_command(THD *thd) DEBUG_SYNC(thd,"before_execute_sql_command"); #endif + /* + Close tables open by HANDLERs before executing DDL statement + which is going to affect those tables. + + This should happen before temporary tables are pre-opened as + otherwise we will get errors about attempt to re-open tables + if table to be changed is open through HANDLER. + + Note that even although this is done before any privilege + checks there is no security problem here as closing open + HANDLER doesn't require any privileges anyway. + */ + if (sql_command_flags[lex->sql_command] & CF_HA_CLOSE) + mysql_ha_rm_tables(thd, all_tables); + + /* + Pre-open temporary tables to simplify privilege checking + for statements which need this. + */ + if (sql_command_flags[lex->sql_command] & CF_PREOPEN_TMP_TABLES) + { + if (open_temporary_tables(thd, all_tables)) + goto error; + } + switch (lex->sql_command) { case SQLCOM_SHOW_EVENTS: @@ -2325,6 +2406,19 @@ case SQLCOM_PREPARE: goto end_with_restore_list; } + /* + Pre-open temporary tables from UNION clause to simplify privilege + checking for them. + */ + if (lex->create_info.merge_list.elements) + { + if (open_temporary_tables(thd, lex->create_info.merge_list.first)) + { + res= 1; + goto end_with_restore_list; + } + } + if ((res= create_table_precheck(thd, select_tables, create_table))) goto end_with_restore_list; @@ -2365,9 +2459,6 @@ case SQLCOM_PREPARE: } #endif - /* Close any open handlers for the table. */ - mysql_ha_rm_tables(thd, create_table); - if (select_lex->item_list.elements) // With select { select_result *result; @@ -2669,6 +2760,13 @@ end_with_restore_list: else { /* + Temporary tables should be opened for SHOW CREATE TABLE, but not + for SHOW CREATE VIEW. + */ + if (open_temporary_tables(thd, all_tables)) + goto error; + + /* The fact that check_some_access() returned FALSE does not mean that access is granted. We need to check if first_table->grant.privilege contains any table-specific privilege. @@ -3146,6 +3244,20 @@ end_with_restore_list: thd->mdl_context.release_transactional_locks(); if (res) goto error; + + /* + Here we have to pre-open temporary tables for LOCK TABLES. + + CF_PREOPEN_TMP_TABLES is not set for this SQL statement simply + because LOCK TABLES calls close_thread_tables() as a first thing + (it's called from unlock_locked_tables() above). So even if + CF_PREOPEN_TMP_TABLES was set and the tables would be pre-opened + in a usual way, they would have been closed. + */ + + if (open_temporary_tables(thd, all_tables)) + goto error; + if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; @@ -4880,6 +4992,12 @@ static bool check_show_access(THD *thd, DBUG_ASSERT(dst_table); + /* + TODO/FIXME: In the upcoming patch we somehow should handle + situation when table in question is a temporary + table. + */ + if (check_access(thd, SELECT_ACL, dst_table->db, &dst_table->grant.privilege, &dst_table->grant.m_internal, @@ -4978,10 +5096,10 @@ check_table_access(THD *thd, ulong requi DBUG_PRINT("info", ("derived: %d view: %d", tables->derived != 0, tables->view != 0)); - if (tables->is_anonymous_derived_table() || - (tables->table && tables->table->s && - (int)tables->table->s->tmp_table)) + + if (tables->is_anonymous_derived_table()) continue; + thd->security_ctx= sctx; if (check_access(thd, want_access, tables->get_db_name(), === modified file 'sql/sql_partition_admin.cc' --- a/sql/sql_partition_admin.cc 2010-12-03 10:05:56 +0000 +++ b/sql/sql_partition_admin.cc 2010-12-23 16:18:19 +0000 @@ -20,7 +20,6 @@ #include "sql_cmd.h" // Sql_cmd #include "sql_alter.h" // Sql_cmd_alter_table #include "sql_partition.h" // struct partition_info, etc. -#include "sql_handler.h" // mysql_ha_rm_tables #include "sql_base.h" // open_and_lock_tables, etc #include "debug_sync.h" // DEBUG_SYNC #include "sql_truncate.h" // mysql_truncate_table, @@ -492,9 +491,6 @@ bool Sql_cmd_alter_table_exchange_partit partition_name= alter_info->partition_names.head(); - /* Clear open tables from the threads table handler cache */ - mysql_ha_rm_tables(thd, table_list); - /* Don't allow to exchange with log table */ swap_table_list= table_list->next_local; if (check_if_log_table(swap_table_list->db_length, swap_table_list->db, === modified file 'sql/sql_prepare.cc' --- a/sql/sql_prepare.cc 2010-12-17 11:28:59 +0000 +++ b/sql/sql_prepare.cc 2010-12-23 16:18:19 +0000 @@ -105,6 +105,7 @@ When one supplies long data for a placeh #include "sp_head.h" #include "sp.h" #include "sp_cache.h" +#include "sql_handler.h" // mysql_ha_rm_tables #include "probes_mysql.h" #ifdef EMBEDDED_LIBRARY /* include MYSQL_BIND headers */ @@ -1715,6 +1716,14 @@ static bool mysql_test_create_table(Prep TABLE_LIST *create_table= lex->query_tables; TABLE_LIST *tables= lex->create_last_non_select_table->next_global; + if (lex->create_info.merge_list.elements) + { + if (open_temporary_tables(thd, lex->create_info.merge_list.first)) + { + DBUG_RETURN(TRUE); + } + } + if (create_table_precheck(thd, tables, create_table)) DBUG_RETURN(TRUE); @@ -1964,6 +1973,19 @@ static bool check_prepared_statement(Pre if (tables) thd->warning_info->opt_clear_warning_info(thd->query_id); + if (sql_command_flags[sql_command] & CF_HA_CLOSE) + mysql_ha_rm_tables(thd, tables); + + /* + Open temporary tables that are known now. Temporary tables added by + prelocking will be opened afterwards (during open_tables()). + */ + if (sql_command_flags[sql_command] & CF_PREOPEN_TMP_TABLES) + { + if (open_temporary_tables(thd, tables)) + goto error; + } + switch (sql_command) { case SQLCOM_REPLACE: case SQLCOM_INSERT: === modified file 'sql/sql_rename.cc' --- a/sql/sql_rename.cc 2010-11-18 16:34:56 +0000 +++ b/sql/sql_rename.cc 2010-12-23 16:18:19 +0000 @@ -139,8 +139,7 @@ bool mysql_rename_tables(THD *thd, TABLE } } - if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout, - MYSQL_OPEN_SKIP_TEMPORARY)) + if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout, 0)) goto err; for (ren_table= table_list; ren_table; ren_table= ren_table->next_local) === modified file 'sql/sql_show.cc' --- a/sql/sql_show.cc 2010-12-21 15:27:40 +0000 +++ b/sql/sql_show.cc 2010-12-23 16:18:19 +0000 @@ -3028,11 +3028,18 @@ fill_schema_show_cols_or_idxs(THD *thd, SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()' */ lex->sql_command= SQLCOM_SHOW_FIELDS; - res= open_normal_and_derived_tables(thd, show_table_list, - (MYSQL_OPEN_IGNORE_FLUSH | - MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL | - (can_deadlock ? - MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0))); + + res= open_temporary_tables(thd, show_table_list); + + if (!res) + { + res= open_normal_and_derived_tables(thd, show_table_list, + (MYSQL_OPEN_IGNORE_FLUSH | + MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL | + (can_deadlock ? + MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0))); + } + lex->sql_command= save_sql_command; DEBUG_SYNC(thd, "after_open_table_ignore_flush"); === modified file 'sql/sql_table.cc' --- a/sql/sql_table.cc 2010-12-17 18:43:38 +0000 +++ b/sql/sql_table.cc 2010-12-23 16:18:19 +0000 @@ -2058,8 +2058,8 @@ bool mysql_rm_table(THD *thd,TABLE_LIST { if (!thd->locked_tables_mode) { - if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout, - MYSQL_OPEN_SKIP_TEMPORARY)) + if (lock_table_names(thd, tables, NULL, + thd->variables.lock_wait_timeout, 0)) DBUG_RETURN(true); for (table= tables; table; table= table->next_local) tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name, @@ -4810,6 +4810,7 @@ bool mysql_create_like_table(THD* thd, T String query(buf, sizeof(buf), system_charset_info); query.length(0); // Have to zero it since constructor doesn't Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN); + bool new_table= FALSE; // Whether newly created table is open. /* The condition avoids a crash as described in BUG#48506. Other @@ -4818,14 +4819,20 @@ bool mysql_create_like_table(THD* thd, T */ if (!table->view) { - /* - Here we open the destination table, on which we already have - exclusive metadata lock. This is needed for store_create_info() - to work. The table will be closed by close_thread_table() at - the end of this branch. - */ - if (open_table(thd, table, thd->mem_root, &ot_ctx)) - goto err; + if (!table->table) + { + /* + In order for store_create_info() to work we need to open + destination table if it is not already open (i.e. if it + has not existed before). We don't need acquire metadata + lock in order to do this as we already hold exclusive + lock on this table. The table will be closed by + close_thread_table() at the end of this branch. + */ + if (open_table(thd, table, thd->mem_root, &ot_ctx)) + goto err; + new_table= TRUE; + } int result __attribute__((unused))= store_create_info(thd, table, &query, @@ -4835,13 +4842,16 @@ bool mysql_create_like_table(THD* thd, T if (write_bin_log(thd, TRUE, query.ptr(), query.length())) goto err; - DBUG_ASSERT(thd->open_tables == table->table); - /* - When opening the table, we ignored the locked tables - (MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table without - risking to close some locked table. - */ - close_thread_table(thd, &thd->open_tables); + if (new_table) + { + DBUG_ASSERT(thd->open_tables == table->table); + /* + When opening the table, we ignored the locked tables + (MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table + without risking to close some locked table. + */ + close_thread_table(thd, &thd->open_tables); + } } } else // Case 1 @@ -4864,9 +4874,9 @@ err: static int mysql_discard_or_import_tablespace(THD *thd, TABLE_LIST *table_list, - enum tablespace_op_type tablespace_op) + Alter_info *alter_info) { - TABLE *table; + Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info); my_bool discard; int error; DBUG_ENTER("mysql_discard_or_import_tablespace"); @@ -4878,21 +4888,30 @@ mysql_discard_or_import_tablespace(THD * thd_proc_info(thd, "discard_or_import_tablespace"); - discard= test(tablespace_op == DISCARD_TABLESPACE); + discard= test(alter_info->tablespace_op == DISCARD_TABLESPACE); /* We set this flag so that ha_innobase::open and ::external_lock() do not complain when we lock the table */ thd->tablespace_op= TRUE; + /* + Adjust values of table-level and metadata which was set in parser + for the case general ALTER TABLE. + */ table_list->mdl_request.set_type(MDL_SHARED_WRITE); - if (!(table=open_ltable(thd, table_list, TL_WRITE, 0))) + table_list->lock_type= TL_WRITE; + /* Do not open views. */ + table_list->required_type= FRMTYPE_TABLE; + + if (open_and_lock_tables(thd, table_list, FALSE, 0, + &alter_prelocking_strategy)) { thd->tablespace_op=FALSE; DBUG_RETURN(-1); } - error= table->file->ha_discard_or_import_tablespace(discard); + error= table_list->table->file->ha_discard_or_import_tablespace(discard); thd_proc_info(thd, "end"); @@ -4923,7 +4942,7 @@ err: DBUG_RETURN(0); } - table->file->print_error(error, MYF(0)); + table_list->table->file->print_error(error, MYF(0)); DBUG_RETURN(-1); } @@ -5945,13 +5964,11 @@ bool mysql_alter_table(THD *thd,char *ne build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0); build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0); - mysql_ha_rm_tables(thd, table_list); - /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */ if (alter_info->tablespace_op != NO_TABLESPACE_OP) /* Conditionally writes to binlog. */ DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list, - alter_info->tablespace_op)); + alter_info)); /* Code below can handle only base tables so ensure that we won't open a view. @@ -6563,14 +6580,12 @@ bool mysql_alter_table(THD *thd,char *ne { if (table->s->tmp_table) { - Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH | - MYSQL_LOCK_IGNORE_TIMEOUT)); TABLE_LIST tbl; bzero((void*) &tbl, sizeof(tbl)); tbl.db= new_db; tbl.table_name= tbl.alias= tmp_name; /* Table is in thd->temporary_tables */ - (void) open_table(thd, &tbl, thd->mem_root, &ot_ctx); + (void) open_temporary_table(thd, &tbl); new_table= tbl.table; } else @@ -7286,13 +7301,6 @@ bool mysql_recreate_table(THD *thd, TABL DBUG_ENTER("mysql_recreate_table"); DBUG_ASSERT(!table_list->next_global); - /* - table_list->table has been closed and freed. Do not reference - uninitialized data. open_tables() could fail. - */ - table_list->table= NULL; - /* Same applies to MDL ticket. */ - table_list->mdl_request.ticket= NULL; /* Set lock type which is appropriate for ALTER TABLE. */ table_list->lock_type= TL_READ_NO_INSERT; /* Same applies to MDL request. */ @@ -7327,16 +7335,40 @@ bool mysql_checksum_table(THD *thd, TABL Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); + /* + Close all temporary tables which were pre-open to simplify + privilege checking. Clear all references to closed tables. + */ + close_thread_tables(thd); + for (table= tables; table; table= table->next_local) + table->table= NULL; + /* Open one table after the other to keep lock time as short as possible. */ for (table= tables; table; table= table->next_local) { char table_name[NAME_LEN*2+2]; TABLE *t; + TABLE_LIST *save_next_global; strxmov(table_name, table->db ,".", table->table_name, NullS); - t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0); - thd->clear_error(); // these errors shouldn't get client + /* Remember old 'next' pointer and break the list. */ + save_next_global= table->next_global; + table->next_global= NULL; + table->lock_type= TL_READ; + /* Allow to open real tables only. */ + table->required_type= FRMTYPE_TABLE; + + if (open_temporary_tables(thd, table) || + open_and_lock_tables(thd, table, FALSE, 0)) + { + t= NULL; + thd->clear_error(); // these errors shouldn't get client + } + else + t= table->table; + + table->next_global= save_next_global; protocol->prepare_for_resend(); protocol->store(table_name, system_charset_info); @@ -7434,11 +7466,6 @@ bool mysql_checksum_table(THD *thd, TABL if (! thd->in_sub_stmt) trans_rollback_stmt(thd); close_thread_tables(thd); - /* - Don't release metadata locks, this will be done at - statement end. - */ - table->table=0; // For query cache } if (protocol->write()) goto err; === modified file 'sql/sql_truncate.cc' --- a/sql/sql_truncate.cc 2010-11-18 13:50:08 +0000 +++ b/sql/sql_truncate.cc 2010-12-23 16:18:19 +0000 @@ -18,9 +18,8 @@ #include "sql_class.h" // THD #include "sql_base.h" // open_and_lock_tables #include "sql_table.h" // write_bin_log -#include "sql_handler.h" // mysql_ha_rm_tables #include "datadict.h" // dd_recreate_table() -#include "lock.h" // MYSQL_OPEN_TEMPORARY_ONLY +#include "lock.h" // MYSQL_OPEN_* flags #include "sql_acl.h" // DROP_ACL #include "sql_parse.h" // check_one_table_access() #include "sql_truncate.h" @@ -194,9 +193,7 @@ int Sql_cmd_truncate_table::handler_trun */ /* If it is a temporary table, no need to take locks. */ - if (is_tmp_table) - flags= MYSQL_OPEN_TEMPORARY_ONLY; - else + if (!is_tmp_table) { /* We don't need to load triggers. */ DBUG_ASSERT(table_ref->trg_event_map == 0); @@ -211,7 +208,7 @@ int Sql_cmd_truncate_table::handler_trun the MDL lock taken above and otherwise there is no way to wait for FLUSH TABLES in deadlock-free fashion. */ - flags= MYSQL_OPEN_IGNORE_FLUSH | MYSQL_OPEN_SKIP_TEMPORARY; + flags= MYSQL_OPEN_IGNORE_FLUSH; /* Even though we have an MDL lock on the table here, we don't pass MYSQL_OPEN_HAS_MDL_LOCK to open_and_lock_tables @@ -340,8 +337,7 @@ bool Sql_cmd_truncate_table::lock_table( /* Acquire an exclusive lock. */ DBUG_ASSERT(table_ref->next_global == NULL); if (lock_table_names(thd, table_ref, NULL, - thd->variables.lock_wait_timeout, - MYSQL_OPEN_SKIP_TEMPORARY)) + thd->variables.lock_wait_timeout, 0)) DBUG_RETURN(TRUE); if (dd_check_storage_engine_flag(thd, table_ref->db, table_ref->table_name, @@ -393,26 +389,27 @@ bool Sql_cmd_truncate_table::lock_table( bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) { int error; - TABLE *table; bool binlog_stmt; DBUG_ENTER("Sql_cmd_truncate_table::truncate_table"); + DBUG_ASSERT((!table_ref->table) || + (table_ref->table && table_ref->table->s)); + /* Initialize, or reinitialize in case of reexecution (SP). */ m_ticket_downgrade= NULL; - /* Remove table from the HANDLER's hash. */ - mysql_ha_rm_tables(thd, table_ref); - /* If it is a temporary table, no need to take locks. */ - if ((table= find_temporary_table(thd, table_ref))) + if (is_temporary_table(table_ref)) { + TABLE *tmp_table= table_ref->table; + /* In RBR, the statement is not binlogged if the table is temporary. */ binlog_stmt= !thd->is_current_stmt_binlog_format_row(); /* Note that a temporary table cannot be partitioned. */ - if (ha_check_storage_engine_flag(table->s->db_type(), HTON_CAN_RECREATE)) + if (ha_check_storage_engine_flag(tmp_table->s->db_type(), HTON_CAN_RECREATE)) { - if ((error= recreate_temporary_table(thd, table))) + if ((error= recreate_temporary_table(thd, tmp_table))) binlog_stmt= FALSE; /* No need to binlog failed truncate-by-recreate. */ DBUG_ASSERT(! thd->transaction.stmt.modified_non_trans_table); === modified file 'sql/sql_view.cc' --- a/sql/sql_view.cc 2010-12-14 11:15:13 +0000 +++ b/sql/sql_view.cc 2010-12-23 16:18:19 +0000 @@ -1639,8 +1639,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIS DBUG_RETURN(TRUE); } - if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout, - MYSQL_OPEN_SKIP_TEMPORARY)) + if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout, 0)) DBUG_RETURN(TRUE); for (view= views; view; view= view->next_local) === modified file 'storage/myisammrg/ha_myisammrg.cc' --- a/storage/myisammrg/ha_myisammrg.cc 2010-10-20 19:02:59 +0000 +++ b/storage/myisammrg/ha_myisammrg.cc 2010-12-23 16:18:19 +0000 @@ -479,6 +479,11 @@ int ha_myisammrg::add_children_list(void child_l->set_table_ref_id(mrg_child_def->get_child_table_ref_type(), mrg_child_def->get_child_def_version()); /* + Copy parent's prelocking attribute to allow opening of child + temporary residing in the prelocking list. + */ + child_l->prelocking_placeholder= parent_l->prelocking_placeholder; + /* For statements which acquire a SNW metadata lock on a parent table and then later try to upgrade it to an X lock (e.g. ALTER TABLE), SNW locks should be also taken on the children tables. --===============1032043632838004218== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/alexander.nozdrin@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: alexander.nozdrin@stripped\ # xwgz2rnc77g8b441 # target_branch: file:///home/alik/MySQL/bzr/00/bug27480/mysql-trunk-\ # bugfixing/ # testament_sha1: 2629ec5a13bdfde8d9a04b6fa80aa1c7f33c7e77 # timestamp: 2010-12-23 19:18:26 +0300 # source_branch: file:///home/alik/MySQL/bzr/00/bug27480/mysql-trunk-\ # bugfixing-bug27480/ # base_revision_id: tor.didriksen@stripped\ # xjnb5ao6u7ws6azd # # Begin bundle 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 iHJ3MOWF//F3JFOFCQkj6hcw --===============1032043632838004218==--