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

@ -1264,6 +1264,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
@ -1293,15 +1348,20 @@ public:
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
@ -1379,8 +1439,11 @@ public:
void reset_open_tables_state(THD *thd)
{
open_tables= temporary_tables= derived_tables= 0;
extra_lock= lock= 0;
open_tables= 0;
temporary_tables= 0;
derived_tables= 0;
extra_lock= 0;
lock= 0;
locked_tables_mode= LTM_NONE;
state_flags= 0U;
m_reprepare_observer= NULL;
@ -3544,13 +3607,13 @@ public:
*/
DBUG_PRINT("debug",
("temporary_tables: %s, in_sub_stmt: %s, system_thread: %s",
YESNO(temporary_tables), YESNO(in_sub_stmt),
YESNO(has_thd_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_thd_temporary_tables())
set_current_stmt_binlog_format_stmt();
}
DBUG_VOID_RETURN;
@ -3950,10 +4013,6 @@ private:
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
@ -3971,26 +4030,85 @@ public:
*/
rpl_gtid last_commit_gtid;
inline void lock_temporary_tables()
{
if (rgi_slave)
rgi_lock_temporary_tables();
}
inline void unlock_temporary_tables()
{
if (rgi_slave)
rgi_unlock_temporary_tables();
}
inline bool have_temporary_tables()
{
return (temporary_tables ||
(rgi_slave && rgi_have_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_thd_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);
TABLE *find_temporary_table(const char *db, const char *table_name);
TABLE *find_temporary_table(const TABLE_LIST *tl);
TMP_TABLE_SHARE *find_tmp_table_share_w_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_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)
{
return uint4korr(share->table_cache_key.str +
share->table_cache_key.length - 4);
}
inline TMP_TABLE_SHARE *tmp_table_share(TABLE *table)
{
DBUG_ASSERT(table->s->tmp_table);
return static_cast<TMP_TABLE_SHARE *>(table->s);
}
public:
inline ulong wsrep_binlog_format() const
{
return WSREP_FORMAT(variables.binlog_format);