1
0
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:
Nirbhay Choubey
2016-06-10 16:19:59 -04:00
parent 547511153f
commit 7305be2f7e
50 changed files with 2315 additions and 1260 deletions

View File

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