Commit e2087c6e authored by Nirbhay Choubey's avatar Nirbhay Choubey

MDEV-5535: Cannot reopen temporary table

Temporary table being created by outer statement
should not be visible to inner statement. And if
inner statement creates a table with same name.
The whole statement should fail with
ER_TABLE_EXISTS_ERROR.

Implemented by temporarily de-linking the TABLE_SHARE
being created by outer statement so that it remains
hidden to the inner statement.
parent 7305be2f
......@@ -19,6 +19,28 @@ DROP TABLE t1;
#
# CREATE & Stored routines
#
CREATE FUNCTION f1() RETURNS INT
BEGIN
DROP TEMPORARY TABLE t1;
RETURN 1;
END|
CREATE TEMPORARY TABLE t1 AS SELECT f1();
ERROR 42S02: Unknown table 'temp_db.t1'
DROP FUNCTION f1;
CREATE FUNCTION f2() RETURNS INT
BEGIN
CREATE TEMPORARY TABLE t2(i INT);
INSERT INTO t2 VALUES(1), (2);
RETURN 1;
END|
CREATE TEMPORARY TABLE t2 AS SELECT f2();
ERROR 42S01: Table 't2' already exists
SELECT * FROM t2;
i
1
2
DROP TABLE t2;
DROP FUNCTION f2;
CREATE TEMPORARY TABLE t3 AS SELECT 1 AS a;
CREATE PROCEDURE p1()
BEGIN
......
......@@ -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);
......
......@@ -24,6 +24,31 @@ DROP TABLE t1;
--echo # CREATE & Stored routines
--echo #
DELIMITER |;
CREATE FUNCTION f1() RETURNS INT
BEGIN
DROP TEMPORARY TABLE t1;
RETURN 1;
END|
DELIMITER ;|
--error ER_BAD_TABLE_ERROR
CREATE TEMPORARY TABLE t1 AS SELECT f1();
DROP FUNCTION f1;
DELIMITER |;
CREATE FUNCTION f2() RETURNS INT
BEGIN
CREATE TEMPORARY TABLE t2(i INT);
INSERT INTO t2 VALUES(1), (2);
RETURN 1;
END|
DELIMITER ;|
--error ER_TABLE_EXISTS_ERROR
CREATE TEMPORARY TABLE t2 AS SELECT f2();
SELECT * FROM t2;
DROP TABLE t2;
DROP FUNCTION f2;
CREATE TEMPORARY TABLE t3 AS SELECT 1 AS a;
DELIMITER |;
CREATE PROCEDURE p1()
......
......@@ -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;
......
......@@ -4066,6 +4066,9 @@ class THD :public Statement,
void mark_tmp_tables_as_free_for_reuse();
void mark_tmp_table_as_free_for_reuse(TABLE *table);
TMP_TABLE_SHARE* save_tmp_table_share(TABLE *table);
void restore_tmp_table_share(TMP_TABLE_SHARE *share);
private:
/* Whether a lock has been acquired? */
bool m_tmp_tables_locked;
......@@ -4617,6 +4620,7 @@ class select_create: public select_insert {
/* m_lock or thd->extra_lock */
MYSQL_LOCK **m_plock;
bool exit_done;
TMP_TABLE_SHARE *saved_tmp_table_share;
public:
select_create(THD *thd_arg, TABLE_LIST *table_arg,
......@@ -4629,7 +4633,8 @@ class select_create: public select_insert {
create_info(create_info_par),
select_tables(select_tables_arg),
alter_info(alter_info_arg),
m_plock(NULL), exit_done(0)
m_plock(NULL), exit_done(0),
saved_tmp_table_share(0)
{}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
......
......@@ -4226,6 +4226,18 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
/* abort() deletes table */
DBUG_RETURN(-1);
if (create_info->tmp_table())
{
/*
When the temporary table was created & opened in create_table_impl(),
the table's TABLE_SHARE (and thus TABLE) object was also linked to THD
temporary tables lists. So, we must temporarily remove it from the
list to keep them inaccessible from inner statements.
e.g. CREATE TEMPORARY TABLE `t1` AS SELECT * FROM `t1`;
*/
saved_tmp_table_share= thd->save_tmp_table_share(create_table->table);
}
if (extra_lock)
{
DBUG_ASSERT(m_plock == NULL);
......@@ -4341,6 +4353,27 @@ bool select_create::send_eof()
DBUG_RETURN(true);
}
if (table->s->tmp_table)
{
/*
Now is good time to add the new table to THD temporary tables list.
But, before that we need to check if same table got created by the sub-
statement.
*/
if (thd->find_tmp_table_share(table->s->table_cache_key.str,
table->s->table_cache_key.length))
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table->alias.c_ptr());
abort_result_set();
DBUG_RETURN(true);
}
else
{
DBUG_ASSERT(saved_tmp_table_share);
thd->restore_tmp_table_share(saved_tmp_table_share);
}
}
/*
Do an implicit commit at end of statement for non-temporary
tables. This can fail, but we should unlock the table
......@@ -4466,6 +4499,12 @@ void select_create::abort_result_set()
{
bool tmp_table= table->s->tmp_table;
if (tmp_table)
{
DBUG_ASSERT(saved_tmp_table_share);
thd->restore_tmp_table_share(saved_tmp_table_share);
}
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
table->auto_increment_field_not_null= FALSE;
......
......@@ -801,6 +801,50 @@ void THD::mark_tmp_table_as_free_for_reuse(TABLE *table)
}
/**
Remove and return the specified table's TABLE_SHARE from the temporary
tables list.
@param table [IN] Table
@return TMP_TABLE_SHARE of the specified table.
*/
TMP_TABLE_SHARE *THD::save_tmp_table_share(TABLE *table)
{
DBUG_ENTER("THD::save_tmp_table_share");
TMP_TABLE_SHARE *share;
lock_temporary_tables();
DBUG_ASSERT(temporary_tables);
share= tmp_table_share(table);
temporary_tables->remove(share);
unlock_temporary_tables();
DBUG_RETURN(share);
}
/**
Add the specified TMP_TABLE_SHARE to the temporary tables list.
@param share [IN] Table share
@return void
*/
void THD::restore_tmp_table_share(TMP_TABLE_SHARE *share)
{
DBUG_ENTER("THD::restore_tmp_table_share");
lock_temporary_tables();
DBUG_ASSERT(temporary_tables);
temporary_tables->push_front(share);
unlock_temporary_tables();
DBUG_VOID_RETURN;
}
/**
If its a replication slave, report whether slave temporary tables
exist (Relay_log_info::save_temporary_tables) or report about THD
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment