1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

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.
This commit is contained in:
Nirbhay Choubey
2016-06-10 16:58:08 -04:00
parent 7305be2f7e
commit e2087c6e8d
7 changed files with 142 additions and 7 deletions

View File

@ -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

View File

@ -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);

View File

@ -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()

View File

@ -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;

View File

@ -4066,6 +4066,9 @@ public:
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 @@ public:
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);

View File

@ -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;

View File

@ -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