summaryrefslogtreecommitdiff
path: root/sql/sql_base.cc
diff options
context:
space:
mode:
authorJan Lindström <jplindst@mariadb.org>2013-09-03 17:50:36 +0300
committerJan Lindström <jplindst@mariadb.org>2013-09-03 17:50:36 +0300
commitba3ff50ab2bfabab6a4307282f92854f6efe6382 (patch)
tree904c9b94cc5f1dfa8727f17af56f50fba4155205 /sql/sql_base.cc
parent81739d308fee0317e56bd70d97e3429ece83dd4b (diff)
parentc8b87ca16f05826c6801c70fb20a88a61959264e (diff)
downloadmariadb-git-ba3ff50ab2bfabab6a4307282f92854f6efe6382.tar.gz
Merge 10.0 to galera-10.0
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r--sql/sql_base.cc1827
1 files changed, 597 insertions, 1230 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 931fb07f70c..1ededf0dd61 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -12,7 +12,7 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/* Basic functions needed by many modules */
@@ -70,9 +70,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)
@@ -81,7 +81,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;
}
@@ -114,9 +114,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
@@ -144,9 +144,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 ||
@@ -167,170 +167,27 @@ 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.
- In particular:
-
- end_of_unused_share
- last_table_id
- oldest_unused_share
- refresh_version
- table_cache_count
- table_def_cache
- table_def_shutdown_in_progress
- unused_tables
- TABLE::next
- TABLE::prev
- TABLE_SHARE::free_tables
- TABLE_SHARE::m_flush_tickets
- TABLE_SHARE::next
- TABLE_SHARE::prev
- TABLE_SHARE::ref_count
- TABLE_SHARE::used_tables
-*/
-mysql_mutex_t LOCK_open;
-
-#ifdef HAVE_PSI_INTERFACE
-static PSI_mutex_key key_LOCK_open;
-static PSI_mutex_info all_tdc_mutexes[]= {
- { &key_LOCK_open, "LOCK_open", PSI_FLAG_GLOBAL }
-};
-
-/**
- Initialize performance schema instrumentation points
- used by the table cache.
-*/
-
-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);
-}
-#endif /* HAVE_PSI_INTERFACE */
-
-
-/**
- Total number of TABLE instances for tables in the table definition cache
- (both in use by threads and not in use). This value is accessible to user
- as "Open_tables" status variable.
-*/
-uint table_cache_count= 0;
-/**
- List that contains all TABLE instances for tables in the table definition
- cache that are not in use by any thread. Recently used TABLE instances are
- appended to the end of the list. Thus the beginning of the list contains
- tables which have been least recently used.
-*/
-TABLE *unused_tables;
-HASH table_def_cache;
-static TABLE_SHARE *oldest_unused_share, end_of_unused_share;
-static bool table_def_inited= 0;
-static bool table_def_shutdown_in_progress= 0;
-
static bool check_and_update_table_version(THD *thd, TABLE_LIST *tables,
TABLE_SHARE *table_share);
static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry);
static bool auto_repair_table(THD *thd, TABLE_LIST *table_list);
-static void free_cache_entry(TABLE *entry);
static bool
has_write_table_with_auto_increment(TABLE_LIST *tables);
static bool
has_write_table_with_auto_increment_and_select(TABLE_LIST *tables);
static bool has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables);
-uint cached_open_tables(void)
-{
- return table_cache_count;
-}
-
-
-#ifdef EXTRA_DEBUG
-static void check_unused(THD *thd)
-{
- uint count= 0, open_files= 0, idx= 0;
- TABLE *cur_link, *start_link, *entry;
- TABLE_SHARE *share;
-
- if ((start_link=cur_link=unused_tables))
- {
- do
- {
- if (cur_link != cur_link->next->prev || cur_link != cur_link->prev->next)
- {
- DBUG_PRINT("error",("Unused_links aren't linked properly")); /* purecov: inspected */
- return; /* purecov: inspected */
- }
- } while (count++ < table_cache_count &&
- (cur_link=cur_link->next) != start_link);
- if (cur_link != start_link)
- {
- DBUG_PRINT("error",("Unused_links aren't connected")); /* purecov: inspected */
- }
- }
- for (idx=0 ; idx < table_def_cache.records ; idx++)
- {
- share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx);
-
- I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
- while ((entry= it++))
- {
- /*
- We must not have TABLEs in the free list that have their file closed.
- */
- DBUG_ASSERT(entry->db_stat && entry->file);
- /* 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 */
- }
- /* extra() may assume that in_use is set */
- entry->in_use= thd;
- DBUG_ASSERT(! entry->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
- entry->in_use= 0;
-
- count--;
- open_files++;
- }
- it.init(share->used_tables);
- while ((entry= it++))
- {
- if (!entry->in_use)
- {
- DBUG_PRINT("error",("Unused table is in share's list of used tables")); /* purecov: inspected */
- }
- open_files++;
- }
- }
- if (count != 0)
- {
- DBUG_PRINT("error",("Unused_links doesn't match open_cache: diff: %d", /* purecov: inspected */
- count)); /* purecov: inspected */
- }
-}
-#else
-#define check_unused(A)
-#endif
+/**
+ Create a table cache/table definition cache key
-/*
- Create a table cache key
-
- SYNOPSIS
- create_tmp_table_def_key()
- thd Thread handler
- key Create key here (must be of size MAX_DBKEY_LENGTH)
- db Database name.
- table_name Table name.
+ @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.
- IMPLEMENTATION
+ @note
The table cache_key is created from:
db_name + \0
table_name + \0
@@ -341,14 +198,13 @@ 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_tmp_table_def_key(THD *thd, char *key,
const char *db, const char *table_name)
{
- uint key_length= create_table_def_key(key, db, table_name);
+ uint key_length= tdc_create_key(key, db, table_name);
int4store(key + key_length, thd->variables.server_id);
int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
key_length+= TMP_TABLE_KEY_EXTRA;
@@ -356,458 +212,47 @@ uint create_tmp_table_def_key(THD *thd, char *key,
}
-
-/*****************************************************************************
- Functions to handle table definition cach (TABLE_SHARE)
-*****************************************************************************/
-
-extern "C" uchar *table_def_key(const uchar *record, size_t *length,
- my_bool not_used __attribute__((unused)))
-{
- TABLE_SHARE *entry=(TABLE_SHARE*) record;
- *length= entry->table_cache_key.length;
- return (uchar*) entry->table_cache_key.str;
-}
-
-
-static void table_def_free_entry(TABLE_SHARE *share)
-{
- DBUG_ENTER("table_def_free_entry");
- mysql_mutex_assert_owner(&LOCK_open);
- if (share->prev)
- {
- /* remove from old_unused_share list */
- *share->prev= share->next;
- share->next->prev= share->prev;
- }
- free_table_share(share);
- DBUG_VOID_RETURN;
-}
-
-
-bool table_def_init(void)
-{
- table_def_inited= 1;
-#ifdef HAVE_PSI_INTERFACE
- init_tdc_psi_keys();
-#endif
- mysql_mutex_init(key_LOCK_open, &LOCK_open, MY_MUTEX_INIT_FAST);
- mysql_mutex_record_order(&LOCK_active_mi, &LOCK_open);
- /*
- When we delete from the table_def_cache(), the free function
- table_def_free_entry() is invoked from my_hash_delete(), which calls
- free_table_share(), which may unload plugins, which can remove status
- variables and hence takes LOCK_status. Record this locking order here.
- */
- mysql_mutex_record_order(&LOCK_open, &LOCK_status);
- 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;
-}
-
-
-/**
- Notify table definition cache that process of shutting down server
- has started so it has to keep number of TABLE and TABLE_SHARE objects
- minimal in order to reduce number of references to pluggable engines.
-*/
-
-void table_def_start_shutdown(void)
-{
- DBUG_ENTER("table_def_start_shutdown");
- if (table_def_inited)
- {
- mysql_mutex_lock(&LOCK_open);
- /*
- Ensure that TABLE and TABLE_SHARE objects which are created for
- tables that are open during process of plugins' shutdown are
- immediately released. This keeps number of references to engine
- plugins minimal and allows shutdown to proceed smoothly.
- */
- table_def_shutdown_in_progress= TRUE;
- mysql_mutex_unlock(&LOCK_open);
- /* Free all cached but unused TABLEs and TABLE_SHAREs. */
- close_cached_tables(NULL, NULL, FALSE, LONG_TIMEOUT);
- }
- DBUG_VOID_RETURN;
-}
-
-
-void table_def_free(void)
-{
- DBUG_ENTER("table_def_free");
- if (table_def_inited)
- {
- table_def_inited= 0;
- /* Free table definitions. */
- my_hash_free(&table_def_cache);
- mysql_mutex_destroy(&LOCK_open);
- }
- DBUG_VOID_RETURN;
-}
-
-
-uint cached_table_definitions(void)
-{
- return table_def_cache.records;
-}
-
-
-/*
- Auxiliary routines for manipulating with per-share used/unused and
- global unused lists of TABLE objects and table_cache_count counter.
- Responsible for preserving invariants between those lists, counter
- and TABLE::in_use member.
- In fact those routines implement sort of implicit table cache as
- part of table definition cache.
-*/
-
-
-/**
- Add newly created TABLE object for table share which is going
- to be used right away.
-*/
-
-static void table_def_add_used_table(THD *thd, TABLE *table)
-{
- DBUG_ASSERT(table->in_use == thd);
- table->s->used_tables.push_front(table);
- table_cache_count++;
-}
-
-
/**
- Prepare used or unused TABLE instance for destruction by removing
- it from share's and global list.
-*/
-
-static void table_def_remove_table(TABLE *table)
-{
- if (table->in_use)
- {
- /* Remove from per-share chain of used TABLE objects. */
- table->s->used_tables.remove(table);
- }
- else
- {
- /* Remove from per-share chain of unused TABLE objects. */
- table->s->free_tables.remove(table);
-
- /* And global unused chain. */
- table->next->prev=table->prev;
- table->prev->next=table->next;
- if (table == unused_tables)
- {
- unused_tables=unused_tables->next;
- if (table == unused_tables)
- unused_tables=0;
- }
- check_unused(current_thd);
- }
- table_cache_count--;
-}
-
-
-/**
- Mark already existing TABLE instance as used.
-*/
-
-static void table_def_use_table(THD *thd, TABLE *table)
-{
- DBUG_ASSERT(!table->in_use);
-
- /* Unlink table from list of unused tables for this share. */
- table->s->free_tables.remove(table);
- /* Unlink able from global unused tables list. */
- if (table == unused_tables)
- { // First unused
- unused_tables=unused_tables->next; // Remove from link
- if (table == unused_tables)
- unused_tables=0;
- }
- table->prev->next=table->next; /* Remove from unused list */
- table->next->prev=table->prev;
- check_unused(thd);
- /* Add table to list of used tables for this share. */
- table->s->used_tables.push_front(table);
- table->in_use= thd;
- /* The ex-unused table must be fully functional. */
- DBUG_ASSERT(table->db_stat && table->file);
- /* The children must be detached from the table. */
- DBUG_ASSERT(! table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
-}
-
-
-/**
- Mark already existing used TABLE instance as unused.
-*/
-
-static void table_def_unuse_table(TABLE *table)
-{
- THD *thd __attribute__((unused))= table->in_use;
- DBUG_ASSERT(table->in_use);
-
- /* We shouldn't put the table to 'unused' list if the share is old. */
- DBUG_ASSERT(! table->s->has_old_version());
-
- table->in_use= 0;
-
- /* Remove table from the list of tables used in this share. */
- table->s->used_tables.remove(table);
- /* Add table to the list of unused TABLE objects for this share. */
- table->s->free_tables.push_front(table);
- /* Also link it last in the global list of unused TABLE objects. */
- if (unused_tables)
- {
- table->next=unused_tables;
- table->prev=unused_tables->prev;
- unused_tables->prev=table;
- table->prev->next=table;
- }
- else
- unused_tables=table->next=table->prev=table;
- check_unused(thd);
-}
-
-
-/*
- Get TABLE_SHARE for a table.
-
- get_table_share()
- thd Thread handle
- table_list Table that should be opened
- key Table cache key
- key_length Length of key
- flags operation: what to open table or view
- hash_value = my_calc_hash(&table_def_cache, key, key_length)
-
- IMPLEMENTATION
- Get a table definition from the table definition cache.
- If it doesn't exist, create a new from the table definition file.
-
- RETURN
- 0 Error
- # Share for table
+ 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.
*/
-TABLE_SHARE *get_table_share(THD *thd, const char *db, const char *table_name,
- char *key, uint key_length, uint flags,
- my_hash_value_type hash_value)
+uint get_table_def_key(const TABLE_LIST *table_list, const char **key)
{
- TABLE_SHARE *share;
- DBUG_ENTER("get_table_share");
-
- mysql_mutex_lock(&LOCK_open);
-
- /* Read table definition from cache */
- share= (TABLE_SHARE*) my_hash_search_using_hash_value(&table_def_cache,
- hash_value, (uchar*) key, key_length);
-
- if (!share)
- {
- if (!(share= alloc_table_share(db, table_name, key, key_length)))
- goto err;
-
- /*
- We assign a new table id under the protection of LOCK_open.
- We do this instead of creating a new mutex
- and using it for the sole purpose of serializing accesses to a
- static variable, we assign the table id here. We assign it to the
- share before inserting it into the table_def_cache to be really
- sure that it cannot be read from the cache without having a table
- id assigned.
-
- CAVEAT. This means that the table cannot be used for
- binlogging/replication purposes, unless get_table_share() has been
- called directly or indirectly.
- */
- assign_new_table_id(share);
-
- if (my_hash_insert(&table_def_cache, (uchar*) share))
- {
- free_table_share(share);
- goto err;
- }
- share->ref_count++; // Mark in use
- share->error= OPEN_FRM_OPEN_ERROR;
- mysql_mutex_lock(&share->LOCK_ha_data);
- mysql_mutex_unlock(&LOCK_open);
-
- if (flags & GTS_FORCE_DISCOVERY)
- ha_discover_table(thd, share); // don't read the frm at all
- else
- open_table_def(thd, share, flags | GTS_FORCE_DISCOVERY); // frm or discover
-
- mysql_mutex_unlock(&share->LOCK_ha_data);
- mysql_mutex_lock(&LOCK_open);
-
- if (share->error)
- {
- share->ref_count--;
- (void) my_hash_delete(&table_def_cache, (uchar*) share);
- goto err;
- }
-
- share->m_psi= PSI_CALL_get_table_share(false, share);
-
- DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u",
- (ulong) share, share->ref_count));
-
- goto end;
- }
-
- /* cannot force discovery of a cached share */
- DBUG_ASSERT(!(flags & GTS_FORCE_DISCOVERY));
-
- /* make sure that open_table_def() for this share is not running */
- mysql_mutex_lock(&share->LOCK_ha_data);
- mysql_mutex_unlock(&share->LOCK_ha_data);
-
/*
- We found an existing table definition. Return it if we didn't get
- an error when reading the table definition from file.
+ 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.
*/
- if (share->error)
- {
- open_table_error(share, share->error, share->open_errno);
- goto err;
- }
-
- if (share->is_view && !(flags & GTS_VIEW))
- {
- open_table_error(share, OPEN_FRM_NOT_A_TABLE, ENOENT);
- goto err;
- }
- if (!share->is_view && !(flags & GTS_TABLE))
- {
- open_table_error(share, OPEN_FRM_NOT_A_VIEW, ENOENT);
- goto err;
- }
+ 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()));
- ++share->ref_count;
-
- if (share->ref_count == 1 && share->prev)
- {
- /*
- Share was not used before and it was in the old_unused_share list
- Unlink share from this list
- */
- DBUG_PRINT("info", ("Unlinking from not used list"));
- *share->prev= share->next;
- share->next->prev= share->prev;
- share->next= 0;
- share->prev= 0;
- }
-
- /* Free cache if too big */
- while (table_def_cache.records > table_def_size &&
- oldest_unused_share->next)
- my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
-
- DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u",
- (ulong) share, share->ref_count));
- goto end;
-
-err:
- mysql_mutex_unlock(&LOCK_open);
- DBUG_RETURN(0);
-
-end:
- if (flags & GTS_NOLOCK)
- {
- release_table_share(share);
- /*
- if GTS_NOLOCK is requested, the returned share pointer cannot be used,
- the share it points to may go away any moment.
- But perhaps the caller is only interested to know whether a share or
- table existed?
- Let's return an invalid pointer here to catch dereferencing attempts.
- */
- share= (TABLE_SHARE*) 1;
- }
-
- mysql_mutex_unlock(&LOCK_open);
- DBUG_RETURN(share);
+ *key= (const char*)table_list->mdl_request.key.ptr() + 1;
+ return table_list->mdl_request.key.length() - 1;
}
-/**
- Mark that we are not using table share anymore.
-
- @param share Table share
-
- If the share has no open tables and (we have done a refresh or
- if we have already too many open table shares) then delete the
- definition.
-*/
-
-void release_table_share(TABLE_SHARE *share)
-{
- DBUG_ENTER("release_table_share");
- DBUG_PRINT("enter",
- ("share: 0x%lx table: %s.%s ref_count: %u version: %lu",
- (ulong) share, share->db.str, share->table_name.str,
- share->ref_count, share->version));
-
- mysql_mutex_assert_owner(&LOCK_open);
-
- DBUG_ASSERT(share->ref_count);
- if (!--share->ref_count)
- {
- if (share->has_old_version() || table_def_shutdown_in_progress)
- my_hash_delete(&table_def_cache, (uchar*) share);
- else
- {
- /* Link share last in used_table_share list */
- DBUG_PRINT("info",("moving share to unused list"));
-
- DBUG_ASSERT(share->next == 0);
- share->prev= end_of_unused_share.prev;
- *end_of_unused_share.prev= share;
- end_of_unused_share.prev= &share->next;
- share->next= &end_of_unused_share;
-
- if (table_def_cache.records > table_def_size)
- {
- /* Delete the least used share to preserve LRU order. */
- my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
- }
- }
- }
-
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Check if table definition exits in cache
-
- SYNOPSIS
- get_cached_table_share()
- db Database name
- table_name Table name
-
- RETURN
- 0 Not cached
- # TABLE_SHARE for table
-*/
-
-TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
-{
- char key[SAFE_NAME_LEN*2+2];
- uint key_length;
- mysql_mutex_assert_owner(&LOCK_open);
-
- key_length= create_table_def_key(key, db, table_name);
- TABLE_SHARE* share= (TABLE_SHARE*)my_hash_search(&table_def_cache,
- (uchar*) key, key_length);
- return !share || share->error ? 0 : share;
-}
+/*****************************************************************************
+ Functions to handle table definition cache (TABLE_SHARE)
+*****************************************************************************/
/*
Create a list for all open tables matching SQL expression
@@ -829,20 +274,19 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
{
- int result = 0;
OPEN_TABLE_LIST **start_list, *open_list;
TABLE_LIST table_list;
+ TABLE_SHARE *share;
+ TDC_iterator tdc_it;
DBUG_ENTER("list_open_tables");
- mysql_mutex_lock(&LOCK_open);
bzero((char*) &table_list,sizeof(table_list));
start_list= &open_list;
open_list=0;
- for (uint idx=0 ; result == 0 && idx < table_def_cache.records; idx++)
+ tdc_it.init();
+ while ((share= tdc_it.next()))
{
- TABLE_SHARE *share= (TABLE_SHARE *)my_hash_element(&table_def_cache, idx);
-
if (db && my_strcasecmp(system_charset_info, db, share->db.str))
continue;
if (wild && wild_compare(share->table_name.str, wild, 0))
@@ -867,14 +311,16 @@ 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);
+ mysql_mutex_lock(&LOCK_open);
+ TABLE_SHARE::TABLE_list::Iterator it(share->tdc.used_tables);
while (it++)
++(*start_list)->in_use;
+ mysql_mutex_unlock(&LOCK_open);
(*start_list)->locked= 0; /* Obsolete. */
start_list= &(*start_list)->next;
*start_list=0;
}
- mysql_mutex_unlock(&LOCK_open);
+ tdc_it.deinit();
DBUG_RETURN(open_list);
}
@@ -890,39 +336,18 @@ void intern_close_table(TABLE *table)
table->s ? table->s->db.str : "?",
table->s ? table->s->table_name.str : "?",
(long) table));
+ mysql_mutex_assert_not_owner(&LOCK_open);
free_io_cache(table);
delete table->triggers;
if (table->file) // Not true if placeholder
(void) closefrm(table, 1); // close file
table->alias.free();
- DBUG_VOID_RETURN;
-}
-
-/*
- Remove table from the open table cache
-
- SYNOPSIS
- free_cache_entry()
- table Table to remove
-
- NOTE
- We need to have a lock on LOCK_open when calling this
-*/
-
-static void free_cache_entry(TABLE *table)
-{
- DBUG_ENTER("free_cache_entry");
-
- /* This should be done before releasing table share. */
- table_def_remove_table(table);
-
- intern_close_table(table);
-
my_free(table);
DBUG_VOID_RETURN;
}
+
/* Free resources allocated by filesort() and read_record() */
void free_io_cache(TABLE *table)
@@ -947,9 +372,9 @@ void free_io_cache(TABLE *table)
@pre Caller should have LOCK_open mutex.
*/
-static void kill_delayed_threads_for_table(TABLE_SHARE *share)
+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->tdc.used_tables);
TABLE *tab;
mysql_mutex_assert_owner(&LOCK_open);
@@ -998,67 +423,48 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout)
{
bool result= FALSE;
- bool found= TRUE;
struct timespec abstime;
DBUG_ENTER("close_cached_tables");
DBUG_ASSERT(thd || (!wait_for_refresh && !tables));
- mysql_mutex_lock(&LOCK_open);
if (!tables)
{
/*
Force close of all open tables.
Note that code in TABLE_SHARE::wait_for_old_version() assumes that
- incrementing of refresh_version and removal of unused tables and
- shares from TDC happens atomically under protection of LOCK_open,
- or putting it another way that TDC does not contain old shares
- which don't have any tables used.
+ incrementing of refresh_version is followed by purge of unused table
+ shares.
*/
- refresh_version++;
- DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu",
- refresh_version));
+ tdc_increment_refresh_version();
kill_delayed_threads();
/*
Get rid of all unused TABLE and TABLE_SHARE instances. By doing
this we automatically close all tables which were marked as "old".
*/
- while (unused_tables)
- free_cache_entry(unused_tables);
+ tc_purge();
/* Free table shares which were not freed implicitly by loop above. */
- while (oldest_unused_share->next)
- (void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
+ tdc_purge(true);
}
else
{
bool found=0;
for (TABLE_LIST *table= tables; table; table= table->next_local)
{
- TABLE_SHARE *share= get_cached_table_share(table->db, table->table_name);
-
- if (share)
- {
- kill_delayed_threads_for_table(share);
- /* tdc_remove_table() calls share->remove_from_cache_at_close() */
- tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db,
- table->table_name, TRUE);
- found=1;
- }
+ /* tdc_remove_table() also sets TABLE_SHARE::version to 0. */
+ found|= tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db,
+ table->table_name, TRUE);
}
if (!found)
wait_for_refresh=0; // Nothing to wait for
}
- mysql_mutex_unlock(&LOCK_open);
-
DBUG_PRINT("info", ("open table definitions: %d",
- (int) table_def_cache.records));
+ (int) tdc_records()));
if (!wait_for_refresh)
DBUG_RETURN(result);
- set_timespec(abstime, timeout);
-
if (thd->locked_tables_mode)
{
/*
@@ -1090,67 +496,68 @@ 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);
}
}
/* Wait until all threads have closed all the tables we are flushing. */
DBUG_PRINT("info", ("Waiting for other threads to close their open tables"));
- while (found && ! thd->killed)
- {
- TABLE_SHARE *share;
- found= FALSE;
- /*
- To a self-deadlock or deadlocks with other FLUSH threads
- waiting on our open HANDLERs, we have to flush them.
- */
- mysql_ha_flush(thd);
- DEBUG_SYNC(thd, "after_flush_unlock");
-
- mysql_mutex_lock(&LOCK_open);
+ /*
+ To a self-deadlock or deadlocks with other FLUSH threads
+ waiting on our open HANDLERs, we have to flush them.
+ */
+ mysql_ha_flush(thd);
+ DEBUG_SYNC(thd, "after_flush_unlock");
- if (!tables)
+ if (!tables)
+ {
+ bool found= true;
+ set_timespec(abstime, timeout);
+ while (found && !thd->killed)
{
- for (uint idx=0 ; idx < table_def_cache.records ; idx++)
+ TABLE_SHARE *share;
+ TDC_iterator tdc_it;
+ found= false;
+
+ tdc_it.init();
+ while ((share= tdc_it.next()))
{
- share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx);
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
if (share->has_old_version())
{
- found= TRUE;
+ /* wait_for_old_version() will unlock mutex and free share */
+ found= true;
break;
}
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
}
- }
- else
- {
- for (TABLE_LIST *table= tables; table; table= table->next_local)
+ tdc_it.deinit();
+
+ if (found)
{
- share= get_cached_table_share(table->db, table->table_name);
- if (share && share->has_old_version())
+ if (share->wait_for_old_version(thd, &abstime,
+ MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
{
- found= TRUE;
+ result= TRUE;
break;
}
}
}
-
- if (found)
+ }
+ else
+ {
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
{
- /*
- The method below temporarily unlocks LOCK_open and frees
- share's memory.
- */
- if (share->wait_for_old_version(thd, &abstime,
- MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
+ if (thd->killed)
+ break;
+ if (tdc_wait_for_old_version(thd, table->db, table->table_name, timeout,
+ MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
{
- mysql_mutex_unlock(&LOCK_open);
result= TRUE;
- goto err_with_reopen;
+ break;
}
}
-
- mysql_mutex_unlock(&LOCK_open);
}
err_with_reopen:
@@ -1163,12 +570,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);
}
@@ -1181,23 +588,26 @@ err_with_reopen:
bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
{
- uint idx;
TABLE_LIST tmp, *tables= NULL;
bool result= FALSE;
+ TABLE_SHARE *share;
+ TDC_iterator tdc_it;
DBUG_ENTER("close_cached_connections");
DBUG_ASSERT(thd);
bzero(&tmp, sizeof(TABLE_LIST));
- mysql_mutex_lock(&LOCK_open);
-
- for (idx= 0; idx < table_def_cache.records; idx++)
+ tdc_it.init();
+ while ((share= tdc_it.next()))
{
- TABLE_SHARE *share= (TABLE_SHARE *) my_hash_element(&table_def_cache, idx);
-
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
/* Ignore if table is not open or does not have a connect_string */
- if (!share->connect_string.length || !share->ref_count)
+ if (!share->connect_string.length || !share->tdc.ref_count)
+ {
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
continue;
+ }
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
/* Compare the connection string */
if (connection &&
@@ -1217,7 +627,7 @@ bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
tables= (TABLE_LIST *) memdup_root(thd->mem_root, (char*)&tmp,
sizeof(TABLE_LIST));
}
- mysql_mutex_unlock(&LOCK_open);
+ tdc_it.deinit();
if (tables)
result= close_cached_tables(thd, tables, FALSE, LONG_TIMEOUT);
@@ -1369,7 +779,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;
@@ -1384,7 +795,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,
@@ -1410,9 +822,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);
+ }
}
@@ -1562,9 +977,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,
@@ -1606,27 +1020,8 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
if (table->file != NULL)
table->file->unbind_psi();
- mysql_mutex_lock(&LOCK_open);
-
- 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);
- table_def_unuse_table(table);
- /*
- We free the least used table, not the subject table,
- to keep the LRU order.
- */
- if (table_cache_count > table_cache_size)
- free_cache_entry(unused_tables);
- }
- mysql_mutex_unlock(&LOCK_open);
- DBUG_RETURN(found_old_table);
+ tc_release_table(table);
+ DBUG_VOID_RETURN;
}
@@ -1769,7 +1164,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)))
{
/*
@@ -1787,7 +1182,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;
@@ -2073,12 +1468,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_tmp_table_def_key(thd, key, db, table_name);
+ return find_temporary_table(thd, key, key_length);
}
@@ -2091,10 +1483,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_tmp_table_def_key(thd, key, tl->db, tl->table_name);
+ 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->variables.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;
}
@@ -2154,14 +1562,15 @@ TABLE *find_temporary_table(THD *thd,
int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans)
{
- TABLE *table;
DBUG_ENTER("drop_temporary_table");
DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
table_list->db, table_list->table_name));
- if (!(table= find_temporary_table(thd, table_list)))
+ if (!is_temporary_table(table_list))
DBUG_RETURN(1);
+ TABLE *table= table_list->table;
+
/* Table might be in use by some outer statement. */
if (table->query_id && table->query_id != thd->query_id)
{
@@ -2169,8 +1578,7 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans)
DBUG_RETURN(-1);
}
- if (is_trans != NULL)
- *is_trans= table->file->has_transactions();
+ *is_trans= table->file->has_transactions();
/*
If LOCK TABLES list is not empty and contains this table,
@@ -2178,6 +1586,7 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans)
*/
mysql_lock_remove(thd, thd->lock, table);
close_temporary_table(thd, table, 1, 1);
+ table_list->table= NULL;
DBUG_RETURN(0);
}
@@ -2238,13 +1647,6 @@ void close_temporary(TABLE *table, bool free_share, bool delete_table)
DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
table->s->db.str, table->s->table_name.str));
- /* in_use is not set for replication temporary tables during shutdown */
- if (table->in_use)
- {
- table->file->update_global_table_stats();
- table->file->update_global_index_stats();
- }
-
free_io_cache(table);
closefrm(table, 0);
if (delete_table)
@@ -2302,19 +1704,19 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db,
*/
bool wait_while_table_is_used(THD *thd, TABLE *table,
- enum ha_extra_function function,
- enum_tdc_remove_table_type remove_type)
+ enum ha_extra_function function)
{
DBUG_ENTER("wait_while_table_is_used");
DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu",
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, remove_type,
+ tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN,
table->s->db.str, table->s->table_name.str,
FALSE);
/* extra() call must come only after all instances above are closed */
@@ -2360,8 +1762,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;
}
@@ -2384,9 +1786,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. */
@@ -2403,9 +1805,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)
@@ -2557,161 +1959,89 @@ open_table_get_mdl_lock(THD *thd, Open_table_context *ot_ctx,
/**
- Check if table's share is being removed from the table definition
- cache and, if yes, wait until the flush is complete.
-
- @param thd Thread context.
- @param table_list Table which share should be checked.
- @param timeout Timeout for waiting.
- @param deadlock_weight Weight of this wait for deadlock detector.
-
- @retval FALSE Success. Share is up to date or has been flushed.
- @retval TRUE Error (OOM, our was killed, the wait resulted
- in a deadlock or timeout). Reported.
-*/
-
-static bool
-tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name,
- ulong wait_timeout, uint deadlock_weight)
-{
- TABLE_SHARE *share;
- bool res= FALSE;
-
- mysql_mutex_lock(&LOCK_open);
- if ((share= get_cached_table_share(db, table_name)) &&
- share->has_old_version())
- {
- struct timespec abstime;
- set_timespec(abstime, wait_timeout);
- res= share->wait_for_old_version(thd, &abstime, deadlock_weight);
- }
- mysql_mutex_unlock(&LOCK_open);
- return res;
-}
-
-
-/*
- 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];
+ TABLE *table;
+ const char *key;
uint key_length;
char *alias= table_list->alias;
uint flags= ot_ctx->get_flags();
MDL_ticket *mdl_ticket;
TABLE_SHARE *share;
- my_hash_value_type hash_value;
uint gts_flags;
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_tmp_table_def_key(thd, key, table_list->db,
- table_list->table_name) -
- 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->variables.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 &&
@@ -2797,7 +2127,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (dd_frm_is_view(thd, path))
{
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
@@ -2806,7 +2136,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
}
/*
No table in the locked tables list. In case of explicit LOCK TABLES
- this can happen if a user did not include the able into the list.
+ this can happen if a user did not include the table into the list.
In case of pre-locked mode locked tables list is generated automatically,
so we may only end up here if the table did not exist when
locked tables list was created.
@@ -2826,19 +2156,6 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
{
/*
- Check if we're trying to take a write lock in a read only transaction.
- */
- if (table_list->mdl_request.type >= MDL_SHARED_WRITE &&
- thd->tx_read_only &&
- !(flags & (MYSQL_OPEN_HAS_MDL_LOCK |
- MYSQL_LOCK_LOG_TABLE |
- MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY)))
- {
- my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
- DBUG_RETURN(true);
- }
-
- /*
We are not under LOCK TABLES and going to acquire write-lock/
modify the base table. We need to acquire protection against
global read lock until end of this statement in order to have
@@ -2909,9 +2226,6 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
mdl_ticket= table_list->mdl_request.ticket;
}
- hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
-
-
if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS)
{
if (!ha_table_exists(thd, table_list->db, table_list->table_name))
@@ -2931,8 +2245,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
retry_share:
- share= get_table_share(thd, table_list->db, table_list->table_name,
- key, key_length, gts_flags, hash_value);
+ share= tdc_acquire_share(thd, table_list->db, table_list->table_name,
+ key, key_length, gts_flags, &table);
if (!share)
{
@@ -2993,20 +2307,15 @@ retry_share:
0, table_list, mem_root))
goto err_lock;
- mysql_mutex_lock(&LOCK_open);
-
/* TODO: Don't free this */
- release_table_share(share);
+ tdc_release_share(share);
DBUG_ASSERT(table_list->view);
- mysql_mutex_unlock(&LOCK_open);
DBUG_RETURN(FALSE);
}
- mysql_mutex_lock(&LOCK_open);
- if (!(flags & MYSQL_OPEN_IGNORE_FLUSH) ||
- (share->protected_against_usage() && !(flags & MYSQL_OPEN_FOR_REPAIR)))
+ if (!(flags & MYSQL_OPEN_IGNORE_FLUSH))
{
if (share->has_old_version())
{
@@ -3019,12 +2328,14 @@ 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.
*/
+ if (table)
+ tc_release_table(table);
+ else
+ tdc_release_share(share);
+
MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
bool wait_result;
- release_table_share(share);
- mysql_mutex_unlock(&LOCK_open);
-
thd->push_internal_handler(&mdl_deadlock_handler);
wait_result= tdc_wait_for_old_version(thd, table_list->db,
table_list->table_name,
@@ -3046,23 +2357,18 @@ retry_share:
and try to reopen them. Note: refresh_version is currently
changed only during FLUSH TABLES.
*/
- release_table_share(share);
- mysql_mutex_unlock(&LOCK_open);
+ if (table)
+ tc_release_table(table);
+ else
+ tdc_release_share(share);
(void)ot_ctx->request_backoff_action(Open_table_context::OT_REOPEN_TABLES,
NULL);
DBUG_RETURN(TRUE);
}
}
- if (!share->free_tables.is_empty())
+ if (table)
{
- table= share->free_tables.front();
- table_def_use_table(thd, table);
-
- /* Release the share as we hold an extra reference to it */
- release_table_share(share);
- mysql_mutex_unlock(&LOCK_open);
-
DBUG_ASSERT(table->file != NULL);
table->file->rebind_psi();
}
@@ -3070,12 +2376,6 @@ retry_share:
{
enum open_frm_error error;
- /* If we have too many TABLE instances around, try to get rid of them */
- while (table_cache_count > table_cache_size && unused_tables)
- free_cache_entry(unused_tables);
-
- mysql_mutex_unlock(&LOCK_open);
-
/* make a new table */
if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
goto err_lock;
@@ -3099,10 +2399,8 @@ 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);
@@ -3111,9 +2409,7 @@ retry_share:
}
/* Add table to the share's used tables list. */
- mysql_mutex_lock(&LOCK_open);
- table_def_add_used_table(thd, table);
- mysql_mutex_unlock(&LOCK_open);
+ tc_add_table(thd, table);
}
table->mdl_ticket= mdl_ticket;
@@ -3132,14 +2428,27 @@ 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);
err_lock:
- mysql_mutex_lock(&LOCK_open);
- release_table_share(share);
- mysql_mutex_unlock(&LOCK_open);
+ tdc_release_share(share);
DBUG_RETURN(TRUE);
}
@@ -3158,7 +2467,7 @@ err_lock:
TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name)
{
char key[MAX_DBKEY_LENGTH];
- uint key_length= create_table_def_key(key, db, table_name);
+ uint key_length= tdc_create_key(key, db, table_name);
for (TABLE *table= list; table ; table=table->next)
{
@@ -3186,9 +2495,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,
@@ -3556,62 +2865,6 @@ Locked_tables_list::reopen_tables(THD *thd)
}
-/*
- Function to assign a new table map id to a table share.
-
- PARAMETERS
-
- share - Pointer to table share structure
-
- DESCRIPTION
-
- We are intentionally not checking that share->mutex is locked
- since this function should only be called when opening a table
- share and before it is entered into the table_def_cache (meaning
- that it cannot be fetched by another thread, even accidentally).
-
- PRE-CONDITION(S)
-
- share is non-NULL
- The LOCK_open mutex is locked.
-
- POST-CONDITION(S)
-
- share->table_map_id is given a value that with a high certainty is
- not used by any other table (the only case where a table id can be
- reused is on wrap-around, which means more than 4 billion table
- share opens have been executed while one table was open all the
- time).
-
- share->table_map_id is not ~0UL.
- */
-static ulong last_table_id= ~0UL;
-
-void assign_new_table_id(TABLE_SHARE *share)
-{
-
- DBUG_ENTER("assign_new_table_id");
-
- /* Preconditions */
- DBUG_ASSERT(share != NULL);
- mysql_mutex_assert_owner(&LOCK_open);
-
- ulong tid= ++last_table_id; /* get next id */
- /*
- There is one reserved number that cannot be used. Remember to
- change this when 6-byte global table id's are introduced.
- */
- if (unlikely(tid == ~0UL))
- tid= ++last_table_id;
- share->table_map_id= tid;
- DBUG_PRINT("info", ("table_id=%lu", tid));
-
- /* Post conditions */
- DBUG_ASSERT(share->table_map_id != ~0UL);
-
- DBUG_VOID_RETURN;
-}
-
#ifndef DBUG_OFF
/* Cause a spurious statement reprepare for debug purposes. */
static bool inject_reprepare(THD *thd)
@@ -3762,27 +3015,42 @@ 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;
TABLE_SHARE *share;
+ bool err= TRUE;
- if (!(share= get_table_share(thd, table_list->db, table_list->table_name,
- cache_key, cache_key_length, GTS_VIEW)))
+ if (!(share= tdc_acquire_share(thd, table_list->db, table_list->table_name,
+ cache_key, cache_key_length, GTS_VIEW)))
return TRUE;
DBUG_ASSERT(share->is_view);
- bool err= open_new_frm(thd, share, alias,
+ 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))
+ goto ret;
+ }
+
+ err= open_new_frm(thd, share, alias,
(HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
HA_GET_INDEX | HA_TRY_READ_ONLY),
READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD | flags,
thd->open_options, &not_used, table_list, mem_root);
- mysql_mutex_lock(&LOCK_open);
- release_table_share(share);
- mysql_mutex_unlock(&LOCK_open);
+ret:
+ tdc_release_share(share);
return err;
}
@@ -3847,8 +3115,7 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
if (!(entry= (TABLE*)my_malloc(sizeof(TABLE), MYF(MY_WME))))
return result;
- if (!(share= get_table_share(thd, table_list->db, table_list->table_name,
- GTS_TABLE)))
+ if (!(share= tdc_acquire_share_shortlived(thd, table_list, GTS_TABLE)))
goto end_free;
DBUG_ASSERT(! share->is_view);
@@ -3877,13 +3144,11 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
result= FALSE;
}
- mysql_mutex_lock(&LOCK_open);
- release_table_share(share);
+ tdc_release_share(share);
/* Remove the repaired share from the table cache. */
tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
table_list->db, table_list->table_name,
- TRUE);
- mysql_mutex_unlock(&LOCK_open);
+ FALSE);
end_free:
my_free(entry);
return result;
@@ -4020,29 +3285,28 @@ recover_from_failed_open(THD *thd)
case OT_DISCOVER:
{
if ((result= lock_table_names(thd, m_failed_table, NULL,
- get_timeout(),
- MYSQL_OPEN_SKIP_TEMPORARY)))
+ get_timeout(), 0)))
break;
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
m_failed_table->table_name, FALSE);
+
+ thd->get_stmt_da()->clear_warning_info(thd->query_id);
+ thd->clear_error(); // Clear error message
+
if ((result=
- !get_table_share(thd, m_failed_table->db,
- m_failed_table->table_name,
- GTS_TABLE | GTS_FORCE_DISCOVERY | GTS_NOLOCK)))
+ !tdc_acquire_share(thd, m_failed_table->db,
+ m_failed_table->table_name,
+ GTS_TABLE | GTS_FORCE_DISCOVERY | GTS_NOLOCK)))
break;
-
- thd->warning_info->clear_warning_info(thd->query_id);
- thd->clear_error(); // Clear error message
thd->mdl_context.release_transactional_locks();
break;
}
case OT_REPAIR:
{
if ((result= lock_table_names(thd, m_failed_table, NULL,
- get_timeout(),
- MYSQL_OPEN_SKIP_TEMPORARY)))
+ get_timeout(), 0)))
break;
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
@@ -4374,9 +3638,35 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
tables->db, tables->table_name, tables)); //psergey: invalid read of size 1 here
(*counter)++;
- /* Not a placeholder: must be a base table or a view. Let us open it. */
- DBUG_ASSERT(!tables->table);
+ /*
+ Not a placeholder: must be a base/temporary table or a view. Let us open it.
+ */
+ if (tables->table)
+ {
+ /*
+ If this TABLE_LIST object has an associated open TABLE object
+ (TABLE_LIST::table is not NULL), that TABLE object must be a pre-opened
+ temporary table.
+ */
+ DBUG_ASSERT(is_temporary_table(tables));
+ }
+ else if (tables->open_type == OT_TEMPORARY_ONLY)
+ {
+ /*
+ OT_TEMPORARY_ONLY means that we are in CREATE TEMPORARY TABLE statement.
+ Also such table list element can't correspond to prelocking placeholder
+ or to underlying table of merge table.
+ So existing temporary table should have been preopened by this moment
+ and we can simply continue without trying to open temporary or base
+ table.
+ */
+ DBUG_ASSERT(tables->open_strategy);
+ DBUG_ASSERT(!tables->prelocking_placeholder);
+ DBUG_ASSERT(!tables->parent_l);
+ DBUG_RETURN(0);
+ }
+ /* Not a placeholder: must be a base table or a view. Let us open it. */
if (tables->prelocking_placeholder)
{
/*
@@ -4387,7 +3677,35 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
*/
No_such_table_error_handler no_such_table_handler;
thd->push_internal_handler(&no_such_table_handler);
- error= open_table(thd, tables, new_frm_mem, ot_ctx);
+
+ /*
+ We're opening a table from the prelocking list.
+
+ Since this table list element might have been added after pre-opening
+ of temporary tables we have to try to open temporary table for it.
+
+ We can't simply skip this table list element and postpone opening of
+ temporary tabletill the execution of substatement for several reasons:
+ - Temporary table can be a MERGE table with base underlying tables,
+ so its underlying tables has to be properly open and locked at
+ prelocking stage.
+ - Temporary table can be a MERGE table and we might be in PREPARE
+ phase for a prepared statement. In this case it is important to call
+ HA_ATTACH_CHILDREN for all merge children.
+ This is necessary because merge children remember "TABLE_SHARE ref type"
+ and "TABLE_SHARE def version" in the HA_ATTACH_CHILDREN operation.
+ If HA_ATTACH_CHILDREN is not called, these attributes are not set.
+ Then, during the first EXECUTE, those attributes need to be updated.
+ That would cause statement re-preparing (because changing those
+ attributes during EXECUTE is caught by THD::m_reprepare_observers).
+ The problem is that since those attributes are not set in merge
+ children, another round of PREPARE will not help.
+ */
+ error= open_temporary_table(thd, tables);
+
+ if (!error && !tables->table)
+ error= open_table(thd, tables, new_frm_mem, ot_ctx);
+
thd->pop_internal_handler();
safe_to_ignore_table= no_such_table_handler.safely_trapped_errors();
}
@@ -4401,12 +3719,29 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
*/
Repair_mrg_table_error_handler repair_mrg_table_handler;
thd->push_internal_handler(&repair_mrg_table_handler);
- error= open_table(thd, tables, new_frm_mem, ot_ctx);
+
+ error= open_temporary_table(thd, tables);
+ if (!error && !tables->table)
+ error= open_table(thd, tables, new_frm_mem, ot_ctx);
+
thd->pop_internal_handler();
safe_to_ignore_table= repair_mrg_table_handler.safely_trapped_errors();
}
else
- error= open_table(thd, tables, new_frm_mem, ot_ctx);
+ {
+ if (tables->parent_l)
+ {
+ /*
+ Even if we are opening table not from the prelocking list we
+ still might need to look for a temporary table if this table
+ list element corresponds to underlying table of a merge table.
+ */
+ error= open_temporary_table(thd, tables);
+ }
+
+ if (!error && !tables->table)
+ error= open_table(thd, tables, new_frm_mem, ot_ctx);
+ }
free_root(new_frm_mem, MYF(MY_KEEP_PREALLOC));
@@ -4633,27 +3968,25 @@ 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 (mdl_requests.is_empty())
@@ -4717,7 +4050,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);
}
@@ -4766,34 +4099,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;
@@ -4833,11 +4165,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;
@@ -4926,7 +4259,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;
}
}
@@ -4981,6 +4314,10 @@ restart:
if (ot_ctx.recover_from_failed_open(thd))
goto err;
+ /* Re-open temporary tables after close_tables_for_reopen(). */
+ if (open_temporary_tables(thd, *start))
+ goto err;
+
error= FALSE;
goto restart;
}
@@ -5034,6 +4371,10 @@ restart:
if (ot_ctx.recover_from_failed_open(thd))
goto err;
+ /* Re-open temporary tables after close_tables_for_reopen(). */
+ if (open_temporary_tables(thd, *start))
+ goto err;
+
error= FALSE;
goto restart;
}
@@ -5464,6 +4805,10 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
bool error;
DBUG_ENTER("open_ltable");
+ /* Ignore temporary tables as they have already ben opened*/
+ if (table_list->table)
+ DBUG_RETURN(table_list->table);
+
/* should not be used in a prelocked_mode context, see NOTE above */
DBUG_ASSERT(thd->locked_tables_mode < LTM_PRELOCKED);
@@ -5473,7 +4818,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())
@@ -5713,7 +5058,6 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
uint flags)
{
TABLE_LIST *table;
-
DBUG_ENTER("lock_tables");
/*
We can't meet statement requiring prelocking if we already
@@ -5981,6 +5325,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;
@@ -5993,7 +5340,8 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
TABLE *open_table_uncached(THD *thd, handlerton *hton,
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;
@@ -6024,7 +5372,7 @@ TABLE *open_table_uncached(THD *thd, handlerton *hton,
strend(saved_cache_key)+1, tmp_path);
share->db_plugin= ha_lock_engine(thd, hton);
- if (open_table_def(thd, share, GTS_TABLE | GTS_FORCE_DISCOVERY))
+ if (open_table_def(thd, share, GTS_TABLE | GTS_USE_DISCOVERY))
{
/* No need to lock share->mutex as this is not needed for tmp tables */
free_table_share(share);
@@ -6035,11 +5383,17 @@ TABLE *open_table_uncached(THD *thd, handlerton *hton,
share->m_psi= PSI_CALL_get_table_share(true, share);
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);
@@ -6048,6 +5402,7 @@ TABLE *open_table_uncached(THD *thd, handlerton *hton,
}
tmp_table->reginfo.lock_type= TL_WRITE; // Simulate locked
+ tmp_table->grant.privilege= TMP_TABLE_ACLS;
share->tmp_table= (tmp_table->file->has_transactions() ?
TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
@@ -6170,6 +5525,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->variables.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.
@@ -8839,7 +8331,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)
{
@@ -8863,7 +8355,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;
@@ -8877,7 +8369,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;
@@ -8892,7 +8384,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);
@@ -8969,7 +8461,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);
@@ -9044,7 +8536,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);
@@ -9194,23 +8686,6 @@ my_bool mysql_rm_tmp_tables(void)
unireg support functions
*****************************************************************************/
-/*
- free all unused tables
-
- NOTE
- This is called by 'handle_manager' when one wants to periodicly flush
- all not used tables.
-*/
-
-void tdc_flush_unused_tables()
-{
- mysql_mutex_lock(&LOCK_open);
- while (unused_tables)
- free_cache_entry(unused_tables);
- mysql_mutex_unlock(&LOCK_open);
-}
-
-
/**
A callback to the server internals that is used to address
special cases of the locking protocol.
@@ -9293,114 +8768,6 @@ bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use,
}
-/**
- Remove all or some (depending on parameter) instances of TABLE and
- TABLE_SHARE from the table definition cache.
-
- @param thd Thread context
- @param remove_type Type of removal:
- TDC_RT_REMOVE_ALL - remove all TABLE instances and
- TABLE_SHARE instance. There
- should be no used TABLE objects
- and caller should have exclusive
- metadata lock on the table.
- TDC_RT_REMOVE_NOT_OWN - remove all TABLE instances
- except those that belong to
- this thread. There should be
- no TABLE objects used by other
- threads and caller should have
- exclusive metadata lock on the
- table.
- TDC_RT_REMOVE_UNUSED - remove all unused TABLE
- instances (if there are no
- used instances will also
- remove TABLE_SHARE).
- @param db Name of database
- @param table_name Name of table
- @param has_lock If TRUE, LOCK_open is already acquired
-
- @note It assumes that table instances are already not used by any
- (other) thread (this should be achieved by using meta-data locks).
-*/
-
-void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
- const char *db, const char *table_name,
- bool has_lock)
-{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
- TABLE *table;
- TABLE_SHARE *share;
- DBUG_ENTER("tdc_remove_table");
- DBUG_PRINT("enter",("name: %s remove_type: %d", table_name, remove_type));
-
- if (! has_lock)
- mysql_mutex_lock(&LOCK_open);
- else
- {
- mysql_mutex_assert_owner(&LOCK_open);
- }
-
- DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED ||
- thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
- MDL_EXCLUSIVE));
-
- key_length= create_table_def_key(key, db, table_name);
-
- 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);
-#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 ||
- remove_type == TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE)
- {
- I_P_List_iterator<TABLE, TABLE_share> it2(share->used_tables);
- while ((table= it2++))
- if (table->in_use != thd)
- {
- DBUG_ASSERT(0);
- }
- }
-#endif
- /*
- Mark share to ensure that it gets automatically deleted once
- it is no longer referenced.
-
- Note that code in TABLE_SHARE::wait_for_old_version() assumes
- that marking share as old and removal of its unused tables
- and of the share itself from TDC happens atomically under
- protection of LOCK_open, or, putting it another way, that
- TDC does not contain old shares which don't have any tables
- used.
- */
- if (remove_type == TDC_RT_REMOVE_NOT_OWN)
- share->remove_from_cache_at_close();
- else
- {
- /* Ensure that no can open the table while it's used */
- share->protect_against_usage();
- }
-
- while ((table= it++))
- free_cache_entry(table);
- }
- else
- (void) my_hash_delete(&table_def_cache, (uchar*) share);
- }
-
- if (! has_lock)
- mysql_mutex_unlock(&LOCK_open);
- DBUG_VOID_RETURN;
-}
-
-
int setup_ftfuncs(SELECT_LEX *select_lex)
{
List_iterator<Item_func_match> li(*(select_lex->ftfunc_list)),
@@ -9778,7 +9145,7 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup)
DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_LOG);
/* Make sure all columns get assigned to a default value */
table->use_all_columns();
- table->no_replicate= 1;
+ DBUG_ASSERT(table->no_replicate);
}
else
thd->restore_backup_open_tables_state(backup);