mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
MDEV-5535: Cannot reopen temporary table
mysqld maintains a list of TABLE objects for all temporary tables created within a session in THD. Here each table is represented by a TABLE object. A query referencing a particular temporary table for more than once, however, failed with ER_CANT_REOPEN_TABLE error because a TABLE_SHARE was allocate together with the TABLE, so temporary tables always had only one TABLE per TABLE_SHARE. This patch lift this restriction by separating TABLE and TABLE_SHARE objects and storing TABLE_SHAREs for temporary tables in a list in THD, and TABLEs in a list within their respective TABLE_SHAREs.
This commit is contained in:
128
sql/sql_table.cc
128
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
|
||||
@ -2030,7 +2030,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);
|
||||
}
|
||||
}
|
||||
@ -2283,23 +2283,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;
|
||||
@ -4635,7 +4629,8 @@ err:
|
||||
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
|
||||
@ -4695,20 +4690,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())
|
||||
@ -4847,17 +4844,12 @@ 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.
|
||||
*/
|
||||
|
||||
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);
|
||||
|
||||
if (!table)
|
||||
{
|
||||
(void) rm_temporary_table(create_info->db_type, path);
|
||||
(void) thd->rm_temporary_table(create_info->db_type, path);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -7143,7 +7135,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.
|
||||
@ -7233,7 +7226,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);
|
||||
@ -8385,7 +8378,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);
|
||||
@ -8821,11 +8819,11 @@ 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)))
|
||||
if (!(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)))
|
||||
goto err_new_table_cleanup;
|
||||
|
||||
/* Set markers for fields in TABLE object for altered table. */
|
||||
@ -8865,7 +8863,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;
|
||||
@ -8876,7 +8874,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;
|
||||
@ -8890,7 +8888,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.
|
||||
@ -8898,7 +8896,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
|
||||
@ -8906,7 +8904,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;
|
||||
}
|
||||
|
||||
@ -8925,7 +8923,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8978,13 +8976,18 @@ 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);
|
||||
if (!tmp_table)
|
||||
{
|
||||
goto err_new_table_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Open the table since we need to copy the data. */
|
||||
if (table->s->tmp_table != NO_TMP_TABLE)
|
||||
{
|
||||
@ -8992,18 +8995,24 @@ 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);
|
||||
}
|
||||
if (!new_table)
|
||||
goto err_new_table_cleanup;
|
||||
@ -9071,10 +9080,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() &&
|
||||
@ -9086,10 +9095,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");
|
||||
@ -9231,8 +9240,7 @@ err_new_table_cleanup:
|
||||
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,
|
||||
@ -9732,7 +9740,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;
|
||||
|
Reference in New Issue
Block a user