summaryrefslogtreecommitdiff
path: root/sql/sql_base.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r--sql/sql_base.cc832
1 files changed, 556 insertions, 276 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 969e2f2c7e4..28f4b7e0ef8 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -66,9 +66,9 @@ bool
No_such_table_error_handler::handle_condition(THD *,
uint sql_errno,
const char*,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char*,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
if (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE)
@@ -77,7 +77,7 @@ No_such_table_error_handler::handle_condition(THD *,
return TRUE;
}
- if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ if (level == Sql_condition::WARN_LEVEL_ERROR)
m_unhandled_errors++;
return FALSE;
}
@@ -110,9 +110,9 @@ public:
bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
/**
Returns TRUE if there were ER_NO_SUCH_/WRONG_MRG_TABLE and there
@@ -140,9 +140,9 @@ bool
Repair_mrg_table_error_handler::handle_condition(THD *,
uint sql_errno,
const char*,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char*,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
if (sql_errno == ER_NO_SUCH_TABLE ||
@@ -164,9 +164,37 @@ Repair_mrg_table_error_handler::handle_condition(THD *,
*/
/**
- Protects table_def_hash, used and unused lists in the
- TABLE_SHARE object, LRU lists of used TABLEs and used
- TABLE_SHAREs, refresh_version and the table id counter.
+ LOCK_open protects the following variables/objects:
+
+ 1) The table_def_hash
+ This is the hash table mapping table name to a table
+ share object. The hash table can only be manipulated
+ while holding LOCK_open.
+ 2) last_table_id
+ Generation of a new unique table_map_id for a table
+ share is done through incrementing last_table_id, a
+ global variable used for this purpose.
+ 3) LOCK_open protects the initialisation of the table share
+ object and all its members and also protects reading the
+ .frm file from where the table share is initialised.
+ 4) In particular the share->ref_count is updated each time
+ a new table object is created that refers to a table share.
+ This update is protected by LOCK_open.
+ 5) oldest_unused_share, end_of_unused_share and share->next
+ and share->prev are variables to handle the lists of table
+ share objects, these can only be read and manipulated while
+ holding the LOCK_open mutex.
+ 6) table_def_shutdown_in_progress can be updated only while
+ holding LOCK_open and ALL table cache mutexes.
+ 7) refresh_version
+ This variable can only be updated while holding LOCK_open AND
+ all table cache mutexes.
+ 8) share->version
+ This variable is initialised while holding LOCK_open. It can only
+ be updated while holding LOCK_open AND all table cache mutexes.
+ So if a table share is found through a reference its version won't
+ change if any of those mutexes are held.
+ 9) share->m_flush_tickets
*/
mysql_mutex_t LOCK_open;
@@ -186,11 +214,8 @@ static void init_tdc_psi_keys(void)
const char *category= "sql";
int count;
- if (PSI_server == NULL)
- return;
-
count= array_elements(all_tdc_mutexes);
- PSI_server->register_mutex(category, all_tdc_mutexes, count);
+ mysql_mutex_register(category, all_tdc_mutexes, count);
}
#endif /* HAVE_PSI_INTERFACE */
@@ -257,7 +282,7 @@ static void check_unused(THD *thd)
{
share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx);
- I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
+ TABLE_SHARE::TABLE_list::Iterator it(share->free_tables);
while ((entry= it++))
{
/*
@@ -267,7 +292,9 @@ static void check_unused(THD *thd)
/* Merge children should be detached from a merge parent */
if (entry->in_use)
{
- DBUG_PRINT("error",("Used table is in share's list of unused tables")); /* purecov: inspected */
+ /* purecov: begin inspected */
+ DBUG_PRINT("error",("Used table is in share's list of unused tables"));
+ /* purecov: end */
}
/* extra() may assume that in_use is set */
entry->in_use= thd;
@@ -297,18 +324,17 @@ static void check_unused(THD *thd)
#define check_unused(A)
#endif
+/**
+ Create a table cache/table definition cache key
-/*
- Create a table cache key
-
- SYNOPSIS
- create_table_def_key()
- thd Thread handler
- key Create key here (must be of size MAX_DBKEY_LENGTH)
- table_list Table definition
- tmp_table Set if table is a tmp table
+ @param thd Thread context
+ @param key Buffer for the key to be created (must be of
+ size MAX_DBKEY_LENGTH).
+ @param db_name Database name.
+ @param table_name Table name.
+ @param tmp_table Set if table is a tmp table.
- IMPLEMENTATION
+ @note
The table cache_key is created from:
db_name + \0
table_name + \0
@@ -319,20 +345,23 @@ static void check_unused(THD *thd)
4 bytes for master thread id
4 bytes pseudo thread id
- RETURN
- Length of key
+ @return Length of key.
*/
-uint create_table_def_key(THD *thd, char *key,
- const TABLE_LIST *table_list,
- bool tmp_table)
+static uint create_table_def_key(THD *thd, char *key,
+ const char *db_name, const char *table_name,
+ bool tmp_table)
{
- char *db_end= strnmov(key, table_list->db, MAX_DBKEY_LENGTH - 2);
- *db_end++= '\0';
- char *table_end= strnmov(db_end, table_list->table_name,
- key + MAX_DBKEY_LENGTH - 1 - db_end);
- *table_end++= '\0';
- uint key_length= (uint) (table_end-key);
+ /*
+ In theory caller should ensure that both db and table_name are
+ not longer than NAME_LEN bytes. In practice we play safe to avoid
+ buffer overruns.
+ */
+ DBUG_ASSERT(strlen(db_name) <= NAME_LEN && strlen(table_name) <= NAME_LEN);
+ uint key_length= static_cast<uint>(strmake(strmake(key, db_name, NAME_LEN) +
+ 1, table_name, NAME_LEN) - key +
+ 1);
+
if (tmp_table)
{
int4store(key + key_length, thd->server_id);
@@ -343,9 +372,46 @@ uint create_table_def_key(THD *thd, char *key,
}
+/**
+ Get table cache key for a table list element.
+
+ @param table_list[in] Table list element.
+ @param key[out] On return points to table cache key for the table.
+
+ @note Unlike create_table_def_key() call this function doesn't construct
+ key in a buffer provider by caller. Instead it relies on the fact
+ that table list element for which key is requested has properly
+ initialized MDL_request object and the fact that table definition
+ cache key is suffix of key used in MDL subsystem. So to get table
+ definition key it simply needs to return pointer to appropriate
+ part of MDL_key object nested in this table list element.
+ Indeed, this means that lifetime of key produced by this call is
+ limited by the lifetime of table list element which it got as
+ parameter.
+
+ @return Length of key.
+*/
+
+uint get_table_def_key(const TABLE_LIST *table_list, const char **key)
+{
+ /*
+ This call relies on the fact that TABLE_LIST::mdl_request::key object
+ is properly initialized, so table definition cache can be produced
+ from key used by MDL subsystem.
+ */
+ DBUG_ASSERT(!strcmp(table_list->get_db_name(),
+ table_list->mdl_request.key.db_name()) &&
+ !strcmp(table_list->get_table_name(),
+ table_list->mdl_request.key.name()));
+
+ *key= (const char*)table_list->mdl_request.key.ptr() + 1;
+ return table_list->mdl_request.key.length() - 1;
+}
+
+
/*****************************************************************************
- Functions to handle table definition cach (TABLE_SHARE)
+ Functions to handle table definition cache (TABLE_SHARE)
*****************************************************************************/
extern "C" uchar *table_def_key(const uchar *record, size_t *length,
@@ -374,7 +440,6 @@ static void table_def_free_entry(TABLE_SHARE *share)
bool table_def_init(void)
{
- table_def_inited= 1;
#ifdef HAVE_PSI_INTERFACE
init_tdc_psi_keys();
#endif
@@ -382,7 +447,6 @@ bool table_def_init(void)
oldest_unused_share= &end_of_unused_share;
end_of_unused_share.prev= &oldest_unused_share;
-
return my_hash_init(&table_def_cache, &my_charset_bin, table_def_size,
0, 0, table_def_key,
(my_hash_free_key) table_def_free_entry, 0) != 0;
@@ -578,8 +642,9 @@ static void table_def_unuse_table(TABLE *table)
# Share for table
*/
-TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
- uint key_length, uint db_flags, int *error,
+TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list,
+ const char *key, uint key_length,
+ uint db_flags, int *error,
my_hash_value_type hash_value)
{
TABLE_SHARE *share;
@@ -635,7 +700,7 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
share->ref_count++; // Mark in use
#ifdef HAVE_PSI_TABLE_INTERFACE
- share->m_psi= PSI_CALL(get_table_share)(false, share);
+ share->m_psi= PSI_TABLE_CALL(get_table_share)(false, share);
#else
share->m_psi= NULL;
#endif
@@ -695,10 +760,9 @@ found:
static TABLE_SHARE *
get_table_share_with_discover(THD *thd, TABLE_LIST *table_list,
- char *key, uint key_length,
+ const char *key, uint key_length,
uint db_flags, int *error,
my_hash_value_type hash_value)
-
{
TABLE_SHARE *share;
bool exists;
@@ -731,8 +795,8 @@ get_table_share_with_discover(THD *thd, TABLE_LIST *table_list,
@todo Rework alternative ways to deal with ER_NO_SUCH TABLE.
*/
if (share ||
- (thd->is_error() && thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE &&
- thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE))
+ (thd->is_error() && thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE &&
+ thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE))
DBUG_RETURN(share);
*error= 0;
@@ -842,14 +906,11 @@ void release_table_share(TABLE_SHARE *share)
TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
{
- char key[SAFE_NAME_LEN*2+2];
- TABLE_LIST table_list;
+ char key[MAX_DBKEY_LENGTH];
uint key_length;
mysql_mutex_assert_owner(&LOCK_open);
- table_list.db= (char*) db;
- table_list.table_name= (char*) table_name;
- key_length= create_table_def_key((THD*) 0, key, &table_list, 0);
+ key_length= create_table_def_key((THD*) 0, key, db, table_name, 0);
return (TABLE_SHARE*) my_hash_search(&table_def_cache,
(uchar*) key, key_length);
}
@@ -913,7 +974,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
share->db.str)+1,
share->table_name.str);
(*start_list)->in_use= 0;
- I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
+ TABLE_SHARE::TABLE_list::Iterator it(share->used_tables);
while (it++)
++(*start_list)->in_use;
(*start_list)->locked= 0; /* Obsolete. */
@@ -995,7 +1056,7 @@ void free_io_cache(TABLE *table)
static void kill_delayed_threads_for_table(TABLE_SHARE *share)
{
- I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
+ TABLE_SHARE::TABLE_list::Iterator it(share->used_tables);
TABLE *tab;
mysql_mutex_assert_owner(&LOCK_open);
@@ -1136,7 +1197,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
result= TRUE;
goto err_with_reopen;
}
- close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED);
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
}
}
@@ -1209,12 +1270,12 @@ err_with_reopen:
*/
thd->locked_tables_list.reopen_tables(thd);
/*
- Since downgrade_exclusive_lock() won't do anything with shared
+ Since downgrade_lock() won't do anything with shared
metadata lock it is much simpler to go through all open tables rather
than picking only those tables that were flushed.
*/
for (TABLE *tab= thd->open_tables; tab; tab= tab->next)
- tab->mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+ tab->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
}
DBUG_RETURN(result);
}
@@ -1415,7 +1476,8 @@ static void close_open_tables(THD *thd)
void
close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
- ha_extra_function extra)
+ ha_extra_function extra,
+ TABLE *skip_table)
{
char key[MAX_DBKEY_LENGTH];
uint key_length= share->table_cache_key.length;
@@ -1430,7 +1492,8 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
TABLE *table= *prev;
if (table->s->table_cache_key.length == key_length &&
- !memcmp(table->s->table_cache_key.str, key, key_length))
+ !memcmp(table->s->table_cache_key.str, key, key_length) &&
+ table != skip_table)
{
thd->locked_tables_list.unlink_from_list(thd,
table->pos_in_locked_tables,
@@ -1456,9 +1519,12 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
prev= &table->next;
}
}
- /* Remove the table share from the cache. */
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name,
- FALSE);
+ if (skip_table == NULL)
+ {
+ /* Remove the table share from the cache. */
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name,
+ FALSE);
+ }
}
@@ -1608,9 +1674,8 @@ void close_thread_tables(THD *thd)
/* move one table to free list */
-bool close_thread_table(THD *thd, TABLE **table_ptr)
+void close_thread_table(THD *thd, TABLE **table_ptr)
{
- bool found_old_table= 0;
TABLE *table= *table_ptr;
DBUG_ENTER("close_thread_table");
DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
@@ -1656,10 +1721,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
if (table->s->has_old_version() || table->needs_reopen() ||
table_def_shutdown_in_progress)
- {
free_cache_entry(table);
- found_old_table= 1;
- }
else
{
DBUG_ASSERT(table->file);
@@ -1672,7 +1734,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
free_cache_entry(unused_tables);
}
mysql_mutex_unlock(&LOCK_open);
- DBUG_RETURN(found_old_table);
+ DBUG_VOID_RETURN;
}
@@ -1815,7 +1877,7 @@ bool close_temporary_tables(THD *thd)
qinfo.db_len= db.length();
thd->variables.character_set_client= cs_save;
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
if ((error= (mysql_bin_log.write(&qinfo) || error)))
{
/*
@@ -1833,7 +1895,7 @@ bool close_temporary_tables(THD *thd)
sql_print_error("Failed to write the DROP statement for "
"temporary tables to binary log");
}
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
thd->variables.pseudo_thread_id= save_pseudo_thread_id;
thd->thread_specific_used= save_thread_specific_used;
@@ -2119,12 +2181,9 @@ void update_non_unique_table_error(TABLE_LIST *update,
TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name)
{
- TABLE_LIST tl;
-
- tl.db= (char*) db;
- tl.table_name= (char*) table_name;
-
- return find_temporary_table(thd, &tl);
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length= create_table_def_key(thd, key, db, table_name, 1);
+ return find_temporary_table(thd, key, key_length);
}
@@ -2137,10 +2196,26 @@ TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name)
TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl)
{
- char key[MAX_DBKEY_LENGTH];
- uint key_length= create_table_def_key(thd, key, tl, 1);
+ const char *key;
+ uint key_length;
+ char key_suffix[TMP_TABLE_KEY_EXTRA];
+ TABLE *table;
- return find_temporary_table(thd, key, key_length);
+ key_length= get_table_def_key(tl, &key);
+
+ int4store(key_suffix, thd->server_id);
+ int4store(key_suffix + 4, thd->variables.pseudo_thread_id);
+
+ for (table= thd->temporary_tables; table; table= table->next)
+ {
+ if ((table->s->table_cache_key.length == key_length +
+ TMP_TABLE_KEY_EXTRA) &&
+ !memcmp(table->s->table_cache_key.str, key, key_length) &&
+ !memcmp(table->s->table_cache_key.str + key_length, key_suffix,
+ TMP_TABLE_KEY_EXTRA))
+ return table;
+ }
+ return NULL;
}
@@ -2318,15 +2393,12 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db,
char *key;
uint key_length;
TABLE_SHARE *share= table->s;
- TABLE_LIST table_list;
DBUG_ENTER("rename_temporary_table");
if (!(key=(char*) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH)))
DBUG_RETURN(1); /* purecov: inspected */
- table_list.db= (char*) db;
- table_list.table_name= (char*) table_name;
- key_length= create_table_def_key(thd, key, &table_list, 1);
+ key_length= create_table_def_key(thd, key, db, table_name, 1);
share->set_table_cache_key(key, key_length);
DBUG_RETURN(0);
}
@@ -2358,8 +2430,9 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
table->s->table_name.str, (ulong) table->s,
table->db_stat, table->s->version));
- if (thd->mdl_context.upgrade_shared_lock_to_exclusive(
- table->mdl_ticket, thd->variables.lock_wait_timeout))
+ if (thd->mdl_context.upgrade_shared_lock(
+ table->mdl_ticket, MDL_EXCLUSIVE,
+ thd->variables.lock_wait_timeout))
DBUG_RETURN(TRUE);
tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN,
@@ -2408,8 +2481,8 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name,
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db_name, table_name,
FALSE);
/* Remove the table from the storage engine and rm the .frm. */
- quick_rm_table(table_type, db_name, table_name, 0);
- }
+ quick_rm_table(thd, table_type, db_name, table_name, 0);
+ }
DBUG_VOID_RETURN;
}
@@ -2497,9 +2570,9 @@ public:
virtual bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
private:
/** Open table context to be used for back-off request. */
@@ -2516,9 +2589,9 @@ private:
bool MDL_deadlock_handler::handle_condition(THD *,
uint sql_errno,
const char*,
- MYSQL_ERROR::enum_warning_level,
+ Sql_condition::enum_warning_level,
const char*,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
if (! m_is_active && sql_errno == ER_LOCK_DEADLOCK)
@@ -2703,51 +2776,46 @@ tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name,
}
-/*
- Open a table.
-
- SYNOPSIS
- open_table()
- thd Thread context.
- table_list Open first table in list.
- action INOUT Pointer to variable of enum_open_table_action type
- which will be set according to action which is
- required to remedy problem appeared during attempt
- to open table.
- flags Bitmap of flags to modify how open works:
- MYSQL_OPEN_IGNORE_FLUSH - Open table even if
- someone has done a flush or there is a pending
- exclusive metadata lock requests against it
- (i.e. request high priority metadata lock).
- No version number checking is done.
- MYSQL_OPEN_TEMPORARY_ONLY - Open only temporary
- table not the base table or view.
- MYSQL_OPEN_TAKE_UPGRADABLE_MDL - Obtain upgradable
- metadata lock for tables on which we are going to
- take some kind of write table-level lock.
-
- IMPLEMENTATION
- Uses a cache of open tables to find a table not in use.
-
- If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is opened
- only if it exists. If the open strategy is OPEN_STUB, the underlying table
- is never opened. In both cases, metadata locks are always taken according
- to the lock strategy.
-
- RETURN
- TRUE Open failed. "action" parameter may contain type of action
- needed to remedy problem before retrying again.
- FALSE Success. Members of TABLE_LIST structure are filled properly (e.g.
- TABLE_LIST::table is set for real tables and TABLE_LIST::view is
- set for views).
+/**
+ Open a base table.
+
+ @param thd Thread context.
+ @param table_list Open first table in list.
+ @param mem_root Temporary MEM_ROOT to be used for
+ parsing .FRMs for views.
+ @param ot_ctx Context with flags which modify how open works
+ and which is used to recover from a failed
+ open_table() attempt.
+ Some examples of flags:
+ MYSQL_OPEN_IGNORE_FLUSH - Open table even if
+ someone has done a flush. No version number
+ checking is done.
+ MYSQL_OPEN_HAS_MDL_LOCK - instead of acquiring
+ metadata locks rely on that caller already has
+ appropriate ones.
+
+ Uses a cache of open tables to find a TABLE instance not in use.
+
+ If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is
+ opened only if it exists. If the open strategy is OPEN_STUB, the
+ underlying table is never opened. In both cases, metadata locks are
+ always taken according to the lock strategy.
+
+ The function used to open temporary tables, but now it opens base tables
+ only.
+
+ @retval TRUE Open failed. "action" parameter may contain type of action
+ needed to remedy problem before retrying again.
+ @retval FALSE Success. Members of TABLE_LIST structure are filled properly
+ (e.g. TABLE_LIST::table is set for real tables and
+ TABLE_LIST::view is set for views).
*/
-
bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
Open_table_context *ot_ctx)
{
reg1 TABLE *table;
- char key[MAX_DBKEY_LENGTH];
+ const char *key;
uint key_length;
char *alias= table_list->alias;
uint flags= ot_ctx->get_flags();
@@ -2756,76 +2824,44 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
TABLE_SHARE *share;
my_hash_value_type hash_value;
bool recycled_free_table;
-
DBUG_ENTER("open_table");
+ /*
+ The table must not be opened already. The table can be pre-opened for
+ some statements if it is a temporary table.
+
+ open_temporary_table() must be used to open temporary tables.
+ */
+ DBUG_ASSERT(!table_list->table);
+
/* an open table operation needs a lot of the stack space */
if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias))
DBUG_RETURN(TRUE);
- if (thd->killed)
+ if (!(flags & MYSQL_OPEN_IGNORE_KILLED) && thd->killed)
DBUG_RETURN(TRUE);
- key_length= (create_table_def_key(thd, key, table_list, 1) -
- TMP_TABLE_KEY_EXTRA);
-
/*
- Unless requested otherwise, try to resolve this table in the list
- of temporary tables of this thread. In MySQL temporary tables
- are always thread-local and "shadow" possible base tables with the
- same name. This block implements the behaviour.
- TODO: move this block into a separate function.
+ Check if we're trying to take a write lock in a read only transaction.
+
+ Note that we allow write locks on log tables as otherwise logging
+ to general/slow log would be disabled in read only transactions.
*/
- if (table_list->open_type != OT_BASE_ONLY &&
- ! (flags & MYSQL_OPEN_SKIP_TEMPORARY))
+ if (table_list->mdl_request.type >= MDL_SHARED_WRITE &&
+ thd->tx_read_only &&
+ !(flags & (MYSQL_LOCK_LOG_TABLE | MYSQL_OPEN_HAS_MDL_LOCK)))
{
- for (table= thd->temporary_tables; table ; table=table->next)
- {
- if (table->s->table_cache_key.length == key_length +
- TMP_TABLE_KEY_EXTRA &&
- !memcmp(table->s->table_cache_key.str, key,
- key_length + TMP_TABLE_KEY_EXTRA))
- {
- /*
- We're trying to use the same temporary table twice in a query.
- Right now we don't support this because a temporary table
- is always represented by only one TABLE object in THD, and
- it can not be cloned. Emit an error for an unsupported behaviour.
- */
- if (table->query_id)
- {
- DBUG_PRINT("error",
- ("query_id: %lu server_id: %u pseudo_thread_id: %lu",
- (ulong) table->query_id, (uint) thd->server_id,
- (ulong) thd->variables.pseudo_thread_id));
- my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
- DBUG_RETURN(TRUE);
- }
- table->query_id= thd->query_id;
- thd->thread_specific_used= TRUE;
- DBUG_PRINT("info",("Using temporary table"));
- goto reset;
- }
- }
+ my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
+ DBUG_RETURN(true);
}
- if (table_list->open_type == OT_TEMPORARY_ONLY ||
- (flags & MYSQL_OPEN_TEMPORARY_ONLY))
- {
- if (table_list->open_strategy == TABLE_LIST::OPEN_NORMAL)
- {
- my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name);
- DBUG_RETURN(TRUE);
- }
- else
- DBUG_RETURN(FALSE);
- }
+ key_length= get_table_def_key(table_list, &key);
/*
- The table is not temporary - if we're in pre-locked or LOCK TABLES
- mode, let's try to find the requested table in the list of pre-opened
- and locked tables. If the table is not there, return an error - we can't
- open not pre-opened tables in pre-locked/LOCK TABLES mode.
+ If we're in pre-locked or LOCK TABLES mode, let's try to find the
+ requested table in the list of pre-opened and locked tables. If the
+ table is not there, return an error - we can't open not pre-opened
+ tables in pre-locked/LOCK TABLES mode.
TODO: move this block into a separate function.
*/
if (thd->locked_tables_mode &&
@@ -2912,7 +2948,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (dd_frm_type(thd, path, &not_used) == FRMTYPE_VIEW)
{
if (!tdc_open_view(thd, table_list, alias, key, key_length,
- mem_root, 0))
+ mem_root, CHECK_METADATA_VERSION))
{
DBUG_ASSERT(table_list->view != 0);
DBUG_RETURN(FALSE); // VIEW
@@ -3141,12 +3177,12 @@ retry_share:
Release our reference to share, wait until old version of
share goes away and then try to get new version of table share.
*/
- MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
- bool wait_result;
-
release_table_share(share);
mysql_mutex_unlock(&LOCK_open);
+ MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
+ bool wait_result;
+
thd->push_internal_handler(&mdl_deadlock_handler);
wait_result= tdc_wait_for_old_version(thd, table_list->db,
table_list->table_name,
@@ -3216,22 +3252,18 @@ retry_share:
else if (share->crashed)
(void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR,
table_list);
-
goto err_lock;
}
-
if (open_table_entry_fini(thd, share, table))
{
closefrm(table, 0);
my_free(table);
goto err_lock;
}
-
mysql_mutex_lock(&LOCK_open);
/* Add table to the share's used tables list. */
table_def_add_used_table(thd, table);
}
-
mysql_mutex_unlock(&LOCK_open);
/* Call rebind_psi outside of the LOCK_open critical section. */
@@ -3257,6 +3289,21 @@ retry_share:
table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
table_list->table= table;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (table->part_info)
+ {
+ /* Set all [named] partitions as used. */
+ if (table->part_info->set_partition_bitmaps(table_list))
+ DBUG_RETURN(true);
+ }
+ else if (table_list->partition_names)
+ {
+ /* Don't allow PARTITION () clause on a nonpartitioned table */
+ my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(true);
+ }
+#endif
+
table->init(thd, table_list);
DBUG_RETURN(FALSE);
@@ -3312,9 +3359,9 @@ TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name)
upgrade the lock and ER_TABLE_NOT_LOCKED_FOR_WRITE will be
reported.
- @return Pointer to TABLE instance with MDL_SHARED_NO_WRITE,
- MDL_SHARED_NO_READ_WRITE, or MDL_EXCLUSIVE metadata
- lock, NULL otherwise.
+ @return Pointer to TABLE instance with MDL_SHARED_UPGRADABLE
+ MDL_SHARED_NO_WRITE, MDL_SHARED_NO_READ_WRITE, or
+ MDL_EXCLUSIVE metadata lock, NULL otherwise.
*/
TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
@@ -3888,7 +3935,7 @@ check_and_update_routine_version(THD *thd, Sroutine_hash_entry *rt,
*/
bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
- char *cache_key, uint cache_key_length,
+ const char *cache_key, uint cache_key_length,
MEM_ROOT *mem_root, uint flags)
{
TABLE not_used;
@@ -3906,6 +3953,24 @@ bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
hash_value)))
goto err;
+ if ((flags & CHECK_METADATA_VERSION))
+ {
+ /*
+ Check TABLE_SHARE-version of view only if we have been instructed to do
+ so. We do not need to check the version if we're executing CREATE VIEW or
+ ALTER VIEW statements.
+
+ In the future, this functionality should be moved out from
+ tdc_open_view(), and tdc_open_view() should became a part of a clean
+ table-definition-cache interface.
+ */
+ if (check_and_update_table_version(thd, table_list, share))
+ {
+ release_table_share(share);
+ goto err;
+ }
+ }
+
if (share->is_view &&
!open_new_frm(thd, share, alias,
(uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
@@ -3977,7 +4042,7 @@ static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry)
static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
{
- char cache_key[MAX_DBKEY_LENGTH];
+ const char *cache_key;
uint cache_key_length;
TABLE_SHARE *share;
TABLE *entry;
@@ -3985,7 +4050,7 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
bool result= TRUE;
my_hash_value_type hash_value;
- cache_key_length= create_table_def_key(thd, cache_key, table_list, 0);
+ cache_key_length= get_table_def_key(table_list, &cache_key);
thd->clear_error();
@@ -4188,7 +4253,7 @@ recover_from_failed_open(THD *thd)
ha_create_table_from_engine(thd, m_failed_table->db,
m_failed_table->table_name);
- thd->warning_info->clear_warning_info(thd->query_id);
+ thd->get_stmt_da()->clear_warning_info(thd->query_id);
thd->clear_error(); // Clear error message
thd->mdl_context.release_transactional_locks();
break;
@@ -4788,29 +4853,62 @@ lock_table_names(THD *thd,
for (table= tables_start; table && table != tables_end;
table= table->next_global)
{
- if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
- !(table->open_type == OT_TEMPORARY_ONLY ||
- (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
- (table->open_type != OT_BASE_ONLY &&
- ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
- find_temporary_table(thd, table))))
+ if (table->mdl_request.type < MDL_SHARED_UPGRADABLE ||
+ table->open_type == OT_TEMPORARY_ONLY ||
+ (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table)))
{
- /*
- Write lock on normal tables is not allowed in a read only transaction.
- */
- if (thd->tx_read_only)
- {
- my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
- DBUG_RETURN(true);
- }
+ continue;
+ }
- if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) &&
- schema_set.insert(table))
- DBUG_RETURN(TRUE);
- mdl_requests.push_front(&table->mdl_request);
+ /*
+ Write lock on normal tables is not allowed in a read only transaction.
+ */
+ if (thd->tx_read_only)
+ {
+ my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
+ DBUG_RETURN(true);
}
+
+ if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) && schema_set.insert(table))
+ DBUG_RETURN(TRUE);
+
+ mdl_requests.push_front(&table->mdl_request);
+ }
+
+ if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) &&
+ ! mdl_requests.is_empty())
+ {
+ /*
+ Scoped locks: Take intention exclusive locks on all involved
+ schemas.
+ */
+ Hash_set<TABLE_LIST, schema_set_get_key>::Iterator it(schema_set);
+ while ((table= it++))
+ {
+ MDL_request *schema_request= new (thd->mem_root) MDL_request;
+ if (schema_request == NULL)
+ return TRUE;
+ schema_request->init(MDL_key::SCHEMA, table->db, "",
+ MDL_INTENTION_EXCLUSIVE,
+ MDL_TRANSACTION);
+ mdl_requests.push_front(schema_request);
+ }
+
+ /*
+ Protect this statement against concurrent global read lock
+ by acquiring global intention exclusive lock with statement
+ duration.
+ */
+ if (thd->global_read_lock.can_acquire_protection())
+ return TRUE;
+ global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_STATEMENT);
+ mdl_requests.push_front(&global_request);
}
+ if (thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout))
+ return TRUE;
+
if (mdl_requests.is_empty())
DBUG_RETURN(FALSE);
@@ -4878,7 +4976,7 @@ lock_table_names(THD *thd,
{
if (thd->lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
tables_start->table_name);
}
@@ -4928,34 +5026,33 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
for (table= tables_start; table && table != tables_end;
table= table->next_global)
{
- if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
- !(table->open_type == OT_TEMPORARY_ONLY ||
- (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
- (table->open_type != OT_BASE_ONLY &&
- ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
- find_temporary_table(thd, table))))
+ if (table->mdl_request.type < MDL_SHARED_UPGRADABLE ||
+ table->open_type == OT_TEMPORARY_ONLY ||
+ (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table)))
{
- /*
- We don't need to do anything about the found TABLE instance as it
- will be handled later in open_tables(), we only need to check that
- an upgradable lock is already acquired. When we enter LOCK TABLES
- mode, SNRW locks are acquired before all other locks. So if under
- LOCK TABLES we find that there is TABLE instance with upgradeable
- lock, all other instances of TABLE for the same table will have the
- same ticket.
-
- Note that this works OK even for CREATE TABLE statements which
- request X type of metadata lock. This is because under LOCK TABLES
- such statements don't create the table but only check if it exists
- or, in most complex case, only insert into it.
- Thus SNRW lock should be enough.
-
- Note that find_table_for_mdl_upgrade() will report an error if
- no suitable ticket is found.
- */
- if (!find_table_for_mdl_upgrade(thd, table->db, table->table_name, false))
- return TRUE;
+ continue;
}
+
+ /*
+ We don't need to do anything about the found TABLE instance as it
+ will be handled later in open_tables(), we only need to check that
+ an upgradable lock is already acquired. When we enter LOCK TABLES
+ mode, SNRW locks are acquired before all other locks. So if under
+ LOCK TABLES we find that there is TABLE instance with upgradeable
+ lock, all other instances of TABLE for the same table will have the
+ same ticket.
+
+ Note that this works OK even for CREATE TABLE statements which
+ request X type of metadata lock. This is because under LOCK TABLES
+ such statements don't create the table but only check if it exists
+ or, in most complex case, only insert into it.
+ Thus SNRW lock should be enough.
+
+ Note that find_table_for_mdl_upgrade() will report an error if
+ no suitable ticket is found.
+ */
+ if (!find_table_for_mdl_upgrade(thd, table->db, table->table_name, false))
+ return TRUE;
}
return FALSE;
@@ -4995,11 +5092,12 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy)
{
/*
- We use pointers to "next_global" member in the last processed TABLE_LIST
- element and to the "next" member in the last processed Sroutine_hash_entry
- element as iterators over, correspondingly, the table list and stored routines
- list which stay valid and allow to continue iteration when new elements are
- added to the tail of the lists.
+ We use pointers to "next_global" member in the last processed
+ TABLE_LIST element and to the "next" member in the last processed
+ Sroutine_hash_entry element as iterators over, correspondingly,
+ the table list and stored routines list which stay valid and allow
+ to continue iteration when new elements are added to the tail of
+ the lists.
*/
TABLE_LIST **table_to_open;
Sroutine_hash_entry **sroutine_to_open;
@@ -5088,7 +5186,7 @@ restart:
for (table= *start; table && table != thd->lex->first_not_own_table();
table= table->next_global)
{
- if (table->mdl_request.type >= MDL_SHARED_NO_WRITE)
+ if (table->mdl_request.type >= MDL_SHARED_UPGRADABLE)
table->mdl_request.ticket= NULL;
}
}
@@ -5619,7 +5717,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
table_list->required_type= FRMTYPE_TABLE;
/* This function can't properly handle requests for such metadata locks. */
- DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_NO_WRITE);
+ DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_UPGRADABLE);
while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx)) &&
ot_ctx.can_recover_from_failed_open())
@@ -6125,6 +6223,9 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
@param add_to_temporary_tables_list Specifies if the opened TABLE
instance should be linked into
THD::temporary_tables list.
+ @param open_in_engine Indicates that we need to open table
+ in storage engine in addition to
+ constructing TABLE object for it.
@note This function is used:
- by alter_table() to open a temporary table;
@@ -6136,7 +6237,8 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
TABLE *open_table_uncached(THD *thd, const char *path, const char *db,
const char *table_name,
- bool add_to_temporary_tables_list)
+ bool add_to_temporary_tables_list,
+ bool open_in_engine)
{
TABLE *tmp_table;
TABLE_SHARE *share;
@@ -6153,13 +6255,20 @@ TABLE *open_table_uncached(THD *thd, const char *path, const char *db,
table_list.db= (char*) db;
table_list.table_name= (char*) table_name;
/* Create the cache_key for temporary tables */
- key_length= create_table_def_key(thd, cache_key, &table_list, 1);
+ key_length= create_table_def_key(thd, cache_key, db, table_name, 1);
if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table) + sizeof(*share) +
strlen(path)+1 + key_length,
MYF(MY_WME))))
DBUG_RETURN(0); /* purecov: inspected */
+#ifndef DBUG_OFF
+ mysql_mutex_lock(&LOCK_open);
+ DBUG_ASSERT(!my_hash_search(&table_def_cache, (uchar*) cache_key,
+ key_length));
+ mysql_mutex_unlock(&LOCK_open);
+#endif
+
share= (TABLE_SHARE*) (tmp_table+1);
tmp_path= (char*) (share+1);
saved_cache_key= strmov(tmp_path, path)+1;
@@ -6177,17 +6286,23 @@ TABLE *open_table_uncached(THD *thd, const char *path, const char *db,
}
#ifdef HAVE_PSI_TABLE_INTERFACE
- share->m_psi= PSI_CALL(get_table_share)(true, share);
+ share->m_psi= PSI_TABLE_CALL(get_table_share)(true, share);
#else
share->m_psi= NULL;
#endif
if (open_table_from_share(thd, share, table_name,
+ open_in_engine ?
(uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
- HA_GET_INDEX),
+ HA_GET_INDEX) : 0,
READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
ha_open_options,
- tmp_table, FALSE))
+ tmp_table,
+ /*
+ Set "is_create_table" if the table does not
+ exist in SE
+ */
+ open_in_engine ? false : true))
{
/* No need to lock share->mutex as this is not needed for tmp tables */
free_table_share(share);
@@ -6217,17 +6332,27 @@ TABLE *open_table_uncached(THD *thd, const char *path, const char *db,
}
-bool rm_temporary_table(handlerton *base, char *path)
+/**
+ Delete a temporary table.
+
+ @param base Handlerton for table to be deleted.
+ @param path Path to the table to be deleted (i.e. path
+ to its .frm without an extension).
+
+ @retval false - success.
+ @retval true - failure.
+*/
+
+bool rm_temporary_table(handlerton *base, const char *path)
{
bool error=0;
handler *file;
- char *ext;
+ char frm_path[FN_REFLEN + 1];
DBUG_ENTER("rm_temporary_table");
- strmov(ext= strend(path), reg_ext);
- if (mysql_file_delete(key_file_frm, path, MYF(0)))
+ strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS);
+ if (mysql_file_delete(key_file_frm, frm_path, MYF(0)))
error=1; /* purecov: inspected */
- *ext= 0; // remove extension
file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base);
if (file && file->ha_delete_table(path))
{
@@ -6308,6 +6433,143 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
}
+/**
+ Find a temporary table specified by TABLE_LIST instance in the cache and
+ prepare its TABLE instance for use.
+
+ This function tries to resolve this table in the list of temporary tables
+ of this thread. Temporary tables are thread-local and "shadow" base
+ tables with the same name.
+
+ @note In most cases one should use open_temporary_tables() instead
+ of this call.
+
+ @note One should finalize process of opening temporary table for table
+ list element by calling open_and_process_table(). This function
+ is responsible for table version checking and handling of merge
+ tables.
+
+ @note We used to check global_read_lock before opening temporary tables.
+ However, that limitation was artificial and is removed now.
+
+ @return Error status.
+ @retval FALSE On success. If a temporary table exists for the given
+ key, tl->table is set.
+ @retval TRUE On error. my_error() has been called.
+*/
+
+bool open_temporary_table(THD *thd, TABLE_LIST *tl)
+{
+ TABLE *table;
+ DBUG_ENTER("open_temporary_table");
+ DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db, tl->table_name));
+
+ /*
+ Code in open_table() assumes that TABLE_LIST::table can
+ be non-zero only for pre-opened temporary tables.
+ */
+ DBUG_ASSERT(tl->table == NULL);
+
+ /*
+ This function should not be called for cases when derived or I_S
+ tables can be met since table list elements for such tables can
+ have invalid db or table name.
+ Instead open_temporary_tables() should be used.
+ */
+ DBUG_ASSERT(!tl->derived && !tl->schema_table);
+
+ if (tl->open_type == OT_BASE_ONLY)
+ {
+ DBUG_PRINT("info", ("skip_temporary is set"));
+ DBUG_RETURN(FALSE);
+ }
+
+ if (!(table= find_temporary_table(thd, tl)))
+ {
+ if (tl->open_type == OT_TEMPORARY_ONLY &&
+ tl->open_strategy == TABLE_LIST::OPEN_NORMAL)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db, tl->table_name);
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+ }
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (tl->partition_names)
+ {
+ /* Partitioned temporary tables is not supported. */
+ DBUG_ASSERT(!table->part_info);
+ my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(true);
+ }
+#endif
+
+ if (table->query_id)
+ {
+ /*
+ We're trying to use the same temporary table twice in a query.
+ Right now we don't support this because a temporary table is always
+ represented by only one TABLE object in THD, and it can not be
+ cloned. Emit an error for an unsupported behaviour.
+ */
+
+ DBUG_PRINT("error",
+ ("query_id: %lu server_id: %u pseudo_thread_id: %lu",
+ (ulong) table->query_id, (uint) thd->server_id,
+ (ulong) thd->variables.pseudo_thread_id));
+ my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
+ DBUG_RETURN(TRUE);
+ }
+
+ table->query_id= thd->query_id;
+ thd->thread_specific_used= TRUE;
+
+ tl->updatable= 1; // It is not derived table nor non-updatable VIEW.
+ tl->table= table;
+
+ table->init(thd, tl);
+
+ DBUG_PRINT("info", ("Using temporary table"));
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Pre-open temporary tables corresponding to table list elements.
+
+ @note One should finalize process of opening temporary tables
+ by calling open_tables(). This function is responsible
+ for table version checking and handling of merge tables.
+
+ @return Error status.
+ @retval FALSE On success. If a temporary tables exists for the
+ given element, tl->table is set.
+ @retval TRUE On error. my_error() has been called.
+*/
+
+bool open_temporary_tables(THD *thd, TABLE_LIST *tl_list)
+{
+ TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
+ DBUG_ENTER("open_temporary_tables");
+
+ for (TABLE_LIST *tl= tl_list; tl && tl != first_not_own; tl= tl->next_global)
+ {
+ if (tl->derived || tl->schema_table)
+ {
+ /*
+ Derived and I_S tables will be handled by a later call to open_tables().
+ */
+ continue;
+ }
+
+ if (open_temporary_table(thd, tl))
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
/*
Find a field by name in a view that uses merge algorithm.
@@ -8976,7 +9238,7 @@ err_no_arena:
@retval false OK.
*/
-static bool
+bool
fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
bool ignore_errors)
{
@@ -9000,7 +9262,7 @@ fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
thus we safely can take table from the first field.
*/
fld= (Item_field*)f++;
- if (!(field= fld->filed_for_view_update()))
+ if (!(field= fld->field_for_view_update()))
{
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name);
goto err;
@@ -9014,7 +9276,7 @@ fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
while ((fld= f++))
{
- if (!(field= fld->filed_for_view_update()))
+ if (!(field= fld->field_for_view_update()))
{
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name);
goto err;
@@ -9029,7 +9291,7 @@ fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
value->type() != Item::NULL_ITEM &&
table->s->table_category != TABLE_CATEGORY_TEMPORARY)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN,
ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
rfield->field_name, table->s->table_name.str);
@@ -9106,7 +9368,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields,
if (fields.elements)
{
fld= (Item_field*)f++;
- item_field= fld->filed_for_view_update();
+ item_field= fld->field_for_view_update();
if (item_field && item_field->field && table && table->vfield)
{
DBUG_ASSERT(table == item_field->field->table);
@@ -9181,7 +9443,7 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
value->type() != Item::NULL_ITEM &&
table->s->table_category != TABLE_CATEGORY_TEMPORARY)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN,
ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
field->field_name, table->s->table_name.str);
@@ -9445,6 +9707,15 @@ bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use,
instances (if there are no
used instances will also
remove TABLE_SHARE).
+ TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE -
+ remove all TABLE instances
+ except those that belong to
+ this thread, but don't mark
+ TABLE_SHARE as old. There
+ should be no TABLE objects
+ used by other threads and
+ caller should have exclusive
+ metadata lock on the table.
@param db Name of database
@param table_name Name of table
@param has_lock If TRUE, LOCK_open is already acquired
@@ -9474,27 +9745,30 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
MDL_EXCLUSIVE));
- key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
+ key_length= create_table_def_key(thd, key, db, table_name, false);
if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key,
key_length)))
{
if (share->ref_count)
{
- I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
+ TABLE_SHARE::TABLE_list::Iterator it(share->free_tables);
#ifndef DBUG_OFF
if (remove_type == TDC_RT_REMOVE_ALL)
{
DBUG_ASSERT(share->used_tables.is_empty());
}
- else if (remove_type == TDC_RT_REMOVE_NOT_OWN)
+ else if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
+ remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
{
- I_P_List_iterator<TABLE, TABLE_share> it2(share->used_tables);
+ TABLE_SHARE::TABLE_list::Iterator it2(share->used_tables);
while ((table= it2++))
+ {
if (table->in_use != thd)
{
DBUG_ASSERT(0);
}
+ }
}
#endif
/*
@@ -9508,13 +9782,17 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
TDC does not contain old shares which don't have any tables
used.
*/
- share->version= 0;
+ if (remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
+ share->version= 0;
while ((table= it++))
free_cache_entry(table);
}
else
+ {
+ DBUG_ASSERT(remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE);
(void) my_hash_delete(&table_def_cache, (uchar*) share);
+ }
}
if (! has_lock)
@@ -9588,12 +9866,14 @@ open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
{
LEX_STRING pathstr;
File_parser *parser;
- char path[FN_REFLEN];
+ char path[FN_REFLEN+1];
DBUG_ENTER("open_new_frm");
/* Create path with extension */
- pathstr.length= (uint) (strxmov(path, share->normalized_path.str, reg_ext,
- NullS)- path);
+ pathstr.length= (uint) (strxnmov(path, sizeof(path) - 1,
+ share->normalized_path.str,
+ reg_ext,
+ NullS) - path);
pathstr.str= path;
if ((parser= sql_parse_prepare(&pathstr, mem_root, 1)))