summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Vojtovich <svoj@mariadb.org>2013-09-20 13:12:53 +0400
committerSergey Vojtovich <svoj@mariadb.org>2013-09-20 13:12:53 +0400
commit815c607dcfa4a43be73e42c325956eda16e24c91 (patch)
treebb36fd8012e2e29163d1b14bac9c05f18b7a01d7
parent8b5938a1276ffc4fe7f63a21c08a342f301cca30 (diff)
downloadmariadb-git-815c607dcfa4a43be73e42c325956eda16e24c91.tar.gz
MDEV-4879 - Merge test cases for new CREATE TEMPORARY TABLE privilege model
- merged test cases for MySQL bug#27480 - fixed that LOCK TABLES was unable to open temporary table (covered by grant2 test, merged appropriate code from 5.6) - commented lines that cause server crash in merge test, reported MDEV-5042 (not relevant to bug#27480)
-rw-r--r--mysql-test/r/grant2.result373
-rw-r--r--mysql-test/r/grant4.result57
-rw-r--r--mysql-test/r/merge.result87
-rw-r--r--mysql-test/r/ps_ddl.result40
-rw-r--r--mysql-test/r/temp_table.result48
-rw-r--r--mysql-test/suite/handler/myisam.result86
-rw-r--r--mysql-test/suite/handler/myisam.test89
-rw-r--r--mysql-test/suite/rpl/r/rpl_create_if_not_exists.result14
-rw-r--r--mysql-test/suite/rpl/t/rpl_create_if_not_exists.test17
-rw-r--r--mysql-test/t/grant2.test362
-rw-r--r--mysql-test/t/grant4.test59
-rw-r--r--mysql-test/t/merge.test80
-rw-r--r--mysql-test/t/ps_ddl.test43
-rw-r--r--mysql-test/t/temp_table.test43
-rw-r--r--sql/sql_parse.cc33
15 files changed, 1429 insertions, 2 deletions
diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result
index 196f6601d48..9d92abbda07 100644
--- a/mysql-test/r/grant2.result
+++ b/mysql-test/r/grant2.result
@@ -472,3 +472,376 @@ 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
+# --
+# -- Bug#12771903: User with create temporary tables priv only has full
+# -- access to a regular table
+# --
+
+############################################################################
+# Setup environment.
+###########################################################################
+DROP DATABASE IF EXISTS mysqltest_db1;
+DROP DATABASE IF EXISTS mysqltest_db2;
+CREATE DATABASE mysqltest_db1;
+CREATE DATABASE mysqltest_db2;
+# 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;
+# mysqltest_u5@localhost has CREATE_TMP_ACL and SELECT_ACL, UPDATE_ACL,
+# DELETE_ACL on mysqltest_db1; and only CREATE_TMP_ACL on mysqltest_db2.
+# By means of this user we check privileges required for merge tables.
+GRANT CREATE TEMPORARY TABLES ON mysqltest_db1.* TO mysqltest_u5@localhost;
+GRANT CREATE TEMPORARY TABLES ON mysqltest_db2.* TO mysqltest_u5@localhost;
+GRANT SELECT, UPDATE, DELETE ON mysqltest_db1.* TO mysqltest_u5@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;
+CREATE TABLE mysqltest_db2.t2_1(a INT);
+###########################################################################
+# Check that CREATE_TMP_ACL is enough to issue almost 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;
+# Check that we do *not* 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);
+ERROR 42000: SELECT, UPDATE, DELETE command denied to user 'mysqltest_u1'@'localhost' for table 't5'
+# Check that we allow creation of MERGE table with no underlying table
+# without additional privileges.
+CREATE TEMPORARY TABLE t6(a INT) ENGINE = MERGE UNION = ();
+#
+# 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 no underlying
+# without additional privileges.
+ALTER TABLE t6 UNION = ();
+# Check that we do *not* allow altering of MERGE table with underlying
+# temporary table without additional privileges.
+ALTER TABLE t6 UNION = (t5);
+ERROR 42000: SELECT, UPDATE, DELETE command denied to user 'mysqltest_u1'@'localhost' for table '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;
+###########################################################################
+# Check special case for MERGE-tables:
+# - CREATE_TMP_ACL is required to create a temporary merge table;
+# - SELECT_ACL, UPDATE_ACL and DELETE_ACL are required to include
+# a temporary table into the underlying-table-list.
+###########################################################################
+
+# -- connect con4, mysqltest_u5@localhost, mysqltest_db1
+CREATE TEMPORARY TABLE t7(a INT);
+CREATE TEMPORARY TABLE t8(a INT);
+CREATE TEMPORARY TABLE t9(a INT);
+CREATE TEMPORARY TABLE t10(a INT) ENGINE = MERGE UNION = (t7, t8);
+ALTER TABLE t10 UNION = (t9);
+ALTER TABLE t10 UNION = (mysqltest_db2.t2_1);
+ERROR 42000: SELECT, UPDATE, DELETE command denied to user 'mysqltest_u5'@'localhost' for table 't2_1'
+CREATE TEMPORARY TABLE mysqltest_db2.t2_2(a INT) ENGINE = MERGE UNION = (t7, t8);
+ALTER TABLE mysqltest_db2.t2_2 UNION = (t9);
+ALTER TABLE mysqltest_db2.t2_2 UNION = ();
+DROP TEMPORARY TABLE mysqltest_db2.t2_2;
+DROP TEMPORARY TABLE t10;
+DROP TEMPORARY TABLE t7;
+DROP TEMPORARY TABLE t8;
+DROP TEMPORARY TABLE t9;
+###########################################################################
+# That's all. Cleanup.
+###########################################################################
+
+# -- connection: default
+# -- disconnect con1
+# All remaining temporary tables are automatically dropped.
+# -- disconnect con2
+# -- disconnect con3
+# -- disconnect con4
+SET GLOBAL keycache1.key_buffer_size = 0;
+DROP DATABASE mysqltest_db1;
+DROP DATABASE mysqltest_db2;
+DROP USER mysqltest_u1@localhost;
+DROP USER mysqltest_u2@localhost;
+DROP USER mysqltest_u3@localhost;
+DROP USER mysqltest_u4@localhost;
+DROP USER mysqltest_u5@localhost;
diff --git a/mysql-test/r/grant4.result b/mysql-test/r/grant4.result
index 65ab92c1afd..60617acbd1f 100644
--- a/mysql-test/r/grant4.result
+++ b/mysql-test/r/grant4.result
@@ -121,3 +121,60 @@ View Create View character_set_client collation_connection
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;
diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result
index ff75f653368..7cb9e5bb423 100644
--- a/mysql-test/r/merge.result
+++ b/mysql-test/r/merge.result
@@ -3826,3 +3826,90 @@ test.m1 repair error Corrupt
drop tables m1, t1, t4;
drop view t3;
End of 5.5 tests
+#
+# 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);
+DROP TRIGGER trg1;
+DROP TABLE t1;
+DROP TABLE m1;
diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result
index 7a8b38bec75..8284e974574 100644
--- a/mysql-test/r/ps_ddl.result
+++ b/mysql-test/r/ps_ddl.result
@@ -2502,3 +2502,43 @@ drop procedure if exists p_verify_reprepare_count;
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;
diff --git a/mysql-test/r/temp_table.result b/mysql-test/r/temp_table.result
index a5182a03e63..0a1701be0d7 100644
--- a/mysql-test/r/temp_table.result
+++ b/mysql-test/r/temp_table.result
@@ -243,3 +243,51 @@ CREATE TEMPORARY TABLE bug48067.t1 (c1 int);
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;
diff --git a/mysql-test/suite/handler/myisam.result b/mysql-test/suite/handler/myisam.result
index fcf6d4c133c..0942a91acbc 100644
--- a/mysql-test/suite/handler/myisam.result
+++ b/mysql-test/suite/handler/myisam.result
@@ -1803,4 +1803,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
diff --git a/mysql-test/suite/handler/myisam.test b/mysql-test/suite/handler/myisam.test
index c6acf1e822c..a2d87630aef 100644
--- a/mysql-test/suite/handler/myisam.test
+++ b/mysql-test/suite/handler/myisam.test
@@ -79,4 +79,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
diff --git a/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result b/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result
index c6f58b66758..14f81fcb9b1 100644
--- a/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result
+++ b/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result
@@ -114,4 +114,18 @@ Note 1050 Table 'v1' already exists
include/show_binlog_events.inc
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
diff --git a/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test b/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test
index c9658105847..72f1201c93c 100644
--- a/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test
+++ b/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test
@@ -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
diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test
index 6c2ba0dd6fc..cde3d12b0ce 100644
--- a/mysql-test/t/grant2.test
+++ b/mysql-test/t/grant2.test
@@ -667,5 +667,367 @@ 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 # -- Bug#12771903: User with create temporary tables priv only has full
+--echo # -- access to a regular table
+--echo # --
+--echo
+
+--echo ############################################################################
+--echo # Setup environment.
+--echo ###########################################################################
+
+--disable_warnings
+DROP DATABASE IF EXISTS mysqltest_db1;
+DROP DATABASE IF EXISTS mysqltest_db2;
+--enable_warnings
+
+CREATE DATABASE mysqltest_db1;
+CREATE DATABASE mysqltest_db2;
+
+--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 # mysqltest_u5@localhost has CREATE_TMP_ACL and SELECT_ACL, UPDATE_ACL,
+--echo # DELETE_ACL on mysqltest_db1; and only CREATE_TMP_ACL on mysqltest_db2.
+--echo # By means of this user we check privileges required for merge tables.
+GRANT CREATE TEMPORARY TABLES ON mysqltest_db1.* TO mysqltest_u5@localhost;
+GRANT CREATE TEMPORARY TABLES ON mysqltest_db2.* TO mysqltest_u5@localhost;
+GRANT SELECT, UPDATE, DELETE ON mysqltest_db1.* TO mysqltest_u5@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;
+
+CREATE TABLE mysqltest_db2.t2_1(a INT);
+
+--echo ###########################################################################
+--echo # Check that CREATE_TMP_ACL is enough to issue almost any supported
+--echo # SQL-statement against temporary tables (loosely follow order in
+--echo # 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 # Check that we do *not* allow creation of MERGE table with underlying
+--echo # temporary table without additional privileges.
+CREATE TEMPORARY TABLE t5(a INT) ENGINE = MyISAM;
+--error ER_TABLEACCESS_DENIED_ERROR
+CREATE TEMPORARY TABLE t6(a INT) ENGINE = MERGE UNION = (t5);
+--echo # Check that we allow creation of MERGE table with no underlying table
+--echo # without additional privileges.
+CREATE TEMPORARY TABLE t6(a INT) ENGINE = MERGE UNION = ();
+
+--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 no underlying
+--echo # without additional privileges.
+ALTER TABLE t6 UNION = ();
+--echo # Check that we do *not* allow altering of MERGE table with underlying
+--echo # temporary table without additional privileges.
+--error ER_TABLEACCESS_DENIED_ERROR
+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 # Check special case for MERGE-tables:
+--echo # - CREATE_TMP_ACL is required to create a temporary merge table;
+--echo # - SELECT_ACL, UPDATE_ACL and DELETE_ACL are required to include
+--echo # a temporary table into the underlying-table-list.
+--echo ###########################################################################
+
+--echo
+--echo # -- connect con4, mysqltest_u5@localhost, mysqltest_db1
+--connect (con4,localhost,mysqltest_u5,,mysqltest_db1)
+
+CREATE TEMPORARY TABLE t7(a INT);
+CREATE TEMPORARY TABLE t8(a INT);
+CREATE TEMPORARY TABLE t9(a INT);
+CREATE TEMPORARY TABLE t10(a INT) ENGINE = MERGE UNION = (t7, t8);
+
+ALTER TABLE t10 UNION = (t9);
+--error ER_TABLEACCESS_DENIED_ERROR
+ALTER TABLE t10 UNION = (mysqltest_db2.t2_1);
+
+CREATE TEMPORARY TABLE mysqltest_db2.t2_2(a INT) ENGINE = MERGE UNION = (t7, t8);
+
+ALTER TABLE mysqltest_db2.t2_2 UNION = (t9);
+ALTER TABLE mysqltest_db2.t2_2 UNION = ();
+
+DROP TEMPORARY TABLE mysqltest_db2.t2_2;
+DROP TEMPORARY TABLE t10;
+
+DROP TEMPORARY TABLE t7;
+DROP TEMPORARY TABLE t8;
+DROP TEMPORARY TABLE t9;
+
+--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
+
+--echo # -- disconnect con4
+--disconnect con4
+
+SET GLOBAL keycache1.key_buffer_size = 0;
+DROP DATABASE mysqltest_db1;
+DROP DATABASE mysqltest_db2;
+DROP USER mysqltest_u1@localhost;
+DROP USER mysqltest_u2@localhost;
+DROP USER mysqltest_u3@localhost;
+DROP USER mysqltest_u4@localhost;
+DROP USER mysqltest_u5@localhost;
+
+
# Wait till we reached the initial number of concurrent sessions
--source include/wait_until_count_sessions.inc
diff --git a/mysql-test/t/grant4.test b/mysql-test/t/grant4.test
index c30e0c28f31..756454237f5 100644
--- a/mysql-test/t/grant4.test
+++ b/mysql-test/t/grant4.test
@@ -142,3 +142,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;
diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test
index 7e198275730..18ec2b3d62f 100644
--- a/mysql-test/t/merge.test
+++ b/mysql-test/t/merge.test
@@ -2873,6 +2873,86 @@ drop view t3;
--echo End of 5.5 tests
+
+--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);
+
+# Uncomment the following to lines when MDEV-5042 is fixed.
+#--error ER_WRONG_MRG_TABLE
+#DELETE FROM t1;
+
+DROP TRIGGER trg1;
+DROP TABLE t1;
+DROP TABLE m1;
+
--disable_result_log
--disable_query_log
eval set global storage_engine=$default;
diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test
index f7d14464e4e..c34800976c7 100644
--- a/mysql-test/t/ps_ddl.test
+++ b/mysql-test/t/ps_ddl.test
@@ -2216,3 +2216,46 @@ 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 ONCE
+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);
+--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;
diff --git a/mysql-test/t/temp_table.test b/mysql-test/t/temp_table.test
index dd4ee2f6676..f594f0c6c48 100644
--- a/mysql-test/t/temp_table.test
+++ b/mysql-test/t/temp_table.test
@@ -276,3 +276,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;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 4663146674c..6a3d5a420e9 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -121,6 +121,7 @@
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
static void sql_kill(THD *thd, longlong id, killed_state state, killed_type type);
static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state);
+static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables);
static bool execute_show_status(THD *, TABLE_LIST *);
static bool execute_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
@@ -3728,8 +3729,7 @@ end_with_restore_list:
if (open_temporary_tables(thd, all_tables))
goto error;
- if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
- FALSE, UINT_MAX, FALSE))
+ if (lock_tables_precheck(thd, all_tables))
goto error;
thd->variables.option_bits|= OPTION_TABLE_LOCK;
@@ -7901,6 +7901,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.
@param thd thread handler