[Commits] 56a2835: MDEV-5535: Cannot reopen temporary table

Nirbhay Choubey nirbhay at mariadb.com
Fri May 27 03:42:49 EEST 2016


revision-id: 56a2835872c4ac7296ec0ae2ff618822855b0fc0 (mariadb-10.1.8-82-g56a2835)
parent(s): 28c289626f318631d707f85b057a90af99018b06
author: Nirbhay Choubey
committer: Nirbhay Choubey
timestamp: 2016-05-26 20:42:47 -0400
message:

MDEV-5535: Cannot reopen temporary table

mysqld maintains a list of TABLE objects for all temporary
tables in THD, where each table is represented by a TABLE
object. A query referencing a temporary table for more than
once, however, failed with ER_CANT_REOPEN_TABLE error.

This patch lift this restriction by preserving the TABLE_SHARE
object of each created temporary table, so that multiple instances
of TABLE objects can be created.

---
 libmysqld/CMakeLists.txt               |    1 +
 mysql-test/r/create.result             |    3 +-
 mysql-test/r/lock.result               |    5 -
 mysql-test/r/reopen_temp_table.result  |  133 +++
 mysql-test/r/sp-error.result           |    6 +-
 mysql-test/r/sp.result                 |   10 +-
 mysql-test/suite/handler/aria.result   |   22 +-
 mysql-test/suite/handler/handler.inc   |    7 +-
 mysql-test/suite/handler/heap.result   |   22 +-
 mysql-test/suite/handler/innodb.result |   22 +-
 mysql-test/suite/handler/myisam.result |   22 +-
 mysql-test/t/create.test               |    1 -
 mysql-test/t/lock.test                 |    5 -
 mysql-test/t/reopen_temp_table.test    |  128 +++
 mysql-test/t/sp-error.test             |    6 +-
 mysql-test/t/sp.test                   |    9 +-
 sql/CMakeLists.txt                     |    4 +-
 sql/log_event.cc                       |    2 +-
 sql/rpl_parallel.cc                    |    2 +-
 sql/rpl_rli.cc                         |   43 +-
 sql/rpl_rli.h                          |    2 +-
 sql/slave.cc                           |    4 +-
 sql/sp_head.cc                         |    2 +-
 sql/sql_admin.cc                       |   10 +-
 sql/sql_alter.cc                       |    6 +-
 sql/sql_base.cc                        |  959 +--------------------
 sql/sql_base.h                         |   26 -
 sql/sql_cache.cc                       |   42 +-
 sql/sql_class.cc                       |   31 +-
 sql/sql_class.h                        |  177 +++-
 sql/sql_db.cc                          |    5 +-
 sql/sql_handler.cc                     |   14 +-
 sql/sql_insert.cc                      |   11 +-
 sql/sql_parse.cc                       |   24 +-
 sql/sql_partition_admin.cc             |   14 +-
 sql/sql_prepare.cc                     |    7 +-
 sql/sql_show.cc                        |    9 +-
 sql/sql_table.cc                       |  150 ++--
 sql/sql_test.cc                        |    2 +-
 sql/sql_trigger.cc                     |    4 +-
 sql/sql_view.cc                        |    4 +-
 sql/sys_vars.cc                        |    5 +-
 sql/table.cc                           |    2 +-
 sql/table.h                            |   11 +-
 sql/table_cache.cc                     |    2 +-
 sql/table_cache.h                      |    1 -
 sql/temporary_tables.cc                | 1453 ++++++++++++++++++++++++++++++++
 sql/wsrep_applier.cc                   |   12 +-
 sql/wsrep_mysqld.cc                    |   23 +-
 sql/wsrep_thd.cc                       |    9 +-
 storage/spider/spd_direct_sql.cc       |    2 +-
 51 files changed, 2209 insertions(+), 1267 deletions(-)

diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt
index d4df327..9c7105c 100644
--- a/libmysqld/CMakeLists.txt
+++ b/libmysqld/CMakeLists.txt
@@ -109,6 +109,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
            ../sql/table_cache.cc ../sql/mf_iocache_encr.cc
            ../sql/item_inetfunc.cc
            ../sql/wsrep_dummy.cc ../sql/encryption.cc
+           ../sql/temporary_tables.cc
            ${GEN_SOURCES}
            ${MYSYS_LIBWRAP_SOURCE}
 )
diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result
index 5fc06e4..3fbd8f4 100644
--- a/mysql-test/r/create.result
+++ b/mysql-test/r/create.result
@@ -1732,7 +1732,8 @@ drop view t1;
 create table t1 (a int) select 1 as a;
 create temporary table if not exists t1 (a int) select * from t1;
 create temporary table if not exists t1 (a int) select * from t1;
-ERROR HY000: Can't reopen table: 't1'
+Warnings:
+Note	1050	Table 't1' already exists
 select * from t1;
 a
 1
diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result
index 501c379..d4e3d39 100644
--- a/mysql-test/r/lock.result
+++ b/mysql-test/r/lock.result
@@ -376,12 +376,7 @@ alter table t1 add column j int;
 unlock tables;
 drop table t1;
 create temporary table t1 (i int);
-#
-# This is just for test coverage purposes, 
-# when this is allowed, remove the --error.
-#
 lock tables t1 write, t1 as a read, t1 as b read;
-ERROR HY000: Can't reopen table: 't1'
 alter table t1 add column j int;
 unlock tables;
 drop table t1;
diff --git a/mysql-test/r/reopen_temp_table.result b/mysql-test/r/reopen_temp_table.result
new file mode 100644
index 0000000..f98ab96
--- /dev/null
+++ b/mysql-test/r/reopen_temp_table.result
@@ -0,0 +1,133 @@
+#
+# MDEV-5535: Cannot reopen temporary table
+#
+DROP DATABASE IF EXISTS temp_db;
+CREATE DATABASE temp_db;
+USE temp_db;
+#
+# Reopen temporary table
+#
+CREATE TEMPORARY TABLE t1(i int)ENGINE=INNODB;
+INSERT INTO t1 VALUES(1), (2);
+SELECT * FROM t1 a, t1 b;
+i	i
+1	1
+2	1
+1	2
+2	2
+DROP TABLE t1;
+#
+# CREATE & Stored routines
+#
+CREATE TEMPORARY TABLE t3 AS SELECT 1 AS a;
+CREATE PROCEDURE p1()
+BEGIN
+DROP TEMPORARY TABLE t3;
+end|
+CREATE FUNCTION f3() RETURNS INT
+BEGIN
+CALL p1();
+RETURN 1;
+END|
+PREPARE STMT FROM "SELECT f3() AS my_Column, a FROM t3";
+EXECUTE STMT;
+ERROR HY000: Can't reopen table: 't3'
+DROP TABLE t3;
+DROP FUNCTION f3;
+DROP PROCEDURE p1;
+CREATE TEMPORARY TABLE t4 (i INT);
+INSERT INTO t4 VALUES(1), (2);
+CREATE FUNCTION f4() RETURNS INT
+BEGIN
+DROP TEMPORARY TABLE t4;
+RETURN 1;
+END|
+SELECT f4() FROM t4;
+ERROR HY000: Can't reopen table: 't4'
+SELECT * FROM t4;
+i
+1
+2
+DROP TABLE t4;
+DROP FUNCTION f4;
+CREATE TEMPORARY TABLE t5 AS SELECT 1 AS a;
+CREATE PROCEDURE p2()
+BEGIN
+DROP TEMPORARY TABLE t5;
+END|
+CREATE FUNCTION f5() RETURNS INT
+BEGIN
+CALL p2();
+RETURN 1;
+END|
+SELECT f5() AS my_column, a FROM t5;
+ERROR HY000: Can't reopen table: 't5'
+DROP TABLE t5;
+DROP FUNCTION f5;
+DROP PROCEDURE p2;
+#
+# CTAS
+#
+CREATE TABLE t1(i INT);
+INSERT INTO t1 VALUES(1), (2);
+CREATE TEMPORARY TABLE t1
+SELECT temp_1.i a, temp_2.i b FROM t1 AS temp_1, t1 AS temp_2;
+SELECT * FROM t1;
+a	b
+1	1
+2	1
+1	2
+2	2
+DROP TABLE t1;
+SELECT * FROM t1;
+i
+1
+2
+DROP TABLE t1;
+#
+# HANDLER
+#
+CREATE TABLE t1 (a INT, KEY a(a));
+INSERT INTO t1 (a) VALUES (1), (2), (3), (4), (5);
+CREATE TABLE t2 (a INT, KEY a (a)) SELECT * FROM t1;
+CREATE TEMPORARY TABLE t3 (a INT, KEY a (a)) SELECT * FROM t2;
+HANDLER t3 OPEN;
+SELECT * FROM t1;
+a
+1
+2
+3
+4
+5
+LOCK TABLE t1 READ;
+HANDLER t3 OPEN;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
+UNLOCK TABLES;
+HANDLER t3 OPEN;
+ERROR 42000: Not unique table/alias: 't3'
+HANDLER t3 READ NEXT;
+a
+1
+HANDLER t3 OPEN AS t3_1;
+HANDLER t3_1 READ NEXT;
+a
+1
+HANDLER t3_1 READ NEXT;
+a
+2
+HANDLER t3 CLOSE;
+HANDLER t3_1 CLOSE;
+DROP TEMPORARY TABLE t3;
+DROP TABLE t1, t2;
+#
+# INSERT-SELECT
+#
+CREATE TEMPORARY TABLE t4 (a INT) ENGINE=MYISAM;
+INSERT INTO t4 VALUES(1), (2);
+INSERT INTO t4 SELECT * FROM t4;
+SELECT COUNT(*) FROM t4;
+COUNT(*)
+4
+DROP TABLE t4;
+# Cleanup
+DROP DATABASE temp_db;
diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result
index c56597e..97ebcf6 100644
--- a/mysql-test/r/sp-error.result
+++ b/mysql-test/r/sp-error.result
@@ -1524,14 +1524,14 @@ drop temporary table t1;
 return 1;
 end|
 create temporary table t1 as select f1();
-ERROR HY000: Can't reopen table: 't1'
+ERROR 42S02: Unknown table 'test.t1'
 create function f2() returns int
 begin
 create temporary table t2 as select f1();
 return 1;
 end|
 create temporary table t1 as select f2();
-ERROR HY000: Can't reopen table: 't1'
+ERROR 42S02: Unknown table 'test.t1'
 drop function f1;
 drop function f2;
 create function f1() returns int
@@ -1545,7 +1545,7 @@ create temporary table t2 as select f1();
 return 1;
 end|
 create temporary table t1 as select f2();
-ERROR HY000: Can't reopen table: 't2'
+ERROR 42S02: Unknown table 'test.t2,test.t1'
 drop function f1;
 drop function f2;
 create temporary table t2(a int);
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
index 51c1b19..0647267 100644
--- a/mysql-test/r/sp.result
+++ b/mysql-test/r/sp.result
@@ -1140,9 +1140,12 @@ insert into t3 values (1), (2), (3);
 return (select count(*) from t3 as a, t3 as b);
 end|
 select f11()|
-ERROR HY000: Can't reopen table: 'a'
+f11()
+9
 select f11() from t1|
-ERROR HY000: Can't reopen table: 'a'
+f11()
+9
+9
 create function f12_1() returns int
 begin
 drop temporary table if exists t3;
@@ -1156,6 +1159,7 @@ drop temporary table t3|
 select f12_1()|
 f12_1()
 3
+drop temporary table t3|
 select f12_1() from t1 limit 1|
 f12_1()
 3
@@ -6929,7 +6933,7 @@ CREATE TEMPORARY TABLE t3 LIKE t1;
 CREATE PROCEDURE p1 () BEGIN SELECT f1 FROM t3 AS A WHERE A.f1 IN ( SELECT f1 FROM t3 ) ;
 END|
 CALL p1;
-ERROR HY000: Can't reopen table: 'A'
+f1
 CREATE VIEW t3 AS SELECT f1 FROM t2 A WHERE A.f1 IN ( SELECT f1 FROM t2 );
 DROP TABLE t3;
 CALL p1;
diff --git a/mysql-test/suite/handler/aria.result b/mysql-test/suite/handler/aria.result
index e0b98bd..f07b154 100644
--- a/mysql-test/suite/handler/aria.result
+++ b/mysql-test/suite/handler/aria.result
@@ -628,7 +628,18 @@ handler a1 read a next;
 a	b
 3	d
 select a,b from t1;
-ERROR HY000: Can't reopen table: 'a1'
+a	b
+0	a
+1	b
+2	c
+3	d
+4	e
+5	f
+6	g
+7	h
+8	i
+9	j
+9	k
 handler a1 read a prev;
 a	b
 2	c
@@ -741,7 +752,7 @@ ERROR HY000: Can't execute the given command because you have active locked tabl
 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 reopen table: 't3'
+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;
@@ -1386,7 +1397,12 @@ handler t2 read a next;
 a	b
 3	NULL
 select * from t2;
-ERROR HY000: Can't reopen table: 't2'
+a	b
+1	NULL
+2	NULL
+3	NULL
+4	NULL
+5	NULL
 handler t2 read a next;
 a	b
 4	NULL
diff --git a/mysql-test/suite/handler/handler.inc b/mysql-test/suite/handler/handler.inc
index c71dc53..a5dc1c8 100644
--- a/mysql-test/suite/handler/handler.inc
+++ b/mysql-test/suite/handler/handler.inc
@@ -477,9 +477,6 @@ drop table t1;
 #
 # Bug#30882 Dropping a temporary table inside a stored function may cause a server crash
 #
-# Test HANDLER statements in conjunction with temporary tables. While the temporary table
-# is open by a HANDLER, no other statement can access it.
-#
 
 create temporary table t1 (a int, b char(1), key a using btree (a), key b using btree (a,b));
 insert into t1 values (0,"a"),(1,"b"),(2,"c"),(3,"d"),(4,"e"),
@@ -489,7 +486,6 @@ handler t1 open as a1;
 handler a1 read a=(1);
 handler a1 read a next;
 handler a1 read a next;
---error ER_CANT_REOPEN_TABLE
 select a,b from t1;
 handler a1 read a prev;
 handler a1 read a prev;
@@ -581,7 +577,7 @@ handler t1 open;
 handler t1 read next;
 --error ER_LOCK_OR_ACTIVE_TRANSACTION
 handler t2 close;
---error ER_CANT_REOPEN_TABLE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 handler t3 open;
 --echo # After UNLOCK TABLES handlers should be around and
 --echo # we should be able to continue reading through them.
@@ -1264,7 +1260,6 @@ handler t1 read a next;
 select * from t1;
 --echo # Sic: the position is not lost
 handler t2 read a next;
---error ER_CANT_REOPEN_TABLE
 select * from t2;
 handler t2 read a next;
 drop table t1;
diff --git a/mysql-test/suite/handler/heap.result b/mysql-test/suite/handler/heap.result
index 527986e..3f39d4d 100644
--- a/mysql-test/suite/handler/heap.result
+++ b/mysql-test/suite/handler/heap.result
@@ -628,7 +628,18 @@ handler a1 read a next;
 a	b
 3	d
 select a,b from t1;
-ERROR HY000: Can't reopen table: 'a1'
+a	b
+0	a
+1	b
+2	c
+3	d
+4	e
+5	f
+6	g
+7	h
+8	i
+9	j
+9	k
 handler a1 read a prev;
 a	b
 2	c
@@ -741,7 +752,7 @@ ERROR HY000: Can't execute the given command because you have active locked tabl
 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 reopen table: 't3'
+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;
@@ -1386,7 +1397,12 @@ handler t2 read a next;
 a	b
 3	NULL
 select * from t2;
-ERROR HY000: Can't reopen table: 't2'
+a	b
+1	NULL
+2	NULL
+3	NULL
+4	NULL
+5	NULL
 handler t2 read a next;
 a	b
 4	NULL
diff --git a/mysql-test/suite/handler/innodb.result b/mysql-test/suite/handler/innodb.result
index 78660b0..8ead3bc 100644
--- a/mysql-test/suite/handler/innodb.result
+++ b/mysql-test/suite/handler/innodb.result
@@ -629,7 +629,18 @@ handler a1 read a next;
 a	b
 3	d
 select a,b from t1;
-ERROR HY000: Can't reopen table: 'a1'
+a	b
+0	a
+1	b
+2	c
+3	d
+4	e
+5	f
+6	g
+7	h
+8	i
+9	j
+9	k
 handler a1 read a prev;
 a	b
 2	c
@@ -743,7 +754,7 @@ ERROR HY000: Can't execute the given command because you have active locked tabl
 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 reopen table: 't3'
+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;
@@ -1390,7 +1401,12 @@ handler t2 read a next;
 a	b
 3	NULL
 select * from t2;
-ERROR HY000: Can't reopen table: 't2'
+a	b
+1	NULL
+2	NULL
+3	NULL
+4	NULL
+5	NULL
 handler t2 read a next;
 a	b
 4	NULL
diff --git a/mysql-test/suite/handler/myisam.result b/mysql-test/suite/handler/myisam.result
index 9081722..43c5e74 100644
--- a/mysql-test/suite/handler/myisam.result
+++ b/mysql-test/suite/handler/myisam.result
@@ -628,7 +628,18 @@ handler a1 read a next;
 a	b
 3	d
 select a,b from t1;
-ERROR HY000: Can't reopen table: 'a1'
+a	b
+0	a
+1	b
+2	c
+3	d
+4	e
+5	f
+6	g
+7	h
+8	i
+9	j
+9	k
 handler a1 read a prev;
 a	b
 2	c
@@ -741,7 +752,7 @@ ERROR HY000: Can't execute the given command because you have active locked tabl
 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 reopen table: 't3'
+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;
@@ -1386,7 +1397,12 @@ handler t2 read a next;
 a	b
 3	NULL
 select * from t2;
-ERROR HY000: Can't reopen table: 't2'
+a	b
+1	NULL
+2	NULL
+3	NULL
+4	NULL
+5	NULL
 handler t2 read a next;
 a	b
 4	NULL
diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test
index 4d0675e..40aa86e 100644
--- a/mysql-test/t/create.test
+++ b/mysql-test/t/create.test
@@ -1618,7 +1618,6 @@ drop view t1;
 
 create table t1 (a int) select 1 as a;
 create temporary table if not exists t1 (a int) select * from t1;
---error ER_CANT_REOPEN_TABLE
 create temporary table if not exists t1 (a int) select * from t1;
 select * from t1;
 drop temporary table t1;
diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test
index 78f0e2e..f1c1360 100644
--- a/mysql-test/t/lock.test
+++ b/mysql-test/t/lock.test
@@ -444,11 +444,6 @@ alter table t1 add column j int;
 unlock tables;
 drop table t1;
 create temporary table t1 (i int);
---echo #
---echo # This is just for test coverage purposes, 
---echo # when this is allowed, remove the --error.
---echo #
---error ER_CANT_REOPEN_TABLE
 lock tables t1 write, t1 as a read, t1 as b read;
 alter table t1 add column j int;
 unlock tables;
diff --git a/mysql-test/t/reopen_temp_table.test b/mysql-test/t/reopen_temp_table.test
new file mode 100644
index 0000000..0cf3f77
--- /dev/null
+++ b/mysql-test/t/reopen_temp_table.test
@@ -0,0 +1,128 @@
+--source include/have_innodb.inc
+
+--echo #
+--echo # MDEV-5535: Cannot reopen temporary table
+--echo #
+
+--disable_warnings
+DROP DATABASE IF EXISTS temp_db;
+--enable_warnings
+
+CREATE DATABASE temp_db;
+USE temp_db;
+
+--echo #
+--echo # Reopen temporary table
+--echo #
+
+CREATE TEMPORARY TABLE t1(i int)ENGINE=INNODB;
+INSERT INTO t1 VALUES(1), (2);
+SELECT * FROM t1 a, t1 b;
+DROP TABLE t1;
+
+--echo #
+--echo # CREATE & Stored routines
+--echo #
+
+CREATE TEMPORARY TABLE t3 AS SELECT 1 AS a;
+DELIMITER |;
+CREATE PROCEDURE p1()
+BEGIN
+  DROP TEMPORARY TABLE t3;
+end|
+CREATE FUNCTION f3() RETURNS INT
+BEGIN
+  CALL p1();
+  RETURN 1;
+END|
+DELIMITER ;|
+PREPARE STMT FROM "SELECT f3() AS my_Column, a FROM t3";
+--error ER_CANT_REOPEN_TABLE
+EXECUTE STMT;
+DROP TABLE t3;
+DROP FUNCTION f3;
+DROP PROCEDURE p1;
+
+CREATE TEMPORARY TABLE t4 (i INT);
+INSERT INTO t4 VALUES(1), (2);
+DELIMITER |;
+CREATE FUNCTION f4() RETURNS INT
+BEGIN
+  DROP TEMPORARY TABLE t4;
+  RETURN 1;
+END|
+DELIMITER ;|
+--error ER_CANT_REOPEN_TABLE
+SELECT f4() FROM t4;
+SELECT * FROM t4;
+DROP TABLE t4;
+DROP FUNCTION f4;
+
+CREATE TEMPORARY TABLE t5 AS SELECT 1 AS a;
+DELIMITER |;
+CREATE PROCEDURE p2()
+BEGIN
+  DROP TEMPORARY TABLE t5;
+END|
+CREATE FUNCTION f5() RETURNS INT
+BEGIN
+  CALL p2();
+  RETURN 1;
+END|
+DELIMITER ;|
+--error ER_CANT_REOPEN_TABLE
+SELECT f5() AS my_column, a FROM t5;
+DROP TABLE t5;
+DROP FUNCTION f5;
+DROP PROCEDURE p2;
+
+--echo #
+--echo # CTAS
+--echo #
+
+CREATE TABLE t1(i INT);
+INSERT INTO t1 VALUES(1), (2);
+CREATE TEMPORARY TABLE t1
+  SELECT temp_1.i a, temp_2.i b FROM t1 AS temp_1, t1 AS temp_2;
+SELECT * FROM t1;
+DROP TABLE t1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+--echo #
+--echo # HANDLER
+--echo #
+
+CREATE TABLE t1 (a INT, KEY a(a));
+INSERT INTO t1 (a) VALUES (1), (2), (3), (4), (5);
+CREATE TABLE t2 (a INT, KEY a (a)) SELECT * FROM t1;
+CREATE TEMPORARY TABLE t3 (a INT, KEY a (a)) SELECT * FROM t2;
+HANDLER t3 OPEN;
+SELECT * FROM t1;
+LOCK TABLE t1 READ;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
+HANDLER t3 OPEN;
+UNLOCK TABLES;
+--error ER_NONUNIQ_TABLE
+HANDLER t3 OPEN;
+HANDLER t3 READ NEXT;
+HANDLER t3 OPEN AS t3_1;
+HANDLER t3_1 READ NEXT;
+HANDLER t3_1 READ NEXT;
+HANDLER t3 CLOSE;
+HANDLER t3_1 CLOSE;
+DROP TEMPORARY TABLE t3;
+DROP TABLE t1, t2;
+
+--echo #
+--echo # INSERT-SELECT
+--echo #
+
+CREATE TEMPORARY TABLE t4 (a INT) ENGINE=MYISAM;
+INSERT INTO t4 VALUES(1), (2);
+INSERT INTO t4 SELECT * FROM t4;
+SELECT COUNT(*) FROM t4;
+DROP TABLE t4;
+
+--echo # Cleanup
+DROP DATABASE temp_db;
diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test
index d403b19..722ab46 100644
--- a/mysql-test/t/sp-error.test
+++ b/mysql-test/t/sp-error.test
@@ -2231,7 +2231,7 @@ begin
  return 1;
 end|
 delimiter ;|
---error ER_CANT_REOPEN_TABLE
+--error ER_BAD_TABLE_ERROR
 create temporary table t1 as select f1();
 
 delimiter |;
@@ -2241,7 +2241,7 @@ begin
  return 1;
 end|
 delimiter ;|
---error ER_CANT_REOPEN_TABLE
+--error ER_BAD_TABLE_ERROR
 create temporary table t1 as select f2();
 
 drop function f1;
@@ -2259,7 +2259,7 @@ begin
  return 1;
 end|
 delimiter ;|
---error ER_CANT_REOPEN_TABLE
+--error ER_BAD_TABLE_ERROR
 create temporary table t1 as select f2();
 
 drop function f1;
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
index 9e6b919..c80e1ea 100644
--- a/mysql-test/t/sp.test
+++ b/mysql-test/t/sp.test
@@ -1368,11 +1368,6 @@ select f10()|
 create table t4 as select 1 as id|
 select f10()|
 
-# Practical cases which we don't handle well (yet)
-#
-# Function which does not work because of well-known and documented
-# limitation of MySQL. We can't use the several instances of the
-# same temporary table in statement.
 create function f11() returns int
 begin
   drop temporary table if exists t3;
@@ -1380,9 +1375,7 @@ begin
   insert into t3 values (1), (2), (3);
   return (select count(*) from t3 as a, t3 as b);
 end|
---error ER_CANT_REOPEN_TABLE
 select f11()|
---error ER_CANT_REOPEN_TABLE
 select f11() from t1|
 # Test that using a single table instance at a time works
 create function f12_1() returns int
@@ -1397,6 +1390,7 @@ create function f12_2() returns int
 
 drop temporary table t3|
 select f12_1()|
+drop temporary table t3|
 select f12_1() from t1 limit 1|
 
 # Cleanup
@@ -8278,7 +8272,6 @@ delimiter |;
 CREATE PROCEDURE p1 () BEGIN SELECT f1 FROM t3 AS A WHERE A.f1 IN ( SELECT f1 FROM t3 ) ;
 END|
 delimiter ;|
---error ER_CANT_REOPEN_TABLE
 CALL p1;
 CREATE VIEW t3 AS SELECT f1 FROM t2 A WHERE A.f1 IN ( SELECT f1 FROM t2 );
 DROP TABLE t3;
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 9b0017c..5977768 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -1,5 +1,5 @@
 # Copyright (c) 2006, 2014, Oracle and/or its affiliates.
-# Copyright (c) 2010, 2015, MariaDB
+# Copyright (c) 2010, 2016, MariaDB Corporation
 # 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -138,7 +138,7 @@ SET (SQL_SOURCE
                rpl_gtid.cc rpl_parallel.cc
                sql_type.cc sql_type.h
 	       ${WSREP_SOURCES}
-               table_cache.cc encryption.cc
+               table_cache.cc encryption.cc temporary_tables.cc
                ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
                ${GEN_SOURCES}
                ${GEN_DIGEST_SOURCES}
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 81b72fe..d9e36d3 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -6148,7 +6148,7 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi,
         update it inside mysql_load().
       */
       List<Item> tmp_list;
-      if (open_temporary_tables(thd, &tables) ||
+      if (thd->open_temporary_tables(&tables) ||
           mysql_load(thd, &ex, &tables, field_list, tmp_list, tmp_list,
                      handle_dup, ignore, net != 0))
         thd->is_slave_error= 1;
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 526c3ec..69f7843 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -1364,7 +1364,7 @@ static void signal_error_to_sql_driver_thread(THD *thd, rpl_group_info *rgi,
   thd->reset_query();
   thd->reset_db(NULL, 0);
   thd_proc_info(thd, "Slave worker thread exiting");
-  thd->temporary_tables= 0;
+  thd->reset_temporary_tables();
   mysql_mutex_lock(&LOCK_thread_count);
   THD_CHECK_SENTRY(thd);
   delete thd;
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 7bdd9c1..77f458b 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -1,5 +1,6 @@
 /* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
    Copyright (c) 2010, 2013, Monty Program Ab
+   Copyright (c) 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -52,8 +53,7 @@ int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
    no_storage(FALSE), replicate_same_server_id(::replicate_same_server_id),
    info_fd(-1), cur_log_fd(-1), relay_log(&sync_relaylog_period),
    sync_counter(0), is_relay_log_recovery(is_slave_recovery),
-   save_temporary_tables(0), mi(0),
-   inuse_relaylog_list(0), last_inuse_relaylog(0),
+   mi(0), inuse_relaylog_list(0), last_inuse_relaylog(0),
    cur_log_old_open_count(0), group_relay_log_pos(0), 
    event_relay_log_pos(0),
 #if HAVE_valgrind
@@ -94,6 +94,7 @@ int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
   mysql_cond_init(key_relay_log_info_stop_cond, &stop_cond, NULL);
   mysql_cond_init(key_relay_log_info_log_space_cond, &log_space_cond, NULL);
   relay_log.init_pthread_objects();
+  save_temporary_tables.empty();
   DBUG_VOID_RETURN;
 }
 
@@ -1060,24 +1061,43 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
 
 void Relay_log_info::close_temporary_tables()
 {
-  TABLE *table,*next;
   DBUG_ENTER("Relay_log_info::close_temporary_tables");
 
-  for (table=save_temporary_tables ; table ; table=next)
+  TMP_TABLE_SHARE *share;
+  TABLE *table;
+
+  while ((share= save_temporary_tables.pop_front()))
   {
-    next=table->next;
+    /*
+      Iterate over the list of tables for this TABLE_SHARE and close them.
+    */
+    while ((table= share->all_tmp_tables.pop_front()))
+    {
+      DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
+                              table->s->db.str, table->s->table_name.str));
+
+      /* Reset in_use as the table may have been created by another thd */
+      table->in_use= 0;
+      free_io_cache(table);
+      /*
+        Lets not free TABLE_SHARE here as there could be multiple TABLEs opened
+        for the same table (TABLE_SHARE).
+      */
+      closefrm(table, false);
+      my_free(table);
+    }
 
-    /* Reset in_use as the table may have been created by another thd */
-    table->in_use=0;
     /*
       Don't ask for disk deletion. For now, anyway they will be deleted when
       slave restarts, but it is a better intention to not delete them.
     */
-    DBUG_PRINT("info", ("table: 0x%lx", (long) table));
-    close_temporary(table, 1, 0);
+
+    free_table_share(share);
+    my_free(share);
   }
-  save_temporary_tables= 0;
-  slave_open_temp_tables= 0;
+
+  save_temporary_tables.empty();
+
   DBUG_VOID_RETURN;
 }
 
@@ -1753,6 +1773,7 @@ void rpl_group_info::cleanup_context(THD *thd, bool error)
   }
   m_table_map.clear_tables();
   slave_close_thread_tables(thd);
+
   if (error)
   {
     thd->mdl_context.release_transactional_locks();
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index 23ab166..bbffd3d 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -148,7 +148,7 @@ class Relay_log_info : public Slave_reporting_capability
 
     Protected by data_lock.
   */
-  TABLE *save_temporary_tables;
+  All_tmp_tables_list save_temporary_tables;
 
   /*
     standard lock acquisition order to avoid deadlocks:
diff --git a/sql/slave.cc b/sql/slave.cc
index 895a8c2..506c95f 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1,5 +1,5 @@
 /* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
-   Copyright (c) 2008, 2015, MariaDB
+   Copyright (c) 2008, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -4915,7 +4915,7 @@ pthread_handler_t handle_slave_sql(void *arg)
     TODO: see if we can do this conditionally in next_event() instead
     to avoid unneeded position re-init
   */
-  thd->temporary_tables = 0; // remove tempation from destructor to close them
+  thd->reset_temporary_tables();
   THD_CHECK_SENTRY(thd);
   rli->sql_driver_thd= 0;
   mysql_mutex_lock(&LOCK_thread_count);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index cf0b8e8..399d1a2 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -3007,7 +3007,7 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
     Check whenever we have access to tables for this statement
     and open and lock them before executing instructions core function.
   */
-  if (open_temporary_tables(thd, tables) ||
+  if (thd->open_temporary_tables(tables) ||
       check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)
       || open_and_lock_tables(thd, tables, TRUE, 0))
     result= -1;
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 0787aa9..76898f0 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -1,5 +1,5 @@
 /* Copyright (c) 2010, 2014, Oracle and/or its affiliates.
-   Copyright (c) 2012, 2015, MariaDB
+   Copyright (c) 2012, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -54,7 +54,7 @@ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list)
 
   DEBUG_SYNC(thd, "ha_admin_try_alter");
   tmp_disable_binlog(thd); // binlogging is done by caller if wanted
-  result_code= (open_temporary_tables(thd, table_list) ||
+  result_code= (thd->open_temporary_tables(table_list) ||
                 mysql_recreate_table(thd, table_list, false));
   reenable_binlog(thd);
   /*
@@ -434,7 +434,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
 
         da->push_warning_info(&tmp_wi);
 
-        open_error= (open_temporary_tables(thd, table) ||
+        open_error= (thd->open_temporary_tables(table) ||
                      open_and_lock_tables(thd, table, TRUE, 0));
 
         da->pop_warning_info();
@@ -449,7 +449,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
           mode. It does make sense for the user to see such errors.
         */
 
-        open_error= (open_temporary_tables(thd, table) ||
+        open_error= (thd->open_temporary_tables(table) ||
                      open_and_lock_tables(thd, table, TRUE, 0));
       }
       thd->prepare_derived_at_open= FALSE;
@@ -915,7 +915,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
         table->mdl_request.ticket= NULL;
         DEBUG_SYNC(thd, "ha_admin_open_ltable");
         table->mdl_request.set_type(MDL_SHARED_WRITE);
-        if (!open_temporary_tables(thd, table) &&
+        if (!thd->open_temporary_tables(table) &&
             (table->table= open_ltable(thd, table, lock_type, 0)))
         {
           uint save_flags;
diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc
index bff45e0..737d3cc 100644
--- a/sql/sql_alter.cc
+++ b/sql/sql_alter.cc
@@ -1,4 +1,5 @@
 /* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+   Copyright (c) 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -16,7 +17,6 @@
 #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"
 #include "wsrep_mysqld.h"
 
@@ -305,10 +305,8 @@ bool Sql_cmd_alter_table::execute(THD *thd)
   thd->enable_slow_log= opt_log_slow_admin_statements;
 
 #ifdef WITH_WSREP
-  TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
-
   if ((!thd->is_current_stmt_binlog_format_row() ||
-       !find_temporary_table(thd, first_table)))
+       !thd->find_temporary_table(first_table)))
   {
     WSREP_TO_ISOLATION_BEGIN(((lex->name.str) ? select_lex->db : NULL),
                              ((lex->name.str) ? lex->name.str : NULL),
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 5c3b7c2..f8bb641 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1,5 +1,5 @@
 /* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
-   Copyright (c) 2010, 2015, MariaDB
+   Copyright (c) 2010, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -176,47 +176,13 @@ static bool check_and_update_table_version(THD *thd, TABLE_LIST *tables,
 
 
 /**
-  Create a table cache/table definition cache key
-
-  @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.
-
-  @note
-    The table cache_key is created from:
-    db_name + \0
-    table_name + \0
-
-    additionally we add the following to make each tmp table
-    unique on the slave:
-
-    4 bytes for master thread id
-    4 bytes pseudo thread id
-
-  @return Length of key.
-*/
-
-uint create_tmp_table_def_key(THD *thd, char *key,
-                              const char *db, const char *table_name)
-{
-  uint key_length= tdc_create_key(key, db, table_name);
-  int4store(key + key_length, thd->variables.server_id);
-  int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
-  key_length+= TMP_TABLE_KEY_EXTRA;
-  return key_length;
-}
-
-
-/**
   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
+        key in a buffer provided 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
@@ -308,7 +274,7 @@ static my_bool list_open_tables_callback(TDC_element *element,
   (*arg->start_list)->in_use= 0;
 
   mysql_mutex_lock(&element->LOCK_table_share);
-  TDC_element::All_share_tables_list::Iterator it(element->all_tables);
+  All_share_tables_list::Iterator it(element->all_tables);
   TABLE *table;
   while ((table= it++))
     if (table->in_use)
@@ -389,7 +355,7 @@ void free_io_cache(TABLE *table)
 
 void kill_delayed_threads_for_table(TDC_element *element)
 {
-  TDC_element::All_share_tables_list::Iterator it(element->all_tables);
+  All_share_tables_list::Iterator it(element->all_tables);
   TABLE *tab;
 
   mysql_mutex_assert_owner(&element->LOCK_table_share);
@@ -684,96 +650,6 @@ bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
 }
 
 
-/**
-  Mark all temporary tables which were used by the current statement or
-  substatement as free for reuse, but only if the query_id can be cleared.
-
-  @param thd thread context
-
-  @remark For temp tables associated with a open SQL HANDLER the query_id
-          is not reset until the HANDLER is closed.
-*/
-
-static void mark_temp_tables_as_free_for_reuse(THD *thd)
-{
-  rpl_group_info *rgi_slave;
-  DBUG_ENTER("mark_temp_tables_as_free_for_reuse");
-
-  if (thd->query_id == 0)
-  {
-    /* Thread has not executed any statement and has not used any tmp tables */
-    DBUG_VOID_RETURN;
-  }
-  
-  rgi_slave=thd->rgi_slave;
-  if ((!rgi_slave && thd->temporary_tables) ||
-      (rgi_slave && unlikely(rgi_slave->rli->save_temporary_tables)))
-  {
-    thd->lock_temporary_tables();
-    for (TABLE *table= thd->temporary_tables ; table ; table= table->next)
-    {
-      if ((table->query_id == thd->query_id) && ! table->open_by_handler)
-        mark_tmp_table_for_reuse(table);
-    }
-    thd->unlock_temporary_tables();
-    if (rgi_slave)
-    {
-      /*
-        Temporary tables are shared with other by sql execution threads.
-        As a safety messure, clear the pointer to the common area.
-      */
-      thd->temporary_tables= 0;
-    }
-  }
-  DBUG_VOID_RETURN;
-}
-
-
-/**
-  Reset a single temporary table.
-  Effectively this "closes" one temporary table,
-  in a session.
-
-  @param table     Temporary table.
-*/
-
-void mark_tmp_table_for_reuse(TABLE *table)
-{
-  DBUG_ENTER("mark_tmp_table_for_reuse");
-  DBUG_ASSERT(table->s->tmp_table);
-
-  table->query_id= 0;
-  table->file->ha_reset();
-
-  /* Detach temporary MERGE children from temporary parent. */
-  DBUG_ASSERT(table->file);
-  table->file->extra(HA_EXTRA_DETACH_CHILDREN);
-
-  /*
-    Reset temporary table lock type to it's default value (TL_WRITE).
-
-    Statements such as INSERT INTO .. SELECT FROM tmp, CREATE TABLE
-    .. SELECT FROM tmp and UPDATE may under some circumstances modify
-    the lock type of the tables participating in the statement. This
-    isn't a problem for non-temporary tables since their lock type is
-    reset at every open, but the same does not occur for temporary
-    tables for historical reasons.
-
-    Furthermore, the lock type of temporary tables is not really that
-    important because they can only be used by one query at a time and
-    not even twice in a query -- a temporary table is represented by
-    only one TABLE object. Nonetheless, it's safer from a maintenance
-    point of view to reset the lock type of this singleton TABLE object
-    as to not cause problems when the table is reused.
-
-    Even under LOCK TABLES mode its okay to reset the lock type as
-    LOCK TABLES is allowed (but ignored) for a temporary table.
-  */
-  table->reginfo.lock_type= TL_WRITE;
-  DBUG_VOID_RETURN;
-}
-
-
 /*
   Mark all tables in the list which were used by current substatement
   as free for reuse.
@@ -993,7 +869,7 @@ void close_thread_tables(THD *thd)
   /*
     Mark all temporary tables used by this statement as free for reuse.
   */
-  mark_temp_tables_as_free_for_reuse(thd);
+  thd->mark_tmp_tables_as_free_for_reuse();
 
   if (thd->locked_tables_mode)
   {
@@ -1107,195 +983,6 @@ void close_thread_table(THD *thd, TABLE **table_ptr)
 }
 
 
-/* close_temporary_tables' internal, 4 is due to uint4korr definition */
-static inline uint  tmpkeyval(THD *thd, TABLE *table)
-{
-  return uint4korr(table->s->table_cache_key.str + table->s->table_cache_key.length - 4);
-}
-
-
-/*
-  Close all temporary tables created by 'CREATE TEMPORARY TABLE' for thread
-  creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread 
-
-  Temporary tables created in a sql slave is closed by
-  Relay_log_info::close_temporary_tables()
-
-*/
-
-bool close_temporary_tables(THD *thd)
-{
-  DBUG_ENTER("close_temporary_tables");
-  TABLE *table;
-  TABLE *next= NULL;
-  TABLE *prev_table;
-  /* Assume thd->variables.option_bits has OPTION_QUOTE_SHOW_CREATE */
-  bool was_quote_show= TRUE;
-  bool error= 0;
-
-  if (!thd->temporary_tables)
-    DBUG_RETURN(FALSE);
-  DBUG_ASSERT(!thd->rgi_slave);
-
-  /*
-    Ensure we don't have open HANDLERs for tables we are about to close.
-    This is necessary when close_temporary_tables() is called as part
-    of execution of BINLOG statement (e.g. for format description event).
-  */
-  mysql_ha_rm_temporary_tables(thd);
-  if (!mysql_bin_log.is_open())
-  {
-    TABLE *tmp_next;
-    for (TABLE *t= thd->temporary_tables; t; t= tmp_next)
-    {
-      tmp_next= t->next;
-      mysql_lock_remove(thd, thd->lock, t);
-      close_temporary(t, 1, 1);
-    }
-    thd->temporary_tables= 0;
-    DBUG_RETURN(FALSE);
-  }
-
-  /* Better add "if exists", in case a RESET MASTER has been done */
-  const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ";
-  char buf[FN_REFLEN];
-  String s_query(buf, sizeof(buf), system_charset_info);
-  bool found_user_tables= FALSE;
-
-  s_query.copy(stub, sizeof(stub)-1, system_charset_info);
-
-  /*
-    Insertion sort of temp tables by pseudo_thread_id to build ordered list
-    of sublists of equal pseudo_thread_id
-  */
-
-  for (prev_table= thd->temporary_tables, table= prev_table->next;
-       table;
-       prev_table= table, table= table->next)
-  {
-    TABLE *prev_sorted /* same as for prev_table */, *sorted;
-    if (is_user_table(table))
-    {
-      if (!found_user_tables)
-        found_user_tables= true;
-      for (prev_sorted= NULL, sorted= thd->temporary_tables; sorted != table;
-           prev_sorted= sorted, sorted= sorted->next)
-      {
-        if (!is_user_table(sorted) ||
-            tmpkeyval(thd, sorted) > tmpkeyval(thd, table))
-        {
-          /* move into the sorted part of the list from the unsorted */
-          prev_table->next= table->next;
-          table->next= sorted;
-          if (prev_sorted)
-          {
-            prev_sorted->next= table;
-          }
-          else
-          {
-            thd->temporary_tables= table;
-          }
-          table= prev_table;
-          break;
-        }
-      }
-    }
-  }
-
-  /* We always quote db,table names though it is slight overkill */
-  if (found_user_tables &&
-      !(was_quote_show= MY_TEST(thd->variables.option_bits &
-                                OPTION_QUOTE_SHOW_CREATE)))
-  {
-    thd->variables.option_bits |= OPTION_QUOTE_SHOW_CREATE;
-  }
-
-  /* scan sorted tmps to generate sequence of DROP */
-  for (table= thd->temporary_tables; table; table= next)
-  {
-    if (is_user_table(table))
-    {
-      bool save_thread_specific_used= thd->thread_specific_used;
-      my_thread_id save_pseudo_thread_id= thd->variables.pseudo_thread_id;
-      char db_buf[FN_REFLEN];
-      String db(db_buf, sizeof(db_buf), system_charset_info);
-
-      /* Set pseudo_thread_id to be that of the processed table */
-      thd->variables.pseudo_thread_id= tmpkeyval(thd, table);
-
-      db.copy(table->s->db.str, table->s->db.length, system_charset_info);
-      /* Reset s_query() if changed by previous loop */
-      s_query.length(sizeof(stub)-1);
-
-      /* Loop forward through all tables that belong to a common database
-         within the sublist of common pseudo_thread_id to create single
-         DROP query 
-      */
-      for (;
-           table && is_user_table(table) &&
-             tmpkeyval(thd, table) == thd->variables.pseudo_thread_id &&
-             table->s->db.length == db.length() &&
-             memcmp(table->s->db.str, db.ptr(), db.length()) == 0;
-           table= next)
-      {
-        /*
-          We are going to add ` around the table names and possible more
-          due to special characters
-        */
-        append_identifier(thd, &s_query, table->s->table_name.str,
-                          strlen(table->s->table_name.str));
-        s_query.append(',');
-        next= table->next;
-        mysql_lock_remove(thd, thd->lock, table);
-        close_temporary(table, 1, 1);
-      }
-      thd->clear_error();
-      CHARSET_INFO *cs_save= thd->variables.character_set_client;
-      thd->variables.character_set_client= system_charset_info;
-      thd->thread_specific_used= TRUE;
-      Query_log_event qinfo(thd, s_query.ptr(),
-                            s_query.length() - 1 /* to remove trailing ',' */,
-                            FALSE, TRUE, FALSE, 0);
-      qinfo.db= db.ptr();
-      qinfo.db_len= db.length();
-      thd->variables.character_set_client= cs_save;
-
-      thd->get_stmt_da()->set_overwrite_status(true);
-      if ((error= (mysql_bin_log.write(&qinfo) || error)))
-      {
-        /*
-          If we're here following THD::cleanup, thence the connection
-          has been closed already. So lets print a message to the
-          error log instead of pushing yet another error into the
-          stmt_da.
-
-          Also, we keep the error flag so that we propagate the error
-          up in the stack. This way, if we're the SQL thread we notice
-          that close_temporary_tables failed. (Actually, the SQL
-          thread only calls close_temporary_tables while applying old
-          Start_log_event_v3 events.)
-        */
-        sql_print_error("Failed to write the DROP statement for "
-                        "temporary tables to binary log");
-      }
-      thd->get_stmt_da()->set_overwrite_status(false);
-
-      thd->variables.pseudo_thread_id= save_pseudo_thread_id;
-      thd->thread_specific_used= save_thread_specific_used;
-    }
-    else
-    {
-      next= table->next;
-      close_temporary(table, 1, 1);
-    }
-  }
-  if (!was_quote_show)
-    thd->variables.option_bits&= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */
-  thd->temporary_tables=0;
-
-  DBUG_RETURN(error);
-}
-
 /*
   Find table in list.
 
@@ -1568,292 +1255,6 @@ void update_non_unique_table_error(TABLE_LIST *update,
 
 
 /**
-  Find temporary table specified by database and table names in the
-  THD::temporary_tables list.
-
-  @return TABLE instance if a temporary table has been found; NULL otherwise.
-*/
-
-TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name)
-{
-  char key[MAX_DBKEY_LENGTH];
-  uint key_length= create_tmp_table_def_key(thd, key, db, table_name);
-  return find_temporary_table(thd, key, key_length);
-}
-
-
-/**
-  Find a temporary table specified by TABLE_LIST instance in the
-  THD::temporary_tables list.
-
-  @return TABLE instance if a temporary table has been found; NULL otherwise.
-*/
-
-TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl)
-{
-  const char *tmp_key;
-  char key[MAX_DBKEY_LENGTH];
-  uint key_length;
-
-  key_length= get_table_def_key(tl, &tmp_key);
-  memcpy(key, tmp_key, key_length);
-  int4store(key + key_length, thd->variables.server_id);
-  int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
-
-  return find_temporary_table(thd, key, key_length + TMP_TABLE_KEY_EXTRA);
-}
-
-
-static bool
-use_temporary_table(THD *thd, TABLE *table, TABLE **out_table)
-{
-  *out_table= table;
-  if (!table)
-    return false;
-  /*
-    Temporary tables are not safe for parallel replication. They were
-    designed to be visible to one thread only, so have no table locking.
-    Thus there is no protection against two conflicting transactions
-    committing in parallel and things like that.
-
-    So for now, anything that uses temporary tables will be serialised
-    with anything before it, when using parallel replication.
-
-    ToDo: We might be able to introduce a reference count or something
-    on temp tables, and have slave worker threads wait for it to reach
-    zero before being allowed to use the temp table. Might not be worth
-    it though, as statement-based replication using temporary tables is
-    in any case rather fragile.
-  */
-  if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec &&
-      thd->wait_for_prior_commit())
-    return true;
-  /*
-    We need to set the THD as it may be different in case of
-    parallel replication
-  */
-  if (table->in_use != thd)
-  {
-    table->in_use= thd;
-#ifdef REMOVE_AFTER_MERGE_WITH_10
-    if (thd->rgi_slave)
-    {
-      /*
-        We may be stealing an opened temporary tables from one slave
-        thread to another, we need to let the performance schema know that,
-        for aggregates per thread to work properly.
-      */
-      MYSQL_UNBIND_TABLE(table->file);
-      MYSQL_REBIND_TABLE(table->file);
-    }
-#endif
-  }
-  return false;
-}
-
-bool
-find_and_use_temporary_table(THD *thd, const char *db, const char *table_name,
-                             TABLE **out_table)
-{
-  return use_temporary_table(thd, find_temporary_table(thd, db, table_name),
-                             out_table);
-}
-
-
-bool
-find_and_use_temporary_table(THD *thd, const TABLE_LIST *tl, TABLE **out_table)
-{
-  return use_temporary_table(thd, find_temporary_table(thd, tl), out_table);
-}
-
-
-/**
-  Find a temporary table specified by a key in the THD::temporary_tables list.
-
-  @return TABLE instance if a temporary table has been found; NULL otherwise.
-*/
-
-TABLE *find_temporary_table(THD *thd,
-                            const char *table_key,
-                            uint table_key_length)
-{
-  TABLE *result= 0;
-  if (!thd->have_temporary_tables())
-    return NULL;
-
-  thd->lock_temporary_tables();
-  for (TABLE *table= thd->temporary_tables; table; table= table->next)
-  {
-    if (table->s->table_cache_key.length == table_key_length &&
-        !memcmp(table->s->table_cache_key.str, table_key, table_key_length))
-    {
-      result= table;
-      break;
-    }
-  }
-  thd->unlock_temporary_tables();
-  return result;
-}
-
-
-/**
-  Drop a temporary table.
-
-  Try to locate the table in the list of thd->temporary_tables.
-  If the table is found:
-   - if the table is being used by some outer statement, fail.
-   - if the table is locked with LOCK TABLES or by prelocking,
-   unlock it and remove it from the list of locked tables
-   (THD::lock). Currently only transactional temporary tables
-   are locked.
-   - Close the temporary table, remove its .FRM
-   - remove the table from the list of temporary tables
-
-  This function is used to drop user temporary tables, as well as
-  internal tables created in CREATE TEMPORARY TABLE ... SELECT
-  or ALTER TABLE. Even though part of the work done by this function
-  is redundant when the table is internal, as long as we
-  link both internal and user temporary tables into the same
-  thd->temporary_tables list, it's impossible to tell here whether
-  we're dealing with an internal or a user temporary table.
-
-  @param thd      Thread handler
-  @param table	  Temporary table to be deleted
-  @param is_trans Is set to the type of the table:
-                  transactional (e.g. innodb) as TRUE or non-transactional
-                  (e.g. myisam) as FALSE.
-
-  @retval  0  the table was found and dropped successfully.
-  @retval -1  the table is in use by a outer query
-*/
-
-int drop_temporary_table(THD *thd, TABLE *table, bool *is_trans)
-{
-  DBUG_ENTER("drop_temporary_table");
-  DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
-                          table->s->db.str, table->s->table_name.str));
-
-  /* Table might be in use by some outer statement. */
-  if (table->query_id && table->query_id != thd->query_id)
-  {
-    DBUG_PRINT("info", ("table->query_id: %lu  thd->query_id: %lu",
-                        (ulong) table->query_id, (ulong) thd->query_id));
-    
-    my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
-    DBUG_RETURN(-1);
-  }
-
-  *is_trans= table->file->has_transactions();
-
-  /*
-    If LOCK TABLES list is not empty and contains this table,
-    unlock the table and remove the table from this list.
-  */
-  mysql_lock_remove(thd, thd->lock, table);
-  close_temporary_table(thd, table, 1, 1);
-  DBUG_RETURN(0);
-}
-
-
-/*
-  unlink from thd->temporary tables and close temporary table
-*/
-
-void close_temporary_table(THD *thd, TABLE *table,
-                           bool free_share, bool delete_table)
-{
-  DBUG_ENTER("close_temporary_table");
-  DBUG_PRINT("tmptable", ("closing table: '%s'.'%s' 0x%lx  alias: '%s'",
-                          table->s->db.str, table->s->table_name.str,
-                          (long) table, table->alias.c_ptr()));
-
-  thd->lock_temporary_tables();
-  if (table->prev)
-  {
-    table->prev->next= table->next;
-    if (table->prev->next)
-      table->next->prev= table->prev;
-  }
-  else
-  {
-    /* removing the item from the list */
-    DBUG_ASSERT(table == thd->temporary_tables);
-    /*
-      slave must reset its temporary list pointer to zero to exclude
-      passing non-zero value to end_slave via rli->save_temporary_tables
-      when no temp tables opened, see an invariant below.
-    */
-    thd->temporary_tables= table->next;
-    if (thd->temporary_tables)
-      table->next->prev= 0;
-  }
-  if (thd->rgi_slave)
-  {
-    /* natural invariant of temporary_tables */
-    DBUG_ASSERT(slave_open_temp_tables || !thd->temporary_tables);
-    thread_safe_decrement32(&slave_open_temp_tables);
-    table->in_use= 0;                           // No statistics
-  }
-  thd->unlock_temporary_tables();
-  close_temporary(table, free_share, delete_table);
-  DBUG_VOID_RETURN;
-}
-
-
-/*
-  Close and delete a temporary table
-
-  NOTE
-    This dosn't unlink table from thd->temporary
-    If this is needed, use close_temporary_table()
-*/
-
-void close_temporary(TABLE *table, bool free_share, bool delete_table)
-{
-  handlerton *table_type= table->s->db_type();
-  DBUG_ENTER("close_temporary");
-  DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
-                          table->s->db.str, table->s->table_name.str));
-
-  free_io_cache(table);
-  closefrm(table, 0);
-  if (delete_table)
-    rm_temporary_table(table_type, table->s->path.str);
-  if (free_share)
-  {
-    free_table_share(table->s);
-    my_free(table);
-  }
-  DBUG_VOID_RETURN;
-}
-
-
-/*
-  Used by ALTER TABLE when the table is a temporary one. It changes something
-  only if the ALTER contained a RENAME clause (otherwise, table_name is the old
-  name).
-  Prepares a table cache key, which is the concatenation of db, table_name and
-  thd->slave_proxy_id, separated by '\0'.
-*/
-
-bool rename_temporary_table(THD* thd, TABLE *table, const char *db,
-			    const char *table_name)
-{
-  char *key;
-  uint key_length;
-  TABLE_SHARE *share= table->s;
-  DBUG_ENTER("rename_temporary_table");
-
-  if (!(key=(char*) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH)))
-    DBUG_RETURN(1);				/* purecov: inspected */
-
-  key_length= create_tmp_table_def_key(thd, key, db, table_name);
-  share->set_table_cache_key(key, key_length);
-  DBUG_RETURN(0);
-}
-
-
-/**
    Force all other threads to stop using the table by upgrading
    metadata lock on it and remove unused TABLE instances from cache.
 
@@ -1918,7 +1319,7 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name,
 {
   DBUG_ENTER("drop_open_table");
   if (table->s->tmp_table)
-    close_temporary_table(thd, table, 1, 1);
+    thd->drop_temporary_table(table, NULL, true);
   else
   {
     DBUG_ASSERT(table == thd->open_tables);
@@ -4010,7 +3411,7 @@ thr_lock_type read_lock_type_for_table(THD *thd,
       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 till 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.
@@ -4026,7 +3427,7 @@ thr_lock_type read_lock_type_for_table(THD *thd,
         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);
+    error= thd->open_temporary_table(tables);
 
     if (!error && !tables->table)
       error= open_table(thd, tables, ot_ctx);
@@ -4045,7 +3446,8 @@ thr_lock_type read_lock_type_for_table(THD *thd,
     Repair_mrg_table_error_handler repair_mrg_table_handler;
     thd->push_internal_handler(&repair_mrg_table_handler);
 
-    error= open_temporary_table(thd, tables);
+    error= thd->open_temporary_table(tables);
+
     if (!error && !tables->table)
       error= open_table(thd, tables, ot_ctx);
 
@@ -4061,7 +3463,7 @@ thr_lock_type read_lock_type_for_table(THD *thd,
         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);
+      error= thd->open_temporary_table(tables);
     }
 
     if (!error && !tables->table)
@@ -4233,6 +3635,7 @@ thr_lock_type read_lock_type_for_table(THD *thd,
   new locks, so use open_tables_check_upgradable_mdl() instead.
 
   @param thd               Thread context.
+  @param options           DDL options.
   @param tables_start      Start of list of tables on which upgradable locks
                            should be acquired.
   @param tables_end        End of list of tables.
@@ -4435,6 +3838,7 @@ thr_lock_type read_lock_type_for_table(THD *thd,
   Open all tables in list
 
   @param[in]     thd      Thread context.
+  @param[in]     options  DDL options.
   @param[in,out] start    List of tables to be open (it can be adjusted for
                           statement that uses tables only implicitly, e.g.
                           for "SELECT f1()").
@@ -4608,7 +4012,7 @@ bool open_tables(THD *thd, const DDL_options_st &options,
             goto error;
 
           /* Re-open temporary tables after close_tables_for_reopen(). */
-          if (open_temporary_tables(thd, *start))
+          if (thd->open_temporary_tables(*start))
             goto error;
 
           error= FALSE;
@@ -4670,7 +4074,7 @@ bool open_tables(THD *thd, const DDL_options_st &options,
               goto error;
 
             /* Re-open temporary tables after close_tables_for_reopen(). */
-            if (open_temporary_tables(thd, *start))
+            if (thd->open_temporary_tables(*start))
               goto error;
 
             error= FALSE;
@@ -5125,7 +4529,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
   bool error;
   DBUG_ENTER("open_ltable");
 
-  /* Ignore temporary tables as they have already ben opened*/
+  /* Ignore temporary tables as they have already been opened. */
   if (table_list->table)
     DBUG_RETURN(table_list->table);
 
@@ -5212,8 +4616,9 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
   Open all tables in list, locks them and optionally process derived tables.
 
   @param thd		      Thread context.
+  @param options              DDL options.
   @param tables	              List of tables for open and locking.
-  @param derived              If to handle derived tables.
+  @param derived              Whether to handle derived tables.
   @param flags                Bitmap of options to be used to open and lock
                               tables (see open_tables() and mysql_lock_tables()
                               for details).
@@ -5667,176 +5072,6 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
 }
 
 
-/**
-  Open a single table without table caching and don't add it to
-  THD::open_tables. Depending on the 'add_to_temporary_tables_list' value,
-  the opened TABLE instance will be addded to THD::temporary_tables list.
-
-  @param thd                          Thread context.
-  @param hton                         Storage engine of the table, if known,
-                                      or NULL otherwise.
-  @param frm                          frm image
-  @param path                         Path (without .frm)
-  @param db                           Database name.
-  @param table_name                   Table name.
-  @param add_to_temporary_tables_list Specifies if the opened TABLE
-                                      instance should be linked into
-                                      THD::temporary_tables list.
-  @param open_in_engine               Indicates that we need to open table
-                                      in storage engine in addition to
-                                      constructing TABLE object for it.
-
-  @note This function is used:
-    - by alter_table() to open a temporary table;
-    - when creating a temporary table with CREATE TEMPORARY TABLE.
-
-  @return TABLE instance for opened table.
-  @retval NULL on error.
-*/
-
-TABLE *open_table_uncached(THD *thd, handlerton *hton,
-                           LEX_CUSTRING *frm,
-                           const char *path, const char *db,
-                           const char *table_name,
-                           bool add_to_temporary_tables_list,
-                           bool open_in_engine)
-{
-  TABLE *tmp_table;
-  TABLE_SHARE *share;
-  char cache_key[MAX_DBKEY_LENGTH], *saved_cache_key, *tmp_path;
-  uint key_length;
-  DBUG_ENTER("open_table_uncached");
-  DBUG_PRINT("enter",
-             ("table: '%s'.'%s'  path: '%s'  server_id: %u  "
-              "pseudo_thread_id: %lu",
-              db, table_name, path,
-              (uint) thd->variables.server_id,
-              (ulong) thd->variables.pseudo_thread_id));
-
-  if (add_to_temporary_tables_list)
-  {
-    /* Temporary tables are not safe for parallel replication. */
-    if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec &&
-        thd->wait_for_prior_commit())
-      DBUG_RETURN(NULL);
-  }
-
-  /* Create the cache_key for temporary tables */
-  key_length= create_tmp_table_def_key(thd, cache_key, db, table_name);
-
-  if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table) + sizeof(*share) +
-                                      strlen(path)+1 + key_length,
-                                      MYF(MY_WME))))
-    DBUG_RETURN(0);				/* purecov: inspected */
-
-  share= (TABLE_SHARE*) (tmp_table+1);
-  tmp_path= (char*) (share+1);
-  saved_cache_key= strmov(tmp_path, path)+1;
-  memcpy(saved_cache_key, cache_key, key_length);
-
-  init_tmp_table_share(thd, share, saved_cache_key, key_length,
-                       strend(saved_cache_key)+1, tmp_path);
-  share->db_plugin= ha_lock_engine(thd, hton);
-
-  /*
-    Use the frm image, if possible, open the file otherwise.
-
-    The image might be unavailable in ALTER TABLE, when the discovering
-    engine took over the ownership (see TABLE::read_frm_image).
-  */
-  int res= frm->str
-    ? share->init_from_binary_frm_image(thd, false, frm->str, frm->length)
-    : open_table_def(thd, share, GTS_TABLE | GTS_USE_DISCOVERY);
-
-  if (res)
-  {
-    /* No need to lock share->mutex as this is not needed for tmp tables */
-    free_table_share(share);
-    my_free(tmp_table);
-    DBUG_RETURN(0);
-  }
-
-  share->m_psi= PSI_CALL_get_table_share(true, share);
-
-  if (open_table_from_share(thd, share, table_name,
-                            open_in_engine ?
-                            (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
-                                    HA_GET_INDEX) : 0,
-                            READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
-                            ha_open_options,
-                            tmp_table,
-                            /*
-                              Set "is_create_table" if the table does not
-                              exist in SE
-                            */
-                            open_in_engine ? false : true))
-  {
-    /* No need to lock share->mutex as this is not needed for tmp tables */
-    free_table_share(share);
-    my_free(tmp_table);
-    DBUG_RETURN(0);
-  }
-
-  tmp_table->reginfo.lock_type= TL_WRITE;	 // Simulate locked
-  tmp_table->grant.privilege= TMP_TABLE_ACLS;
-  share->tmp_table= (tmp_table->file->has_transactions() ? 
-                     TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
-
-  if (add_to_temporary_tables_list)
-  {
-    thd->lock_temporary_tables();
-    /* growing temp list at the head */
-    tmp_table->next= thd->temporary_tables;
-    if (tmp_table->next)
-      tmp_table->next->prev= tmp_table;
-    thd->temporary_tables= tmp_table;
-    thd->temporary_tables->prev= 0;
-    if (thd->rgi_slave)
-    {
-      thread_safe_increment32(&slave_open_temp_tables);
-    }
-    thd->unlock_temporary_tables();
-  }
-  tmp_table->pos_in_table_list= 0;
-  DBUG_PRINT("tmptable", ("opened table: '%s'.'%s' 0x%lx", tmp_table->s->db.str,
-                          tmp_table->s->table_name.str, (long) tmp_table));
-  DBUG_RETURN(tmp_table);
-}
-
-
-/**
-  Delete a temporary table.
-
-  @param base  Handlerton for table to be deleted.
-  @param path  Path to the table to be deleted (i.e. path
-               to its .frm without an extension).
-
-  @retval false - success.
-  @retval true  - failure.
-*/
-
-bool rm_temporary_table(handlerton *base, const char *path)
-{
-  bool error=0;
-  handler *file;
-  char frm_path[FN_REFLEN + 1];
-  DBUG_ENTER("rm_temporary_table");
-
-  strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS);
-  if (mysql_file_delete(key_file_frm, frm_path, MYF(0)))
-    error=1; /* purecov: inspected */
-  file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base);
-  if (file && file->ha_delete_table(path))
-  {
-    error=1;
-    sql_print_warning("Could not remove temporary table: '%s', error: %d",
-                      path, my_errno);
-  }
-  delete file;
-  DBUG_RETURN(error);
-}
-
-
 /*****************************************************************************
 * The following find_field_in_XXX procedures implement the core of the
 * name resolution functionality. The entry point to resolve a column name in a
@@ -5905,164 +5140,6 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
 }
 
 
-/**
-  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)
-{
-  TABLE *table;
-  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 || !thd->have_temporary_tables())
-  {
-    DBUG_PRINT("info", ("skip_temporary is set or no temporary tables"));
-    DBUG_RETURN(FALSE);
-  }
-
-  if (find_and_use_temporary_table(thd, tl, &table))
-    DBUG_RETURN(TRUE);
-  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);
-  }
-
-  /*
-    Temporary tables are not safe for parallel replication. They were
-    designed to be visible to one thread only, so have no table locking.
-    Thus there is no protection against two conflicting transactions
-    committing in parallel and things like that.
-
-    So for now, anything that uses temporary tables will be serialised
-    with anything before it, when using parallel replication.
-
-    ToDo: We might be able to introduce a reference count or something
-    on temp tables, and have slave worker threads wait for it to reach
-    zero before being allowed to use the temp table. Might not be worth
-    it though, as statement-based replication using temporary tables is
-    in any case rather fragile.
-  */
-  if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec &&
-      thd->wait_for_prior_commit())
-    DBUG_RETURN(true);
-
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-  if (tl->partition_names)
-  {
-    /* Partitioned temporary tables is not supported. */
-    DBUG_ASSERT(!table->part_info);
-    my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
-    DBUG_RETURN(true);
-  }
-#endif
-
-  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->variables.server_id,
-                (ulong) thd->variables.pseudo_thread_id));
-    my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
-    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.
 
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 3a23e2e..289fc68 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -59,8 +59,6 @@ enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
 				  IGNORE_ERRORS, REPORT_EXCEPT_NON_UNIQUE,
                                   IGNORE_EXCEPT_NON_UNIQUE};
 
-uint create_tmp_table_def_key(THD *thd, char *key, const char *db,
-                              const char *table_name);
 uint get_table_def_key(const TABLE_LIST *table_list, const char **key);
 TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
                    uint lock_flags);
@@ -120,11 +118,6 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
 
 bool get_key_map_from_key_list(key_map *map, TABLE *table,
                                List<String> *index_list);
-TABLE *open_table_uncached(THD *thd, handlerton *hton,
-                           LEX_CUSTRING *frm, const char *path,
-                           const char *db, const char *table_name,
-                           bool add_to_temporary_tables_list,
-                           bool open_in_engine);
 TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name);
 TABLE *find_write_locked_table(TABLE *list, const char *db,
                                const char *table_name);
@@ -134,21 +127,12 @@ thr_lock_type read_lock_type_for_table(THD *thd,
                                        bool routine_modifies_data);
 
 my_bool mysql_rm_tmp_tables(void);
-bool rm_temporary_table(handlerton *base, const char *path);
 void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
                              const MDL_savepoint &start_of_statement_svp);
 TABLE_LIST *find_table_in_list(TABLE_LIST *table,
                                TABLE_LIST *TABLE_LIST::*link,
                                const char *db_name,
                                const char *table_name);
-TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name);
-bool find_and_use_temporary_table(THD *thd, const char *db,
-                                  const char *table_name, TABLE **out_table);
-TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
-bool find_and_use_temporary_table(THD *thd, const TABLE_LIST *tl,
-                                  TABLE **out_table);
-TABLE *find_temporary_table(THD *thd, const char *table_key,
-                            uint table_key_length);
 void close_thread_tables(THD *thd);
 bool fill_record_n_invoke_before_triggers(THD *thd, TABLE *table,
                                           List<Item> &fields,
@@ -270,17 +254,8 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags,
 void intern_close_table(TABLE *entry);
 void kill_delayed_threads_for_table(TDC_element *element);
 void close_thread_table(THD *thd, TABLE **table_ptr);
-bool close_temporary_tables(THD *thd);
 TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
                          bool check_alias);
-int drop_temporary_table(THD *thd, TABLE *table, bool *is_trans);
-void close_temporary_table(THD *thd, TABLE *table, bool free_share,
-                           bool delete_table);
-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);
 
 class Open_tables_backup;
@@ -318,7 +293,6 @@ static inline bool tdc_open_view(THD *thd, TABLE_LIST *table_list,
 TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
                                   const char *table_name,
                                   bool no_error);
-void mark_tmp_table_for_reuse(TABLE *table);
 
 int update_virtual_fields(THD *thd, TABLE *table,
       enum enum_vcol_update_mode vcol_update_mode= VCOL_UPDATE_FOR_READ);
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 03505de..67e3910 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1983,32 +1983,28 @@ size_t build_normalized_name(char *buff, size_t bufflen,
     Query_cache_table *table = block_table->parent;
 
     /*
-      Check that we have not temporary tables with same names of tables
-      of this query. If we have such tables, we will not send data from
-      query cache, because temporary tables hide real tables by which
+      Check that we do not have temporary tables with same names as that of
+      base tables from this query. If we have such tables, we will not send
+      data from query cache, because temporary tables hide real tables by which
       query in query cache was made.
     */
-    for (tmptable= thd->temporary_tables; tmptable ; tmptable= tmptable->next)
+    if ((tmptable=
+         thd->find_temporary_table_with_base_key((char *) table->data(),
+                                                 table->key_length())))
     {
-      if (tmptable->s->table_cache_key.length - TMP_TABLE_KEY_EXTRA == 
-          table->key_length() &&
-          !memcmp(tmptable->s->table_cache_key.str, table->data(),
-                  table->key_length()))
-      {
-        DBUG_PRINT("qcache",
-                   ("Temporary table detected: '%s.%s'",
-                    tmptable->s->db.str, tmptable->alias.c_ptr()));
-        unlock();
-        /*
-          We should not store result of this query because it contain
-          temporary tables => assign following variable to make check
-          faster.
-        */
-        thd->query_cache_is_applicable= 0;      // Query can't be cached
-        thd->lex->safe_to_cache_query= 0;       // For prepared statements
-        BLOCK_UNLOCK_RD(query_block);
-        DBUG_RETURN(-1);
-      }
+      DBUG_PRINT("qcache",
+                 ("Temporary table detected: '%s.%s'",
+                  tmptable->s->db.str, tmptable->s->table_name.str));
+      unlock();
+      /*
+        We should not store result of this query because it contain
+        temporary tables => assign following variable to make check
+        faster.
+      */
+      thd->query_cache_is_applicable= 0;        // Query can't be cached
+      thd->lex->safe_to_cache_query= 0;         // For prepared statements
+      BLOCK_UNLOCK_RD(query_block);
+      DBUG_RETURN(-1);
     }
 
     bzero((char*) &table_list,sizeof(table_list));
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 6656a84..6bc88a4 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1,6 +1,6 @@
 /*
    Copyright (c) 2000, 2015, Oracle and/or its affiliates.
-   Copyright (c) 2008, 2015, MariaDB
+   Copyright (c) 2008, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -37,7 +37,7 @@
 #include "tztime.h"                           // MYSQL_TIME <-> my_time_t
 #include "sql_acl.h"                          // NO_ACCESS,
                                               // acl_getroot_no_password
-#include "sql_base.h"                         // close_temporary_tables
+#include "sql_base.h"
 #include "sql_handler.h"                      // mysql_ha_cleanup
 #include "rpl_rli.h"
 #include "rpl_filter.h"
@@ -1458,6 +1458,10 @@ void THD::init(void)
   debug_sync_init_thread(this);
 #endif /* defined(ENABLED_DEBUG_SYNC) */
   apc_target.init(&LOCK_thd_data);
+
+  /* Initialize temporary tables */
+  temporary_tables.empty();
+
   DBUG_VOID_RETURN;
 }
 
@@ -1577,7 +1581,7 @@ void THD::cleanup(void)
   locked_tables_list.unlock_locked_tables(this);
 
   delete_dynamic(&user_var_events);
-  close_temporary_tables(this);
+  close_temporary_tables();
 
   transaction.xid_state.xa_state= XA_NOTR;
   trans_rollback(this);
@@ -4230,7 +4234,8 @@ void THD::restore_backup_open_tables_state(Open_tables_backup *backup)
     Before we will throw away current open tables state we want
     to be sure that it was properly cleaned up.
   */
-  DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 &&
+  DBUG_ASSERT(open_tables == 0 &&
+              !has_temporary_tables() &&
               derived_tables == 0 &&
               lock == 0 &&
               locked_tables_mode == LTM_NONE &&
@@ -6782,24 +6787,6 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
 }
 
 
-void THD::rgi_lock_temporary_tables()
-{
-  mysql_mutex_lock(&rgi_slave->rli->data_lock);
-  temporary_tables= rgi_slave->rli->save_temporary_tables;
-}
-
-void THD::rgi_unlock_temporary_tables()
-{
-  rgi_slave->rli->save_temporary_tables= temporary_tables;
-  mysql_mutex_unlock(&rgi_slave->rli->data_lock);
-}
-
-bool THD::rgi_have_temporary_tables()
-{
-  return rgi_slave->rli->save_temporary_tables != 0;
-}
-
-
 void
 wait_for_commit::reinit()
 {
diff --git a/sql/sql_class.h b/sql/sql_class.h
index ae240ae..a70c4a9 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1,6 +1,6 @@
 /*
    Copyright (c) 2000, 2015, Oracle and/or its affiliates.
-   Copyright (c) 2009, 2015, MariaDB
+   Copyright (c) 2009, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -1247,6 +1247,61 @@ enum enum_locked_tables_mode
   LTM_PRELOCKED_UNDER_LOCK_TABLES
 };
 
+/**
+  The following structure is an extension to TABLE_SHARE and is
+  exclusively for temporary tables.
+
+  @note:
+  Although, TDC_element has data members (like next, prev &
+  all_tables) to store the list of TABLE_SHARE & TABLE objects
+  related to a particular TABLE_SHARE, they cannot be moved to
+  TABLE_SHARE in order to be reused for temporary tables. This
+  is because, as concurrent threads iterating through hash of
+  TDC_element's may need access to all_tables, but if all_tables
+  is made part of TABLE_SHARE, then TDC_element->share->all_tables
+  is not always guaranteed to be valid, as TDC_element can live
+  longer than TABLE_SHARE.
+*/
+struct TMP_TABLE_SHARE : public TABLE_SHARE
+{
+private:
+  /*
+   Link to all temporary table shares. Declared as private to
+   avoid direct manipulation with those objects. One should
+   use methods of I_P_List template instead.
+  */
+  TMP_TABLE_SHARE *tmp_next;
+  TMP_TABLE_SHARE **tmp_prev;
+
+  friend struct All_tmp_table_shares;
+
+public:
+  /*
+    Doubly-linked (back-linked) lists of used and unused TABLE objects
+    for this share.
+  */
+  All_share_tables_list all_tmp_tables;
+};
+
+/**
+  Helper class which specifies which members of TMP_TABLE_SHARE are
+  used for participation in the list of temporary tables.
+*/
+
+struct All_tmp_table_shares
+{
+  static inline TMP_TABLE_SHARE **next_ptr(TMP_TABLE_SHARE *l)
+  {
+    return &l->tmp_next;
+  }
+  static inline TMP_TABLE_SHARE ***prev_ptr(TMP_TABLE_SHARE *l)
+  {
+    return &l->tmp_prev;
+  }
+};
+
+/* Also used in rpl_rli.h. */
+typedef I_P_List <TMP_TABLE_SHARE, All_tmp_table_shares> All_tmp_tables_list;
 
 /**
   Class that holds information about tables which were opened and locked
@@ -1276,15 +1331,20 @@ class Open_tables_state
     base tables that were opened with @see open_tables().
   */
   TABLE *open_tables;
+
   /**
-    List of temporary tables used by this thread. Contains user-level
-    temporary tables, created with CREATE TEMPORARY TABLE, and
-    internal temporary tables, created, e.g., to resolve a SELECT,
+    A list of temporary tables used by this thread. This includes
+    user-level temporary tables, created with CREATE TEMPORARY TABLE,
+    and internal temporary tables, created, e.g., to resolve a SELECT,
     or for an intermediate table used in ALTER.
-    XXX Why are internal temporary tables added to this list?
   */
-  TABLE *temporary_tables;
+  All_tmp_tables_list temporary_tables;
+
+  /*
+    Derived tables.
+  */
   TABLE *derived_tables;
+
   /*
     During a MySQL session, one can lock tables in two modes: automatic
     or manual. In automatic mode all necessary tables are locked just before
@@ -1362,11 +1422,14 @@ class Open_tables_state
 
   void reset_open_tables_state(THD *thd)
   {
-    open_tables= temporary_tables= derived_tables= 0;
-    extra_lock= lock= 0;
+    open_tables= 0;
+    derived_tables= 0;
+    extra_lock= 0;
+    lock= 0;
     locked_tables_mode= LTM_NONE;
     state_flags= 0U;
     m_reprepare_observer= NULL;
+    temporary_tables.empty();
   }
 };
 
@@ -3506,13 +3569,13 @@ class THD :public Statement,
     */
     DBUG_PRINT("debug",
                ("temporary_tables: %s, in_sub_stmt: %s, system_thread: %s",
-                YESNO(temporary_tables), YESNO(in_sub_stmt),
+                YESNO(has_temporary_tables()), YESNO(in_sub_stmt),
                 show_system_thread(system_thread)));
     if (in_sub_stmt == 0)
     {
       if (wsrep_binlog_format() == BINLOG_FORMAT_ROW)
         set_current_stmt_binlog_format_row();
-      else if (temporary_tables == NULL)
+      else if (!has_temporary_tables())
         set_current_stmt_binlog_format_stmt();
     }
     DBUG_VOID_RETURN;
@@ -3910,10 +3973,6 @@ class THD :public Statement,
   LEX_STRING invoker_user;
   LEX_STRING invoker_host;
 
-  /* Protect against add/delete of temporary tables in parallel replication */
-  void rgi_lock_temporary_tables();
-  void rgi_unlock_temporary_tables();
-  bool rgi_have_temporary_tables();
 public:
   /*
     Flag, mutex and condition for a thread to wait for a signal from another
@@ -3931,26 +3990,86 @@ class THD :public Statement,
   */
   rpl_gtid last_commit_gtid;
 
-  inline void lock_temporary_tables()
+  LF_PINS *tdc_hash_pins;
+  LF_PINS *xid_hash_pins;
+  bool fix_xid_hash_pins();
+
+/* Members related to temporary tables. */
+public:
+  bool has_temporary_tables();
+  void reset_temporary_tables();
+
+  TABLE *create_and_open_tmp_table(handlerton *hton,
+                                   LEX_CUSTRING *frm,
+                                   const char *path,
+                                   const char *db,
+                                   const char *table_name,
+                                   bool open_in_engine,
+                                   bool *created);
+
+  TABLE *find_temporary_table(const char *db, const char *table_name);
+  TABLE *find_temporary_table(const TABLE_LIST *tl);
+  TABLE *find_temporary_table_with_base_key(const char *key, uint key_length);
+
+  TMP_TABLE_SHARE *find_tmp_table_share(const char *db,
+                                        const char *table_name);
+  TMP_TABLE_SHARE *find_tmp_table_share(const TABLE_LIST *tl);
+  TMP_TABLE_SHARE *find_tmp_table_share(const char *key, uint key_length);
+
+  bool open_temporary_table(TABLE_LIST *tl);
+  bool open_temporary_tables(TABLE_LIST *tl);
+
+  bool close_temporary_tables();
+  bool rename_temporary_table(TABLE *table, const char *db,
+                              const char *table_name);
+  bool drop_temporary_table(TABLE *table, bool *is_trans, bool delete_table);
+  bool rm_temporary_table(handlerton *hton, const char *path);
+  void mark_tmp_tables_as_free_for_reuse();
+  void mark_tmp_table_as_free_for_reuse(TABLE *table);
+
+private:
+  /* Whether a lock has been acquired? */
+  bool m_tmp_tables_locked;
+
+  /* Opened table states. */
+  enum Temporary_table_state {
+    TMP_TABLE_IN_USE,
+    TMP_TABLE_NOT_IN_USE,
+    TMP_TABLE_ANY
+  };
+
+  bool has_slave_temporary_tables();
+  uint create_tmp_table_def_key(char *key, const char *db,
+                                const char *table_name);
+  TMP_TABLE_SHARE *create_temporary_table(handlerton *hton, LEX_CUSTRING *frm,
+                                          const char *path, const char *db,
+                                          const char *table_name);
+  TABLE *find_temporary_table(const char *key, uint key_length,
+                              Temporary_table_state state);
+  TABLE *open_temporary_table(TMP_TABLE_SHARE *share, const char *alias,
+                              bool open_in_engine);
+  bool find_and_use_tmp_table(const TABLE_LIST *tl, TABLE **out_table);
+  bool use_temporary_table(TABLE *table, TABLE **out_table);
+  void close_temporary_table(TABLE *table);
+  bool log_events_and_free_tmp_shares();
+  void free_tmp_table_share(TMP_TABLE_SHARE *share, bool delete_table);
+  void free_temporary_table(TABLE *table);
+  bool lock_temporary_tables();
+  void unlock_temporary_tables();
+
+  inline uint tmpkeyval(TMP_TABLE_SHARE *share)
   {
-    if (rgi_slave)
-      rgi_lock_temporary_tables();
+    return uint4korr(share->table_cache_key.str +
+                     share->table_cache_key.length - 4);
   }
-  inline void unlock_temporary_tables()
-  {
-    if (rgi_slave)
-      rgi_unlock_temporary_tables();
-  }    
-  inline bool have_temporary_tables()
+
+  inline TMP_TABLE_SHARE *tmp_table_share(TABLE *table)
   {
-    return (temporary_tables ||
-            (rgi_slave && rgi_have_temporary_tables()));
+    DBUG_ASSERT(table->s->tmp_table);
+    return static_cast<TMP_TABLE_SHARE *>(table->s);
   }
 
-  LF_PINS *tdc_hash_pins;
-  LF_PINS *xid_hash_pins;
-  bool fix_xid_hash_pins();
-
+public:
   inline ulong wsrep_binlog_format() const
   {
     return WSREP_FORMAT(variables.binlog_format);
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 2ba67cb..be835e4 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -1,6 +1,6 @@
 /*
    Copyright (c) 2000, 2014, Oracle and/or its affiliates.
-   Copyright (c) 2009, 2015, MariaDB
+   Copyright (c) 2009, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -886,7 +886,8 @@ bool mysql_alter_db(THD *thd, const char *db,
     {
       LEX_STRING db_name= { table->db, table->db_length };
       LEX_STRING table_name= { table->table_name, table->table_name_length };
-      if (table->open_type == OT_BASE_ONLY || !find_temporary_table(thd, table))
+      if (table->open_type == OT_BASE_ONLY ||
+          !thd->find_temporary_table(table))
         (void) delete_statistics_for_table(thd, &db_name, &table_name);
     }
   }
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index e8ade81..95417c7 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -1,5 +1,5 @@
 /* Copyright (c) 2001, 2015, Oracle and/or its affiliates.
-   Copyright (c) 2011, 2015, MariaDB
+   Copyright (c) 2011, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -178,9 +178,8 @@ static void mysql_ha_close_table(SQL_HANDLER *handler)
   {
     /* Must be a temporary table */
     table->file->ha_index_or_rnd_end();
-    table->query_id= thd->query_id;
     table->open_by_handler= 0;
-    mark_tmp_table_for_reuse(table);
+    thd->mark_tmp_table_as_free_for_reuse(table);
   }
   my_free(handler->lock);
   handler->init();
@@ -294,7 +293,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
     open_ltable() or open_table() because we would like to be able
     to open a temporary table.
   */
-  error= (open_temporary_tables(thd, tables) ||
+  error= (thd->open_temporary_tables(tables) ||
           open_tables(thd, &tables, &counter, 0));
 
   if (error)
@@ -389,8 +388,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
   /*
     Assert that the above check prevents opening of views and merge tables.
     For temporary tables, TABLE::next can be set even if only one table
-    was opened for HANDLER as it is used to link them together
-    (see thd->temporary_tables).
+    was opened for HANDLER as it is used to link them together.
   */
   DBUG_ASSERT(sql_handler->table->next == NULL ||
               sql_handler->table->s->tmp_table);
@@ -1195,10 +1193,10 @@ void mysql_ha_set_explicit_lock_duration(THD *thd)
   Remove temporary tables from the HANDLER's hash table. The reason
   for having a separate function, rather than calling
   mysql_ha_rm_tables() is that it is not always feasible (e.g. in
-  close_temporary_tables) to obtain a TABLE_LIST containing the
+  THD::close_temporary_tables) to obtain a TABLE_LIST containing the
   temporary tables.
 
-  @See close_temporary_tables
+  @See THD::close_temporary_tables()
   @param thd Thread identifier.
 */
 void mysql_ha_rm_temporary_tables(THD *thd)
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index aa2cae5..de45c10 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1,6 +1,6 @@
 /*
    Copyright (c) 2000, 2015, Oracle and/or its affiliates.
-   Copyright (c) 2010, 2015, MariaDB
+   Copyright (c) 2010, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -4051,7 +4051,12 @@ static TABLE *create_table_from_items(THD *thd,
     }
     else
     {
-      if (open_temporary_table(thd, create_table))
+      /*
+        The pointer to the newly created temporary table has been stored in
+        table->create_info.
+      */
+      create_table->table= create_info->table;
+      if (!create_table->table)
       {
         /*
           This shouldn't happen as creation of temporary table should make
@@ -4060,7 +4065,6 @@ static TABLE *create_table_from_items(THD *thd,
         */
         DBUG_ASSERT(0);
       }
-      DBUG_ASSERT(create_table->table == create_info->table);
     }
   }
   else
@@ -4431,6 +4435,7 @@ void select_create::abort_result_set()
   if (table)
   {
     bool tmp_table= table->s->tmp_table;
+
     table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
     table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
     table->auto_increment_field_not_null= FALSE;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 52dcc7e..2a62340 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1,5 +1,5 @@
 /* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
-   Copyright (c) 2008, 2015, MariaDB
+   Copyright (c) 2008, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -23,7 +23,10 @@
                               // set_handler_table_locks,
                               // lock_global_read_lock,
                               // make_global_read_lock_block_commit
-#include "sql_base.h"         // find_temporary_table
+#include "sql_base.h"         // open_tables, open_and_lock_tables,
+                              // lock_tables, unique_table,
+                              // close_thread_tables, is_temporary_table
+                              // table_cache.h
 #include "sql_cache.h"        // QUERY_CACHE_FLAGS_SIZE, query_cache_*
 #include "sql_show.h"         // mysqld_list_*, mysqld_show_*,
                               // calc_sum_of_all_status
@@ -192,7 +195,7 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
   for (TABLE_LIST *table= tables; table; table= table->next_global)
   {
     DBUG_ASSERT(table->db && table->table_name);
-    if (table->updating && !find_temporary_table(thd, table))
+    if (table->updating && !thd->find_tmp_table_share(table))
       return 1;
   }
   return 0;
@@ -1655,7 +1658,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
     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))
+    if (thd->open_temporary_tables(&table_list))
       break;
 
     if (check_table_access(thd, SELECT_ACL, &table_list,
@@ -2874,7 +2877,7 @@ static bool do_execute_sp(THD *thd, sp_head *sp)
   */
   if (sql_command_flags[lex->sql_command] & CF_PREOPEN_TMP_TABLES)
   {
-    if (open_temporary_tables(thd, all_tables))
+    if (thd->open_temporary_tables(all_tables))
       goto error;
   }
 
@@ -3676,7 +3679,7 @@ static bool do_execute_sp(THD *thd, sp_head *sp)
           Temporary tables should be opened for SHOW CREATE TABLE, but not
           for SHOW CREATE VIEW.
         */
-        if (open_temporary_tables(thd, all_tables))
+        if (thd->open_temporary_tables(all_tables))
           goto error;
 
         /*
@@ -3884,7 +3887,8 @@ static bool do_execute_sp(THD *thd, sp_head *sp)
     */
     if (first_table->lock_type != TL_WRITE_DELAYED)
     {
-      if ((res= open_temporary_tables(thd, all_tables)))
+      res= (thd->open_temporary_tables(all_tables)) ? TRUE : FALSE;
+      if (res)
         break;
     }
 
@@ -4196,7 +4200,7 @@ static bool do_execute_sp(THD *thd, sp_head *sp)
      {
        if (!lex->tmp_table() &&
           (!thd->is_current_stmt_binlog_format_row() ||
-	   !find_temporary_table(thd, table)))
+	   !thd->find_temporary_table(table)))
        {
          WSREP_TO_ISOLATION_BEGIN(NULL, NULL, all_tables);
          break;
@@ -4349,7 +4353,7 @@ static bool do_execute_sp(THD *thd, sp_head *sp)
       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))
+    if (thd->open_temporary_tables(all_tables))
       goto error;
 
     if (lock_tables_precheck(thd, all_tables))
@@ -6369,7 +6373,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
     /*
       Open temporary tables to be able to detect them during privilege check.
     */
-    if (open_temporary_tables(thd, dst_table))
+    if (thd->open_temporary_tables(dst_table))
       return TRUE;
 
     if (check_access(thd, SELECT_ACL, dst_table->db,
diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc
index 9e67a21..18f25ab 100644
--- a/sql/sql_partition_admin.cc
+++ b/sql/sql_partition_admin.cc
@@ -1,5 +1,6 @@
 /* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    Copyright (c) 2014, SkySQL Ab.
+   Copyright (c) 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -534,12 +535,9 @@ static bool exchange_name_with_ddl_log(THD *thd,
 #ifdef WITH_WSREP
   if (WSREP_ON)
   {
-    /* Forward declaration */
-    TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
-
     if ((!thd->is_current_stmt_binlog_format_row() ||
          /* TODO: Do we really need to check for temp tables in this case? */
-         !find_temporary_table(thd, table_list)) &&
+         !thd->find_temporary_table(table_list)) &&
         wsrep_to_isolation_begin(thd, table_list->db, table_list->table_name,
                                  NULL))
     {
@@ -783,11 +781,9 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
     DBUG_RETURN(TRUE);
 
 #ifdef WITH_WSREP
-  /* Forward declaration */
-  TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
-
-  if (WSREP(thd) && (!thd->is_current_stmt_binlog_format_row() ||
-       !find_temporary_table(thd, first_table))  &&
+  if (WSREP(thd) &&
+      (!thd->is_current_stmt_binlog_format_row() ||
+       !thd->find_temporary_table(first_table))  &&
       wsrep_to_isolation_begin(
         thd, first_table->db, first_table->table_name, NULL)
       )
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index dfe6d1c..806665b 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1,5 +1,6 @@
 /* Copyright (c) 2002, 2013, Oracle and/or its affiliates.
    Copyright (c) 2008, 2014, Monty Program Ab
+   Copyright (c) 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -1261,7 +1262,7 @@ static bool mysql_test_insert(Prepared_statement *stmt,
   */
   if (table_list->lock_type != TL_WRITE_DELAYED)
   {
-    if (open_temporary_tables(thd, table_list))
+    if (thd->open_temporary_tables(table_list))
       goto error;
   }
 
@@ -1840,7 +1841,7 @@ static bool mysql_test_create_view(Prepared_statement *stmt)
     Since we can't pre-open temporary tables for SQLCOM_CREATE_VIEW,
     (see mysql_create_view) we have to do it here instead.
   */
-  if (open_temporary_tables(thd, tables))
+  if (thd->open_temporary_tables(tables))
     goto err;
 
   if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL,
@@ -2092,7 +2093,7 @@ static bool check_prepared_statement(Prepared_statement *stmt)
   */
   if (sql_command_flags[sql_command] & CF_PREOPEN_TMP_TABLES)
   {
-    if (open_temporary_tables(thd, tables))
+    if (thd->open_temporary_tables(tables))
       goto error;
   }
 
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 1fb57d1..b66095d 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1,5 +1,5 @@
 /* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
-   Copyright (c) 2009, 2015, MariaDB
+   Copyright (c) 2009, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -4076,7 +4076,7 @@ int schema_tables_add(THD *thd, Dynamic_array<LEX_STRING*> *files,
   if (is_show_fields_or_keys)
   {
     /*
-      Restore thd->temporary_tables to be able to process
+      Restore thd->all_temp_tables to be able to process
       temporary tables (only for 'show index' & 'show columns').
       This should be changed when processing of temporary tables for
       I_S tables will be done.
@@ -4101,7 +4101,7 @@ int schema_tables_add(THD *thd, Dynamic_array<LEX_STRING*> *files,
     'only_view_structure()'.
   */
   lex->sql_command= SQLCOM_SHOW_FIELDS;
-  result= (open_temporary_tables(thd, table_list) ||
+  result= (thd->open_temporary_tables(table_list) ||
            open_normal_and_derived_tables(thd, table_list,
                                           (MYSQL_OPEN_IGNORE_FLUSH |
                                            MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
@@ -4163,7 +4163,8 @@ int schema_tables_add(THD *thd, Dynamic_array<LEX_STRING*> *files,
     For safety reset list of open temporary tables before closing
     all tables open within this Open_tables_state.
   */
-  thd->temporary_tables= NULL;
+  thd->reset_temporary_tables();
+
   close_thread_tables(thd);
   /*
     Release metadata lock we might have acquired.
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 40032c3..f0692ed 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -25,7 +25,7 @@
 #include "sql_table.h"
 #include "sql_parse.h"                        // test_if_data_home_dir
 #include "sql_cache.h"                          // query_cache_*
-#include "sql_base.h"   // open_table_uncached, lock_table_names
+#include "sql_base.h"   // lock_table_names
 #include "lock.h"       // mysql_unlock_tables
 #include "strfunc.h"    // find_type2, find_set
 #include "sql_truncate.h"                       // regenerate_locked_table 
@@ -2028,7 +2028,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
         LEX_STRING db_name= { table->db, table->db_length };
         LEX_STRING table_name= { table->table_name, table->table_name_length };
         if (table->open_type == OT_BASE_ONLY ||
-            !find_temporary_table(thd, table))
+            !thd->find_temporary_table(table))
           (void) delete_statistics_for_table(thd, &db_name, &table_name);
       }
     }
@@ -2281,23 +2281,17 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
     */
     DBUG_ASSERT(!(thd->locked_tables_mode &&
                   table->open_type != OT_BASE_ONLY &&
-                  find_temporary_table(thd, table) &&
+                  thd->find_temporary_table(table) &&
                   table->mdl_request.ticket != NULL));
 
-    /*
-      drop_temporary_table may return one of the following error codes:
-      .  0 - a temporary table was successfully dropped.
-      .  1 - a temporary table was not found.
-      . -1 - a temporary table is used by an outer statement.
-    */
     if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table))
       error= 1;
     else
     {
       table_creation_was_logged= table->table->s->table_creation_was_logged;
-      if ((error= drop_temporary_table(thd, table->table, &is_trans)) == -1)
+      if (thd->drop_temporary_table(table->table, &is_trans, true))
       {
-        DBUG_ASSERT(thd->in_sub_stmt);
+        error= 1;
         goto err;
       }
       table->table= 0;
@@ -4608,7 +4602,8 @@ handler *mysql_create_frm_image(THD *thd,
                              which was created.
   @param[out] key_count      Number of keys in table which was created.
 
-  If one creates a temporary table, this is automatically opened
+  If one creates a temporary table, its is automatically opened and its
+  TABLE_SHARE is added to THD::all_temp_tables list.
 
   Note that this function assumes that caller already have taken
   exclusive metadata lock on table being created or used some other
@@ -4668,20 +4663,22 @@ int create_table_impl(THD *thd,
   /* Check if table exists */
   if (create_info->tmp_table())
   {
-    TABLE *tmp_table;
-    if (find_and_use_temporary_table(thd, db, table_name, &tmp_table))
-      goto err;
+    /*
+      If a table exists, it must have been pre-opened. Try looking for one
+      in-use in THD::all_temp_tables list of TABLE_SHAREs.
+    */
+    TABLE *tmp_table= thd->find_temporary_table(db, table_name);
+
     if (tmp_table)
     {
       bool table_creation_was_logged= tmp_table->s->table_creation_was_logged;
       if (options.or_replace())
       {
-        bool tmp;
         /*
           We are using CREATE OR REPLACE on an existing temporary table
           Remove the old table so that we can re-create it.
         */
-        if (drop_temporary_table(thd, tmp_table, &tmp))
+        if (thd->drop_temporary_table(tmp_table, NULL, true))
           goto err;
       }
       else if (options.if_not_exists())
@@ -4820,17 +4817,18 @@ int create_table_impl(THD *thd,
   create_info->table= 0;
   if (!frm_only && create_info->tmp_table())
   {
-    /*
-      Open a table (skipping table cache) and add it into
-      THD::temporary_tables list.
-    */
+    bool created;
 
-    TABLE *table= open_table_uncached(thd, create_info->db_type, frm, path,
-                                      db, table_name, true, true);
+    TABLE *table= thd->create_and_open_tmp_table(create_info->db_type, frm,
+                                                 path, db, table_name, true,
+                                                 &created);
 
     if (!table)
     {
-      (void) rm_temporary_table(create_info->db_type, path);
+      if (created)
+      {
+        (void) thd->rm_temporary_table(create_info->db_type, path);
+      }
       goto err;
     }
 
@@ -7095,7 +7093,8 @@ static bool mysql_inplace_alter_table(THD *thd,
                             HA_EXTRA_NOT_USED,
                             NULL);
   table_list->table= table= NULL;
-  close_temporary_table(thd, altered_table, true, false);
+
+  thd->drop_temporary_table(altered_table, NULL, false);
 
   /*
     Replace the old .FRM with the new .FRM, but keep the old name for now.
@@ -7185,7 +7184,7 @@ static bool mysql_inplace_alter_table(THD *thd,
       thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
     /* QQ; do something about metadata locks ? */
   }
-  close_temporary_table(thd, altered_table, true, false);
+  thd->drop_temporary_table(altered_table, NULL, false);
   // Delete temporary .frm/.par
   (void) quick_rm_table(thd, create_info->db_type, alter_ctx->new_db,
                         alter_ctx->tmp_name, FN_IS_TMP | NO_HA_TABLE);
@@ -8337,7 +8336,12 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
   {
     if (table->s->tmp_table != NO_TMP_TABLE)
     {
-      if (find_temporary_table(thd, alter_ctx.new_db, alter_ctx.new_name))
+      /*
+        Check whether a temporary table exists with same requested new name.
+        If such table exists, there must be a corresponding TABLE_SHARE in
+        THD::all_temp_tables list.
+      */
+      if (thd->find_tmp_table_share(alter_ctx.new_db, alter_ctx.new_name))
       {
         my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias);
         DBUG_RETURN(true);
@@ -8736,6 +8740,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
   /* Remember that we have not created table in storage engine yet. */
   bool no_ha_table= true;
 
+  /* Whether the temporary table was created. */
+  bool created= false;
+
   if (alter_info->requested_algorithm != Alter_info::ALTER_TABLE_ALGORITHM_COPY)
   {
     Alter_inplace_info ha_alter_info(create_info, alter_info,
@@ -8773,12 +8780,19 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
     // We assume that the table is non-temporary.
     DBUG_ASSERT(!table->s->tmp_table);
 
-    if (!(altered_table= open_table_uncached(thd, new_db_type, &frm,
-                                             alter_ctx.get_tmp_path(),
-                                             alter_ctx.new_db,
-                                             alter_ctx.tmp_name,
-                                             true, false)))
+    altered_table=
+      thd->create_and_open_tmp_table(new_db_type, &frm,
+                                     alter_ctx.get_tmp_path(),
+                                     alter_ctx.new_db, alter_ctx.tmp_name,
+                                     false, &created);
+    if (!altered_table)
+    {
+      if (created)
+      {
+        (void) thd->rm_temporary_table(new_db_type, alter_ctx.get_tmp_path());
+      }
       goto err_new_table_cleanup;
+    }
 
     /* Set markers for fields in TABLE object for altered table. */
     update_altered_table(ha_alter_info, altered_table);
@@ -8817,7 +8831,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
       {
         ha_alter_info.report_unsupported_error("LOCK=NONE/SHARED",
                                                "LOCK=EXCLUSIVE");
-        close_temporary_table(thd, altered_table, true, false);
+        thd->drop_temporary_table(altered_table, NULL, false);
         goto err_new_table_cleanup;
       }
       break;
@@ -8828,7 +8842,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
           Alter_info::ALTER_TABLE_LOCK_NONE)
       {
         ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED");
-        close_temporary_table(thd, altered_table, true, false);
+        thd->drop_temporary_table(altered_table, NULL, false);
         goto err_new_table_cleanup;
       }
       break;
@@ -8842,7 +8856,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
       {
         ha_alter_info.report_unsupported_error("ALGORITHM=INPLACE",
                                                "ALGORITHM=COPY");
-        close_temporary_table(thd, altered_table, true, false);
+        thd->drop_temporary_table(altered_table, NULL, false);
         goto err_new_table_cleanup;
       }
       // COPY with LOCK=NONE is not supported, no point in trying.
@@ -8850,7 +8864,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
           Alter_info::ALTER_TABLE_LOCK_NONE)
       {
         ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED");
-        close_temporary_table(thd, altered_table, true, false);
+        thd->drop_temporary_table(altered_table, NULL, false);
         goto err_new_table_cleanup;
       }
       // Otherwise use COPY
@@ -8858,7 +8872,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
       break;
     case HA_ALTER_ERROR:
     default:
-      close_temporary_table(thd, altered_table, true, false);
+      thd->drop_temporary_table(altered_table, NULL, false);
       goto err_new_table_cleanup;
     }
 
@@ -8877,7 +8891,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
     }
     else
     {
-      close_temporary_table(thd, altered_table, true, false);
+      thd->drop_temporary_table(altered_table, NULL, false);
     }
   }
 
@@ -8930,13 +8944,22 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
 
   if (create_info->tmp_table())
   {
-    if (!open_table_uncached(thd, new_db_type, &frm,
-                             alter_ctx.get_tmp_path(),
-                             alter_ctx.new_db, alter_ctx.tmp_name,
-                             true, true))
+    TABLE *tmp_table=
+      thd->create_and_open_tmp_table(new_db_type, &frm,
+                                     alter_ctx.get_tmp_path(),
+                                     alter_ctx.new_db, alter_ctx.tmp_name,
+                                     true, &created);
+    if (!tmp_table)
+    {
+      if (created)
+      {
+        (void) thd->rm_temporary_table(new_db_type, alter_ctx.get_tmp_path());
+      }
       goto err_new_table_cleanup;
+    }
   }
 
+
   /* Open the table since we need to copy the data. */
   if (table->s->tmp_table != NO_TMP_TABLE)
   {
@@ -8944,18 +8967,28 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
     tbl.init_one_table(alter_ctx.new_db, strlen(alter_ctx.new_db),
                        alter_ctx.tmp_name, strlen(alter_ctx.tmp_name),
                        alter_ctx.tmp_name, TL_READ_NO_INSERT);
-    /* Table is in thd->temporary_tables */
-    (void) open_temporary_table(thd, &tbl);
+    /*
+      Table can be found in the list of open tables in THD::all_temp_tables
+      list.
+    */
+    tbl.table= thd->find_temporary_table(&tbl);
     new_table= tbl.table;
   }
   else
   {
-    /* table is a normal table: Create temporary table in same directory */
-    /* Open our intermediate table. */
-    new_table= open_table_uncached(thd, new_db_type, &frm,
-                                   alter_ctx.get_tmp_path(),
-                                   alter_ctx.new_db, alter_ctx.tmp_name,
-                                   true, true);
+    /*
+      table is a normal table: Create temporary table in same directory.
+      Open our intermediate table.
+    */
+    new_table=
+      thd->create_and_open_tmp_table(new_db_type, &frm,
+                                     alter_ctx.get_tmp_path(),
+                                     alter_ctx.new_db, alter_ctx.tmp_name,
+                                     true, &created);
+    if (!new_table && created)
+    {
+      (void) thd->rm_temporary_table(new_db_type, alter_ctx.get_tmp_path());
+    }
   }
   if (!new_table)
     goto err_new_table_cleanup;
@@ -9023,10 +9056,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
     new_table->s->table_creation_was_logged=
       table->s->table_creation_was_logged;
     /* Remove link to old table and rename the new one */
-    close_temporary_table(thd, table, true, true);
+    thd->drop_temporary_table(table, NULL, true);
     /* Should pass the 'new_name' as we store table name in the cache */
-    if (rename_temporary_table(thd, new_table,
-                               alter_ctx.new_db, alter_ctx.new_name))
+    if (thd->rename_temporary_table(new_table, alter_ctx.new_db,
+                                    alter_ctx.new_name))
       goto err_new_table_cleanup;
     /* We don't replicate alter table statement on temporary tables */
     if (!thd->is_current_stmt_binlog_format_row() &&
@@ -9038,10 +9071,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
 
   /*
     Close the intermediate table that will be the new table, but do
-    not delete it! Even altough MERGE tables do not have their children
-    attached here it is safe to call close_temporary_table().
+    not delete it! Even though MERGE tables do not have their children
+    attached here it is safe to call THD::drop_temporary_table().
   */
-  close_temporary_table(thd, new_table, true, false);
+  thd->drop_temporary_table(new_table, NULL, false);
   new_table= NULL;
 
   DEBUG_SYNC(thd, "alter_table_before_rename_result_table");
@@ -9183,8 +9216,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
   my_free(const_cast<uchar*>(frm.str));
   if (new_table)
   {
-    /* close_temporary_table() frees the new_table pointer. */
-    close_temporary_table(thd, new_table, true, true);
+    thd->drop_temporary_table(new_table, NULL, true);
   }
   else
     (void) quick_rm_table(thd, new_db_type,
@@ -9670,7 +9702,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
     /* Allow to open real tables only. */
     table->required_type= FRMTYPE_TABLE;
 
-    if (open_temporary_tables(thd, table) ||
+    if (thd->open_temporary_tables(table) ||
         open_and_lock_tables(thd, table, FALSE, 0))
     {
       t= NULL;
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 8e75258..8b76eaa 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -82,7 +82,7 @@ static my_bool print_cached_tables_callback(TDC_element *element,
   TABLE *entry;
 
   mysql_mutex_lock(&element->LOCK_table_share);
-  TDC_element::All_share_tables_list::Iterator it(element->all_tables);
+  All_share_tables_list::Iterator it(element->all_tables);
   while ((entry= it++))
   {
     THD *in_use= entry->in_use;
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 58fd7ae..fec54bc 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -24,7 +24,7 @@
 #include "sql_parse.h"                          // parse_sql
 #include "parse_file.h"
 #include "sp.h"
-#include "sql_base.h"                          // find_temporary_table
+#include "sql_base.h"
 #include "sql_show.h"                // append_definer, append_identifier
 #include "sql_table.h"                        // build_table_filename,
                                               // check_n_cut_mysql50_prefix
@@ -507,7 +507,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
   DBUG_ASSERT(tables->next_global == 0);
 
   /* We do not allow creation of triggers on temporary tables. */
-  if (create && find_temporary_table(thd, tables))
+  if (create && thd->find_tmp_table_share(tables))
   {
     my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias);
     goto end;
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 62e6790..774410b 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -1,5 +1,5 @@
 /* Copyright (c) 2004, 2013, Oracle and/or its affiliates.
-   Copyright (c) 2011, 2015, MariaDB
+   Copyright (c) 2011, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -429,7 +429,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
   lex->link_first_table_back(view, link_to_local);
   view->open_type= OT_BASE_ONLY;
 
-  if (open_temporary_tables(thd, lex->query_tables) ||
+  if (thd->open_temporary_tables(lex->query_tables) ||
       open_and_lock_tables(thd, lex->query_tables, TRUE, 0))
   {
     view= lex->unlink_first_table(&link_to_local);
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index f5c2f6b..7bb6121 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -1,5 +1,5 @@
 /* Copyright (c) 2002, 2015, Oracle and/or its affiliates.
-   Copyright (c) 2012, 2015, MariaDB
+   Copyright (c) 2012, 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -476,7 +476,8 @@ static bool binlog_format_check(sys_var *self, THD *thd, set_var *var)
      switching @@SESSION.binlog_format from MIXED to STATEMENT when there are
      open temp tables and we are logging in row format.
   */
-  if (thd->temporary_tables && var->type == OPT_SESSION &&
+  if (thd->has_temporary_tables() &&
+      var->type == OPT_SESSION &&
       var->save_result.ulonglong_value == BINLOG_FORMAT_STMT &&
       ((thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
         thd->is_current_stmt_binlog_format_row()) ||
diff --git a/sql/table.cc b/sql/table.cc
index 84f70cd..44753cb 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -3933,7 +3933,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
   tdc->all_tables_refs++;
   mysql_mutex_unlock(&tdc->LOCK_table_share);
 
-  TDC_element::All_share_tables_list::Iterator tables_it(tdc->all_tables);
+  All_share_tables_list::Iterator tables_it(tdc->all_tables);
 
   /*
     In case of multiple searches running in parallel, avoid going
diff --git a/sql/table.h b/sql/table.h
index ab39603..7f31814 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -2,6 +2,7 @@
 #define TABLE_INCLUDED
 /* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
    Copyright (c) 2009, 2014, SkySQL Ab.
+   Copyright (c) 2016, MariaDB Corporation
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -1420,6 +1421,7 @@ struct All_share_tables
   }
 };
 
+typedef I_P_List <TABLE, All_share_tables> All_share_tables_list;
 
 enum enum_schema_table_state
 { 
@@ -2640,15 +2642,6 @@ inline bool is_infoschema_db(const char *name)
 
 TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings);
 
-/**
-  return true if the table was created explicitly.
-*/
-inline bool is_user_table(TABLE * table)
-{
-  const char *name= table->s->table_name.str;
-  return strncmp(name, tmp_file_prefix, tmp_file_prefix_length);
-}
-
 inline void mark_as_null_row(TABLE *table)
 {
   table->null_row=1;
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
index b307841..40fbb7d 100644
--- a/sql/table_cache.cc
+++ b/sql/table_cache.cc
@@ -904,7 +904,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
   if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
       remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
   {
-    TDC_element::All_share_tables_list::Iterator it(element->all_tables);
+    All_share_tables_list::Iterator it(element->all_tables);
     while ((table= it++))
     {
       my_refs++;
diff --git a/sql/table_cache.h b/sql/table_cache.h
index ac36269..2acc833 100644
--- a/sql/table_cache.h
+++ b/sql/table_cache.h
@@ -33,7 +33,6 @@ class TDC_element
   TABLE_SHARE *share;
 
   typedef I_P_List <TABLE, TABLE_share> TABLE_list;
-  typedef I_P_List <TABLE, All_share_tables> All_share_tables_list;
   /**
     Protects ref_count, m_flush_tickets, all_tables, free_tables, flushed,
     all_tables_refs.
diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc
new file mode 100644
index 0000000..1514b72
--- /dev/null
+++ b/sql/temporary_tables.cc
@@ -0,0 +1,1453 @@
+/*
+  Copyright (c) 2016 MariaDB Corporation
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/**
+  All methods pertaining to temporary tables.
+*/
+
+#include "sql_acl.h"                            /* TMP_TABLE_ACLS */
+#include "sql_base.h"                           /* free_io_cache,
+                                                   tdc_create_key */
+#include "lock.h"                               /* mysql_lock_remove */
+#include "log_event.h"                          /* Query_log_event */
+#include "sql_show.h"                           /* append_identifier */
+#include "sql_handler.h"                        /* mysql_ha_rm_temporary_tables */
+#include "rpl_rli.h"                            /* rpl_group_info */
+
+#define IS_USER_TABLE(A) ((A->tmp_table == TRANSACTIONAL_TMP_TABLE) || \
+                          (A->tmp_table == NON_TRANSACTIONAL_TMP_TABLE))
+
+
+/**
+  Check whether temporary tables exist. The decision is made based on the
+  existence of TMP_TABLE_SHAREs in Open_tables_state::temporary_tables list.
+
+  @return false                       Temporary tables exist
+          true                        No temporary table exist
+*/
+bool THD::has_temporary_tables()
+{
+  DBUG_ENTER("THD::has_temporary_tables");
+  bool result= temporary_tables.is_empty();
+  DBUG_RETURN(!result);
+}
+
+
+/**
+  Reset Open_tables_state::temporary_tables list.
+
+  @return void
+*/
+void THD::reset_temporary_tables()
+{
+  DBUG_ENTER("THD::reset_temporary_tables");
+  temporary_tables.empty();
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Create a temporary table, open it and return the TABLE handle.
+
+  @param hton [IN]                    Handlerton
+  @param frm  [IN]                    Binary frm image
+  @param path [IN]                    File path (without extension)
+  @param db   [IN]                    Schema name
+  @param table_name [IN]              Table name
+  @param open_in_engine [IN]          Whether open table in SE
+  @param created [OUT]                Whether table was created?
+
+
+  @return Success                     A pointer to table object
+          Failure                     NULL
+*/
+TABLE *THD::create_and_open_tmp_table(handlerton *hton,
+                                      LEX_CUSTRING *frm,
+                                      const char *path,
+                                      const char *db,
+                                      const char *table_name,
+                                      bool open_in_engine,
+                                      bool *created)
+{
+  DBUG_ENTER("THD::create_and_open_tmp_table");
+
+  TMP_TABLE_SHARE *share;
+  TABLE *table= NULL;
+
+  *created= false;
+
+  if ((share= create_temporary_table(hton, frm, path, db, table_name)))
+  {
+    *created= true;
+    table= open_temporary_table(share, table_name, open_in_engine);
+  }
+
+  DBUG_RETURN(table);
+}
+
+
+/**
+  Check whether an open table with db/table name is in use.
+
+  @param db [IN]                      Database name
+  @param table_name [IN]              Table name
+
+  @return Success                     Pointer to first used table instance.
+          Failure                     NULL
+*/
+TABLE *THD::find_temporary_table(const char *db,
+                                 const char *table_name)
+{
+  DBUG_ENTER("THD::find_temporary_table");
+
+  TABLE *table;
+  char key[MAX_DBKEY_LENGTH];
+  uint key_length;
+  bool locked;
+
+  if (!(has_temporary_tables() || (rgi_slave && has_slave_temporary_tables())))
+  {
+    DBUG_RETURN(NULL);
+  }
+
+  key_length= create_tmp_table_def_key(key, db, table_name);
+
+  locked= lock_temporary_tables();
+  table = find_temporary_table(key, key_length, TMP_TABLE_IN_USE);
+  if (locked)
+  {
+    DBUG_ASSERT(m_tmp_tables_locked);
+    unlock_temporary_tables();
+  }
+
+  DBUG_RETURN(table);
+}
+
+
+/**
+  Check whether an open table specified in TABLE_LIST is in use.
+
+  @return tl [IN]                     TABLE_LIST
+
+  @return Success                     Pointer to first used table instance.
+          Failure                     NULL
+*/
+TABLE *THD::find_temporary_table(const TABLE_LIST *tl)
+{
+  DBUG_ENTER("THD::find_temporary_table");
+  TABLE *table= find_temporary_table(tl->get_db_name(), tl->get_table_name());
+  DBUG_RETURN(table);
+}
+
+
+/**
+  Check whether an open table with the specified key is in use.
+  The key, in this case, is not the usual key used for temporary tables.
+  It does not contain server_id & pseudo_thread_id. This function is
+  essentially used use to check whether there is any temporary table
+  which _shadows_ a base table.
+  (see: Query_cache::send_result_to_client())
+
+  @return Success                     A pointer to table share object
+          Failure                     NULL
+*/
+TABLE *THD::find_temporary_table_with_base_key(const char *key,
+                                               uint key_length)
+{
+  DBUG_ENTER("THD::find_temporary_table_with_base_key");
+
+  TMP_TABLE_SHARE *share;
+  TABLE *table;
+  TABLE *result= NULL;
+  bool locked;
+
+  locked= lock_temporary_tables();
+
+  All_tmp_tables_list::Iterator it(temporary_tables);
+  while ((share= it++))
+  {
+    if ((share->table_cache_key.length - TMP_TABLE_KEY_EXTRA) == key_length
+        && !memcmp(share->table_cache_key.str, key, key_length))
+    {
+      /*
+        A matching TMP_TABLE_SHARE is found. We now need to find a TABLE
+        instance in use.
+      */
+      All_share_tables_list::Iterator tables_it(share->all_tmp_tables);
+      while ((table= tables_it ++))
+      {
+        if (table->query_id != 0)
+        {
+          result= table;
+          break;
+        }
+      }
+    }
+  }
+
+  if (locked)
+  {
+    DBUG_ASSERT(m_tmp_tables_locked);
+    unlock_temporary_tables();
+  }
+
+  DBUG_RETURN(result);
+}
+
+
+/**
+  Lookup the TMP_TABLE_SHARE using the given db/table_name.The server_id and
+  pseudo_thread_id used to generate table definition key is taken from THD
+  (see create_tmp_table_def_key()). Return NULL is none found.
+
+  @return Success                     A pointer to table share object
+          Failure                     NULL
+*/
+TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *db,
+                                           const char *table_name)
+{
+  DBUG_ENTER("THD::find_tmp_table_share");
+
+  TMP_TABLE_SHARE *share;
+  char key[MAX_DBKEY_LENGTH];
+  uint key_length;
+
+  key_length= create_tmp_table_def_key(key, db, table_name);
+  share= find_tmp_table_share(key, key_length);
+
+  DBUG_RETURN(share);
+}
+
+
+/**
+  Lookup TMP_TABLE_SHARE using the specified TABLE_LIST element.
+  Return NULL is none found.
+
+  @param tl [IN]                      Table
+
+  @return Success                     A pointer to table share object
+          Failure                     NULL
+*/
+TMP_TABLE_SHARE *THD::find_tmp_table_share(const TABLE_LIST *tl)
+{
+  DBUG_ENTER("THD::find_tmp_table_share");
+  TMP_TABLE_SHARE *share= find_tmp_table_share(tl->get_db_name(),
+                                               tl->get_table_name());
+  DBUG_RETURN(share);
+}
+
+
+/**
+  Lookup TMP_TABLE_SHARE using the specified table definition key.
+  Return NULL is none found.
+
+  @return Success                     A pointer to table share object
+          Failure                     NULL
+*/
+TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *key, uint key_length)
+{
+  DBUG_ENTER("THD::find_tmp_table_share");
+
+  TMP_TABLE_SHARE *share;
+  TMP_TABLE_SHARE *result= NULL;
+  bool locked;
+
+  locked= lock_temporary_tables();
+
+  All_tmp_tables_list::Iterator it(temporary_tables);
+  while ((share= it++))
+  {
+    if (share->table_cache_key.length == key_length &&
+        !(memcmp(share->table_cache_key.str, key, key_length)))
+    {
+      result= share;
+      break;
+    }
+  }
+
+  if (locked)
+  {
+    DBUG_ASSERT(m_tmp_tables_locked);
+    unlock_temporary_tables();
+  }
+
+  DBUG_RETURN(result);
+}
+
+
+/**
+  Find a temporary table specified by TABLE_LIST instance in the open table
+  list and prepare its TABLE instance for use. If
+
+  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 THD::open_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.
+
+  @param tl [IN]                      TABLE_LIST
+
+  @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 THD::open_temporary_table(TABLE_LIST *tl)
+{
+  DBUG_ENTER("THD::open_temporary_table");
+  DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db, tl->table_name));
+
+  TMP_TABLE_SHARE *share;
+  TABLE *table= NULL;
+
+  /*
+    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 THD::open_tables() should be used.
+  */
+  DBUG_ASSERT(!tl->derived && !tl->schema_table);
+
+  if (tl->open_type == OT_BASE_ONLY ||
+      !(has_temporary_tables() || (rgi_slave && has_slave_temporary_tables())))
+  {
+    DBUG_PRINT("info", ("skip_temporary is set or no temporary tables"));
+    DBUG_RETURN(false);
+  }
+
+  /*
+    First check if there is a reusable open table available in the
+    open table list.
+  */
+  if (find_and_use_tmp_table(tl, &table))
+  {
+    DBUG_RETURN(true);                          /* Error */
+  }
+
+  /*
+    No reusable table was found. We will have to open a new instance.
+  */
+  if (!table && (share= find_tmp_table_share(tl)))
+  {
+    table= open_temporary_table(share, tl->get_table_name(), true);
+  }
+
+  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);
+  }
+
+  /*
+    Temporary tables are not safe for parallel replication. They were
+    designed to be visible to one thread only, so have no table locking.
+    Thus there is no protection against two conflicting transactions
+    committing in parallel and things like that.
+
+    So for now, anything that uses temporary tables will be serialised
+    with anything before it, when using parallel replication.
+
+    TODO: We might be able to introduce a reference count or something
+    on temp tables, and have slave worker threads wait for it to reach
+    zero before being allowed to use the temp table. Might not be worth
+    it though, as statement-based replication using temporary tables is
+    in any case rather fragile.
+  */
+  if (rgi_slave &&
+      !rgi_slave->rli->save_temporary_tables.is_empty() &&
+      rgi_slave->is_parallel_exec &&
+      wait_for_prior_commit())
+    DBUG_RETURN(true);
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+  if (tl->partition_names)
+  {
+    /* Partitioned temporary tables is not supported. */
+    DBUG_ASSERT(!table->part_info);
+    my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
+    DBUG_RETURN(true);
+  }
+#endif
+
+  table->query_id= query_id;
+  thread_specific_used= true;
+
+  /* It is neither a derived table nor non-updatable view. */
+  tl->updatable= true;
+  tl->table= table;
+
+  table->init(this, 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.
+
+  @param tl [IN]                      TABLE_LIST
+
+  @return false                       On success. If a temporary table exists
+                                      for the given element, tl->table is set.
+          true                        On error. my_error() has been called.
+*/
+bool THD::open_temporary_tables(TABLE_LIST *tl)
+{
+  DBUG_ENTER("THD::open_temporary_tables");
+
+  TABLE_LIST *first_not_own= lex->first_not_own_table();
+
+  for (TABLE_LIST *table= tl; table && table != first_not_own;
+       table= table->next_global)
+  {
+    if (table->derived || table->schema_table)
+    {
+      /*
+        Derived and I_S tables will be handled by a later call to open_tables().
+      */
+      continue;
+    }
+
+    if (open_temporary_table(table))
+    {
+      DBUG_RETURN(true);
+    }
+  }
+
+  DBUG_RETURN(false);
+}
+
+
+/**
+  Close all temporary tables created by 'CREATE TEMPORARY TABLE' for thread
+  creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread.
+
+  Temporary tables created in a sql slave is closed by
+  Relay_log_info::close_temporary_tables().
+
+  @return false                       Success
+          true                        Failure
+*/
+bool THD::close_temporary_tables()
+{
+  DBUG_ENTER("THD::close_temporary_tables");
+
+  TMP_TABLE_SHARE *share;
+  TABLE *table;
+
+  bool error= false;
+
+  if (temporary_tables.is_empty())
+  {
+    DBUG_RETURN(false);
+  }
+  DBUG_ASSERT(!rgi_slave);
+
+  /*
+    Ensure we don't have open HANDLERs for tables we are about to close.
+    This is necessary when THD::close_temporary_tables() is called as
+    part of execution of BINLOG statement (e.g. for format description event).
+  */
+  mysql_ha_rm_temporary_tables(this);
+
+  /* Close all open temporary tables. */
+  All_tmp_tables_list::Iterator it(temporary_tables);
+  while ((share= it++))
+  {
+    /* Traverse the table list. */
+    while ((table= share->all_tmp_tables.pop_front()))
+    {
+      free_temporary_table(table);
+    }
+  }
+
+  // Write DROP TEMPORARY TABLE query log events to binary log.
+  if (mysql_bin_log.is_open())
+  {
+    error= log_events_and_free_tmp_shares();
+  }
+  else
+  {
+    while ((share= temporary_tables.pop_front()))
+    {
+      free_tmp_table_share(share, true);
+    }
+  }
+  reset_temporary_tables();
+
+  DBUG_RETURN(error);
+}
+
+
+/**
+  Rename a temporary table.
+
+  @param table [IN]                   Table handle
+  @param db [IN]                      New schema name
+  @param table_name [IN]              New table name
+
+  @return false                       Success
+          true                        Error
+*/
+bool THD::rename_temporary_table(TABLE *table,
+                                 const char *db,
+                                 const char *table_name)
+{
+  DBUG_ENTER("THD::rename_temporary_table");
+
+  char *key;
+  uint key_length;
+
+  TABLE_SHARE *share= table->s;
+
+  if (!(key= (char *) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH)))
+  {
+    DBUG_RETURN(true);
+  }
+
+  /*
+    Temporary tables are renamed by simply changing their table definition key.
+  */
+  key_length= create_tmp_table_def_key(key, db, table_name);
+  share->set_table_cache_key(key, key_length);
+
+  DBUG_RETURN(false);
+}
+
+
+/**
+  Drop a temporary table.
+
+  Try to locate the table in the list of open temporary tables.
+  If the table is found:
+   - If the table is locked with LOCK TABLES or by prelocking,
+     unlock it and remove it from the list of locked tables
+     (THD::lock). Currently only transactional temporary tables
+     are locked.
+   - Close the temporary table, remove its .FRM.
+   - Remove the table share from the list of temporary table shares.
+
+  This function is used to drop user temporary tables, as well as
+  internal tables created in CREATE TEMPORARY TABLE ... SELECT
+  or ALTER TABLE.
+
+  @param table [IN]                   Temporary table to be deleted
+  @param is_trans [OUT]               Is set to the type of the table:
+                                      transactional (e.g. innodb) as true or
+                                      non-transactional (e.g. myisam) as false.
+  @paral delete_table [IN]            Whether to delete the table files?
+
+  @return false                       Table was dropped
+          true                        Error
+*/
+bool THD::drop_temporary_table(TABLE *table,
+                               bool *is_trans,
+                               bool delete_table)
+{
+  DBUG_ENTER("THD::drop_temporary_table");
+
+  TMP_TABLE_SHARE *share;
+  TABLE *tab;
+  bool result= false;
+  bool locked;
+
+  DBUG_ASSERT(table);
+  DBUG_ASSERT(table->query_id);
+  DBUG_PRINT("tmptable", ("Dropping table: '%s'.'%s'",
+                          table->s->db.str, table->s->table_name.str));
+
+  locked= lock_temporary_tables();
+
+  share= tmp_table_share(table);
+
+  /* Table might be in use by some outer statement. */
+  All_share_tables_list::Iterator it(share->all_tmp_tables);
+  while ((tab= it++))
+  {
+    if (tab != table && tab->query_id != 0)
+    {
+      /* Found a table instance in use. This table cannot be be dropped. */
+      my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
+      result= true;
+      goto end;
+    }
+  }
+
+  if (is_trans)
+    *is_trans= table->file->has_transactions();
+
+  /*
+    Iterate over the list of open tables and close them.
+  */
+  while ((tab= share->all_tmp_tables.pop_front()))
+  {
+    free_temporary_table(tab);
+  }
+
+  /* Remove the TABLE_SHARE from the list of temporary tables. */
+  temporary_tables.remove(share);
+
+  /* Free the TABLE_SHARE and/or delete the files. */
+  free_tmp_table_share(share, delete_table);
+
+end:
+  if (locked)
+  {
+    DBUG_ASSERT(m_tmp_tables_locked);
+    unlock_temporary_tables();
+  }
+
+  DBUG_RETURN(result);
+}
+
+
+/**
+  Delete the temporary table files.
+
+  @param base [IN]                    Handlerton for table to be deleted.
+  @param path [IN]                    Path to the table to be deleted (i.e. path
+                                      to its .frm without an extension).
+
+  @return false                       Success
+          true                        Error
+*/
+bool THD::rm_temporary_table(handlerton *base, const char *path)
+{
+  DBUG_ENTER("THD::rm_temporary_table");
+
+  bool error= false;
+  handler *file;
+  char frm_path[FN_REFLEN + 1];
+
+  strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS);
+  if (mysql_file_delete(key_file_frm, frm_path, MYF(0)))
+  {
+    error= true;
+  }
+  file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base);
+  if (file && file->ha_delete_table(path))
+  {
+    error= true;
+    sql_print_warning("Could not remove temporary table: '%s', error: %d",
+                      path, my_errno);
+  }
+
+  delete file;
+  DBUG_RETURN(error);
+}
+
+
+/**
+  Mark all temporary tables which were used by the current statement or
+  sub-statement as free for reuse, but only if the query_id can be cleared.
+
+  @remark For temp tables associated with a open SQL HANDLER the query_id
+          is not reset until the HANDLER is closed.
+*/
+void THD::mark_tmp_tables_as_free_for_reuse()
+{
+  DBUG_ENTER("THD::mark_tmp_tables_as_free_for_reuse");
+
+  TMP_TABLE_SHARE *share;
+  TABLE *table;
+  bool locked;
+
+  if (query_id == 0)
+  {
+    /*
+      Thread has not executed any statement and has not used any
+      temporary tables.
+    */
+    DBUG_VOID_RETURN;
+  }
+
+  locked= lock_temporary_tables();
+
+  All_tmp_tables_list::Iterator it(temporary_tables);
+  while ((share= it++))
+  {
+    All_share_tables_list::Iterator tables_it(share->all_tmp_tables);
+    while ((table= tables_it++))
+    {
+      if ((table->query_id == query_id) && !table->open_by_handler)
+      {
+        mark_tmp_table_as_free_for_reuse(table);
+      }
+    }
+  }
+
+  if (locked)
+  {
+    DBUG_ASSERT(m_tmp_tables_locked);
+    unlock_temporary_tables();
+  }
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Reset a single temporary table. Effectively this "closes" one temporary
+  table in a session.
+
+  @param table              Temporary table
+
+  @return void
+*/
+void THD::mark_tmp_table_as_free_for_reuse(TABLE *table)
+{
+  DBUG_ENTER("THD::mark_tmp_table_as_free_for_reuse");
+
+  DBUG_ASSERT(table->s->tmp_table);
+
+  table->query_id= 0;
+  table->file->ha_reset();
+
+  /* Detach temporary MERGE children from temporary parent. */
+  DBUG_ASSERT(table->file);
+  table->file->extra(HA_EXTRA_DETACH_CHILDREN);
+
+  /*
+    Reset temporary table lock type to it's default value (TL_WRITE).
+
+    Statements such as INSERT INTO .. SELECT FROM tmp, CREATE TABLE
+    .. SELECT FROM tmp and UPDATE may under some circumstances modify
+    the lock type of the tables participating in the statement. This
+    isn't a problem for non-temporary tables since their lock type is
+    reset at every open, but the same does not occur for temporary
+    tables for historical reasons.
+
+    Furthermore, the lock type of temporary tables is not really that
+    important because they can only be used by one query at a time.
+    Nonetheless, it's safer from a maintenance point of view to reset
+    the lock type of this singleton TABLE object as to not cause problems
+    when the table is reused.
+
+    Even under LOCK TABLES mode its okay to reset the lock type as
+    LOCK TABLES is allowed (but ignored) for a temporary table.
+  */
+  table->reginfo.lock_type= TL_WRITE;
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Check whether slave temporary tables exist. The decision is made based on
+  the existence of TMP_TABLE_SHAREs in Relay_log_info::save_temporary_tables
+  list. Check THD::rgi_slave before calling this method.
+
+  @return false                       Temporary tables exist
+          true                        No temporary table exist
+*/
+bool THD::has_slave_temporary_tables()
+{
+  DBUG_ENTER("THD::has_slave_temporary_tables");
+  DBUG_ASSERT(rgi_slave);
+  bool result= rgi_slave->rli->save_temporary_tables.is_empty();
+  DBUG_RETURN(!result);
+}
+
+
+/**
+  Create a table definition key.
+
+  @param key [OUT]                    Buffer for the key to be created (must
+                                      be of size MAX_DBKRY_LENGTH)
+  @param db [IN]                      Database name
+  @param table_name [IN]              Table name
+
+  @return                             Key length.
+
+  @note
+    The table key is create from:
+    db + \0
+    table_name + \0
+
+    Additionally, we add the following to make each temporary table unique on
+    the slave.
+
+    4 bytes of master thread id
+    4 bytes of pseudo thread id
+*/
+uint THD::create_tmp_table_def_key(char *key, const char *db,
+                                    const char *table_name)
+{
+  DBUG_ENTER("THD::create_tmp_table_def_key");
+
+  uint key_length;
+
+  key_length= tdc_create_key(key, db, table_name);
+  int4store(key + key_length, variables.server_id);
+  int4store(key + key_length + 4, variables.pseudo_thread_id);
+  key_length += TMP_TABLE_KEY_EXTRA;
+
+  DBUG_RETURN(key_length);
+}
+
+
+/**
+  Create a temporary table.
+
+  @param hton [IN]                    Handlerton
+  @param frm  [IN]                    Binary frm image
+  @param path [IN]                    File path (without extension)
+  @param db   [IN]                    Schema name
+  @param table_name [IN]              Table name
+
+  @return Success                     A pointer to table share object
+          Failure                     NULL
+*/
+TMP_TABLE_SHARE *THD::create_temporary_table(handlerton *hton,
+                                             LEX_CUSTRING *frm,
+                                             const char *path,
+                                             const char *db,
+                                             const char *table_name)
+{
+  DBUG_ENTER("THD::create_temporary_table");
+
+  TMP_TABLE_SHARE *share;
+  char key_cache[MAX_DBKEY_LENGTH];
+  char *saved_key_cache;
+  char *tmp_path;
+  uint key_length;
+  bool locked;
+  int res;
+
+  /* Temporary tables are not safe for parallel replication. */
+  if (rgi_slave &&
+      !rgi_slave->rli->save_temporary_tables.is_empty() &&
+      rgi_slave->is_parallel_exec &&
+      wait_for_prior_commit())
+    DBUG_RETURN(NULL);
+
+  /* Create the table definition key for the temporary table. */
+  key_length= create_tmp_table_def_key(key_cache, db, table_name);
+
+  if (!(share= (TMP_TABLE_SHARE *) my_malloc(sizeof(TMP_TABLE_SHARE) +
+                                             strlen(path) + 1 + key_length,
+                                             MYF(MY_WME))))
+  {
+    DBUG_RETURN(NULL);                          /* Out of memory */
+  }
+
+  tmp_path= (char *)(share + 1);
+  saved_key_cache= strmov(tmp_path, path) + 1;
+  memcpy(saved_key_cache, key_cache, key_length);
+
+  init_tmp_table_share(this, share, saved_key_cache, key_length,
+                       strend(saved_key_cache) + 1, tmp_path);
+
+  share->db_plugin= ha_lock_engine(this, hton);
+
+  /*
+    Prefer using frm image over file. The image might not be available in
+    ALTER TABLE, when the discovering engine took over the ownership (see
+    TABLE::read_frm_image).
+  */
+  res= (frm->str)
+    ? share->init_from_binary_frm_image(this, false, frm->str, frm->length)
+    : open_table_def(this, share, GTS_TABLE | GTS_USE_DISCOVERY);
+
+  if (res)
+  {
+    /*
+      No need to lock share->mutex as this is not needed for temporary tables.
+    */
+    free_table_share(share);
+    my_free(share);
+    DBUG_RETURN(NULL);
+  }
+
+  share->m_psi= PSI_CALL_get_table_share(true, share);
+
+  locked= lock_temporary_tables();
+
+  /* Initialize the all_tmp_tables list. */
+  share->all_tmp_tables.empty();
+
+  if (locked)
+  {
+    DBUG_ASSERT(m_tmp_tables_locked);
+    unlock_temporary_tables();
+  }
+
+  /* Add share to the head of the temporary table share list. */
+  temporary_tables.push_front(share);
+
+  DBUG_RETURN(share);
+}
+
+
+/**
+  Find a table with the specified key.
+
+  @param key [IN]                     Key
+  @param key_length [IN]              Key length
+  @param state [IN]                   Open table state to look for
+
+  @return Success                     Pointer to the table instance.
+          Failure                     NULL
+*/
+TABLE *THD::find_temporary_table(const char *key, uint key_length,
+                                 Temporary_table_state state)
+{
+  DBUG_ENTER("THD::find_temporary_table");
+
+  TMP_TABLE_SHARE *share;
+  TABLE *table;
+  TABLE *result= NULL;
+  bool locked;
+
+  locked= lock_temporary_tables();
+
+  All_tmp_tables_list::Iterator it(temporary_tables);
+  while ((share= it++))
+  {
+    if (share->table_cache_key.length == key_length &&
+        !(memcmp(share->table_cache_key.str, key, key_length)))
+    {
+      /* A matching TMP_TABLE_SHARE is found. */
+      All_share_tables_list::Iterator tables_it(share->all_tmp_tables);
+
+      while ((table= tables_it++))
+      {
+        switch (state)
+        {
+        case TMP_TABLE_IN_USE:
+          if (table->query_id > 0)
+          {
+            result= table;
+            goto done;
+          }
+          break;
+        case TMP_TABLE_NOT_IN_USE:
+          if (table->query_id == 0)
+          {
+            result= table;
+            goto done;
+          }
+          break;
+        case TMP_TABLE_ANY:
+          {
+            result= table;
+            goto done;
+          }
+          break;
+        default:                                /* Invalid */
+          DBUG_ASSERT(0);
+          goto done;
+        }
+      }
+    }
+  }
+
+done:
+  if (locked)
+  {
+    DBUG_ASSERT(m_tmp_tables_locked);
+    unlock_temporary_tables();
+  }
+
+  DBUG_RETURN(result);
+}
+
+
+
+/**
+  Open a table from the specified TABLE_SHARE with the given alias.
+
+  @param share [IN]                   Table share
+  @param alias [IN]                   Table alias
+  @param open_in_engine [IN]          Whether open table in SE
+
+  @return Success                     A pointer to table object
+          Failure                     NULL
+*/
+TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
+                                 const char *alias,
+                                 bool open_in_engine)
+{
+  DBUG_ENTER("THD::open_temporary_table");
+
+  TABLE *table;
+
+  if (!(table= (TABLE *) my_malloc(sizeof(TABLE), MYF(MY_WME))))
+  {
+    DBUG_RETURN(NULL);                          /* Out of memory */
+  }
+
+  if (open_table_from_share(this, share, alias,
+                            (open_in_engine) ?
+                            (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+                                    HA_GET_INDEX) : 0,
+                            (uint) (READ_KEYINFO | COMPUTE_TYPES |
+                                    EXTRA_RECORD),
+                            ha_open_options,
+                            table,
+                            open_in_engine ? false : true))
+  {
+    my_free(table);
+    DBUG_RETURN(NULL);
+  }
+
+  table->reginfo.lock_type= TL_WRITE;           /* Simulate locked */
+  table->grant.privilege= TMP_TABLE_ACLS;
+  share->tmp_table= (table->file->has_transactions() ?
+                     TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
+
+  table->pos_in_table_list= 0;
+  table->query_id= query_id;
+
+  /* Add table to the head of table list. */
+  share->all_tmp_tables.push_front(table);
+
+  /* Increment Slave_open_temp_table_definitions status variable count. */
+  if (rgi_slave)
+  {
+    thread_safe_increment32(&slave_open_temp_tables);
+  }
+
+  DBUG_PRINT("tmptable", ("Opened table: '%s'.'%s' 0x%lx", table->s->db.str,
+                          table->s->table_name.str, (long) table));
+  DBUG_RETURN(table);
+}
+
+
+/**
+  Find a reusable table in the open table list using the specified TABLE_LIST.
+
+  @param tl [IN]                      Table list
+  @param out_table [OUT]              Pointer to the requested TABLE object
+
+  @return Success                     false
+          Failure                     true
+*/
+bool THD::find_and_use_tmp_table(const TABLE_LIST *tl,
+                                 TABLE **out_table)
+{
+  DBUG_ENTER("THD::find_and_use_tmp_table");
+
+  char key[MAX_DBKEY_LENGTH];
+  uint key_length;
+  bool result;
+
+  key_length= create_tmp_table_def_key(key, tl->get_db_name(),
+                                        tl->get_table_name());
+  result=
+    use_temporary_table(find_temporary_table(key, key_length,
+                                             TMP_TABLE_NOT_IN_USE),
+                        out_table);
+
+  DBUG_RETURN(result);
+}
+
+/**
+  Mark table as in-use.
+
+  @param table [IN]                   Table to be marked in-use
+  @param out_table [OUT]              Pointer to the specified table
+
+  @return false                       Success
+          true                        Error
+*/
+bool THD::use_temporary_table(TABLE *table, TABLE **out_table)
+{
+  DBUG_ENTER("THD::use_temporary_table");
+
+  *out_table= table;
+  if (!table)
+    DBUG_RETURN(false);
+
+  /*
+    Temporary tables are not safe for parallel replication. They were
+    designed to be visible to one thread only, so have no table locking.
+    Thus there is no protection against two conflicting transactions
+    committing in parallel and things like that.
+
+    So for now, anything that uses temporary tables will be serialised
+    with anything before it, when using parallel replication.
+
+    TODO: We might be able to introduce a reference count or something
+    on temp tables, and have slave worker threads wait for it to reach
+    zero before being allowed to use the temp table. Might not be worth
+    it though, as statement-based replication using temporary tables is
+    in any case rather fragile.
+  */
+  if (rgi_slave &&
+      !rgi_slave->rli->save_temporary_tables.is_empty() &&
+      rgi_slave->is_parallel_exec &&
+      wait_for_prior_commit())
+    DBUG_RETURN(true);
+
+  /*
+    We need to set the THD as it may be different in case of
+    parallel replication
+  */
+  if (table->in_use != this)
+  {
+    table->in_use= this;
+  }
+
+  DBUG_RETURN(false);
+}
+
+
+/**
+  Close a temporary table.
+
+  @param table [IN]                   Table handle
+
+  @return void
+*/
+void THD::close_temporary_table(TABLE *table)
+{
+  DBUG_ENTER("THD::close_temporary_table");
+
+  DBUG_PRINT("tmptable", ("closing table: '%s'.'%s' 0x%lx  alias: '%s'",
+                          table->s->db.str, table->s->table_name.str,
+                          (long) table, table->alias.c_ptr()));
+
+  free_io_cache(table);
+  closefrm(table, false);
+  my_free(table);
+
+  /* Decrement Slave_open_temp_table_definitions status variable count. */
+  if (rgi_slave)
+  {
+    thread_safe_decrement32(&slave_open_temp_tables);
+  }
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Write query log events with "DROP TEMPORARY TABLES .." for each pseudo
+  thread to the binary log.
+
+  @return false                       Success
+          true                        Error
+*/
+bool THD::log_events_and_free_tmp_shares()
+{
+  DBUG_ENTER("THD::log_events_and_free_tmp_shares");
+
+  DBUG_ASSERT(!rgi_slave);
+
+  TMP_TABLE_SHARE *share;
+  TMP_TABLE_SHARE *sorted;
+  TMP_TABLE_SHARE *prev_sorted;
+  // Assume thd->variables.option_bits has OPTION_QUOTE_SHOW_CREATE.
+  bool was_quote_show= true;
+  bool error= false;
+  bool found_user_tables= false;
+  // Better add "IF EXISTS" in case a RESET MASTER has been done.
+  const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ";
+  char buf[FN_REFLEN];
+
+  String s_query(buf, sizeof(buf), system_charset_info);
+  s_query.copy(stub, sizeof(stub) - 1, system_charset_info);
+
+  /*
+    Insertion sort of temporary tables by pseudo_thread_id to build ordered
+    list of sublists of equal pseudo_thread_id.
+  */
+  All_tmp_tables_list::Iterator it_sorted(temporary_tables);
+  All_tmp_tables_list::Iterator it_unsorted(temporary_tables);
+  uint sorted_count= 0;
+  while((share= it_unsorted++))
+  {
+    if (IS_USER_TABLE(share))
+    {
+      prev_sorted= NULL;
+
+      if (!found_user_tables) found_user_tables= true;
+
+      for (uint i= 0; i < sorted_count; i ++)
+      {
+        sorted= it_sorted ++;
+
+        if (!IS_USER_TABLE(sorted) ||
+            (tmpkeyval(sorted) > tmpkeyval(share)))
+        {
+          /*
+            Insert this share before the current element in
+            the sorted part of the list.
+          */
+          temporary_tables.remove(share);
+
+          if (prev_sorted)
+          {
+            temporary_tables.insert_after(prev_sorted, share);
+          }
+          else
+          {
+            temporary_tables.push_front(share);
+          }
+          break;
+        }
+        prev_sorted= sorted;
+      }
+      it_sorted.rewind();
+    }
+    sorted_count ++;
+  }
+
+  /*
+    We always quote db & table names.
+  */
+  if (found_user_tables &&
+      !(was_quote_show= MY_TEST(variables.option_bits &
+                                OPTION_QUOTE_SHOW_CREATE)))
+  {
+    variables.option_bits |= OPTION_QUOTE_SHOW_CREATE;
+  }
+
+  /*
+    Scan sorted temporary tables to generate sequence of DROP.
+  */
+  share= temporary_tables.pop_front();
+  while (share)
+  {
+    if (IS_USER_TABLE(share))
+    {
+      bool save_thread_specific_used= thread_specific_used;
+      my_thread_id save_pseudo_thread_id= variables.pseudo_thread_id;
+      char db_buf[FN_REFLEN];
+      String db(db_buf, sizeof(db_buf), system_charset_info);
+
+      /*
+        Set pseudo_thread_id to be that of the processed table.
+      */
+      variables.pseudo_thread_id= tmpkeyval(share);
+
+      db.copy(share->db.str, share->db.length, system_charset_info);
+      /*
+        Reset s_query() if changed by previous loop.
+      */
+      s_query.length(sizeof(stub) - 1);
+
+      /*
+        Loop forward through all tables that belong to a common database
+        within the sublist of common pseudo_thread_id to create single
+        DROP query.
+      */
+      for (;
+           share && IS_USER_TABLE(share) &&
+           tmpkeyval(share) == variables.pseudo_thread_id &&
+           share->db.length == db.length() &&
+           memcmp(share->db.str, db.ptr(), db.length()) == 0;
+           /* Get the next TABLE_SHARE in the list. */
+           share= temporary_tables.pop_front())
+      {
+        /*
+          We are going to add ` around the table names and possible more
+          due to special characters.
+        */
+        append_identifier(this, &s_query, share->table_name.str,
+                          share->table_name.length);
+        s_query.append(',');
+        rm_temporary_table(share->db_type(), share->path.str);
+        free_table_share(share);
+        my_free(share);
+      }
+
+      clear_error();
+      CHARSET_INFO *cs_save= variables.character_set_client;
+      variables.character_set_client= system_charset_info;
+      thread_specific_used= true;
+
+      Query_log_event qinfo(this, s_query.ptr(),
+                            s_query.length() - 1 /* to remove trailing ',' */,
+                            false, true, false, 0);
+      qinfo.db= db.ptr();
+      qinfo.db_len= db.length();
+      variables.character_set_client= cs_save;
+
+      get_stmt_da()->set_overwrite_status(true);
+      if ((error= (mysql_bin_log.write(&qinfo) || error)))
+      {
+        /*
+          If we're here following THD::cleanup, thence the connection
+          has been closed already. So lets print a message to the
+          error log instead of pushing yet another error into the
+          stmt_da.
+
+          Also, we keep the error flag so that we propagate the error
+          up in the stack. This way, if we're the SQL thread we notice
+          that THD::close_tables failed. (Actually, the SQL
+          thread only calls THD::close_tables while applying
+          old Start_log_event_v3 events.)
+        */
+        sql_print_error("Failed to write the DROP statement for "
+                        "temporary tables to binary log");
+      }
+
+      get_stmt_da()->set_overwrite_status(false);
+      variables.pseudo_thread_id= save_pseudo_thread_id;
+      thread_specific_used= save_thread_specific_used;
+    }
+    else
+    {
+      free_tmp_table_share(share, true);
+      /* Get the next TABLE_SHARE in the list. */
+      share= temporary_tables.pop_front();
+    }
+  }
+
+  if (!was_quote_show)
+  {
+    /*
+      Restore option.
+    */
+    variables.option_bits&= ~OPTION_QUOTE_SHOW_CREATE;
+  }
+
+  DBUG_RETURN(error);
+}
+
+
+/**
+  Delete the files and free the specified table share.
+
+  @param share [IN]                   TABLE_SHARE to free
+  @param delete_table [IN]            Whether to delete the table files?
+
+  @return void
+*/
+void THD::free_tmp_table_share(TMP_TABLE_SHARE *share,
+                               bool delete_table)
+{
+  DBUG_ENTER("THD::free_tmp_table_share");
+
+  if (delete_table)
+  {
+    rm_temporary_table(share->db_type(), share->path.str);
+  }
+  free_table_share(share);
+  my_free(share);
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Free the specified table object.
+
+  @param table [IN]                   Table object to free.
+
+  @return void
+*/
+void THD::free_temporary_table(TABLE *table)
+{
+  DBUG_ENTER("THD::free_temporary_table");
+
+  /*
+    If LOCK TABLES list is not empty and contains this table, unlock the table
+    and remove the table from this list.
+  */
+  mysql_lock_remove(this, lock, table);
+
+  close_temporary_table(table);
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  On replication slave, acquire the Relay_log_info's data_lock and use slave
+  temporary tables.
+
+  @return true                        Lock acquired
+          false                       Lock wasn't acquired
+*/
+bool THD::lock_temporary_tables()
+{
+  DBUG_ENTER("THD::lock_temporary_tables");
+
+  /* Do not proceed if a lock has already been taken. */
+  if (m_tmp_tables_locked)
+  {
+    DBUG_RETURN(false);
+  }
+
+  if (rgi_slave)
+  {
+    mysql_mutex_lock(&rgi_slave->rli->data_lock);
+    temporary_tables= rgi_slave->rli->save_temporary_tables;
+    m_tmp_tables_locked= true;
+  }
+
+  DBUG_RETURN(m_tmp_tables_locked);
+}
+
+
+/**
+  On replication slave, release the Relay_log_info::data_lock previously
+  acquired to use slave temporary tables.
+
+  @return void
+*/
+void THD::unlock_temporary_tables()
+{
+  DBUG_ENTER("THD::unlock_temporary_tables");
+
+  if (!m_tmp_tables_locked)
+  {
+    DBUG_VOID_RETURN;
+  }
+
+  if (rgi_slave)
+  {
+    rgi_slave->rli->save_temporary_tables= temporary_tables;
+    mysql_mutex_unlock(&rgi_slave->rli->data_lock);
+    m_tmp_tables_locked= false;
+    /*
+      Temporary tables are shared with other by sql execution threads.
+      As a safety measure, clear the pointer to the common area.
+    */
+    reset_temporary_tables();
+  }
+
+  DBUG_VOID_RETURN;
+}
+
diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc
index c1c6a90..b45fb2c 100644
--- a/sql/wsrep_applier.cc
+++ b/sql/wsrep_applier.cc
@@ -61,7 +61,6 @@ static Log_event* wsrep_read_log_event(
 
 #include "transaction.h" // trans_commit(), trans_rollback()
 #include "rpl_rli.h"     // class Relay_log_info;
-#include "sql_base.h"    // close_temporary_table()
 
 void wsrep_set_apply_format(THD* thd, Format_description_log_event* ev)
 {
@@ -266,14 +265,11 @@ wsrep_cb_status_t wsrep_apply_cb(void* const             ctx,
     wsrep_dump_rbr_buf_with_header(thd, buf, buf_len);
   }
 
-  TABLE *tmp;
-  while ((tmp = thd->temporary_tables))
+  if (thd->has_temporary_tables())
   {
-    WSREP_DEBUG("Applier %lu, has temporary tables: %s.%s",
-		thd->thread_id,
-		(tmp->s) ? tmp->s->db.str : "void",
-		(tmp->s) ? tmp->s->table_name.str : "void");
-    close_temporary_table(thd, tmp, 1, 1);
+    WSREP_DEBUG("Applier %lu, has temporary tables. Closing them now..",
+                thd->thread_id);
+    thd->close_temporary_tables();
   }
 
   return rcode;
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index 90b1c13..ce6f01a 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -1077,8 +1077,6 @@ static bool wsrep_prepare_keys_for_isolation(THD*              thd,
     ka->keys= 0;
     ka->keys_len= 0;
 
-    extern TABLE* find_temporary_table(THD*, const TABLE_LIST*);
-
     if (db || table)
     {
         TABLE_LIST tmp_table;
@@ -1090,7 +1088,7 @@ static bool wsrep_prepare_keys_for_isolation(THD*              thd,
 				   (table) ? table : "",
 				   MDL_INTENTION_EXCLUSIVE, MDL_STATEMENT);
 
-        if (!table || !find_temporary_table(thd, &tmp_table))
+        if (!table || !thd->find_temporary_table(&tmp_table))
         {
             if (!(ka->keys= (wsrep_key_t*)my_malloc(sizeof(wsrep_key_t), MYF(0))))
             {
@@ -1118,7 +1116,7 @@ static bool wsrep_prepare_keys_for_isolation(THD*              thd,
 
     for (const TABLE_LIST* table= table_list; table; table= table->next_global)
     {
-        if (!find_temporary_table(thd, table))
+        if (!thd->find_temporary_table(table))
         {
             wsrep_key_t* tmp;
             tmp= (wsrep_key_t*)my_realloc(
@@ -2433,26 +2431,13 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table,
                              TABLE_LIST* src_table,
                              HA_CREATE_INFO *create_info)
 {
-  TABLE *tmp_table;
-  bool is_tmp_table= FALSE;
-
-  for (tmp_table= thd->temporary_tables; tmp_table; tmp_table=tmp_table->next)
-  {
-      if (!strcmp(src_table->db, tmp_table->s->db.str)     &&
-          !strcmp(src_table->table_name, tmp_table->s->table_name.str))
-      {
-        is_tmp_table= TRUE;
-        break;
-      }
-  }
   if (create_info->tmp_table())
   {
-
     /* CREATE TEMPORARY TABLE LIKE must be skipped from replication */
     WSREP_DEBUG("CREATE TEMPORARY TABLE LIKE... skipped replication\n %s", 
                 thd->query());
   }
-  else if (!is_tmp_table)
+  else if (!(thd->find_temporary_table(src_table)))
   {
     /* this is straight CREATE TABLE LIKE... eith no tmp tables */
     WSREP_TO_ISOLATION_BEGIN(table->db, table->table_name, NULL);
@@ -2467,7 +2452,7 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table,
     bzero((void*) &tbl, sizeof(tbl));
     tbl.db= src_table->db;
     tbl.table_name= tbl.alias= src_table->table_name;
-    tbl.table= tmp_table;
+    tbl.table= src_table->table;
     char buf[2048];
     String query(buf, sizeof(buf), system_charset_info);
     query.length(0);  // Have to zero it since constructor doesn't
diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc
index ab09a9e..6182fc4 100644
--- a/sql/wsrep_thd.cc
+++ b/sql/wsrep_thd.cc
@@ -357,13 +357,10 @@ static void wsrep_replication_process(THD *thd)
   mysql_cond_broadcast(&COND_thread_count);
   mysql_mutex_unlock(&LOCK_thread_count);
 
-  TABLE *tmp;
-  while ((tmp = thd->temporary_tables))
+  if(thd->has_temporary_tables())
   {
-    WSREP_WARN("Applier %lu, has temporary tables at exit: %s.%s",
-                  thd->thread_id, 
-                  (tmp->s) ? tmp->s->db.str : "void",
-                  (tmp->s) ? tmp->s->table_name.str : "void");
+    WSREP_WARN("Applier %lu, has temporary tables at exit.",
+               thd->thread_id);
   }
   wsrep_return_from_bf_mode(thd, &shadow);
   DBUG_VOID_RETURN;
diff --git a/storage/spider/spd_direct_sql.cc b/storage/spider/spd_direct_sql.cc
index 2704179..47b96ba 100644
--- a/storage/spider/spd_direct_sql.cc
+++ b/storage/spider/spd_direct_sql.cc
@@ -1629,7 +1629,7 @@ long long spider_direct_sql_body(
     table_list.table_name = direct_sql->table_names[roop_count];
 #endif
     if (!(direct_sql->tables[roop_count] =
-      find_temporary_table(thd, &table_list)))
+          thd->find_temporary_table(&table_list)))
     {
 #if MYSQL_VERSION_ID < 50500
 #else


More information about the commits mailing list