3365 Alexander Nozdrin 2011-05-04 [merge]
Auto-merge patches for Bug#27480 from mysql-trunk-bug27480.
added:
mysql-test/collections/mysql-trunk-bug27480.push
modified:
mysql-test/include/handler.inc
mysql-test/r/alter_table.result
mysql-test/r/flush_read_lock.result
mysql-test/r/grant2.result
mysql-test/r/grant4.result
mysql-test/r/handler_innodb.result
mysql-test/r/handler_myisam.result
mysql-test/r/merge.result
mysql-test/r/ps_ddl.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/flush_read_lock.test
mysql-test/t/grant2.test
mysql-test/t/grant4.test
mysql-test/t/handler_myisam.test
mysql-test/t/merge.test
mysql-test/t/ps_ddl.test
mysql-test/t/temp_table.test
sql/log_event.cc
sql/sp_head.cc
sql/sql_acl.cc
sql/sql_acl.h
sql/sql_admin.cc
sql/sql_alter.cc
sql/sql_base.cc
sql/sql_base.h
sql/sql_class.h
sql/sql_db.cc
sql/sql_handler.cc
sql/sql_handler.h
sql/sql_insert.cc
sql/sql_lex.h
sql/sql_parse.cc
sql/sql_parse.h
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
sql/sql_yacc.yy
sql/table.cc
sql/table.h
storage/myisammrg/ha_myisammrg.cc
3364 Alexander Nozdrin 2011-05-04 [merge]
Manual merge from mysql-5.5.
modified:
mysql-test/r/events_1.result
mysql-test/r/events_restart.result
mysql-test/t/events_1.test
mysql-test/t/events_restart.test
sql/event_db_repository.cc
=== added file 'mysql-test/collections/mysql-trunk-bug27480.push'
--- a/mysql-test/collections/mysql-trunk-bug27480.push 1970-01-01 00:00:00 +0000
+++ b/mysql-test/collections/mysql-trunk-bug27480.push 2011-03-24 08:45:04 +0000
@@ -0,0 +1,4 @@
+perl mysql-test-run.pl --timer --force --parallel=auto --comment=n_mix --vardir=var-n_mix --mysqld=--binlog-format=mixed --experimental=collections/default.experimental --skip-ndb --skip-test-list=collections/disabled-per-push.list
+perl mysql-test-run.pl --timer --force --parallel=auto --comment=main_ps_row --vardir=var-main-ps_row --suite=main --ps-protocol --mysqld=--binlog-format=row --experimental=collections/default.experimental --skip-ndb --skip-test-list=collections/disabled-per-push.list
+perl mysql-test-run.pl --timer --force --parallel=auto --comment=main_embedded --vardir=var-main_emebbed --suite=main --embedded --experimental=collections/default.experimental --skip-ndb
+perl mysql-test-run.pl --timer --force --parallel=auto --comment=funcs_1 --vardir=var-funcs_1 --suite=funcs_1 --experimental=collections/default.experimental --skip-ndb
=== modified file 'mysql-test/include/handler.inc'
--- a/mysql-test/include/handler.inc 2010-11-18 16:34:56 +0000
+++ b/mysql-test/include/handler.inc 2011-04-05 12:37:41 +0000
@@ -804,6 +804,7 @@ create table t1 (a int, key a (a));
insert into t1 (a) values (1), (2), (3), (4), (5);
create table t2 (a int, key a (a)) select * from t1;
create temporary table t3 (a int, key a (a)) select * from t2;
+create temporary table t4 like t3;
handler t1 open;
handler t2 open;
handler t3 open;
@@ -820,6 +821,8 @@ handler t1 read next;
handler t2 close;
--error ER_LOCK_OR_ACTIVE_TRANSACTION
handler t3 open;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
+handler t4 open;
--echo # After UNLOCK TABLES handlers should be around and
--echo # we should be able to continue reading through them.
unlock tables;
@@ -829,7 +832,7 @@ handler t2 read next;
handler t2 close;
handler t3 read next;
handler t3 close;
-drop temporary table t3;
+drop temporary tables t3, t4;
--echo #
--echo # Other operations that implicitly close handler:
--echo #
=== modified file 'mysql-test/r/alter_table.result'
--- a/mysql-test/r/alter_table.result 2011-04-13 07:13:13 +0000
+++ b/mysql-test/r/alter_table.result 2011-05-04 07:51:15 +0000
@@ -1391,6 +1391,16 @@ CREATE DATABASE db1 CHARACTER SET utf8;
CREATE TABLE db1.t1 (bar TINYTEXT, KEY (bar(100)));
ALTER TABLE db1.t1 ADD baz INT;
DROP DATABASE db1;
+# 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;
#
# Bug#11938039 RE-EXECUTION OF FRM-ONLY ALTER TABLE WITH RENAME
# CLAUSE FAILS OR ABORTS SERVER.
=== modified file 'mysql-test/r/flush_read_lock.result'
--- a/mysql-test/r/flush_read_lock.result 2011-02-23 11:54:58 +0000
+++ b/mysql-test/r/flush_read_lock.result 2011-05-04 07:51:15 +0000
@@ -544,11 +544,10 @@ Success: Was not able to run 'drop table
Success: 'drop table t2_base' is blocked by FTWRL active in another connection.
Success: FTWRL is blocked when 'drop table t2_base' is active in another connection.
# 13.1.b) DROP TABLES which affects only temporary tables
-# in theory can be compatible with FTWRL.
-# In practice it is not yet.
-Success: Was not able to run 'drop table t2_temp' under FTWRL.
-Success: 'drop table t2_temp' is blocked by FTWRL active in another connection.
-Success: FTWRL is blocked when 'drop table t2_temp' is active in another connection.
+# is compatible with FTWRL.
+Success: Was able to run 'drop table t2_temp' under FTWRL.
+Success: Was able to run 'drop table t2_temp' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'drop table t2_temp' was active in another connection.
#
# 13.1.c) DROP TEMPORARY TABLES should be compatible with FTWRL.
Success: Was able to run 'drop temporary table t2_temp' under FTWRL.
=== modified file 'mysql-test/r/grant2.result'
--- a/mysql-test/r/grant2.result 2011-03-18 14:16:17 +0000
+++ b/mysql-test/r/grant2.result 2011-05-04 07:51:15 +0000
@@ -470,3 +470,330 @@ root localhost Y
FLUSH PRIVILEGES;
USE test;
End of 5.1 tests
+
+# --
+# -- Bug#11746602: 27480 - Extend CREATE TEMPORARY TABLES privilege to
+# -- allow temp table operations
+# --
+
+############################################################################
+# Setup environment.
+###########################################################################
+DROP DATABASE IF EXISTS mysqltest_db1;
+CREATE DATABASE mysqltest_db1;
+# mysqltest_u1@localhost has CREATE_TMP_ACL, FILE_ACL and EXECUTE_ACL only
+# (EXECUTE_ACL is needed to call p0, and FILE_ACL is needed for SELECT
+# OUTFILE/LOAD DATA INFILE).
+GRANT FILE ON *.* TO mysqltest_u1@localhost;
+GRANT CREATE TEMPORARY TABLES, EXECUTE ON mysqltest_db1.* TO mysqltest_u1@localhost;
+# mysqltest_u2@localhost has all privileges but CREATE_TMP_ACL.
+GRANT ALL PRIVILEGES ON mysqltest_db1.* TO mysqltest_u2@localhost;
+REVOKE CREATE TEMPORARY TABLES ON mysqltest_db1.* FROM mysqltest_u2@localhost;
+# mysqltest_u3@localhost has CREATE_TMP_ACL & EXECUTE_ACL.
+# This user is required to check SUID-stored-routines.
+GRANT CREATE TEMPORARY TABLES ON mysqltest_db1.* TO mysqltest_u3@localhost;
+GRANT EXECUTE ON mysqltest_db1.* TO mysqltest_u3@localhost;
+# mysqltest_u4@localhost has only EXECUTE_ACL.
+# We need this user to check that once created temporary tables
+# are accessible by anyone.
+GRANT EXECUTE ON mysqltest_db1.* TO mysqltest_u4@localhost;
+# Create stored routine to test how privilege checking is done for its
+# arguments.
+CREATE PROCEDURE mysqltest_db1.p0(i INT) SELECT i;
+# Create SUID-stored-routines.
+CREATE DEFINER = mysqltest_u3@localhost PROCEDURE mysqltest_db1.p1()
+CREATE TEMPORARY TABLE t4(x INT);
+CREATE DEFINER = mysqltest_u3@localhost PROCEDURE mysqltest_db1.p2()
+INSERT INTO t4 VALUES (1), (2), (3);
+CREATE DEFINER = mysqltest_u3@localhost PROCEDURE mysqltest_db1.p3()
+SELECT * FROM t4 ORDER BY x;
+# We need separate key cache to test CACHE INDEX and LOAD INDEX.
+SET GLOBAL keycache1.key_buffer_size = 128 * 1024;
+###########################################################################
+# Check that CREATE_TMP_ACL is enough to issue any supported SQL-statement
+# against temporary tables (loosely follow order in sql_command enum).
+###########################################################################
+
+# -- connect con1, mysqltest_u1@localhost, mysqltest_db1
+#
+# Variants of CREATE TABLE.
+#
+CREATE TEMPORARY TABLE t1(a INT);
+CREATE TEMPORARY TABLE t2 LIKE t1;
+CREATE TEMPORARY TABLE t3(a INT, b INT, PRIMARY KEY (a));
+CREATE TEMPORARY TABLE t4 SELECT * FROM t1;
+# Also check that we allow creation of MERGE table with underlying
+# temporary table without additional privileges.
+CREATE TEMPORARY TABLE t5(a INT) ENGINE = MyISAM;
+CREATE TEMPORARY TABLE t6(a INT) ENGINE = MERGE UNION = (t5);
+#
+# SELECT.
+#
+INSERT INTO t1 VALUES (1), (2), (3);
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+#
+# CREATE/DROP INDEX.
+#
+CREATE INDEX idx1 ON t3(b);
+DROP INDEX idx1 ON t3;
+#
+# ALTER TABLE.
+#
+ALTER TABLE t4 ADD COLUMN b INT;
+# Check that we allow altering of MERGE table with underlying
+# temporary table without additional privileges.
+ALTER TABLE t6 UNION = ();
+ALTER TABLE t6 UNION = (t5);
+#
+# Simple INSERT and INSERT ... SELECT.
+#
+INSERT INTO t1 VALUES (4);
+INSERT INTO t2 SELECT a FROM t1;
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+4
+SELECT * FROM t2 ORDER BY a;
+a
+1
+2
+3
+4
+#
+# UPDATE and multi-UPDATE.
+#
+UPDATE t1 SET a = a * 10;
+UPDATE t1 SET a = 100 WHERE a = 10;
+UPDATE t1, t2 SET t1.a = 200 WHERE t1.a = t2.a * 10 AND t1.a = 20;
+SELECT * FROM t1 ORDER BY a;
+a
+30
+40
+100
+200
+#
+# DELETE and multi-DELETE.
+#
+DELETE FROM t1 WHERE a = 100;
+DELETE t1 FROM t1, t2 WHERE t1.a = t2.a * 100 AND t1.a = 200;
+SELECT * FROM t1 ORDER BY a;
+a
+30
+40
+#
+# TRUNCATE TABLE.
+#
+TRUNCATE TABLE t1;
+SELECT * FROM t1 ORDER BY a;
+a
+#
+# SHOW COLUMNS/DESCRIBE and SHOW KEYS.
+#
+SHOW COLUMNS FROM t1;
+Field Type Null Key Default Extra
+SHOW KEYS FROM t3;
+Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
+t3 0 PRIMARY 1 a A 0 NULL NULL BTREE
+#
+# SHOW CREATE TABLE.
+#
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TEMPORARY TABLE `t1` (
+ `a` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+#
+# LOAD DATA INFILE (also SELECT INTO OUTFILE).
+#
+INSERT INTO t1 VALUES (1), (2), (3);
+SELECT a INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/bug27480.txt' FROM t1 ;
+LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/bug27480.txt' INTO TABLE t1;
+SELECT * FROM t1 ORDER BY a;
+a
+1
+1
+2
+2
+3
+3
+#
+# SET.
+#
+SET @a := (SELECT COUNT(*) FROM t1);
+SELECT @a;
+@a
+6
+#
+# LOCK TABLES.
+#
+LOCK TABLES t1 READ;
+UNLOCK TABLES;
+#
+# CHECK/REPAIR/ANALYZE/OPTIMIZE and CHECKSUM.
+#
+ANALYZE TABLE t1;
+Table Op Msg_type Msg_text
+mysqltest_db1.t1 analyze status OK
+CHECK TABLE t1;
+Table Op Msg_type Msg_text
+mysqltest_db1.t1 check status OK
+OPTIMIZE TABLE t1;
+Table Op Msg_type Msg_text
+mysqltest_db1.t1 optimize status Table is already up to date
+REPAIR TABLE t1;
+Table Op Msg_type Msg_text
+mysqltest_db1.t1 repair status OK
+#
+# REPLACE and REPLACE ... SELECT.
+#
+INSERT INTO t3 VALUES (1, 111), (2, 222), (3, 333);
+REPLACE INTO t3 VALUES (1, 1111), (4, 444), (0, 001);
+REPLACE INTO t2 SELECT b FROM t3;
+SELECT * FROM t2 ORDER BY a;
+a
+1
+1
+2
+3
+4
+222
+333
+444
+1111
+SELECT * FROM t3 ORDER BY a;
+a b
+0 1
+1 1111
+2 222
+3 333
+4 444
+#
+# CACHE and LOAD INDEX.
+#
+CACHE INDEX t3 IN keycache1;
+Table Op Msg_type Msg_text
+mysqltest_db1.t3 assign_to_keycache status OK
+LOAD INDEX INTO CACHE t3;
+Table Op Msg_type Msg_text
+mysqltest_db1.t3 preload_keys status OK
+#
+# RENAME (doesn't work for temporary tables, thus should fail).
+#
+RENAME TABLE t3 TO t3_1;
+ERROR 42000: DROP, ALTER command denied to user 'mysqltest_u1'@'localhost' for table 't3'
+#
+# HANDLER OPEN/READ/CLOSE.
+#
+HANDLER t1 OPEN;
+HANDLER t1 READ NEXT;
+a
+1
+HANDLER t1 CLOSE;
+#
+# DO.
+#
+DO (SELECT COUNT(*) FROM t1);
+#
+# CHECKSUM TABLE.
+#
+DELETE FROM t1;
+CHECKSUM TABLE t1;
+Table Checksum
+mysqltest_db1.t1 0
+#
+# CALL.
+#
+CALL p0((SELECT COUNT(*) FROM t1));
+i
+0
+#
+# PREPARE, EXECUTE and DEALLOCATE.
+#
+PREPARE stmt1 FROM 'SELECT * FROM t1 ORDER BY a';
+PREPARE stmt2 FROM 'SELECT * FROM t2 ORDER BY a';
+EXECUTE stmt1;
+a
+EXECUTE stmt2;
+a
+1
+1
+2
+3
+4
+222
+333
+444
+1111
+DEALLOCATE PREPARE stmt1;
+DEALLOCATE PREPARE stmt2;
+#
+# DROP TABLE and DROP TEMPORARY TABLE.
+#
+DROP TABLE t1;
+CREATE TEMPORARY TABLE t1(a INT);
+DROP TEMPORARY TABLE t1;
+###########################################################################
+# - Check that even having all privileges but CREATE_TMP_ACL is not enough
+# to create temporary tables.
+# - Check that creation/working with temporary tables is possible via
+# SUID-stored-routines.
+# - Check that even outside of SUID context we can access temporary
+# table once it is created.
+###########################################################################
+
+# -- connect con2, mysqltest_u2@localhost, mysqltest_db1
+CREATE TEMPORARY TABLE t2(a INT);
+ERROR 42000: Access denied for user 'mysqltest_u2'@'localhost' to database 'mysqltest_db1'
+CALL p1();
+CALL p2();
+CALL p3();
+x
+1
+2
+3
+# Check that once table is created it can be accessed even
+# outside of such a SUID context.
+INSERT INTO t4 VALUES (4);
+UPDATE t4 SET x = 10 WHERE x = 1;
+DELETE FROM t4 WHERE x < 3;
+SELECT * FROM t4 ORDER BY x;
+x
+3
+4
+10
+DROP TEMPORARY TABLE t4;
+###########################################################################
+# - Check that once table is created it can be accessed from within any
+# context, even by user without any privileges on tables.
+###########################################################################
+
+# -- connect con3, mysqltest_u4@localhost, mysqltest_db1
+CALL p1();
+INSERT INTO t4 VALUES (4);
+UPDATE t4 SET x = 10 WHERE x = 1;
+DELETE FROM t4 WHERE x < 3;
+SELECT * FROM t4 ORDER BY x;
+x
+4
+DROP TEMPORARY TABLE t4;
+###########################################################################
+# That's all. Cleanup.
+###########################################################################
+
+# -- connection: default
+# -- disconnect con1
+# All remaining temporary tables are automatically dropped.
+# -- disconnect con2
+# -- disconnect con3
+SET GLOBAL keycache1.key_buffer_size = 0;
+DROP DATABASE mysqltest_db1;
+DROP USER mysqltest_u1@localhost;
+DROP USER mysqltest_u2@localhost;
+DROP USER mysqltest_u3@localhost;
+DROP USER mysqltest_u4@localhost;
=== 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 2011-01-17 16:27:07 +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_innodb.result'
--- a/mysql-test/r/handler_innodb.result 2010-11-18 16:34:56 +0000
+++ b/mysql-test/r/handler_innodb.result 2011-04-05 12:37:41 +0000
@@ -784,6 +784,7 @@ create table t1 (a int, key a (a));
insert into t1 (a) values (1), (2), (3), (4), (5);
create table t2 (a int, key a (a)) select * from t1;
create temporary table t3 (a int, key a (a)) select * from t2;
+create temporary table t4 like t3;
handler t1 open;
handler t2 open;
handler t3 open;
@@ -800,6 +801,8 @@ handler t2 close;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
handler t3 open;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
+handler t4 open;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
# After UNLOCK TABLES handlers should be around and
# we should be able to continue reading through them.
unlock tables;
@@ -815,7 +818,7 @@ handler t3 read next;
a
1
handler t3 close;
-drop temporary table t3;
+drop temporary tables t3, t4;
#
# Other operations that implicitly close handler:
#
=== 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 2011-04-05 12:37:41 +0000
@@ -782,6 +782,7 @@ create table t1 (a int, key a (a));
insert into t1 (a) values (1), (2), (3), (4), (5);
create table t2 (a int, key a (a)) select * from t1;
create temporary table t3 (a int, key a (a)) select * from t2;
+create temporary table t4 like t3;
handler t1 open;
handler t2 open;
handler t3 open;
@@ -798,6 +799,8 @@ handler t2 close;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
handler t3 open;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
+handler t4 open;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
# After UNLOCK TABLES handlers should be around and
# we should be able to continue reading through them.
unlock tables;
@@ -813,7 +816,7 @@ handler t3 read next;
a
1
handler t3 close;
-drop temporary table t3;
+drop temporary tables t3, t4;
#
# Other operations that implicitly close handler:
#
@@ -1858,4 +1861,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 2011-03-26 10:56:27 +0000
@@ -2716,13 +2716,14 @@ DROP TABLE tm1, t1, t2, t3, t4, t5;
# Bug#47633 - assert in ha_myisammrg::info during OPTIMIZE
#
CREATE TEMPORARY TABLE t1 (c1 INT);
-ALTER TABLE t1 ENGINE=MERGE UNION(t_not_exists,t1);
+CREATE TEMPORARY TABLE t2 (c1 INT);
+ALTER TABLE t1 ENGINE=MERGE UNION(t_not_exists, t2);
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
-DROP TABLE t1;
+DROP TABLE t1, t2;
#
# Bug#36171 - CREATE TEMPORARY TABLE and MERGE engine
# More tests with TEMPORARY MERGE table and permanent children.
@@ -3676,4 +3677,93 @@ 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;
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS m1;
+DROP TRIGGER IF EXISTS trg1;
+DROP TABLE IF EXISTS q1;
+DROP TABLE IF EXISTS q2;
+CREATE TABLE t1(a INT);
+CREATE TABLE m1(a INT) ENGINE = MERGE UNION (q1, q2);
+CREATE TRIGGER trg1 BEFORE DELETE ON t1
+FOR EACH ROW
+INSERT INTO m1 VALUES (1);
+DELETE FROM t1;
+ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
+DROP TRIGGER trg1;
+DROP TABLE t1;
+DROP TABLE m1;
End of 6.0 tests
=== modified file 'mysql-test/r/ps_ddl.result'
--- a/mysql-test/r/ps_ddl.result 2010-08-18 09:35:41 +0000
+++ b/mysql-test/r/ps_ddl.result 2011-04-01 18:08:48 +0000
@@ -2316,3 +2316,43 @@ drop procedure if exists p_verify_reprep
drop procedure if exists p1;
drop function if exists f1;
drop view if exists v1, v2;
+#
+# Additional coverage for refactoring which was made as part of work
+# on bug '27480: Extend CREATE TEMPORARY TABLES privilege to allow
+# temp table operations'.
+#
+# Check that we don't try to pre-open temporary tables for the elements
+# from prelocking list, as this can lead to unwarranted ER_CANT_REOPEN
+# errors.
+DROP TABLE IF EXISTS t1, tm;
+CREATE TABLE t1 (a INT);
+CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
+SET @a:= (SELECT COUNT(*) FROM t1);
+# Prelocking list for the below statement should
+# contain t1 twice - once for the INSERT and once
+# SELECT from the trigger.
+PREPARE stmt1 FROM 'INSERT INTO t1 VALUES (1)';
+EXECUTE stmt1;
+# Create temporary table which will shadow t1.
+CREATE TEMPORARY TABLE t1 (b int);
+# The below execution of statement should not fail with ER_CANT_REOPEN
+# error. Instead stmt1 should be auto-matically reprepared and succeed.
+EXECUTE stmt1;
+DEALLOCATE PREPARE stmt1;
+DROP TEMPORARY TABLE t1;
+DROP TABLE t1;
+#
+# Also check that we properly reset table list elements from UNION
+# clause of CREATE TABLE and ALTER TABLE statements.
+#
+CREATE TEMPORARY TABLE t1 (i INT);
+PREPARE stmt2 FROM 'CREATE TEMPORARY TABLE tm (i INT) ENGINE=MERGE UNION=(t1)';
+EXECUTE stmt2;
+DROP TEMPORARY TABLE tm;
+EXECUTE stmt2;
+DEALLOCATE PREPARE stmt2;
+PREPARE stmt3 FROM 'ALTER TABLE tm UNION=(t1)';
+EXECUTE stmt3;
+EXECUTE stmt3;
+DEALLOCATE PREPARE stmt3;
+DROP TEMPORARY TABLES tm, t1;
=== 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 2011-01-17 16:27:07 +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 2011-04-27 09:10:45 +0000
+++ b/mysql-test/suite/innodb/r/innodb_mysql.result 2011-05-04 07:51:15 +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 2011-01-17 16:27:07 +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 2011-01-17 16:27:07 +0000
@@ -117,4 +117,18 @@ show binlog events from <binlog_start>;
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 2011-01-17 16:27:07 +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 2011-04-13 06:16:40 +0000
+++ b/mysql-test/t/alter_table.test 2011-05-04 07:51:15 +0000
@@ -1161,6 +1161,24 @@ ALTER TABLE db1.t1 ADD baz INT;
DROP DATABASE db1;
+--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;
+
+
--echo #
--echo # Bug#11938039 RE-EXECUTION OF FRM-ONLY ALTER TABLE WITH RENAME
--echo # CLAUSE FAILS OR ABORTS SERVER.
=== modified file 'mysql-test/t/flush_read_lock.test'
--- a/mysql-test/t/flush_read_lock.test 2011-02-23 11:54:58 +0000
+++ b/mysql-test/t/flush_read_lock.test 2011-05-04 07:51:15 +0000
@@ -708,11 +708,10 @@ let $cleanup_stmt1= create table t2_base
--source include/check_ftwrl_incompatible.inc
--echo # 13.1.b) DROP TABLES which affects only temporary tables
---echo # in theory can be compatible with FTWRL.
---echo # In practice it is not yet.
+--echo # is compatible with FTWRL.
let $statement= drop table t2_temp;
-let $cleanup_stmt1= create temporary table t2_temp(j int);
---source include/check_ftwrl_incompatible.inc
+let $cleanup_stmt= create temporary table t2_temp(j int);
+--source include/check_ftwrl_compatible.inc
--echo #
--echo # 13.1.c) DROP TEMPORARY TABLES should be compatible with FTWRL.
=== modified file 'mysql-test/t/grant2.test'
--- a/mysql-test/t/grant2.test 2010-08-09 08:32:50 +0000
+++ b/mysql-test/t/grant2.test 2011-03-26 10:56:27 +0000
@@ -667,5 +667,308 @@ USE test;
--echo End of 5.1 tests
+
+--echo
+--echo # --
+--echo # -- Bug#11746602: 27480 - Extend CREATE TEMPORARY TABLES privilege to
+--echo # -- allow temp table operations
+--echo # --
+--echo
+
+--echo ############################################################################
+--echo # Setup environment.
+--echo ###########################################################################
+
+--disable_warnings
+DROP DATABASE IF EXISTS mysqltest_db1;
+--enable_warnings
+
+CREATE DATABASE mysqltest_db1;
+
+--echo # mysqltest_u1@localhost has CREATE_TMP_ACL, FILE_ACL and EXECUTE_ACL only
+--echo # (EXECUTE_ACL is needed to call p0, and FILE_ACL is needed for SELECT
+--echo # OUTFILE/LOAD DATA INFILE).
+GRANT FILE ON *.* TO mysqltest_u1@localhost;
+GRANT CREATE TEMPORARY TABLES, EXECUTE ON mysqltest_db1.* TO mysqltest_u1@localhost;
+
+--echo # mysqltest_u2@localhost has all privileges but CREATE_TMP_ACL.
+GRANT ALL PRIVILEGES ON mysqltest_db1.* TO mysqltest_u2@localhost;
+REVOKE CREATE TEMPORARY TABLES ON mysqltest_db1.* FROM mysqltest_u2@localhost;
+
+--echo # mysqltest_u3@localhost has CREATE_TMP_ACL & EXECUTE_ACL.
+--echo # This user is required to check SUID-stored-routines.
+GRANT CREATE TEMPORARY TABLES ON mysqltest_db1.* TO mysqltest_u3@localhost;
+GRANT EXECUTE ON mysqltest_db1.* TO mysqltest_u3@localhost;
+
+--echo # mysqltest_u4@localhost has only EXECUTE_ACL.
+--echo # We need this user to check that once created temporary tables
+--echo # are accessible by anyone.
+GRANT EXECUTE ON mysqltest_db1.* TO mysqltest_u4@localhost;
+
+--echo # Create stored routine to test how privilege checking is done for its
+--echo # arguments.
+CREATE PROCEDURE mysqltest_db1.p0(i INT) SELECT i;
+
+--echo # Create SUID-stored-routines.
+CREATE DEFINER = mysqltest_u3@localhost PROCEDURE mysqltest_db1.p1()
+ CREATE TEMPORARY TABLE t4(x INT);
+
+CREATE DEFINER = mysqltest_u3@localhost PROCEDURE mysqltest_db1.p2()
+ INSERT INTO t4 VALUES (1), (2), (3);
+
+CREATE DEFINER = mysqltest_u3@localhost PROCEDURE mysqltest_db1.p3()
+ SELECT * FROM t4 ORDER BY x;
+
+--echo # We need separate key cache to test CACHE INDEX and LOAD INDEX.
+SET GLOBAL keycache1.key_buffer_size = 128 * 1024;
+
+--echo ###########################################################################
+--echo # Check that CREATE_TMP_ACL is enough to issue any supported SQL-statement
+--echo # against temporary tables (loosely follow order in sql_command enum).
+--echo ###########################################################################
+
+--echo
+--echo # -- connect con1, mysqltest_u1@localhost, mysqltest_db1
+--connect (con1,localhost,mysqltest_u1,,mysqltest_db1)
+
+--echo #
+--echo # Variants of CREATE TABLE.
+--echo #
+CREATE TEMPORARY TABLE t1(a INT);
+CREATE TEMPORARY TABLE t2 LIKE t1;
+CREATE TEMPORARY TABLE t3(a INT, b INT, PRIMARY KEY (a));
+CREATE TEMPORARY TABLE t4 SELECT * FROM t1;
+--echo # Also check that we allow creation of MERGE table with underlying
+--echo # temporary table without additional privileges.
+CREATE TEMPORARY TABLE t5(a INT) ENGINE = MyISAM;
+CREATE TEMPORARY TABLE t6(a INT) ENGINE = MERGE UNION = (t5);
+
+--echo #
+--echo # SELECT.
+--echo #
+INSERT INTO t1 VALUES (1), (2), (3);
+SELECT * FROM t1 ORDER BY a;
+
+--echo #
+--echo # CREATE/DROP INDEX.
+--echo #
+CREATE INDEX idx1 ON t3(b);
+DROP INDEX idx1 ON t3;
+
+--echo #
+--echo # ALTER TABLE.
+--echo #
+ALTER TABLE t4 ADD COLUMN b INT;
+--echo # Check that we allow altering of MERGE table with underlying
+--echo # temporary table without additional privileges.
+ALTER TABLE t6 UNION = ();
+ALTER TABLE t6 UNION = (t5);
+
+--echo #
+--echo # Simple INSERT and INSERT ... SELECT.
+--echo #
+INSERT INTO t1 VALUES (4);
+INSERT INTO t2 SELECT a FROM t1;
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+
+--echo #
+--echo # UPDATE and multi-UPDATE.
+--echo #
+UPDATE t1 SET a = a * 10;
+UPDATE t1 SET a = 100 WHERE a = 10;
+UPDATE t1, t2 SET t1.a = 200 WHERE t1.a = t2.a * 10 AND t1.a = 20;
+SELECT * FROM t1 ORDER BY a;
+
+--echo #
+--echo # DELETE and multi-DELETE.
+--echo #
+DELETE FROM t1 WHERE a = 100;
+DELETE t1 FROM t1, t2 WHERE t1.a = t2.a * 100 AND t1.a = 200;
+SELECT * FROM t1 ORDER BY a;
+
+--echo #
+--echo # TRUNCATE TABLE.
+--echo #
+TRUNCATE TABLE t1;
+SELECT * FROM t1 ORDER BY a;
+
+--echo #
+--echo # SHOW COLUMNS/DESCRIBE and SHOW KEYS.
+--echo #
+SHOW COLUMNS FROM t1;
+SHOW KEYS FROM t3;
+
+--echo #
+--echo # SHOW CREATE TABLE.
+--echo #
+SHOW CREATE TABLE t1;
+
+--echo #
+--echo # LOAD DATA INFILE (also SELECT INTO OUTFILE).
+--echo #
+INSERT INTO t1 VALUES (1), (2), (3);
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+--eval SELECT a INTO OUTFILE '$MYSQLTEST_VARDIR/tmp/bug27480.txt' FROM t1
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+--eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/bug27480.txt' INTO TABLE t1
+--remove_file $MYSQLTEST_VARDIR/tmp/bug27480.txt
+SELECT * FROM t1 ORDER BY a;
+
+--echo #
+--echo # SET.
+--echo #
+SET @a := (SELECT COUNT(*) FROM t1);
+SELECT @a;
+
+--echo #
+--echo # LOCK TABLES.
+--echo #
+LOCK TABLES t1 READ;
+UNLOCK TABLES;
+
+--echo #
+--echo # CHECK/REPAIR/ANALYZE/OPTIMIZE and CHECKSUM.
+--echo #
+ANALYZE TABLE t1;
+CHECK TABLE t1;
+OPTIMIZE TABLE t1;
+REPAIR TABLE t1;
+
+--echo #
+--echo # REPLACE and REPLACE ... SELECT.
+--echo #
+INSERT INTO t3 VALUES (1, 111), (2, 222), (3, 333);
+REPLACE INTO t3 VALUES (1, 1111), (4, 444), (0, 001);
+REPLACE INTO t2 SELECT b FROM t3;
+SELECT * FROM t2 ORDER BY a;
+SELECT * FROM t3 ORDER BY a;
+
+--echo #
+--echo # CACHE and LOAD INDEX.
+--echo #
+CACHE INDEX t3 IN keycache1;
+LOAD INDEX INTO CACHE t3;
+
+--echo #
+--echo # RENAME (doesn't work for temporary tables, thus should fail).
+--echo #
+--error ER_TABLEACCESS_DENIED_ERROR
+RENAME TABLE t3 TO t3_1;
+
+--echo #
+--echo # HANDLER OPEN/READ/CLOSE.
+--echo #
+HANDLER t1 OPEN;
+HANDLER t1 READ NEXT;
+HANDLER t1 CLOSE;
+
+--echo #
+--echo # DO.
+--echo #
+DO (SELECT COUNT(*) FROM t1);
+
+--echo #
+--echo # CHECKSUM TABLE.
+--echo #
+DELETE FROM t1;
+CHECKSUM TABLE t1;
+
+--echo #
+--echo # CALL.
+--echo #
+CALL p0((SELECT COUNT(*) FROM t1));
+
+--echo #
+--echo # PREPARE, EXECUTE and DEALLOCATE.
+--echo #
+PREPARE stmt1 FROM 'SELECT * FROM t1 ORDER BY a';
+PREPARE stmt2 FROM 'SELECT * FROM t2 ORDER BY a';
+EXECUTE stmt1;
+EXECUTE stmt2;
+DEALLOCATE PREPARE stmt1;
+DEALLOCATE PREPARE stmt2;
+
+--echo #
+--echo # DROP TABLE and DROP TEMPORARY TABLE.
+--echo #
+DROP TABLE t1;
+
+CREATE TEMPORARY TABLE t1(a INT);
+DROP TEMPORARY TABLE t1;
+
+
+--echo ###########################################################################
+--echo # - Check that even having all privileges but CREATE_TMP_ACL is not enough
+--echo # to create temporary tables.
+--echo # - Check that creation/working with temporary tables is possible via
+--echo # SUID-stored-routines.
+--echo # - Check that even outside of SUID context we can access temporary
+--echo # table once it is created.
+--echo ###########################################################################
+
+--echo
+--echo # -- connect con2, mysqltest_u2@localhost, mysqltest_db1
+--connect (con2,localhost,mysqltest_u2,,mysqltest_db1)
+
+--error ER_DBACCESS_DENIED_ERROR
+CREATE TEMPORARY TABLE t2(a INT);
+
+CALL p1();
+
+CALL p2();
+
+CALL p3();
+
+--echo # Check that once table is created it can be accessed even
+--echo # outside of such a SUID context.
+INSERT INTO t4 VALUES (4);
+UPDATE t4 SET x = 10 WHERE x = 1;
+DELETE FROM t4 WHERE x < 3;
+SELECT * FROM t4 ORDER BY x;
+DROP TEMPORARY TABLE t4;
+
+--echo ###########################################################################
+--echo # - Check that once table is created it can be accessed from within any
+--echo # context, even by user without any privileges on tables.
+--echo ###########################################################################
+
+--echo
+--echo # -- connect con3, mysqltest_u4@localhost, mysqltest_db1
+--connect (con3,localhost,mysqltest_u4,,mysqltest_db1)
+
+CALL p1();
+INSERT INTO t4 VALUES (4);
+UPDATE t4 SET x = 10 WHERE x = 1;
+DELETE FROM t4 WHERE x < 3;
+SELECT * FROM t4 ORDER BY x;
+DROP TEMPORARY TABLE t4;
+
+--echo ###########################################################################
+--echo # That's all. Cleanup.
+--echo ###########################################################################
+
+--echo
+--echo # -- connection: default
+--connection default
+
+--echo # -- disconnect con1
+--echo # All remaining temporary tables are automatically dropped.
+--disconnect con1
+
+--echo # -- disconnect con2
+--disconnect con2
+
+--echo # -- disconnect con3
+--disconnect con3
+
+SET GLOBAL keycache1.key_buffer_size = 0;
+DROP DATABASE mysqltest_db1;
+DROP USER mysqltest_u1@localhost;
+DROP USER mysqltest_u2@localhost;
+DROP USER mysqltest_u3@localhost;
+DROP USER mysqltest_u4@localhost;
+
+
# Wait till we reached the initial number of concurrent sessions
--source include/wait_until_count_sessions.inc
=== 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 2011-01-17 16:27:07 +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 2011-01-17 16:27:07 +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 2011-03-26 10:56:27 +0000
@@ -2194,9 +2194,10 @@ DROP TABLE tm1, t1, t2, t3, t4, t5;
--echo # Bug#47633 - assert in ha_myisammrg::info during OPTIMIZE
--echo #
CREATE TEMPORARY TABLE t1 (c1 INT);
-ALTER TABLE t1 ENGINE=MERGE UNION(t_not_exists,t1);
+CREATE TEMPORARY TABLE t2 (c1 INT);
+ALTER TABLE t1 ENGINE=MERGE UNION(t_not_exists, t2);
OPTIMIZE TABLE t1;
-DROP TABLE t1;
+DROP TABLE t1, t2;
--echo #
--echo # Bug#36171 - CREATE TEMPORARY TABLE and MERGE engine
@@ -2798,6 +2799,84 @@ 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;
+
+# Check effect of Bug#27480-preliminary patch:
+# a merge-table with non-existing children, opened from a prelocked list.
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS m1;
+DROP TRIGGER IF EXISTS trg1;
+DROP TABLE IF EXISTS q1;
+DROP TABLE IF EXISTS q2;
+--enable_warnings
+
+CREATE TABLE t1(a INT);
+CREATE TABLE m1(a INT) ENGINE = MERGE UNION (q1, q2);
+
+CREATE TRIGGER trg1 BEFORE DELETE ON t1
+FOR EACH ROW
+ INSERT INTO m1 VALUES (1);
+
+--error ER_WRONG_MRG_TABLE
+DELETE FROM t1;
+
+DROP TRIGGER trg1;
+DROP TABLE t1;
+DROP TABLE m1;
+
--echo End of 6.0 tests
--disable_result_log
=== modified file 'mysql-test/t/ps_ddl.test'
--- a/mysql-test/t/ps_ddl.test 2010-02-06 10:28:06 +0000
+++ b/mysql-test/t/ps_ddl.test 2011-04-01 18:08:48 +0000
@@ -2098,3 +2098,47 @@ drop procedure if exists p1;
drop function if exists f1;
drop view if exists v1, v2;
--enable_warnings
+
+
+--echo #
+--echo # Additional coverage for refactoring which was made as part of work
+--echo # on bug '27480: Extend CREATE TEMPORARY TABLES privilege to allow
+--echo # temp table operations'.
+--echo #
+--echo # Check that we don't try to pre-open temporary tables for the elements
+--echo # from prelocking list, as this can lead to unwarranted ER_CANT_REOPEN
+--echo # errors.
+--disable_warnings
+DROP TABLE IF EXISTS t1, tm;
+--enable_warnings
+CREATE TABLE t1 (a INT);
+CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
+ SET @a:= (SELECT COUNT(*) FROM t1);
+--echo # Prelocking list for the below statement should
+--echo # contain t1 twice - once for the INSERT and once
+--echo # SELECT from the trigger.
+PREPARE stmt1 FROM 'INSERT INTO t1 VALUES (1)';
+EXECUTE stmt1;
+--echo # Create temporary table which will shadow t1.
+CREATE TEMPORARY TABLE t1 (b int);
+--echo # The below execution of statement should not fail with ER_CANT_REOPEN
+--echo # error. Instead stmt1 should be auto-matically reprepared and succeed.
+EXECUTE stmt1;
+DEALLOCATE PREPARE stmt1;
+DROP TEMPORARY TABLE t1;
+DROP TABLE t1;
+--echo #
+--echo # Also check that we properly reset table list elements from UNION
+--echo # clause of CREATE TABLE and ALTER TABLE statements.
+--echo #
+CREATE TEMPORARY TABLE t1 (i INT);
+PREPARE stmt2 FROM 'CREATE TEMPORARY TABLE tm (i INT) ENGINE=MERGE UNION=(t1)';
+EXECUTE stmt2;
+DROP TEMPORARY TABLE tm;
+EXECUTE stmt2;
+DEALLOCATE PREPARE stmt2;
+PREPARE stmt3 FROM 'ALTER TABLE tm UNION=(t1)';
+EXECUTE stmt3;
+EXECUTE stmt3;
+DEALLOCATE PREPARE stmt3;
+DROP TEMPORARY TABLES tm, t1;
=== 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 2011-01-17 16:27:07 +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 2011-03-25 12:15:56 +0000
+++ b/sql/log_event.cc 2011-05-04 07:51:15 +0000
@@ -5319,7 +5319,8 @@ int Load_log_event::do_apply_event(NET*
update it inside mysql_load().
*/
List<Item> 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 2011-04-15 12:14:35 +0000
+++ b/sql/sp_head.cc 2011-05-04 07:51:15 +0000
@@ -3038,8 +3038,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_acl.cc'
--- a/sql/sql_acl.cc 2011-04-19 03:29:06 +0000
+++ b/sql/sql_acl.cc 2011-05-04 07:51:15 +0000
@@ -4619,6 +4619,20 @@ bool check_grant(THD *thd, ulong want_ac
}
continue;
}
+
+ if (is_temporary_table(tl))
+ {
+ /*
+ If this table list element corresponds to a pre-opened temporary
+ table skip checking of all relevant table-level privileges for it.
+ Note that during creation of temporary table we still need to check
+ if user has CREATE_TMP_ACL.
+ */
+ tl->grant.privilege|= TMP_TABLE_ACLS;
+ tl->grant.want_privilege= 0;
+ continue;
+ }
+
GRANT_TABLE *grant_table= table_hash_search(sctx->host, sctx->ip,
tl->get_db_name(),
sctx->priv_user,
=== modified file 'sql/sql_acl.h'
--- a/sql/sql_acl.h 2010-10-21 09:49:16 +0000
+++ b/sql/sql_acl.h 2011-03-26 10:56:27 +0000
@@ -95,6 +95,14 @@
CREATE_ACL | DROP_ACL | ALTER_ACL | INDEX_ACL | \
TRIGGER_ACL | REFERENCES_ACL | GRANT_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL)
+/**
+ Table-level privileges which are automatically "granted" to everyone on
+ existing temporary tables (CREATE_ACL is necessary for ALTER ... RENAME).
+*/
+#define TMP_TABLE_ACLS \
+(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
+ INDEX_ACL | ALTER_ACL)
+
/*
Defines to change the above bits to how things are stored in tables
This is needed as the 'host' and 'db' table is missing a few privileges
=== modified file 'sql/sql_admin.cc'
--- a/sql/sql_admin.cc 2011-04-15 12:14:35 +0000
+++ b/sql/sql_admin.cc 2011-05-04 07:51:15 +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
@@ -66,7 +65,7 @@ static int prepare_for_repair(THD *thd,
if (!(table= table_list->table))
{
- char key[MAX_DBKEY_LENGTH];
+ const char *key;
uint key_length;
/*
If the table didn't exist, we have a shared metadata lock
@@ -82,17 +81,17 @@ static int prepare_for_repair(THD *thd,
*/
my_hash_value_type hash_value;
- key_length= create_table_def_key(thd, key, table_list, 0);
table_list->mdl_request.init(MDL_key::TABLE,
table_list->db, table_list->table_name,
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;
+ key_length= get_table_def_key(table_list, &key);
+
hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
mysql_mutex_lock(&LOCK_open);
share= get_table_share(thd, table_list, key, key_length, 0,
@@ -292,7 +291,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)
{
@@ -351,7 +356,10 @@ static bool mysql_admin_table(THD* thd,
thd->warning_info= &wi;
- 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->warning_info= wi_saved;
}
@@ -365,7 +373,10 @@ static bool mysql_admin_table(THD* thd,
mode. It does make sense for the user to see such errors.
*/
- 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);
}
table->next_global= save_next_global;
@@ -581,10 +592,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);
@@ -709,6 +736,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(
@@ -721,7 +757,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.
@@ -735,14 +773,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)
@@ -882,8 +921,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);
}
@@ -919,8 +956,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));
}
@@ -946,8 +983,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_alter.cc'
--- a/sql/sql_alter.cc 2010-10-05 09:15:51 +0000
+++ b/sql/sql_alter.cc 2011-04-01 18:08:48 +0000
@@ -16,6 +16,7 @@
#include "sql_parse.h" // check_access
#include "sql_table.h" // mysql_alter_table,
// mysql_exchange_partition
+#include "sql_base.h" // open_temporary_tables
#include "sql_alter.h"
bool Sql_cmd_alter_table::execute(THD *thd)
@@ -64,10 +65,40 @@ bool Sql_cmd_alter_table::execute(THD *t
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 (create_info.merge_list.first)
+ {
+ TABLE_LIST *tl;
+
+ /* Pre-open underlying temporary tables to simplify privilege checking. */
+ if (open_temporary_tables(thd, create_info.merge_list.first))
+ DBUG_RETURN(TRUE);
+
+ if (check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
+ create_info.merge_list.first, FALSE, UINT_MAX, FALSE))
+ DBUG_RETURN(TRUE);
+
+ /*
+ Since one of tables in UNION clause might be already used by table being
+ altered we need to close all tables opened for UNION checking to allow
+ normal open of table being altered.
+ To do this we simply close all tables and then open only tables from the
+ main statement's table list.
+ */
+ close_thread_tables(thd);
+
+ /*
+ To make things safe for re-execution and upcoming open_temporary_tables()
+ we need to reset TABLE_LIST::table pointers in both main table list and
+ and UNION clause.
+ */
+ for (tl= lex->query_tables; tl; tl= tl->next_global)
+ tl->table= NULL;
+ for (tl= lex->create_info.merge_list.first; tl; tl= tl->next_global)
+ tl->table= NULL;
+
+ if (open_temporary_tables(thd, lex->query_tables))
+ DBUG_RETURN(TRUE);
+ }
if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE))
DBUG_RETURN(TRUE); /* purecov: inspected */
=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc 2011-04-19 03:29:06 +0000
+++ b/sql/sql_base.cc 2011-05-04 07:51:15 +0000
@@ -218,17 +218,17 @@ static void check_unused(void)
#endif
-/*
+/**
Create a table cache key
- SYNOPSIS
- create_table_def_key()
- thd Thread handler
- key Create key here (must be of size MAX_DBKEY_LENGTH)
- table_list Table definition
- tmp_table Set if table is a tmp table
+ @param thd Thread context
+ @param key Buffer for the key to be created (must be of
+ size MAX_DBKEY_LENGTH).
+ @param db_name Database name.
+ @param table_name Table name.
+ @param tmp_table Set if table is a tmp table.
- IMPLEMENTATION
+ @note
The table cache_key is created from:
db_name + \0
table_name + \0
@@ -239,16 +239,15 @@ static void check_unused(void)
4 bytes for master thread id
4 bytes pseudo thread id
- RETURN
- Length of key
+ @return Length of key.
*/
-uint create_table_def_key(THD *thd, char *key,
- const TABLE_LIST *table_list,
- bool tmp_table)
+static uint create_table_def_key(THD *thd, char *key,
+ const char *db_name, const char *table_name,
+ bool tmp_table)
{
- uint key_length= (uint) (strmov(strmov(key, table_list->db)+1,
- table_list->table_name)-key)+1;
+ uint key_length= (uint) (strmov(strmov(key, db_name) + 1, table_name) -
+ key) + 1;
if (tmp_table)
{
int4store(key + key_length, thd->server_id);
@@ -259,6 +258,41 @@ uint create_table_def_key(THD *thd, char
}
+/**
+ Get table cache key for a table list element.
+
+ @param table_list[in] Table list element.
+ @param key[out] On return points to table cache key for the table.
+
+ @note Unlike create_table_def_key() call this function doesn't construct
+ key in a buffer provider by caller. Instead it relies on the fact
+ that table list element for which key is requested has properly
+ initialized MDL_request object and the fact that table definition
+ cache key is suffix of key used in MDL subsystem. So to get table
+ definition key it simply needs to return pointer to appropriate
+ part of MDL_key object nested in this table list element.
+ Indeed, this means that lifetime of key produced by this call is
+ limited by the lifetime of table list element which it got as
+ parameter.
+
+ @return Length of key.
+*/
+
+uint get_table_def_key(const TABLE_LIST *table_list, const char **key)
+{
+ /*
+ This call relies on the fact that TABLE_LIST::mdl_request::key object
+ is properly initialized, so table definition cache can be produced
+ from key used by MDL subsystem.
+ */
+ DBUG_ASSERT(!strcmp(table_list->db, table_list->mdl_request.key.db_name()) &&
+ !strcmp(table_list->table_name, table_list->mdl_request.key.name()));
+
+ *key= (const char*)table_list->mdl_request.key.ptr() + 1;
+ return table_list->mdl_request.key.length() - 1;
+}
+
+
/*****************************************************************************
Functions to handle table definition cach (TABLE_SHARE)
@@ -490,8 +524,9 @@ static void table_def_unuse_table(TABLE
# Share for table
*/
-TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
- uint key_length, uint db_flags, int *error,
+TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list,
+ const char *key, uint key_length,
+ uint db_flags, int *error,
my_hash_value_type hash_value)
{
TABLE_SHARE *share;
@@ -606,7 +641,7 @@ found:
static TABLE_SHARE *
get_table_share_with_discover(THD *thd, TABLE_LIST *table_list,
- char *key, uint key_length,
+ const char *key, uint key_length,
uint db_flags, int *error,
my_hash_value_type hash_value)
@@ -751,14 +786,11 @@ void release_table_share(TABLE_SHARE *sh
TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
{
- char key[NAME_LEN*2+2];
- TABLE_LIST table_list;
+ char key[MAX_DBKEY_LENGTH];
uint key_length;
mysql_mutex_assert_owner(&LOCK_open);
- table_list.db= (char*) db;
- table_list.table_name= (char*) table_name;
- key_length= create_table_def_key((THD*) 0, key, &table_list, 0);
+ key_length= create_table_def_key((THD*) 0, key, db, table_name, 0);
return (TABLE_SHARE*) my_hash_search(&table_def_cache,
(uchar*) key, key_length);
}
@@ -1986,12 +2018,9 @@ void update_non_unique_table_error(TABLE
TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name)
{
- TABLE_LIST tl;
-
- tl.db= (char*) db;
- tl.table_name= (char*) table_name;
-
- return find_temporary_table(thd, &tl);
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length= create_table_def_key(thd, key, db, table_name, 1);
+ return find_temporary_table(thd, key, key_length);
}
@@ -2004,10 +2033,26 @@ TABLE *find_temporary_table(THD *thd, co
TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl)
{
- char key[MAX_DBKEY_LENGTH];
- uint key_length= create_table_def_key(thd, key, tl, 1);
+ const char *key;
+ uint key_length;
+ char key_suffix[TMP_TABLE_KEY_EXTRA];
+ TABLE *table;
- return find_temporary_table(thd, key, key_length);
+ key_length= get_table_def_key(tl, &key);
+
+ int4store(key_suffix, thd->server_id);
+ int4store(key_suffix + 4, thd->variables.pseudo_thread_id);
+
+ 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) &&
+ !memcmp(table->s->table_cache_key.str + key_length, key_suffix,
+ TMP_TABLE_KEY_EXTRA))
+ return table;
+ }
+ return NULL;
}
@@ -2055,10 +2100,13 @@ TABLE *find_temporary_table(THD *thd,
thd->temporary_tables list, it's impossible to tell here whether
we're dealing with an internal or a user temporary table.
- If is_trans is not null, we return the type of the table:
+ In is_trans out-parameter, we return the type of the table:
either transactional (e.g. innodb) as TRUE or non-transactional
(e.g. myisam) as FALSE.
+ This function assumes that table to be dropped was pre-opened
+ using table list provided.
+
@retval 0 the table was found and dropped successfully.
@retval 1 the table was not found in the list of temporary tables
of this thread
@@ -2067,14 +2115,15 @@ TABLE *find_temporary_table(THD *thd,
int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans)
{
- TABLE *table;
DBUG_ENTER("drop_temporary_table");
DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
table_list->db, table_list->table_name));
- if (!(table= find_temporary_table(thd, table_list)))
+ if (!is_temporary_table(table_list))
DBUG_RETURN(1);
+ TABLE *table= table_list->table;
+
/* Table might be in use by some outer statement. */
if (table->query_id && table->query_id != thd->query_id)
{
@@ -2082,8 +2131,7 @@ int drop_temporary_table(THD *thd, TABLE
DBUG_RETURN(-1);
}
- if (is_trans != NULL)
- *is_trans= table->file->has_transactions();
+ *is_trans= table->file->has_transactions();
/*
If LOCK TABLES list is not empty and contains this table,
@@ -2091,6 +2139,7 @@ int drop_temporary_table(THD *thd, TABLE
*/
mysql_lock_remove(thd, thd->lock, table);
close_temporary_table(thd, table, 1, 1);
+ table_list->table= NULL;
DBUG_RETURN(0);
}
@@ -2178,15 +2227,12 @@ bool rename_temporary_table(THD* thd, TA
char *key;
uint key_length;
TABLE_SHARE *share= table->s;
- TABLE_LIST table_list;
DBUG_ENTER("rename_temporary_table");
if (!(key=(char*) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH)))
DBUG_RETURN(1); /* purecov: inspected */
- table_list.db= (char*) db;
- table_list.table_name= (char*) table_name;
- key_length= create_table_def_key(thd, key, &table_list, 1);
+ key_length= create_table_def_key(thd, key, db, table_name, 1);
share->set_table_cache_key(key, key_length);
DBUG_RETURN(0);
}
@@ -2553,52 +2599,47 @@ 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)
{
reg1 TABLE *table;
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
+ const char *key;
+ uint key_length;
char *alias= table_list->alias;
uint flags= ot_ctx->get_flags();
MDL_ticket *mdl_ticket;
@@ -2607,6 +2648,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);
@@ -2614,66 +2663,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);
+ key_length= get_table_def_key(table_list, &key);
/*
- 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);
- }
-
- /*
- 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 &&
@@ -2769,7 +2765,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.
@@ -2781,10 +2777,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))
{
@@ -3709,7 +3702,7 @@ check_and_update_routine_version(THD *th
*/
bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
- char *cache_key, uint cache_key_length,
+ const char *cache_key, uint cache_key_length,
MEM_ROOT *mem_root, uint flags)
{
TABLE not_used;
@@ -3810,15 +3803,15 @@ static bool open_table_entry_fini(THD *t
static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
{
- char cache_key[MAX_DBKEY_LENGTH];
- uint cache_key_length;
+ const char *cache_key;
+ uint cache_key_length;
TABLE_SHARE *share;
TABLE *entry;
int not_used;
bool result= TRUE;
my_hash_value_type hash_value;
- cache_key_length= create_table_def_key(thd, cache_key, table_list, 0);
+ cache_key_length= get_table_def_key(table_list, &cache_key);
thd->clear_error();
@@ -4012,8 +4005,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,
@@ -4029,8 +4021,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,
@@ -4363,10 +4354,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
@@ -4376,12 +4389,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));
@@ -4469,6 +4523,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)
@@ -4479,6 +4534,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. */
@@ -4566,18 +4623,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) &&
@@ -4645,34 +4701,33 @@ 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))))
+ 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)))
{
- /*
- 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, table->db, table->table_name, false))
- return TRUE;
+ 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, table->db, table->table_name, false))
+ return TRUE;
}
return FALSE;
@@ -4900,6 +4955,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;
}
@@ -4946,6 +5005,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;
}
@@ -4976,6 +5039,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)
{
@@ -5859,7 +5927,6 @@ TABLE *open_table_uncached(THD *thd, con
TABLE_SHARE *share;
char cache_key[MAX_DBKEY_LENGTH], *saved_cache_key, *tmp_path;
uint key_length;
- TABLE_LIST table_list;
DBUG_ENTER("open_table_uncached");
DBUG_PRINT("enter",
("table: '%s'.'%s' path: '%s' server_id: %u "
@@ -5867,10 +5934,8 @@ TABLE *open_table_uncached(THD *thd, con
db, table_name, path,
(uint) thd->server_id, (ulong) thd->variables.pseudo_thread_id));
- table_list.db= (char*) db;
- table_list.table_name= (char*) table_name;
/* Create the cache_key for temporary tables */
- key_length= create_table_def_key(thd, cache_key, &table_list, 1);
+ key_length= create_table_def_key(thd, cache_key, db, table_name, 1);
if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table) + sizeof(*share) +
strlen(path)+1 + key_length,
@@ -6021,6 +6086,135 @@ 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)
+{
+ TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
+ DBUG_ENTER("open_temporary_tables");
+
+ for (TABLE_LIST *tl= tl_list; tl && tl != first_not_own; 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 2011-04-26 08:49:10 +0000
+++ b/sql/sql_base.h 2011-05-04 07:51:15 +0000
@@ -78,11 +78,10 @@ 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,
- 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,
+uint get_table_def_key(const TABLE_LIST *table_list, const char **key);
+TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list,
+ const char *key, uint key_length,
+ uint db_flags, int *error,
my_hash_value_type hash_value);
void release_table_share(TABLE_SHARE *share);
TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name);
@@ -93,7 +92,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 +105,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 +137,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,
@@ -264,6 +261,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. */
@@ -289,7 +288,7 @@ void tdc_remove_table(THD *thd, enum_tdc
const char *db, const char *table_name,
bool has_lock);
bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
- char *cache_key, uint cache_key_length,
+ const char *cache_key, uint cache_key_length,
MEM_ROOT *mem_root, uint flags);
void tdc_flush_unused_tables();
TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
@@ -567,6 +566,30 @@ private:
/**
+ Check if a TABLE_LIST instance represents a pre-opened temporary table.
+*/
+
+inline bool is_temporary_table(TABLE_LIST *tl)
+{
+ if (tl->view || tl->schema_table)
+ return FALSE;
+
+ if (!tl->table)
+ return FALSE;
+
+ /*
+ NOTE: 'table->s' might be NULL for specially constructed TABLE
+ instances. See SHOW TRIGGERS for example.
+ */
+
+ if (!tl->table->s)
+ return FALSE;
+
+ return tl->table->s->tmp_table != NO_TMP_TABLE;
+}
+
+
+/**
This internal handler is used to trap ER_NO_SUCH_TABLE.
*/
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2011-04-15 12:14:35 +0000
+++ b/sql/sql_class.h 2011-05-04 07:51:15 +0000
@@ -3806,6 +3806,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 2011-04-19 03:29:06 +0000
+++ b/sql/sql_db.cc 2011-05-04 07:51:15 +0000
@@ -814,8 +814,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 2011-02-11 14:38:34 +0000
+++ b/sql/sql_handler.cc 2011-05-04 07:51:15 +0000
@@ -60,12 +60,15 @@
#include "sql_base.h" // insert_fields
#include "sql_select.h"
#include "transaction.h"
+#include "sql_parse.h" // check_table_access
#define HANDLER_TABLES_HASH_SIZE 120
static enum enum_ha_read_modes rkey_to_rnext[]=
{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
+static bool mysql_ha_open_table(THD *thd, TABLE_LIST *table);
+
/*
Get hash key and hash key length.
@@ -150,40 +153,25 @@ static void mysql_ha_close_table(THD *th
tables->mdl_request.ticket= NULL;
}
-/*
- Open a HANDLER table.
- SYNOPSIS
- mysql_ha_open()
- thd Thread identifier.
- tables A list of tables with the first entry to open.
- reopen Re-open a previously opened handler table.
+/**
+ Execute a HANDLER OPEN statement.
- DESCRIPTION
- Though this function takes a list of tables, only the first list entry
- will be opened.
- 'reopen' is set when a handler table is to be re-opened. In this case,
- 'tables' is the pointer to the hashed TABLE_LIST object which has been
- saved on the original open.
- 'reopen' is also used to suppress the sending of an 'ok' message.
+ @param thd The current thread.
- RETURN
- FALSE OK
- TRUE Error
+ @retval FALSE on success.
+ @retval TRUE on failure.
*/
-bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
+bool Sql_cmd_handler_open::execute(THD *thd)
{
TABLE_LIST *hash_tables = NULL;
char *db, *name, *alias;
- uint dblen, namelen, aliaslen, counter;
- bool error;
- TABLE *backup_open_tables;
- MDL_savepoint mdl_savepoint;
- DBUG_ENTER("mysql_ha_open");
- DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
- tables->db, tables->table_name, tables->alias,
- (int) reopen));
+ uint dblen, namelen, aliaslen;
+ TABLE_LIST *tables= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
+ DBUG_ENTER("Sql_cmd_handler_open::execute");
+ DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
+ tables->db, tables->table_name, tables->alias));
if (thd->locked_tables_mode)
{
@@ -212,8 +200,15 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
DBUG_RETURN(TRUE);
}
}
- else if (! reopen) /* Otherwise we have 'tables' already. */
+ else
{
+ /*
+ Otherwise we might have handler with the same name already.
+
+ Note that it is safe to disclose this information before doing privilege
+ check. Current user can always find out that handler is open by using
+ HANDLER ... READ command, which doesn't requires any privileges.
+ */
if (my_hash_search(&thd->handler_tables_hash, (uchar*) tables->alias,
strlen(tables->alias) + 1))
{
@@ -224,50 +219,84 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
}
}
- if (! reopen)
+ /* copy the TABLE_LIST struct */
+ dblen= strlen(tables->db) + 1;
+ namelen= strlen(tables->table_name) + 1;
+ aliaslen= strlen(tables->alias) + 1;
+ if (!(my_multi_malloc(MYF(MY_WME),
+ &hash_tables, (uint) sizeof(*hash_tables),
+ &db, (uint) dblen,
+ &name, (uint) namelen,
+ &alias, (uint) aliaslen,
+ NullS)))
{
- /* copy the TABLE_LIST struct */
- dblen= strlen(tables->db) + 1;
- namelen= strlen(tables->table_name) + 1;
- aliaslen= strlen(tables->alias) + 1;
- if (!(my_multi_malloc(MYF(MY_WME),
- &hash_tables, (uint) sizeof(*hash_tables),
- &db, (uint) dblen,
- &name, (uint) namelen,
- &alias, (uint) aliaslen,
- NullS)))
- {
- DBUG_PRINT("exit",("ERROR"));
- DBUG_RETURN(TRUE);
- }
- /* structure copy */
- *hash_tables= *tables;
- hash_tables->db= db;
- hash_tables->table_name= name;
- hash_tables->alias= alias;
- memcpy(hash_tables->db, tables->db, dblen);
- memcpy(hash_tables->table_name, tables->table_name, namelen);
- memcpy(hash_tables->alias, tables->alias, aliaslen);
- /*
- We can't request lock with explicit duration for this table
- right from the start as open_tables() can't handle properly
- back-off for such locks.
- */
- hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED,
- MDL_TRANSACTION);
- /* for now HANDLER can be used only for real TABLES */
- hash_tables->required_type= FRMTYPE_TABLE;
+ DBUG_PRINT("exit",("ERROR"));
+ DBUG_RETURN(TRUE);
+ }
+ /* structure copy */
+ *hash_tables= *tables;
+ hash_tables->db= db;
+ hash_tables->table_name= name;
+ hash_tables->alias= alias;
+ memcpy(hash_tables->db, tables->db, dblen);
+ memcpy(hash_tables->table_name, tables->table_name, namelen);
+ memcpy(hash_tables->alias, tables->alias, aliaslen);
+ /*
+ We can't request lock with explicit duration for this table
+ right from the start as open_tables() can't handle properly
+ back-off for such locks.
+ */
+ hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED,
+ MDL_TRANSACTION);
+ /* for now HANDLER can be used only for real TABLES */
+ hash_tables->required_type= FRMTYPE_TABLE;
- /* add to hash */
- if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
- {
- my_free(hash_tables);
- DBUG_PRINT("exit",("ERROR"));
- DBUG_RETURN(TRUE);
- }
+ /* add to hash */
+ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
+ {
+ my_free(hash_tables);
+ DBUG_PRINT("exit",("ERROR"));
+ DBUG_RETURN(TRUE);
}
- else
- hash_tables= tables;
+
+ if (open_temporary_tables(thd, hash_tables) ||
+ check_table_access(thd, SELECT_ACL, hash_tables, FALSE, UINT_MAX,
+ FALSE) ||
+ mysql_ha_open_table(thd, hash_tables))
+
+ {
+ my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
+ DBUG_PRINT("exit",("ERROR"));
+ DBUG_RETURN(TRUE);
+ }
+
+ my_ok(thd);
+
+ DBUG_PRINT("exit",("OK"));
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Auxiliary function which opens or re-opens table for HANDLER statements.
+
+ @param thd Thread context..
+ @param hash_tables Table list element for table to open.
+
+ @retval FALSE - Success.
+ @retval TRUE - Failure.
+*/
+
+static bool mysql_ha_open_table(THD *thd, TABLE_LIST *hash_tables)
+{
+ TABLE *backup_open_tables;
+ MDL_savepoint mdl_savepoint;
+ uint counter;
+ bool error;
+
+ DBUG_ENTER("mysql_ha_open_table");
+
+ DBUG_ASSERT(!thd->locked_tables_mode);
/*
Save and reset the open_tables list so that open_tables() won't
@@ -282,22 +311,17 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
mdl_savepoint= thd->mdl_context.mdl_savepoint();
/*
- open_tables() will set 'hash_tables->table' if successful.
- It must be NULL for a real open when calling open_tables().
+ 'hash_tables->table' must be NULL, unless there is pre-opened
+ temporary table. open_tables() will set it if successful.
*/
- DBUG_ASSERT(! hash_tables->table);
+ DBUG_ASSERT(! hash_tables->table || is_temporary_table(hash_tables));
- /*
- 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.
- */
error= open_tables(thd, &hash_tables, &counter, 0);
if (! error &&
! (hash_tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
{
- my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
+ my_error(ER_ILLEGAL_HA, MYF(0), hash_tables->alias);
error= TRUE;
}
if (!error &&
@@ -313,21 +337,16 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
{
/*
No need to rollback statement transaction, it's not started.
- If called with reopen flag, no need to rollback either,
+ If called for re-open, no need to rollback either,
it will be done at statement end.
*/
DBUG_ASSERT(thd->transaction.stmt.is_empty());
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
thd->set_open_tables(backup_open_tables);
- if (!reopen)
- my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
- else
- {
- hash_tables->table= NULL;
- /* Safety, cleanup the pointer to satisfy MDL assertions. */
- hash_tables->mdl_request.ticket= NULL;
- }
+ hash_tables->table= NULL;
+ /* Safety, cleanup the pointer to satisfy MDL assertions. */
+ hash_tables->mdl_request.ticket= NULL;
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
}
@@ -354,34 +373,28 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
*/
hash_tables->table->open_by_handler= 1;
- if (! reopen)
- my_ok(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
}
-/*
- Close a HANDLER table by alias or table name
+/**
+ Execute a HANDLER CLOSE statement.
- SYNOPSIS
- mysql_ha_close()
- thd Thread identifier.
- tables A list of tables with the first entry to close.
+ @param thd The current thread.
- DESCRIPTION
- Closes the table that is associated (on the handler tables hash) with the
- name (table->alias) of the specified table.
+ @note Closes the table that is associated (on the handler tables hash)
+ with the name (TABLE_LIST::alias) of the specified table.
- RETURN
- FALSE ok
- TRUE error
+ @retval FALSE on success.
+ @retval TRUE on failure.
*/
-bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
+bool Sql_cmd_handler_close::execute(THD *thd)
{
+ TABLE_LIST *tables= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
TABLE_LIST *hash_tables;
- DBUG_ENTER("mysql_ha_close");
+ DBUG_ENTER("Sql_cmd_handler_close::execute");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
@@ -467,31 +480,19 @@ handle_condition(THD *thd,
}
-/*
- Read from a HANDLER table.
+/**
+ Execute a HANDLER READ statement.
- SYNOPSIS
- mysql_ha_read()
- thd Thread identifier.
- tables A list of tables with the first entry to read.
- mode
- keyname
- key_expr
- ha_rkey_mode
- cond
- select_limit_cnt
- offset_limit_cnt
+ @param thd The current thread.
- RETURN
- FALSE ok
- TRUE error
+ @note Closes the table that is associated (on the handler tables hash)
+ with the name (TABLE_LIST::alias) of the specified table.
+
+ @retval FALSE on success.
+ @retval TRUE on failure.
*/
-
-bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
- enum enum_ha_read_modes mode, char *keyname,
- List<Item> *key_expr,
- enum ha_rkey_function ha_rkey_mode, Item *cond,
- ha_rows select_limit_cnt, ha_rows offset_limit_cnt)
+
+bool Sql_cmd_handler_read::execute(THD *thd)
{
TABLE_LIST *hash_tables;
TABLE *table, *backup_open_tables;
@@ -505,7 +506,14 @@ bool mysql_ha_read(THD *thd, TABLE_LIST
uchar *UNINIT_VAR(key);
uint UNINIT_VAR(key_len);
Sql_handler_lock_error_handler sql_handler_lock_error;
- DBUG_ENTER("mysql_ha_read");
+ LEX *lex= thd->lex;
+ SELECT_LEX *select_lex= &lex->select_lex;
+ SELECT_LEX_UNIT *unit= &lex->unit;
+ TABLE_LIST *tables= select_lex->table_list.first;
+ enum enum_ha_read_modes mode= m_read_mode;
+ Item *cond= select_lex->where;
+ ha_rows select_limit_cnt, offset_limit_cnt;
+ DBUG_ENTER("Sql_cmd_handler_read::execute");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
@@ -515,8 +523,19 @@ bool mysql_ha_read(THD *thd, TABLE_LIST
DBUG_RETURN(TRUE);
}
- thd->lex->select_lex.context.resolve_in_table_list_only(tables);
- list.push_front(new Item_field(&thd->lex->select_lex.context,
+ /*
+ There is no need to check for table permissions here, because
+ if a user has no permissions to read a table, he won't be
+ able to open it (with SQLCOM_HA_OPEN) in the first place.
+ */
+
+ /* Get limit counters from SELECT_LEX. */
+ unit->set_limit(select_lex);
+ select_limit_cnt= unit->select_limit_cnt;
+ offset_limit_cnt= unit->offset_limit_cnt;
+
+ select_lex->context.resolve_in_table_list_only(tables);
+ list.push_front(new Item_field(&select_lex->context,
NULL, NULL, "*"));
List_iterator<Item> it(list);
it++;
@@ -535,7 +554,7 @@ retry:
/*
The handler table has been closed. Re-open it.
*/
- if (mysql_ha_open(thd, hash_tables, 1))
+ if (mysql_ha_open_table(thd, hash_tables))
{
DBUG_PRINT("exit",("reopen failed"));
goto err0;
@@ -611,12 +630,14 @@ retry:
goto err;
}
- if (keyname)
+ if (m_key_name)
{
- if ((keyno= find_type(keyname, &table->s->keynames,
- FIND_TYPE_NO_PREFIX) - 1) < 0)
+ keyno= find_type((char*) m_key_name,
+ &table->s->keynames,
+ FIND_TYPE_NO_PREFIX) - 1;
+ if (keyno < 0)
{
- my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname, tables->alias);
+ my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), m_key_name, tables->alias);
goto err;
}
/* Check if the same index involved. */
@@ -629,7 +650,7 @@ retry:
}
}
- if (insert_fields(thd, &thd->lex->select_lex.context,
+ if (insert_fields(thd, &select_lex->context,
tables->db, tables->alias, &it, 0))
goto err;
@@ -649,7 +670,7 @@ retry:
case RNEXT:
if (table->file->inited != handler::NONE)
{
- if (keyname)
+ if (m_key_name)
{
/* Check if we read from the same index. */
DBUG_ASSERT((uint) keyno == table->file->get_index());
@@ -663,7 +684,7 @@ retry:
}
/* else fall through */
case RFIRST:
- if (keyname)
+ if (m_key_name)
{
table->file->ha_index_or_rnd_end();
table->file->ha_index_init(keyno, 1);
@@ -678,7 +699,7 @@ retry:
mode=RNEXT;
break;
case RPREV:
- DBUG_ASSERT(keyname != 0);
+ DBUG_ASSERT(m_key_name != 0);
/* Check if we read from the same index. */
DBUG_ASSERT((uint) keyno == table->file->get_index());
if (table->file->inited != handler::NONE)
@@ -688,7 +709,7 @@ retry:
}
/* else fall through */
case RLAST:
- DBUG_ASSERT(keyname != 0);
+ DBUG_ASSERT(m_key_name != 0);
table->file->ha_index_or_rnd_end();
table->file->ha_index_init(keyno, 1);
error= table->file->ha_index_last(table->record[0]);
@@ -696,20 +717,20 @@ retry:
break;
case RNEXT_SAME:
/* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */
- DBUG_ASSERT(keyname != 0);
+ DBUG_ASSERT(m_key_name != 0);
error= table->file->ha_index_next_same(table->record[0], key, key_len);
break;
case RKEY:
{
- DBUG_ASSERT(keyname != 0);
+ DBUG_ASSERT(m_key_name != 0);
KEY *keyinfo=table->key_info+keyno;
KEY_PART_INFO *key_part=keyinfo->key_part;
- if (key_expr->elements > keyinfo->key_parts)
+ if (m_key_expr->elements > keyinfo->key_parts)
{
my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts);
goto err;
}
- List_iterator<Item> it_ke(*key_expr);
+ List_iterator<Item> it_ke(*m_key_expr);
Item *item;
key_part_map keypart_map;
for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
@@ -738,8 +759,8 @@ retry:
table->file->ha_index_init(keyno, 1);
key_copy(key, table->record[0], table->key_info + keyno, key_len);
error= table->file->ha_index_read_map(table->record[0],
- key, keypart_map, ha_rkey_mode);
- mode=rkey_to_rnext[(int)ha_rkey_mode];
+ key, keypart_map, m_rkey_mode);
+ mode=rkey_to_rnext[(int)m_rkey_mode];
break;
}
default:
=== modified file 'sql/sql_handler.h'
--- a/sql/sql_handler.h 2010-11-18 16:34:56 +0000
+++ b/sql/sql_handler.h 2011-04-05 12:37:41 +0000
@@ -23,10 +23,103 @@
class THD;
struct TABLE_LIST;
-bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen);
-bool mysql_ha_close(THD *thd, TABLE_LIST *tables);
-bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *,
- List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
+/**
+ Sql_cmd_handler_open represents HANDLER OPEN statement.
+
+ @note Some information about this statement, for example, table to be
+ opened is still kept in LEX class.
+*/
+
+class Sql_cmd_handler_open : public Sql_cmd
+{
+public:
+ Sql_cmd_handler_open()
+ {}
+
+ virtual ~Sql_cmd_handler_open()
+ {}
+
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_HA_OPEN;
+ }
+
+ virtual bool execute(THD *thd);
+};
+
+
+/**
+ Sql_cmd_handler_read represents HANDLER READ statement.
+
+ @note Some information about this statement, for example, table
+ list element which identifies HANDLER to be read from,
+ WHERE and LIMIT clauses is still kept in LEX class.
+*/
+
+class Sql_cmd_handler_read : public Sql_cmd
+{
+public:
+ Sql_cmd_handler_read(enum_ha_read_modes read_mode,
+ const char *key_name,
+ List<Item> *key_expr,
+ ha_rkey_function rkey_mode)
+ : m_read_mode(read_mode), m_key_name(key_name), m_key_expr(key_expr),
+ m_rkey_mode(rkey_mode)
+ {}
+
+ virtual ~Sql_cmd_handler_read()
+ {}
+
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_HA_READ;
+ }
+
+ virtual bool execute(THD *thd);
+
+private:
+ /** Read mode for HANDLER READ: FIRST, NEXT, LAST, ... */
+ enum enum_ha_read_modes m_read_mode;
+
+ /**
+ Name of key to be used for reading,
+ NULL in cases when natural row-order is to be used.
+ */
+ const char *m_key_name;
+
+ /** Key values to be satisfied. */
+ List<Item> *m_key_expr;
+
+ /** Type of condition for key values to be satisfied. */
+ enum ha_rkey_function m_rkey_mode;
+};
+
+
+/**
+ Sql_cmd_handler_close represents HANDLER CLOSE statement.
+
+ @note Table list element which identifies HANDLER to be closed
+ still resides in LEX class.
+*/
+
+class Sql_cmd_handler_close : public Sql_cmd
+{
+public:
+ Sql_cmd_handler_close()
+ {}
+
+ virtual ~Sql_cmd_handler_close()
+ {}
+
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_HA_CLOSE;
+ }
+
+ virtual bool execute(THD *thd);
+};
+
+
void mysql_ha_flush(THD *thd);
void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables);
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables);
=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc 2011-04-04 08:47:25 +0000
+++ b/sql/sql_insert.cc 2011-05-04 07:51:15 +0000
@@ -3793,18 +3793,19 @@ 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
- it preparable for open. But let us do close_temporary_table() here
- just in case.
+ it preparable for open. Anyway we can't drop temporary table if
+ we are unable to fint it.
*/
- drop_temporary_table(thd, create_table, NULL);
+ DBUG_ASSERT(0);
}
else
+ {
table= create_table->table;
+ }
}
}
if (!table) // open failed
=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h 2011-03-28 10:56:41 +0000
+++ b/sql/sql_lex.h 2011-05-04 07:51:15 +0000
@@ -2149,11 +2149,7 @@ struct LEX: public Query_tables_list
enum SSL_type ssl_type; /* defined in violite.h */
enum enum_duplicates duplicates;
enum enum_tx_isolation tx_isolation;
- enum enum_ha_read_modes ha_read_mode;
- union {
- enum ha_rkey_function ha_rkey_mode;
- enum xa_option_words xa_opt;
- };
+ enum xa_option_words xa_opt;
enum enum_var_type option_type;
enum enum_view_create_mode create_view_mode;
enum enum_drop_mode drop_mode;
@@ -2459,6 +2455,7 @@ public:
m_set_signal_info.clear();
m_lock_type= TL_READ_DEFAULT;
m_mdl_type= MDL_SHARED_READ;
+ m_ha_rkey_mode= HA_READ_KEY_EXACT;
}
~Yacc_state();
@@ -2471,6 +2468,7 @@ public:
{
m_lock_type= TL_READ_DEFAULT;
m_mdl_type= MDL_SHARED_READ;
+ m_ha_rkey_mode= HA_READ_KEY_EXACT; /* Let us be future-proof. */
}
/**
@@ -2516,6 +2514,9 @@ public:
*/
enum_mdl_type m_mdl_type;
+ /** Type of condition for key in HANDLER READ statement. */
+ enum ha_rkey_function m_ha_rkey_mode;
+
/*
TODO: move more attributes from the LEX structure here.
*/
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2011-04-19 03:29:06 +0000
+++ b/sql/sql_parse.cc 2011-05-04 07:51:15 +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
@@ -115,7 +114,9 @@
"FUNCTION" : "PROCEDURE")
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
+static bool check_show_access(THD *thd, TABLE_LIST *table);
static void sql_kill(THD *thd, ulong id, bool only_kill_query);
+static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables);
const char *any_db="*any*"; // Special symbol for check_access
@@ -446,6 +447,62 @@ 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_DROP_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_DROP_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)
@@ -1235,6 +1292,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;
@@ -2063,27 +2123,41 @@ mysql_execute_command(THD *thd)
DEBUG_SYNC(thd,"before_execute_sql_command");
#endif
- switch (lex->sql_command) {
+ /*
+ Close tables open by HANDLERs before executing DDL statement
+ which is going to affect those tables.
- case SQLCOM_SHOW_EVENTS:
-#ifndef HAVE_EVENT_SCHEDULER
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
- break;
-#endif
- case SQLCOM_SHOW_STATUS_PROC:
- case SQLCOM_SHOW_STATUS_FUNC:
- if ((res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
- UINT_MAX, FALSE)))
+ 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;
- res= execute_sqlcom_select(thd, all_tables);
- break;
+ }
+
+ switch (lex->sql_command) {
+
case SQLCOM_SHOW_STATUS:
{
system_status_var old_status_var= thd->status_var;
thd->initial_status_var= &old_status_var;
- if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
- UINT_MAX, FALSE)))
+
+ if (!(res= select_precheck(thd, lex, all_tables, first_table)))
res= execute_sqlcom_select(thd, all_tables);
+
/* Don't log SHOW STATUS commands to slow query log */
thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED |
SERVER_QUERY_NO_GOOD_INDEX_USED);
@@ -2098,6 +2172,13 @@ mysql_execute_command(THD *thd)
mysql_mutex_unlock(&LOCK_status);
break;
}
+ case SQLCOM_SHOW_EVENTS:
+#ifndef HAVE_EVENT_SCHEDULER
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
+ break;
+#endif
+ case SQLCOM_SHOW_STATUS_PROC:
+ case SQLCOM_SHOW_STATUS_FUNC:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TRIGGERS:
@@ -2115,21 +2196,7 @@ mysql_execute_command(THD *thd)
{
thd->status_var.last_query_cost= 0.0;
- /*
- lex->exchange != NULL implies SELECT .. INTO OUTFILE and this
- requires FILE_ACL access.
- */
- ulong privileges_requested= lex->exchange ? SELECT_ACL | FILE_ACL :
- SELECT_ACL;
-
- if (all_tables)
- res= check_table_access(thd,
- privileges_requested,
- all_tables, FALSE, UINT_MAX, FALSE);
- else
- res= check_access(thd, privileges_requested, any_db, NULL, NULL, 0, 0);
-
- if (res)
+ if ((res= select_precheck(thd, lex, all_tables, first_table)))
break;
res= execute_sqlcom_select(thd, all_tables);
@@ -2396,9 +2463,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;
@@ -2711,6 +2775,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.
@@ -3188,8 +3259,21 @@ end_with_restore_list:
thd->mdl_context.release_transactional_locks();
if (res)
goto error;
- if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
- FALSE, UINT_MAX, FALSE))
+
+ /*
+ 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 (lock_tables_precheck(thd, all_tables))
goto error;
thd->variables.option_bits|= OPTION_TABLE_LOCK;
@@ -3682,29 +3766,6 @@ end_with_restore_list:
break;
}
#endif
- case SQLCOM_HA_OPEN:
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE))
- goto error;
- res= mysql_ha_open(thd, first_table, 0);
- break;
- case SQLCOM_HA_CLOSE:
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- res= mysql_ha_close(thd, first_table);
- break;
- case SQLCOM_HA_READ:
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- /*
- There is no need to check for table permissions here, because
- if a user has no permissions to read a table, he won't be
- able to open it (with SQLCOM_HA_OPEN) in the first place.
- */
- unit->set_limit(select_lex);
- res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
- lex->insert_list, lex->ha_rkey_mode, select_lex->where,
- unit->select_limit_cnt, unit->offset_limit_cnt);
- break;
-
case SQLCOM_BEGIN:
if (trans_begin(thd, lex->start_transaction_opt))
goto error;
@@ -4391,6 +4452,9 @@ create_sp_error:
case SQLCOM_REPAIR:
case SQLCOM_TRUNCATE:
case SQLCOM_ALTER_TABLE:
+ case SQLCOM_HA_OPEN:
+ case SQLCOM_HA_READ:
+ case SQLCOM_HA_CLOSE:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
/* fall through */
case SQLCOM_SIGNAL:
@@ -4871,20 +4935,19 @@ check_access(THD *thd, ulong want_access
}
+/**
+ Check if user has enough privileges for execution of SHOW statement,
+ which was converted to query to one of I_S tables.
+
+ @param thd Thread context.
+ @param table Table list element for I_S table to be queried..
+
+ @retval FALSE - Success.
+ @retval TRUE - Failure.
+*/
+
static bool check_show_access(THD *thd, TABLE_LIST *table)
{
- /*
- This is a SHOW command using an INFORMATION_SCHEMA table.
- check_access() has not been called for 'table',
- and SELECT is currently always granted on the I_S, so we automatically
- grant SELECT on table here, to bypass a call to check_access().
- Note that not calling check_access(table) is an optimization,
- which needs to be revisited if the INFORMATION_SCHEMA does
- not always automatically grant SELECT but use the grant tables.
- See Bug#38837 need a way to disable information_schema for security
- */
- table->grant.privilege= SELECT_ACL;
-
switch (get_schema_table_idx(table->schema_table)) {
case SCH_SCHEMATA:
return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
@@ -4924,6 +4987,12 @@ static bool check_show_access(THD *thd,
DBUG_ASSERT(dst_table);
+ /*
+ Open temporary tables to be able to detect them during privilege check.
+ */
+ if (open_temporary_tables(thd, dst_table))
+ return TRUE;
+
if (check_access(thd, SELECT_ACL, dst_table->db,
&dst_table->grant.privilege,
&dst_table->grant.m_internal,
@@ -4937,6 +5006,9 @@ static bool check_show_access(THD *thd,
if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE))
return TRUE; /* Access denied */
+ close_thread_tables(thd);
+ dst_table->table= NULL;
+
/* Access granted */
return FALSE;
}
@@ -5013,19 +5085,23 @@ check_table_access(THD *thd, ulong requi
*/
tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
- if (tables->schema_table_reformed)
- {
- if (check_show_access(thd, tables))
- goto deny;
- continue;
- }
+ /*
+ We should not encounter table list elements for reformed SHOW
+ statements unless this is first table list element in the main
+ select.
+ Such table list elements require additional privilege check
+ (see check_show_access()). This check is carried out by caller,
+ but only for the first table list element from the main select.
+ */
+ DBUG_ASSERT(!tables->schema_table_reformed ||
+ tables == thd->lex->select_lex.table_list.first);
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(),
@@ -5148,6 +5224,13 @@ bool check_some_access(THD *thd, ulong w
DBUG_RETURN(1);
}
+#else
+
+static bool check_show_access(THD *thd, TABLE_LIST *table)
+{
+ return false;
+}
+
#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
@@ -6610,6 +6693,45 @@ Item * all_any_subquery_creator(Item *le
/**
+ Perform first stage of privilege checking for SELECT statement.
+
+ @param thd Thread context.
+ @param lex LEX for SELECT statement.
+ @param tables List of tables used by statement.
+ @param first_table First table in the main SELECT of the SELECT
+ statement.
+
+ @retval FALSE - Success (column-level privilege checks might be required).
+ @retval TRUE - Failure, privileges are insufficient.
+*/
+
+bool select_precheck(THD *thd, LEX *lex, TABLE_LIST *tables,
+ TABLE_LIST *first_table)
+{
+ bool res;
+ /*
+ lex->exchange != NULL implies SELECT .. INTO OUTFILE and this
+ requires FILE_ACL access.
+ */
+ ulong privileges_requested= lex->exchange ? SELECT_ACL | FILE_ACL :
+ SELECT_ACL;
+
+ if (tables)
+ {
+ res= check_table_access(thd,
+ privileges_requested,
+ tables, FALSE, UINT_MAX, FALSE) ||
+ (first_table && first_table->schema_table_reformed &&
+ check_show_access(thd, first_table));
+ }
+ else
+ res= check_access(thd, privileges_requested, any_db, NULL, NULL, 0, 0);
+
+ return res;
+}
+
+
+/**
Multi update query pre-check.
@param thd Thread handler
@@ -6707,6 +6829,19 @@ bool multi_delete_precheck(THD *thd, TAB
TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
DBUG_ENTER("multi_delete_precheck");
+ /*
+ Temporary tables are pre-opened in 'tables' list only. Here we need to
+ initialize TABLE instances in 'aux_tables' list.
+ */
+ for (TABLE_LIST *tl= aux_tables; tl; tl= tl->next_global)
+ {
+ if (tl->table)
+ continue;
+
+ if (tl->correspondent_table)
+ tl->table= tl->correspondent_table->table;
+ }
+
/* sql_yacc guarantees that tables and aux_tables are not zero */
DBUG_ASSERT(aux_tables != 0);
if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
@@ -6975,9 +7110,9 @@ bool create_table_precheck(THD *thd, TAB
CREATE TABLE ... SELECT, also require INSERT.
*/
- want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
- CREATE_TMP_ACL : CREATE_ACL) |
- (select_lex->item_list.elements ? INSERT_ACL : 0);
+ want_priv= (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
+ CREATE_TMP_ACL :
+ (CREATE_ACL | (select_lex->item_list.elements ? INSERT_ACL : 0));
if (check_access(thd, want_priv, create_table->db,
&create_table->grant.privilege,
@@ -6986,11 +7121,43 @@ bool create_table_precheck(THD *thd, TAB
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 (lex->create_info.merge_list.first)
+ {
+ TABLE_LIST *tl;
+ /*
+ Pre-open temporary tables from UNION clause to simplify privilege
+ checking for them.
+ */
+ if (open_temporary_tables(thd, lex->create_info.merge_list.first))
+ goto err;
+
+ if (check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
+ lex->create_info.merge_list.first,
+ FALSE, UINT_MAX, FALSE))
+ goto err;
+
+ /*
+ Since the MERGE table in question might already exist with exactly
+ the same definition we need to close all tables opened for underlying
+ checking to allow normal open of table being created.
+ To do this we simply close all tables and then open only tables from
+ the main statement's table list.
+ */
+ close_thread_tables(thd);
+
+ /*
+ To make things safe for re-execution and upcoming open_temporary_tables()
+ we need to reset TABLE_LIST::table pointers in both main table list and
+ and UNION clause.
+ */
+ for (tl= lex->query_tables; tl; tl= tl->next_global)
+ tl->table= NULL;
+ for (tl= lex->create_info.merge_list.first; tl; tl= tl->next_global)
+ tl->table= NULL;
+
+ if (open_temporary_tables(thd, lex->query_tables))
+ DBUG_RETURN(TRUE);
+ }
if (want_priv != CREATE_TMP_ACL &&
check_grant(thd, want_priv, create_table, FALSE, 1, FALSE))
@@ -7015,6 +7182,35 @@ err:
}
+/**
+ Check privileges for LOCK TABLES statement.
+
+ @param thd Thread context.
+ @param tables List of tables to be locked.
+
+ @retval FALSE - Success.
+ @retval TRUE - Failure.
+*/
+
+static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables)
+{
+ TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
+
+ for (TABLE_LIST *table= tables; table != first_not_own_table && table;
+ table= table->next_global)
+ {
+ if (is_temporary_table(table))
+ continue;
+
+ if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, table,
+ FALSE, 1, FALSE))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
/**
negate given expression.
=== modified file 'sql/sql_parse.h'
--- a/sql/sql_parse.h 2011-03-09 20:54:55 +0000
+++ b/sql/sql_parse.h 2011-05-04 07:51:15 +0000
@@ -35,6 +35,8 @@ enum enum_mysql_completiontype {
extern "C" int test_if_data_home_dir(const char *dir);
+bool select_precheck(THD *thd, LEX *lex, TABLE_LIST *tables,
+ TABLE_LIST *first_table);
bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
int mysql_multi_update_prepare(THD *thd);
=== modified file 'sql/sql_partition_admin.cc'
--- a/sql/sql_partition_admin.cc 2011-03-17 17:39:31 +0000
+++ b/sql/sql_partition_admin.cc 2011-05-04 07:51:15 +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 2011-04-15 12:14:35 +0000
+++ b/sql/sql_prepare.cc 2011-05-04 07:51:15 +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 */
@@ -1456,13 +1457,7 @@ static int mysql_test_select(Prepared_st
lex->select_lex.context.resolve_in_select_list= TRUE;
- ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
- if (tables)
- {
- if (check_table_access(thd, privilege, tables, FALSE, UINT_MAX, FALSE))
- goto error;
- }
- else if (check_access(thd, privilege, any_db, NULL, NULL, 0, 0))
+ if (select_precheck(thd, lex, tables, lex->select_lex.table_list.first))
goto error;
if (!lex->result && !(lex->result= new (stmt->mem_root) select_send))
@@ -1964,6 +1959,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 2011-01-17 16:27:07 +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 2011-05-02 11:16:36 +0000
+++ b/sql/sql_show.cc 2011-05-04 07:51:15 +0000
@@ -3032,11 +3032,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");
@@ -3268,7 +3275,7 @@ static int fill_schema_table_from_frm(TH
uint res= 0;
int not_used;
my_hash_value_type hash_value;
- char key[MAX_DBKEY_LENGTH];
+ const char *key;
uint key_length;
char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1];
@@ -3341,7 +3348,7 @@ static int fill_schema_table_from_frm(TH
goto end;
}
- key_length= create_table_def_key(thd, key, &table_list, 0);
+ key_length= get_table_def_key(&table_list, &key);
hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
mysql_mutex_lock(&LOCK_open);
share= get_table_share(thd, &table_list, key,
@@ -7940,7 +7947,7 @@ bool show_create_trigger(THD *thd, const
/*
Open the table by name in order to load Table_triggers_list object.
*/
- if (open_tables(thd, &lst, &num_tables, MYSQL_OPEN_SKIP_TEMPORARY |
+ if (open_tables(thd, &lst, &num_tables,
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL))
{
my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0),
=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc 2011-04-13 07:13:13 +0000
+++ b/sql/sql_table.cc 2011-05-04 07:51:15 +0000
@@ -2052,24 +2052,26 @@ bool mysql_rm_table(THD *thd,TABLE_LIST
}
}
- mysql_ha_rm_tables(thd, tables);
-
if (!drop_temporary)
{
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)
+ {
+ if (is_temporary_table(table))
+ continue;
+
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name,
false);
+ }
}
else
{
for (table= tables; table; table= table->next_local)
- if (table->open_type != OT_BASE_ONLY &&
- find_temporary_table(thd, table))
+ if (is_temporary_table(table))
{
/*
A temporary table.
@@ -2136,6 +2138,9 @@ bool mysql_rm_table(THD *thd,TABLE_LIST
@note This function assumes that metadata locks have already been taken.
It is also assumed that the tables have been removed from TDC.
+ @note This function assumes that temporary tables to be dropped have
+ been pre-opened using corresponding table list elements.
+
@todo When logging to the binary log, we should log
tmp_tables and transactional tables as separate statements if we
are in a transaction; This is needed to get these tables into the
@@ -4808,6 +4813,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
@@ -4816,14 +4822,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,
@@ -4833,13 +4845,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
@@ -4862,9 +4877,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");
@@ -4876,21 +4891,30 @@ mysql_discard_or_import_tablespace(THD *
THD_STAGE_INFO(thd, stage_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_STAGE_INFO(thd, stage_end);
@@ -4921,7 +4945,7 @@ err:
DBUG_RETURN(0);
}
- table->file->print_error(error, MYF(0));
+ table_list->table->file->print_error(error, MYF(0));
DBUG_RETURN(-1);
}
@@ -5938,13 +5962,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.
@@ -6560,14 +6582,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;
+ tbl.init_one_table(new_db, strlen(new_db),
+ tmp_name, strlen(tmp_name),
+ tmp_name, TL_READ_NO_INSERT);
/* 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
@@ -7292,13 +7312,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. */
@@ -7333,16 +7346,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);
@@ -7440,11 +7477,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 2011-03-07 09:23:36 +0000
+++ b/sql/sql_truncate.cc 2011-03-09 12:24:36 +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"
@@ -185,7 +184,7 @@ int Sql_cmd_truncate_table::handler_trun
bool is_tmp_table)
{
int error= 0;
- uint flags;
+ uint flags= 0;
DBUG_ENTER("Sql_cmd_truncate_table::handler_truncate");
/*
@@ -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 2011-03-17 17:39:31 +0000
+++ b/sql/sql_view.cc 2011-05-04 07:51:15 +0000
@@ -209,13 +209,14 @@ static void make_valid_column_names(List
static bool
fill_defined_view_parts (THD *thd, TABLE_LIST *view)
{
- char key[MAX_DBKEY_LENGTH];
+ const char *key;
uint key_length;
LEX *lex= thd->lex;
TABLE_LIST decoy;
memcpy (&decoy, view, sizeof (TABLE_LIST));
- key_length= create_table_def_key(thd, key, view, 0);
+
+ key_length= get_table_def_key(view, &key);
if (tdc_open_view(thd, &decoy, decoy.alias, key, key_length,
thd->mem_root, OPEN_VIEW_NO_PARSE))
@@ -1654,8 +1655,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 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy 2011-03-09 20:54:55 +0000
+++ b/sql/sql_yacc.yy 2011-05-04 07:51:15 +0000
@@ -55,6 +55,7 @@
#include "sql_truncate.h" // Sql_cmd_truncate_table
#include "sql_admin.h" // Sql_cmd_analyze/Check..._table
#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part.
+#include "sql_handler.h" // Sql_cmd_handler_*
#include "sql_signal.h"
#include "event_parse_data.h"
#include <myisam.h>
@@ -752,6 +753,7 @@ static bool add_create_index (LEX *lex,
handlerton *db_type;
enum row_type row_type;
enum ha_rkey_function ha_rkey_mode;
+ enum enum_ha_read_modes ha_read_mode;
enum enum_tx_isolation tx_isolation;
enum Cast_target cast_type;
enum Item_udftype udf_type;
@@ -1530,6 +1532,9 @@ bool my_yyoverflow(short **a, YYSTYPE **
%type <tx_isolation> isolation_types
%type <ha_rkey_mode> handler_rkey_mode
+
+%type <ha_read_mode> handler_read_or_scan handler_scan_function
+ handler_rkey_function
%type <cast_type> cast_type
@@ -1586,7 +1591,6 @@ bool my_yyoverflow(short **a, YYSTYPE **
equal optional_braces
opt_mi_check_type opt_to mi_check_types normal_join
table_to_table_list table_to_table opt_table_list opt_as
- handler_rkey_function handler_read_or_scan
single_multi table_wild_list table_wild_one opt_wild
union_clause union_list
precision subselect_start opt_and charset
@@ -13375,6 +13379,7 @@ unlock:
handler:
HANDLER_SYM table_ident OPEN_SYM opt_table_alias
{
+ THD *thd= YYTHD;
LEX *lex= Lex;
if (lex->sphead)
{
@@ -13382,11 +13387,15 @@ handler:
MYSQL_YYABORT;
}
lex->sql_command = SQLCOM_HA_OPEN;
- if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0))
+ if (!lex->current_select->add_table_to_list(thd, $2, $4, 0))
+ MYSQL_YYABORT;
+ lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_handler_open();
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
| HANDLER_SYM table_ident_nodb CLOSE_SYM
{
+ THD *thd= YYTHD;
LEX *lex= Lex;
if (lex->sphead)
{
@@ -13394,7 +13403,10 @@ handler:
MYSQL_YYABORT;
}
lex->sql_command = SQLCOM_HA_CLOSE;
- if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0))
+ if (!lex->current_select->add_table_to_list(thd, $2, 0, 0))
+ MYSQL_YYABORT;
+ lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_handler_close();
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
| HANDLER_SYM table_ident_nodb READ_SYM
@@ -13407,7 +13419,6 @@ handler:
}
lex->expr_allows_subselect= FALSE;
lex->sql_command = SQLCOM_HA_READ;
- lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */
Item *one= new (YYTHD->mem_root) Item_int((int32) 1);
if (one == NULL)
MYSQL_YYABORT;
@@ -13418,42 +13429,50 @@ handler:
}
handler_read_or_scan where_clause opt_limit_clause
{
+ THD *thd= YYTHD;
+ LEX *lex= Lex;
Lex->expr_allows_subselect= TRUE;
/* Stored functions are not supported for HANDLER READ. */
- if (Lex->uses_stored_routines())
+ if (lex->uses_stored_routines())
{
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
"stored functions in HANDLER ... READ");
MYSQL_YYABORT;
}
+ lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_handler_read($5,
+ lex->ident.str, lex->insert_list,
+ thd->m_parser_state->m_yacc.m_ha_rkey_mode);
+ if (lex->m_sql_cmd == NULL)
+ MYSQL_YYABORT;
}
;
handler_read_or_scan:
- handler_scan_function { Lex->ident= null_lex_str; }
- | ident handler_rkey_function { Lex->ident= $1; }
+ handler_scan_function { Lex->ident= null_lex_str; $$=$1; }
+ | ident handler_rkey_function { Lex->ident= $1; $$=$2; }
;
handler_scan_function:
- FIRST_SYM { Lex->ha_read_mode = RFIRST; }
- | NEXT_SYM { Lex->ha_read_mode = RNEXT; }
+ FIRST_SYM { $$= RFIRST; }
+ | NEXT_SYM { $$= RNEXT; }
;
handler_rkey_function:
- FIRST_SYM { Lex->ha_read_mode = RFIRST; }
- | NEXT_SYM { Lex->ha_read_mode = RNEXT; }
- | PREV_SYM { Lex->ha_read_mode = RPREV; }
- | LAST_SYM { Lex->ha_read_mode = RLAST; }
+ FIRST_SYM { $$= RFIRST; }
+ | NEXT_SYM { $$= RNEXT; }
+ | PREV_SYM { $$= RPREV; }
+ | LAST_SYM { $$= RLAST; }
| handler_rkey_mode
{
- LEX *lex=Lex;
- lex->ha_read_mode = RKEY;
- lex->ha_rkey_mode=$1;
- if (!(lex->insert_list = new List_item))
+ YYTHD->m_parser_state->m_yacc.m_ha_rkey_mode= $1;
+ Lex->insert_list= new List_item;
+ if (! Lex->insert_list)
MYSQL_YYABORT;
}
'(' values ')'
- {}
+ {
+ $$= RKEY;
+ }
;
handler_rkey_mode:
=== modified file 'sql/table.cc'
--- a/sql/table.cc 2011-04-26 20:35:24 +0000
+++ b/sql/table.cc 2011-05-04 07:51:15 +0000
@@ -310,7 +310,7 @@ TABLE_CATEGORY get_table_category(const
# Share
*/
-TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
+TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, const char *key,
uint key_length)
{
MEM_ROOT mem_root;
=== modified file 'sql/table.h'
--- a/sql/table.h 2011-04-04 10:06:13 +0000
+++ b/sql/table.h 2011-05-04 07:51:15 +0000
@@ -2162,7 +2162,7 @@ void init_mdl_requests(TABLE_LIST *table
int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
uint db_stat, uint prgflag, uint ha_open_flags,
TABLE *outparam, bool is_create_table);
-TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
+TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, const char *key,
uint key_length);
void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
uint key_length,
=== modified file 'storage/myisammrg/ha_myisammrg.cc'
--- a/storage/myisammrg/ha_myisammrg.cc 2011-04-23 20:44:45 +0000
+++ b/storage/myisammrg/ha_myisammrg.cc 2011-05-04 07:51:15 +0000
@@ -475,6 +475,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.
@@ -617,6 +622,13 @@ extern "C" MI_INFO *myisammrg_attach_chi
goto end;
}
child= child_l->table;
+
+ if (!child)
+ {
+ DBUG_PRINT("myrg", ("Child table does not exist"));
+ my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
+ goto end;
+ }
/* Prepare for next child. */
param->next();
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk branch (alexander.nozdrin:3364 to 3365) Bug#27480 | Alexander Nozdrin | 4 May |