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.cc2667
1 files changed, 2007 insertions, 660 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 98e1f2dacd5..a97d285810e 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -24,7 +24,7 @@
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
-#ifdef __WIN__
+#ifdef __WIN__
#include <io.h>
#endif
@@ -83,28 +83,35 @@ bool Prelock_error_handler::safely_trapped_errors()
TABLE *unused_tables; /* Used by mysql_test */
HASH open_cache; /* Used by mysql_test */
-
-static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
- const char *name, const char *alias,
- TABLE_LIST *table_list, MEM_ROOT *mem_root,
- uint flags);
+static HASH table_def_cache;
+static TABLE_SHARE *oldest_unused_share, end_of_unused_share;
+static pthread_mutex_t LOCK_table_share;
+static bool table_def_inited= 0;
+
+static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list,
+ const char *alias,
+ char *cache_key, uint cache_key_length,
+ MEM_ROOT *mem_root, uint flags);
static void free_cache_entry(TABLE *entry);
-static bool open_new_frm(THD *thd, const char *path, const char *alias,
- const char *db, const char *table_name,
+static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
uint db_stat, uint prgflag,
uint ha_open_flags, TABLE *outparam,
TABLE_LIST *table_desc, MEM_ROOT *mem_root);
static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
bool send_refresh);
+static bool
+has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables);
+
extern "C" byte *table_cache_key(const byte *record,uint *length,
my_bool not_used __attribute__((unused)))
{
TABLE *entry=(TABLE*) record;
- *length= entry->s->key_length;
- return (byte*) entry->s->table_cache_key;
+ *length= entry->s->table_cache_key.length;
+ return (byte*) entry->s->table_cache_key.str;
}
+
bool table_cache_init(void)
{
return hash_init(&open_cache, &my_charset_bin, table_cache_size+16,
@@ -115,21 +122,25 @@ bool table_cache_init(void)
void table_cache_free(void)
{
DBUG_ENTER("table_cache_free");
- close_cached_tables((THD*) 0,0,(TABLE_LIST*) 0);
- if (!open_cache.records) // Safety first
- hash_free(&open_cache);
+ if (table_def_inited)
+ {
+ close_cached_tables((THD*) 0,0,(TABLE_LIST*) 0);
+ if (!open_cache.records) // Safety first
+ hash_free(&open_cache);
+ }
DBUG_VOID_RETURN;
}
-uint cached_tables(void)
+uint cached_open_tables(void)
{
return open_cache.records;
}
+
#ifdef EXTRA_DEBUG
static void check_unused(void)
{
- uint count=0,idx=0;
+ uint count= 0, open_files= 0, idx= 0;
TABLE *cur_link,*start_link;
if ((start_link=cur_link=unused_tables))
@@ -153,17 +164,557 @@ static void check_unused(void)
TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
if (!entry->in_use)
count--;
+ if (entry->file)
+ open_files++;
}
if (count != 0)
{
DBUG_PRINT("error",("Unused_links doesn't match open_cache: diff: %d", /* purecov: inspected */
count)); /* purecov: inspected */
}
+
+#ifdef NOT_SAFE_FOR_REPAIR
+ /*
+ check that open cache and table definition cache has same number of
+ aktive tables
+ */
+ count= 0;
+ for (idx=0 ; idx < table_def_cache.records ; idx++)
+ {
+ TABLE_SHARE *entry= (TABLE_SHARE*) hash_element(&table_def_cache,idx);
+ count+= entry->ref_count;
+ }
+ if (count != open_files)
+ {
+ DBUG_PRINT("error", ("table_def ref_count: %u open_cache: %u",
+ count, open_files));
+ DBUG_ASSERT(count == open_files);
+ }
+#endif
}
#else
#define check_unused()
#endif
+
+/*
+ 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
+
+ IMPLEMENTATION
+ The table cache_key is created from:
+ db_name + \0
+ table_name + \0
+
+ if the table is a tmp table, we add the following to make each tmp table
+ unique on the slave:
+
+ 4 bytes for master thread id
+ 4 bytes pseudo thread id
+
+ RETURN
+ Length of key
+*/
+
+uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list,
+ bool tmp_table)
+{
+ uint key_length= (uint) (strmov(strmov(key, table_list->db)+1,
+ table_list->table_name)-key)+1;
+ if (tmp_table)
+ {
+ int4store(key + key_length, thd->server_id);
+ int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
+ key_length+= TMP_TABLE_KEY_EXTRA;
+ }
+ return key_length;
+}
+
+
+
+/*****************************************************************************
+ Functions to handle table definition cach (TABLE_SHARE)
+*****************************************************************************/
+
+extern "C" byte *table_def_key(const byte *record, uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ TABLE_SHARE *entry=(TABLE_SHARE*) record;
+ *length= entry->table_cache_key.length;
+ return (byte*) entry->table_cache_key.str;
+}
+
+
+static void table_def_free_entry(TABLE_SHARE *share)
+{
+ DBUG_ENTER("table_def_free_entry");
+ if (share->prev)
+ {
+ /* remove from old_unused_share list */
+ pthread_mutex_lock(&LOCK_table_share);
+ *share->prev= share->next;
+ share->next->prev= share->prev;
+ pthread_mutex_unlock(&LOCK_table_share);
+ }
+ free_table_share(share);
+ DBUG_VOID_RETURN;
+}
+
+
+bool table_def_init(void)
+{
+ table_def_inited= 1;
+ pthread_mutex_init(&LOCK_table_share, MY_MUTEX_INIT_FAST);
+ oldest_unused_share= &end_of_unused_share;
+ end_of_unused_share.prev= &oldest_unused_share;
+
+ return hash_init(&table_def_cache, &my_charset_bin, table_def_size,
+ 0, 0, table_def_key,
+ (hash_free_key) table_def_free_entry, 0) != 0;
+}
+
+
+void table_def_free(void)
+{
+ DBUG_ENTER("table_def_free");
+ if (table_def_inited)
+ {
+ table_def_inited= 0;
+ pthread_mutex_destroy(&LOCK_table_share);
+ hash_free(&table_def_cache);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+uint cached_table_definitions(void)
+{
+ return table_def_cache.records;
+}
+
+
+/*
+ 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
+ db_flags Flags to open_table_def():
+ OPEN_VIEW
+ error out: Error code from open_table_def()
+
+ IMPLEMENTATION
+ Get a table definition from the table definition cache.
+ If it doesn't exist, create a new from the table definition file.
+
+ NOTES
+ We must have wrlock on LOCK_open when we come here
+ (To be changed later)
+
+ RETURN
+ 0 Error
+ # 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 *share;
+ DBUG_ENTER("get_table_share");
+
+ *error= 0;
+
+ /* Read table definition from cache */
+ if ((share= (TABLE_SHARE*) hash_search(&table_def_cache,(byte*) key,
+ key_length)))
+ goto found;
+
+ if (!(share= alloc_table_share(table_list, key, key_length)))
+ {
+#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3
+ pthread_mutex_unlock(&LOCK_open);
+#endif
+ DBUG_RETURN(0);
+ }
+
+#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3
+ // We need a write lock to be able to add a new entry
+ pthread_mutex_unlock(&LOCK_open);
+ pthread_mutex_lock(&LOCK_open);
+ /* Check that another thread didn't insert the same table in between */
+ if ((old_share= hash_search(&table_def_cache, (byte*) key, key_length)))
+ {
+ (void) pthread_mutex_lock(&share->mutex);
+ free_table_share(share);
+ share= old_share;
+ goto found;
+ }
+#endif
+
+ /*
+ Lock mutex to be able to read table definition from file without
+ conflicts
+ */
+ (void) pthread_mutex_lock(&share->mutex);
+
+ /*
+ We assign a new table id under the protection of the LOCK_open and
+ the share's own mutex. We do this insted 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, (byte*) share))
+ {
+#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3
+ pthread_mutex_unlock(&LOCK_open);
+ (void) pthread_mutex_unlock(&share->mutex);
+#endif
+ free_table_share(share);
+ DBUG_RETURN(0); // return error
+ }
+#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3
+ pthread_mutex_unlock(&LOCK_open);
+#endif
+ if (open_table_def(thd, share, db_flags))
+ {
+#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3
+ /*
+ No such table or wrong table definition file
+ Lock first the table cache and then the mutex.
+ This will ensure that no other thread is using the share
+ structure.
+ */
+ (void) pthread_mutex_unlock(&share->mutex);
+ (void) pthread_mutex_lock(&LOCK_open);
+ (void) pthread_mutex_lock(&share->mutex);
+#endif
+ *error= share->error;
+ (void) hash_delete(&table_def_cache, (byte*) share);
+ DBUG_RETURN(0);
+ }
+ share->ref_count++; // Mark in use
+ DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u",
+ (ulong) share, share->ref_count));
+ (void) pthread_mutex_unlock(&share->mutex);
+ DBUG_RETURN(share);
+
+found:
+ /*
+ We found an existing table definition. Return it if we didn't get
+ an error when reading the table definition from file.
+ */
+
+ /* We must do a lock to ensure that the structure is initialized */
+ (void) pthread_mutex_lock(&share->mutex);
+#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3
+ pthread_mutex_unlock(&LOCK_open);
+#endif
+ if (share->error)
+ {
+ /* Table definition contained an error */
+ open_table_error(share, share->error, share->open_errno, share->errarg);
+ (void) pthread_mutex_unlock(&share->mutex);
+ DBUG_RETURN(0);
+ }
+ if (share->is_view && !(db_flags & OPEN_VIEW))
+ {
+ open_table_error(share, 1, ENOENT, 0);
+ (void) pthread_mutex_unlock(&share->mutex);
+ DBUG_RETURN(0);
+ }
+
+ if (!share->ref_count++ && 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"));
+ pthread_mutex_lock(&LOCK_table_share);
+ *share->prev= share->next;
+ share->next->prev= share->prev;
+ share->next= 0;
+ share->prev= 0;
+ pthread_mutex_unlock(&LOCK_table_share);
+ }
+ (void) pthread_mutex_unlock(&share->mutex);
+
+ /* Free cache if too big */
+ while (table_def_cache.records > table_def_size &&
+ oldest_unused_share->next)
+ {
+ pthread_mutex_lock(&oldest_unused_share->mutex);
+ VOID(hash_delete(&table_def_cache, (byte*) oldest_unused_share));
+ }
+
+ DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u",
+ (ulong) share, share->ref_count));
+ DBUG_RETURN(share);
+}
+
+
+/*
+ Get a table share. If it didn't exist, try creating it from engine
+
+ For arguments and return values, see get_table_from_share()
+*/
+
+static TABLE_SHARE
+*get_table_share_with_create(THD *thd, TABLE_LIST *table_list,
+ char *key, uint key_length,
+ uint db_flags, int *error)
+{
+ TABLE_SHARE *share;
+ int tmp;
+ DBUG_ENTER("get_table_share_with_create");
+
+ if ((share= get_table_share(thd, table_list, key, key_length,
+ db_flags, error)) ||
+ thd->net.last_errno != ER_NO_SUCH_TABLE)
+ DBUG_RETURN(share);
+
+ /* Table didn't exist. Check if some engine can provide it */
+ if ((tmp= ha_create_table_from_engine(thd, table_list->db,
+ table_list->table_name)) < 0)
+ {
+ /*
+ No such table in any engine.
+ Hide "Table doesn't exist" errors if table belong to view
+ */
+ if (table_list->belong_to_view)
+ {
+ TABLE_LIST *view= table_list->belong_to_view;
+ thd->clear_error();
+ my_error(ER_VIEW_INVALID, MYF(0),
+ view->view_db.str, view->view_name.str);
+ }
+ DBUG_RETURN(0);
+ }
+ if (tmp)
+ {
+ /* Give right error message */
+ thd->clear_error();
+ DBUG_PRINT("error", ("Discovery of %s/%s failed", table_list->db,
+ table_list->table_name));
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "Failed to open '%-.64s', error while "
+ "unpacking from engine",
+ MYF(0), table_list->table_name);
+ DBUG_RETURN(0);
+ }
+ /* Table existed in engine. Let's open it */
+ mysql_reset_errors(thd, 1); // Clear warnings
+ thd->clear_error(); // Clear error message
+ DBUG_RETURN(get_table_share(thd, table_list, key, key_length,
+ db_flags, error));
+}
+
+
+/*
+ Mark that we are not using table share anymore.
+
+ SYNOPSIS
+ release_table_share()
+ share Table share
+ release_type How the release should be done:
+ RELEASE_NORMAL
+ - Release without checking
+ RELEASE_WAIT_FOR_DROP
+ - Don't return until we get a signal that the
+ table is deleted or the thread is killed.
+
+ IMPLEMENTATION
+ If ref_count goes to zero and (we have done a refresh or if we have
+ already too many open table shares) then delete the definition.
+
+ If type == RELEASE_WAIT_FOR_DROP then don't return until we get a signal
+ that the table is deleted or the thread is killed.
+*/
+
+void release_table_share(TABLE_SHARE *share, enum release_type type)
+{
+ bool to_be_deleted= 0;
+ 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));
+
+ safe_mutex_assert_owner(&LOCK_open);
+
+ pthread_mutex_lock(&share->mutex);
+ if (!--share->ref_count)
+ {
+ if (share->version != refresh_version)
+ to_be_deleted=1;
+ else
+ {
+ /* Link share last in used_table_share list */
+ DBUG_PRINT("info",("moving share to unused list"));
+
+ DBUG_ASSERT(share->next == 0);
+ pthread_mutex_lock(&LOCK_table_share);
+ 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;
+ pthread_mutex_unlock(&LOCK_table_share);
+
+ to_be_deleted= (table_def_cache.records > table_def_size);
+ }
+ }
+
+ if (to_be_deleted)
+ {
+ DBUG_PRINT("info", ("Deleting share"));
+ hash_delete(&table_def_cache, (byte*) share);
+ DBUG_VOID_RETURN;
+ }
+ pthread_mutex_unlock(&share->mutex);
+ DBUG_VOID_RETURN;
+
+
+#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3
+ if (to_be_deleted)
+ {
+ /*
+ We must try again with new locks as we must get LOCK_open
+ before share->mutex
+ */
+ pthread_mutex_unlock(&share->mutex);
+ pthread_mutex_lock(&LOCK_open);
+ pthread_mutex_lock(&share->mutex);
+ if (!share->ref_count)
+ { // No one is using this now
+ TABLE_SHARE *name_lock;
+ if (share->replace_with_name_lock && (name_lock=get_name_lock(share)))
+ {
+ /*
+ This code is execured when someone does FLUSH TABLES while on has
+ locked tables.
+ */
+ (void) hash_search(&def_cache,(byte*) key,key_length);
+ hash_replace(&def_cache, def_cache.current_record,(byte*) name_lock);
+ }
+ else
+ {
+ /* Remove table definition */
+ hash_delete(&def_cache,(byte*) share);
+ }
+ pthread_mutex_unlock(&LOCK_open);
+ free_table_share(share);
+ }
+ else
+ {
+ pthread_mutex_unlock(&LOCK_open);
+ if (type == RELEASE_WAIT_FOR_DROP)
+ wait_for_table(share, "Waiting for close");
+ else
+ pthread_mutex_unlock(&share->mutex);
+ }
+ }
+ else if (type == RELEASE_WAIT_FOR_DROP)
+ wait_for_table(share, "Waiting for close");
+ else
+ pthread_mutex_unlock(&share->mutex);
+#endif
+}
+
+
+/*
+ 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[NAME_LEN*2+2];
+ TABLE_LIST table_list;
+ uint key_length;
+ safe_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);
+ return (TABLE_SHARE*) hash_search(&table_def_cache,(byte*) key, key_length);
+}
+
+
+/*
+ Close file handle, but leave the table in the table cache
+
+ SYNOPSIS
+ close_handle_and_leave_table_as_lock()
+ table Table handler
+
+ NOTES
+ By leaving the table in the table cache, it disallows any other thread
+ to open the table
+
+ thd->killed will be set if we run out of memory
+*/
+
+
+void close_handle_and_leave_table_as_lock(TABLE *table)
+{
+ TABLE_SHARE *share, *old_share= table->s;
+ char *key_buff;
+ MEM_ROOT *mem_root= &table->mem_root;
+ DBUG_ENTER("close_handle_and_leave_table_as_lock");
+
+ DBUG_ASSERT(table->db_stat);
+
+ /*
+ Make a local copy of the table share and free the current one.
+ This has to be done to ensure that the table share is removed from
+ the table defintion cache as soon as the last instance is removed
+ */
+ if (multi_alloc_root(mem_root,
+ &share, sizeof(*share),
+ &key_buff, old_share->table_cache_key.length,
+ NULL))
+ {
+ bzero((char*) share, sizeof(*share));
+ share->set_table_cache_key(key_buff, old_share->table_cache_key.str,
+ old_share->table_cache_key.length);
+ share->tmp_table= INTERNAL_TMP_TABLE; // for intern_close_table()
+ }
+
+ table->file->close();
+ table->db_stat= 0; // Mark file closed
+ release_table_share(table->s, RELEASE_NORMAL);
+ table->s= share;
+
+ DBUG_VOID_RETURN;
+}
+
+
+
/*
Create a list for all open tables matching SQL expression
@@ -200,17 +751,14 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
TABLE_SHARE *share= entry->s;
- DBUG_ASSERT(share->table_name != 0);
- if ((!share->table_name)) // To be removed
- continue; // Shouldn't happen
- if (db && my_strcasecmp(system_charset_info, db, share->db))
+ if (db && my_strcasecmp(system_charset_info, db, share->db.str))
continue;
- if (wild && wild_compare(share->table_name,wild,0))
+ if (wild && wild_compare(share->table_name.str, wild, 0))
continue;
/* Check if user has SELECT privilege for any column in the table */
- table_list.db= (char*) share->db;
- table_list.table_name= (char*) share->table_name;
+ table_list.db= share->db.str;
+ table_list.table_name= share->table_name.str;
table_list.grant.privilege=0;
if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list,1))
@@ -218,8 +766,8 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
/* need to check if we haven't already listed it */
for (table= open_list ; table ; table=table->next)
{
- if (!strcmp(table->table,share->table_name) &&
- !strcmp(table->db,entry->s->db))
+ if (!strcmp(table->table, share->table_name.str) &&
+ !strcmp(table->db, share->db.str))
{
if (entry->in_use)
table->in_use++;
@@ -231,15 +779,15 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
if (table)
continue;
if (!(*start_list = (OPEN_TABLE_LIST *)
- sql_alloc(sizeof(**start_list)+share->key_length)))
+ sql_alloc(sizeof(**start_list)+share->table_cache_key.length)))
{
open_list=0; // Out of memory
break;
}
strmov((*start_list)->table=
strmov(((*start_list)->db= (char*) ((*start_list)+1)),
- entry->s->db)+1,
- entry->s->table_name);
+ share->db.str)+1,
+ share->table_name.str);
(*start_list)->in_use= entry->in_use ? 1 : 0;
(*start_list)->locked= entry->locked_by_name ? 1 : 0;
start_list= &(*start_list)->next;
@@ -256,10 +804,13 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
void intern_close_table(TABLE *table)
{ // Free all structures
+ DBUG_ENTER("intern_close_table");
+
free_io_cache(table);
delete table->triggers;
- if (table->file)
- VOID(closefrm(table)); // close file
+ if (table->file) // Not true if name lock
+ VOID(closefrm(table, 1)); // close file
+ DBUG_VOID_RETURN;
}
/*
@@ -276,7 +827,6 @@ void intern_close_table(TABLE *table)
static void free_cache_entry(TABLE *table)
{
DBUG_ENTER("free_cache_entry");
- safe_mutex_assert_owner(&LOCK_open);
intern_close_table(table);
if (!table->in_use)
@@ -309,6 +859,7 @@ void free_io_cache(TABLE *table)
DBUG_VOID_RETURN;
}
+
/*
Close all tables which aren't in use by any thread
@@ -317,15 +868,17 @@ void free_io_cache(TABLE *table)
*/
bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
- TABLE_LIST *tables)
+ TABLE_LIST *tables, bool have_lock)
{
bool result=0;
DBUG_ENTER("close_cached_tables");
DBUG_ASSERT(thd || (!if_wait_for_refresh && !tables));
- VOID(pthread_mutex_lock(&LOCK_open));
+ if (!have_lock)
+ VOID(pthread_mutex_lock(&LOCK_open));
if (!tables)
{
+ refresh_version++; // Force close of open tables
while (unused_tables)
{
#ifdef EXTRA_DEBUG
@@ -335,14 +888,20 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
VOID(hash_delete(&open_cache,(byte*) unused_tables));
#endif
}
- refresh_version++; // Force close of open tables
+ /* Free table shares */
+ while (oldest_unused_share->next)
+ {
+ pthread_mutex_lock(&oldest_unused_share->mutex);
+ VOID(hash_delete(&table_def_cache, (byte*) oldest_unused_share));
+ }
}
else
{
bool found=0;
for (TABLE_LIST *table= tables; table; table= table->next_local)
{
- if (remove_table_from_cache(thd, table->db, table->table_name,
+ if ((!table->table || !table->table->s->log_table) &&
+ remove_table_from_cache(thd, table->db, table->table_name,
RTFC_OWNED_BY_THD_FLAG))
found=1;
}
@@ -390,7 +949,8 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
are employed by CREATE TABLE as in this case table simply does not
exist yet.
*/
- if (table->needs_reopen_or_name_lock() && table->db_stat)
+ if (!table->s->log_table &&
+ (table->needs_reopen_or_name_lock() && table->db_stat))
{
found=1;
DBUG_PRINT("signal", ("Waiting for COND_refresh"));
@@ -411,7 +971,8 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
for (TABLE *table=thd->open_tables; table ; table= table->next)
table->s->version= refresh_version;
}
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (!have_lock)
+ VOID(pthread_mutex_unlock(&LOCK_open));
if (if_wait_for_refresh)
{
pthread_mutex_lock(&thd->mysys_var->mutex);
@@ -425,6 +986,71 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
/*
+ Close all tables which match specified connection string or
+ if specified string is NULL, then any table with a connection string.
+*/
+
+bool close_cached_connection_tables(THD *thd, bool if_wait_for_refresh,
+ LEX_STRING *connection, bool have_lock)
+{
+ uint idx;
+ TABLE_LIST tmp, *tables= NULL;
+ bool result= FALSE;
+ DBUG_ENTER("close_cached_connections");
+ DBUG_ASSERT(thd);
+
+ bzero(&tmp, sizeof(TABLE_LIST));
+
+ if (!have_lock)
+ VOID(pthread_mutex_lock(&LOCK_open));
+
+ for (idx= 0; idx < table_def_cache.records; idx++)
+ {
+ TABLE_SHARE *share= (TABLE_SHARE *) hash_element(&table_def_cache, idx);
+
+ /* Ignore if table is not open or does not have a connect_string */
+ if (!share->connect_string.length || !share->ref_count)
+ continue;
+
+ /* Compare the connection string */
+ if (connection &&
+ (connection->length > share->connect_string.length ||
+ (connection->length < share->connect_string.length &&
+ (share->connect_string.str[connection->length] != '/' &&
+ share->connect_string.str[connection->length] != '\\')) ||
+ strncasecmp(connection->str, share->connect_string.str,
+ connection->length)))
+ continue;
+
+ /* close_cached_tables() only uses these elements */
+ tmp.db= share->db.str;
+ tmp.table_name= share->table_name.str;
+ tmp.next_local= tables;
+
+ tables= (TABLE_LIST *) memdup_root(thd->mem_root, (char*)&tmp,
+ sizeof(TABLE_LIST));
+ }
+
+ if (tables)
+ result= close_cached_tables(thd, FALSE, tables, TRUE);
+
+ if (!have_lock)
+ VOID(pthread_mutex_unlock(&LOCK_open));
+
+ if (if_wait_for_refresh)
+ {
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->mysys_var->current_mutex= 0;
+ thd->mysys_var->current_cond= 0;
+ thd->proc_info=0;
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+ }
+
+ DBUG_RETURN(result);
+}
+
+
+/*
Mark all tables in the list which were used by current substatement
as free for reuse.
@@ -450,8 +1076,13 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
{
for (; table ; table= table->next)
+ {
if (table->query_id == thd->query_id)
+ {
table->query_id= 0;
+ table->file->ha_reset();
+ }
+ }
}
@@ -531,21 +1162,13 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
*/
ha_commit_stmt(thd);
+ /* Ensure we are calling ha_reset() for all used tables */
+ mark_used_tables_as_free_for_reuse(thd, thd->open_tables);
+
/* We are under simple LOCK TABLES so should not do anything else. */
- if (!prelocked_mode)
+ if (!prelocked_mode || !thd->lex->requires_prelocking())
DBUG_VOID_RETURN;
- if (!thd->lex->requires_prelocking())
- {
- /*
- If we are executing one of substatements we have to mark
- all tables which it used as free for reuse.
- */
- mark_used_tables_as_free_for_reuse(thd, thd->open_tables);
- DBUG_VOID_RETURN;
- }
-
- DBUG_ASSERT(prelocked_mode);
/*
We are in prelocked mode, so we have to leave it now with doing
implicit UNLOCK TABLES if need.
@@ -563,6 +1186,16 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
if (thd->lock)
{
+ /*
+ For RBR we flush the pending event just before we unlock all the
+ tables. This means that we are at the end of a topmost
+ statement, so we ensure that the STMT_END_F flag is set on the
+ pending event. For statements that are *inside* stored
+ functions, the pending event will not be flushed: that will be
+ handled either before writing a query log event (inside
+ binlog_query()) or when preparing a pending event.
+ */
+ thd->binlog_flush_pending_rows_event(TRUE);
mysql_unlock_tables(thd, thd->lock);
thd->lock=0;
}
@@ -574,18 +1207,16 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
saves some work in 2pc too)
see also sql_parse.cc - dispatch_command()
*/
- bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
+ if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
+ bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
if (!thd->active_transaction())
thd->transaction.xid_state.xid.null();
- /* VOID(pthread_sigmask(SIG_SETMASK,&thd->block_signals,NULL)); */
if (!lock_in_use)
VOID(pthread_mutex_lock(&LOCK_open));
- safe_mutex_assert_owner(&LOCK_open);
- DBUG_PRINT("info", ("thd->open_tables: %p", thd->open_tables));
+ DBUG_PRINT("info", ("thd->open_tables: 0x%lx", (long) thd->open_tables));
-
/*
End open index scans and table scans and remove references to the tables
from the handler tables hash. After this preparation it is safe to close
@@ -595,7 +1226,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
found_old_table= 0;
while (thd->open_tables)
- found_old_table|=close_thread_table(thd, &thd->open_tables);
+ found_old_table|= close_thread_table(thd, &thd->open_tables);
thd->some_tables_deleted=0;
/* Free tables to hold down open files */
@@ -624,6 +1255,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
DBUG_VOID_RETURN;
}
+
/* move one table to free list */
bool close_thread_table(THD *thd, TABLE **table_ptr)
@@ -649,16 +1281,8 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
*/
DBUG_ASSERT(!table->open_placeholder);
- if (table->s->flush_version != flush_version)
- {
- table->s->flush_version= flush_version;
- table->file->extra(HA_EXTRA_FLUSH);
- }
- else
- {
- // Free memory and reset for next loop
- table->file->reset();
- }
+ /* Free memory and reset for next loop */
+ table->file->ha_reset();
table->in_use=0;
if (unused_tables)
{
@@ -673,64 +1297,62 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
DBUG_RETURN(found_old_table);
}
- /* Close and delete temporary tables */
-
-void close_temporary(TABLE *table,bool delete_table)
-{
- DBUG_ENTER("close_temporary");
- char path[FN_REFLEN];
- db_type table_type=table->s->db_type;
- strmov(path,table->s->path);
- free_io_cache(table);
- closefrm(table);
- my_free((char*) table,MYF(0));
- if (delete_table)
- rm_temporary_table(table_type, path);
- DBUG_VOID_RETURN;
-}
/* close_temporary_tables' internal, 4 is due to uint4korr definition */
static inline uint tmpkeyval(THD *thd, TABLE *table)
{
- return uint4korr(table->s->table_cache_key + table->s->key_length - 4);
+ return uint4korr(table->s->table_cache_key.str + table->s->table_cache_key.length - 4);
}
-/* Creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread */
+
+/*
+ Close all temporary tables created by 'CREATE TEMPORARY TABLE' for thread
+ creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread
+*/
void close_temporary_tables(THD *thd)
{
TABLE *table;
+ TABLE *next;
+ /*
+ TODO: 5.1 maintains prev link in temporary_tables
+ double-linked list so we could fix it. But it is not necessary
+ at this time when the list is being destroyed
+ */
+ TABLE *prev_table;
+ /* Assume thd->options has OPTION_QUOTE_SHOW_CREATE */
+ bool was_quote_show= TRUE;
+
if (!thd->temporary_tables)
return;
- if (!mysql_bin_log.is_open())
+ if (!mysql_bin_log.is_open() || thd->current_stmt_binlog_row_based)
{
TABLE *next;
for (table= thd->temporary_tables; table; table= next)
{
- next= table->next;
- close_temporary(table, 1);
+ next=table->next;
+ close_temporary(table, 1, 1);
}
thd->temporary_tables= 0;
return;
}
- TABLE *next,
- *prev_table /* prev link is not maintained in TABLE's double-linked list */;
- bool was_quote_show= true; /* to assume thd->options has OPTION_QUOTE_SHOW_CREATE */
- // Better add "if exists", in case a RESET MASTER has been done
+ /* Better add "if exists", in case a RESET MASTER has been done */
const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ";
uint stub_len= sizeof(stub) - 1;
char buf[256];
- memcpy(buf, stub, stub_len);
String s_query= String(buf, sizeof(buf), system_charset_info);
- bool found_user_tables= false;
+ bool found_user_tables= FALSE;
LINT_INIT(next);
+ memcpy(buf, stub, stub_len);
+
/*
insertion sort of temp tables by pseudo_thread_id to build ordered list
of sublists of equal pseudo_thread_id
*/
+
for (prev_table= thd->temporary_tables, table= prev_table->next;
table;
prev_table= table, table= table->next)
@@ -776,10 +1398,13 @@ void close_temporary_tables(THD *thd)
{
if (is_user_table(table))
{
+ my_thread_id save_pseudo_thread_id= thd->variables.pseudo_thread_id;
/* Set pseudo_thread_id to be that of the processed table */
thd->variables.pseudo_thread_id= tmpkeyval(thd, table);
- /* Loop forward through all tables within the sublist of
- common pseudo_thread_id to create single DROP query */
+ /*
+ Loop forward through all tables within the sublist of
+ common pseudo_thread_id to create single DROP query.
+ */
for (s_query.length(stub_len);
table && is_user_table(table) &&
tmpkeyval(thd, table) == thd->variables.pseudo_thread_id;
@@ -789,13 +1414,13 @@ void close_temporary_tables(THD *thd)
We are going to add 4 ` around the db/table names and possible more
due to special characters in the names
*/
- append_identifier(thd, &s_query, table->s->db, strlen(table->s->db));
- s_query.q_append('.');
- append_identifier(thd, &s_query, table->s->table_name,
- strlen(table->s->table_name));
- s_query.q_append(',');
+ append_identifier(thd, &s_query, table->s->db.str, strlen(table->s->db.str));
+ s_query.append('.');
+ append_identifier(thd, &s_query, table->s->table_name.str,
+ strlen(table->s->table_name.str));
+ s_query.append(',');
next= table->next;
- close_temporary(table, 1);
+ close_temporary(table, 1, 1);
}
thd->clear_error();
CHARSET_INFO *cs_save= thd->variables.character_set_client;
@@ -805,29 +1430,30 @@ void close_temporary_tables(THD *thd)
0, FALSE);
thd->variables.character_set_client= cs_save;
/*
- Imagine the thread had created a temp table, then was doing a SELECT, and
- the SELECT was killed. Then it's not clever to mark the statement above as
- "killed", because it's not really a statement updating data, and there
- are 99.99% chances it will succeed on slave.
- If a real update (one updating a persistent table) was killed on the
- master, then this real update will be logged with error_code=killed,
- rightfully causing the slave to stop.
+ Imagine the thread had created a temp table, then was doing a
+ SELECT, and the SELECT was killed. Then it's not clever to
+ mark the statement above as "killed", because it's not really
+ a statement updating data, and there are 99.99% chances it
+ will succeed on slave. If a real update (one updating a
+ persistent table) was killed on the master, then this real
+ update will be logged with error_code=killed, rightfully
+ causing the slave to stop.
*/
qinfo.error_code= 0;
mysql_bin_log.write(&qinfo);
+ thd->variables.pseudo_thread_id= save_pseudo_thread_id;
}
else
{
next= table->next;
- close_temporary(table, 1);
+ close_temporary(table, 1, 1);
}
}
if (!was_quote_show)
- thd->options &= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */
+ thd->options&= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */
thd->temporary_tables=0;
}
-
/*
Find table in list.
@@ -1015,43 +1641,119 @@ void update_non_unique_table_error(TABLE_LIST *update,
}
-TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name)
+TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name)
{
- char key[MAX_DBKEY_LENGTH];
- uint key_length= (uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
- TABLE *table,**prev;
+ TABLE_LIST table_list;
+
+ table_list.db= (char*) db;
+ table_list.table_name= (char*) table_name;
+ return find_temporary_table(thd, &table_list);
+}
- int4store(key+key_length,thd->server_id);
- key_length += 4;
- int4store(key+key_length,thd->variables.pseudo_thread_id);
- key_length += 4;
- prev= &thd->temporary_tables;
- for (table=thd->temporary_tables ; table ; table=table->next)
+TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ TABLE *table;
+ DBUG_ENTER("find_temporary_table");
+ DBUG_PRINT("enter", ("table: '%s'.'%s'",
+ table_list->db, table_list->table_name));
+
+ key_length= create_table_def_key(thd, key, table_list, 1);
+ for (table=thd->temporary_tables ; table ; table= table->next)
{
- if (table->s->key_length == key_length &&
- !memcmp(table->s->table_cache_key,key,key_length))
- return prev;
- prev= &table->next;
+ if (table->s->table_cache_key.length == key_length &&
+ !memcmp(table->s->table_cache_key.str, key, key_length))
+ {
+ DBUG_PRINT("info",
+ ("Found table. server_id: %u pseudo_thread_id: %lu",
+ (uint) thd->server_id,
+ (ulong) thd->variables.pseudo_thread_id));
+ DBUG_RETURN(table);
+ }
}
- return 0; // Not a temporary table
+ DBUG_RETURN(0); // Not a temporary table
}
-bool close_temporary_table(THD *thd, const char *db, const char *table_name)
+
+/*
+ Close temporary table and unlink from thd->temporary tables
+*/
+
+bool close_temporary_table(THD *thd, TABLE_LIST *table_list)
{
- TABLE *table,**prev;
+ TABLE *table;
- if (!(prev=find_temporary_table(thd,db,table_name)))
+ if (!(table= find_temporary_table(thd, table_list)))
return 1;
- table= *prev;
- *prev= table->next;
- close_temporary(table, 1);
- if (thd->slave_thread)
- --slave_open_temp_tables;
+ close_temporary_table(thd, table, 1, 1);
return 0;
}
/*
+ unlink from thd->temporary tables and close temporary table
+*/
+
+void close_temporary_table(THD *thd, TABLE *table,
+ bool free_share, bool delete_table)
+{
+ if (table->prev)
+ {
+ table->prev->next= table->next;
+ if (table->prev->next)
+ table->next->prev= table->prev;
+ }
+ else
+ {
+ /* removing the item from the list */
+ DBUG_ASSERT(table == thd->temporary_tables);
+ /*
+ slave must reset its temporary list pointer to zero to exclude
+ passing non-zero value to end_slave via rli->save_temporary_tables
+ when no temp tables opened, see an invariant below.
+ */
+ thd->temporary_tables= table->next;
+ if (thd->temporary_tables)
+ table->next->prev= 0;
+ }
+ if (thd->slave_thread)
+ {
+ /* natural invariant of temporary_tables */
+ DBUG_ASSERT(slave_open_temp_tables || !thd->temporary_tables);
+ slave_open_temp_tables--;
+ }
+ close_temporary(table, free_share, delete_table);
+}
+
+
+/*
+ Close and delete a temporary table
+
+ NOTE
+ This dosn't unlink table from thd->temporary
+ If this is needed, use close_temporary_table()
+*/
+
+void close_temporary(TABLE *table, bool free_share, bool delete_table)
+{
+ handlerton *table_type= table->s->db_type();
+ DBUG_ENTER("close_temporary");
+
+ free_io_cache(table);
+ closefrm(table, 0);
+ if (delete_table)
+ rm_temporary_table(table_type, table->s->path.str);
+ if (free_share)
+ {
+ free_table_share(table->s);
+ my_free((char*) table,MYF(0));
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
Used by ALTER TABLE when the table is a temporary one. It changes something
only if the ALTER contained a RENAME clause (otherwise, table_name is the old
name).
@@ -1063,22 +1765,19 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db,
const char *table_name)
{
char *key;
+ uint key_length;
TABLE_SHARE *share= table->s;
+ TABLE_LIST table_list;
+ DBUG_ENTER("rename_temporary_table");
- if (!(key=(char*) alloc_root(&table->mem_root,
- (uint) strlen(db)+
- (uint) strlen(table_name)+6+4)))
- return 1; /* purecov: inspected */
- share->key_length= (uint)
- (strmov((char*) (share->table_name= strmov(share->table_cache_key= key,
- db)+1),
- table_name) - share->table_cache_key)+1;
- share->db= share->table_cache_key;
- int4store(key+share->key_length, thd->server_id);
- share->key_length+= 4;
- int4store(key+share->key_length, thd->variables.pseudo_thread_id);
- share->key_length+= 4;
- return 0;
+ 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);
+ share->set_table_cache_key(key, key_length);
+ DBUG_RETURN(0);
}
@@ -1100,26 +1799,40 @@ static void relink_unused(TABLE *table)
}
-/*
- Remove all instances of table from the current open list
- Free all locks on tables that are done with LOCK TABLES
- */
+/**
+ @brief Remove all instances of table from thread's open list and
+ table cache.
+
+ @param thd Thread context
+ @param find Table to remove
+ @param unlock TRUE - free all locks on tables removed that are
+ done with LOCK TABLES
+ FALSE - otherwise
+
+ @note When unlock parameter is FALSE or current thread doesn't have
+ any tables locked with LOCK TABLES tables are assumed to be
+ not locked (for example already unlocked).
+*/
-TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find)
+void unlink_open_table(THD *thd, TABLE *find, bool unlock)
{
char key[MAX_DBKEY_LENGTH];
- uint key_length= find->s->key_length;
- TABLE *start=list,**prev,*next;
- prev= &start;
+ uint key_length= find->s->table_cache_key.length;
+ TABLE *list, **prev, *next;
+ DBUG_ENTER("unlink_open_table");
+
+ safe_mutex_assert_owner(&LOCK_open);
- memcpy(key, find->s->table_cache_key, key_length);
+ list= thd->open_tables;
+ prev= &thd->open_tables;
+ memcpy(key, find->s->table_cache_key.str, key_length);
for (; list ; list=next)
{
next=list->next;
- if (list->s->key_length == key_length &&
- !memcmp(list->s->table_cache_key, key, key_length))
+ if (list->s->table_cache_key.length == key_length &&
+ !memcmp(list->s->table_cache_key.str, key, key_length))
{
- if (thd->locked_tables)
+ if (unlock && thd->locked_tables)
mysql_lock_remove(thd, thd->locked_tables,list);
VOID(hash_delete(&open_cache,(byte*) list)); // Close table
}
@@ -1132,7 +1845,7 @@ TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find)
*prev=0;
// Notify any 'refresh' threads
broadcast_refresh();
- return start;
+ DBUG_VOID_RETURN;
}
@@ -1146,53 +1859,65 @@ TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find)
@note This routine assumes that table to be closed is open only
by calling thread so we needn't wait until other threads
- will close the table. Also unless called under implicit or
- explicit LOCK TABLES mode it assumes that table to be
- dropped is already unlocked. In the former case it will
- also remove lock on the table. But one should not rely on
- this behaviour as it may change in future.
+ will close the table. It also assumes that table to be
+ dropped is already unlocked.
*/
void drop_open_table(THD *thd, TABLE *table, const char *db_name,
const char *table_name)
{
if (table->s->tmp_table)
- close_temporary_table(thd, db_name, table_name);
+ close_temporary_table(thd, table, 1, 1);
else
{
- enum db_type table_type= table->s->db_type;
+ handlerton *table_type= table->s->db_type();
VOID(pthread_mutex_lock(&LOCK_open));
/*
unlink_open_table() also tells threads waiting for refresh or close
that something has happened.
*/
- thd->open_tables= unlink_open_table(thd, thd->open_tables, table);
- quick_rm_table(table_type, db_name, table_name);
+ unlink_open_table(thd, table, FALSE);
+ quick_rm_table(table_type, db_name, table_name, 0);
VOID(pthread_mutex_unlock(&LOCK_open));
}
}
/*
- When we call the following function we must have a lock on
- LOCK_open ; This lock will be unlocked on return.
+ Wait for condition but allow the user to send a kill to mysqld
+
+ SYNOPSIS
+ wait_for_condition()
+ thd Thread handler
+ mutex mutex that is currently hold that is associated with condition
+ Will be unlocked on return
+ cond Condition to wait for
*/
-void wait_for_refresh(THD *thd)
+void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond)
{
- DBUG_ENTER("wait_for_refresh");
- safe_mutex_assert_owner(&LOCK_open);
-
/* Wait until the current table is up to date */
const char *proc_info;
- thd->mysys_var->current_mutex= &LOCK_open;
- thd->mysys_var->current_cond= &COND_refresh;
+ thd->mysys_var->current_mutex= mutex;
+ thd->mysys_var->current_cond= cond;
proc_info=thd->proc_info;
thd->proc_info="Waiting for table";
+ DBUG_ENTER("wait_for_condition");
if (!thd->killed)
- (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+ (void) pthread_cond_wait(cond, mutex);
- pthread_mutex_unlock(&LOCK_open); // Must be unlocked first
+ /*
+ We must unlock mutex first to avoid deadlock becasue conditions are
+ sent to this thread by doing locks in the following order:
+ lock(mysys_var->mutex)
+ lock(mysys_var->current_mutex)
+
+ One by effect of this that one can only use wait_for_condition with
+ condition variables that are guranteed to not disapper (freed) even if this
+ mutex is unlocked
+ */
+
+ pthread_mutex_unlock(mutex);
pthread_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
@@ -1229,10 +1954,7 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
{
TABLE *table= table_list->table;
TABLE_SHARE *share;
- char *db= table_list->db;
char *table_name= table_list->table_name;
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
TABLE orig_table;
DBUG_ENTER("reopen_name_locked_table");
@@ -1242,12 +1964,10 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
DBUG_RETURN(TRUE);
orig_table= *table;
- key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
- if (open_unireg_entry(thd, table, db, table_name, table_name, 0,
- thd->mem_root, 0) ||
- !(table->s->table_cache_key= memdup_root(&table->mem_root, (char*) key,
- key_length)))
+ if (open_unireg_entry(thd, table, table_list, table_name,
+ table->s->table_cache_key.str,
+ table->s->table_cache_key.length, thd->mem_root, 0))
{
intern_close_table(table);
/*
@@ -1261,8 +1981,6 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
}
share= table->s;
- share->db= share->table_cache_key;
- share->key_length=key_length;
/*
We want to prevent other connections from opening this table until end
of statement as it is likely that modifications of table's metadata are
@@ -1272,7 +1990,6 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
before we will get table-level lock on this table.
*/
share->version=0;
- share->flush_version=0;
table->in_use = thd;
check_unused();
@@ -1295,8 +2012,6 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
table->const_table=0;
table->null_row= table->maybe_null= table->force_index= 0;
table->status=STATUS_NO_RECORD;
- table->keys_in_use_for_query= share->keys_in_use;
- table->used_keys= share->keys_for_keyread;
DBUG_RETURN(FALSE);
}
@@ -1306,9 +2021,9 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
which will prevent its opening (or creation) (a.k.a lock
table name).
- @param thd Thread context
- @param key Table cache key for name to be locked
- @param key_length Table cache key length
+ @param thd Thread context
+ @param key Table cache key for name to be locked
+ @param key_length Table cache key length
@return Pointer to TABLE object used for name locking or 0 in
case of failure.
@@ -1318,6 +2033,7 @@ TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
uint key_length)
{
TABLE *table;
+ TABLE_SHARE *share;
char *key_buff;
DBUG_ENTER("table_cache_insert_placeholder");
@@ -1330,18 +2046,16 @@ TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
*/
if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
&table, sizeof(*table),
+ &share, sizeof(*share),
&key_buff, key_length,
NULL))
DBUG_RETURN(NULL);
- table->s= &table->share_not_to_be_used;
- memcpy(key_buff, key, key_length);
- table->s->table_cache_key= key_buff;
- table->s->db= table->s->table_cache_key;
- table->s->table_name= table->s->table_cache_key + strlen(table->s->db) + 1;
- table->s->key_length= key_length;
+ table->s= share;
+ share->set_table_cache_key(key_buff, key, key_length);
+ share->tmp_table= INTERNAL_TMP_TABLE; // for intern_close_table
table->in_use= thd;
- table->locked_by_name= 1;
+ table->locked_by_name=1;
if (my_hash_insert(&open_cache, (byte*)table))
{
@@ -1354,57 +2068,60 @@ TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
/**
- @brief Check if table cache contains an open placeholder for the
- table and if this placeholder was created by another thread.
-
- @param thd Thread context
- @param db Name of database for table in question
- @param table_name Table name
-
- @note The presence of open placeholder indicates that either some
- other thread is trying to create table in question and obtained
- an exclusive name-lock on it or that this table already exists
- and is being flushed at the moment.
-
- @note One should acquire LOCK_open mutex before calling this function.
-
- @note This function is a hack which was introduced in 5.0 only to
- minimize code changes. It doesn't present in 5.1.
-
- @retval TRUE Table cache contains open placeholder for the table
- which was created by some other thread.
- @retval FALSE Otherwise.
+ @brief Obtain an exclusive name lock on the table if it is not cached
+ in the table cache.
+
+ @param thd Thread context
+ @param db Name of database
+ @param table_name Name of table
+ @param[out] table Out parameter which is either:
+ - set to NULL if table cache contains record for
+ the table or
+ - set to point to the TABLE instance used for
+ name-locking.
+
+ @note This function takes into account all records for table in table
+ cache, even placeholders used for name-locking. This means that
+ 'table' parameter can be set to NULL for some situations when
+ table does not really exist.
+
+ @retval TRUE Error occured (OOM)
+ @retval FALSE Success. 'table' parameter set according to above rules.
*/
-bool table_cache_has_open_placeholder(THD *thd, const char *db,
- const char *table_name)
+bool lock_table_name_if_not_cached(THD *thd, const char *db,
+ const char *table_name, TABLE **table)
{
char key[MAX_DBKEY_LENGTH];
uint key_length;
- HASH_SEARCH_STATE state;
- TABLE *search;
- DBUG_ENTER("table_cache_has_open_placeholder");
+ DBUG_ENTER("lock_table_name_if_not_cached");
- safe_mutex_assert_owner(&LOCK_open);
+ key_length= (uint)(strmov(strmov(key, db) + 1, table_name) - key) + 1;
+ VOID(pthread_mutex_lock(&LOCK_open));
- key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
- for (search= (TABLE*) hash_first(&open_cache, (byte*) key, key_length,
- &state);
- search ;
- search= (TABLE*) hash_next(&open_cache, (byte*) key, key_length,
- &state))
+ if (hash_search(&open_cache, (byte *)key, key_length))
{
- if (search->in_use == thd)
- continue;
- if (search->open_placeholder)
- DBUG_RETURN(1);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_PRINT("info", ("Table is cached, name-lock is not obtained"));
+ *table= 0;
+ DBUG_RETURN(FALSE);
}
- DBUG_RETURN(0);
+ if (!(*table= table_cache_insert_placeholder(thd, key, key_length)))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(TRUE);
+ }
+ (*table)->open_placeholder= 1;
+ (*table)->next= thd->open_tables;
+ thd->open_tables= *table;
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(FALSE);
}
/**
- @brief Check that table exists on disk or in some storage engine.
+ @brief Check that table exists in table definition cache, on disk
+ or in some storage engine.
@param thd Thread context
@param table Table list element
@@ -1433,7 +2150,11 @@ bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists)
*exists= TRUE;
- build_table_path(path, sizeof(path), table->db, table->table_name, reg_ext);
+ if (get_cached_table_share(table->db, table->table_name))
+ DBUG_RETURN(FALSE);
+
+ build_table_filename(path, sizeof(path) - 1, table->db, table->table_name,
+ reg_ext, 0);
if (!access(path, F_OK))
DBUG_RETURN(FALSE);
@@ -1515,10 +2236,9 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (thd->killed)
DBUG_RETURN(0);
- key_length= (uint) (strmov(strmov(key, table_list->db)+1,
- table_list->table_name)-key)+1;
- int4store(key + key_length, thd->server_id);
- int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
+
+ 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
@@ -1531,8 +2251,9 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
{
for (table= thd->temporary_tables; table ; table=table->next)
{
- if (table->s->key_length == key_length + TMP_TABLE_KEY_EXTRA &&
- !memcmp(table->s->table_cache_key, key,
+ 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))
{
/*
@@ -1544,6 +2265,10 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (table->query_id == thd->query_id ||
thd->prelocked_mode && 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);
DBUG_RETURN(0);
}
@@ -1578,8 +2303,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
(int) TL_WRITE_ALLOW_WRITE);
for (table=thd->open_tables; table ; table=table->next)
{
- if (table->s->key_length == key_length &&
- !memcmp(table->s->table_cache_key, key, key_length))
+ if (table->s->table_cache_key.length == key_length &&
+ !memcmp(table->s->table_cache_key.str, key, key_length))
{
if (check_if_used && table->query_id &&
table->query_id != thd->query_id)
@@ -1591,7 +2316,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
is not already open by some calling stamement.
*/
my_error(ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG, MYF(0),
- table->s->table_name);
+ table->s->table_name.str);
DBUG_RETURN(0);
}
if (!my_strcasecmp(system_charset_info, table->alias, alias) &&
@@ -1645,10 +2370,9 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
*/
{
char path[FN_REFLEN];
- db_type not_used;
- strxnmov(path, FN_REFLEN, mysql_data_home, "/", table_list->db, "/",
- table_list->table_name, reg_ext, NullS);
- (void) unpack_filename(path, path);
+ enum legacy_db_type not_used;
+ build_table_filename(path, sizeof(path) - 1,
+ table_list->db, table_list->table_name, reg_ext, 0);
if (mysql_frm_type(thd, path, &not_used) == FRMTYPE_VIEW)
{
/*
@@ -1658,9 +2382,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
TABLE tab;
table= &tab;
VOID(pthread_mutex_lock(&LOCK_open));
- if (!open_unireg_entry(thd, table, table_list->db,
- table_list->table_name,
- alias, table_list, mem_root, 0))
+ if (!open_unireg_entry(thd, table, table_list, alias,
+ key, key_length, mem_root, 0))
{
DBUG_ASSERT(table_list->view != 0);
VOID(pthread_mutex_unlock(&LOCK_open));
@@ -1742,6 +2465,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
&state))
{
/*
+ Here we flush tables marked for flush. However we never flush log
+ tables here. They are flushed only on FLUSH LOGS.
Normally, table->s->version contains the value of
refresh_version from the moment when this table was
(re-)opened and added to the cache.
@@ -1758,11 +2483,12 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
c1: name lock t2; -- blocks
c2: open t1; -- blocks
*/
- if (table->needs_reopen_or_name_lock())
+ if (table->needs_reopen_or_name_lock() && !table->s->log_table)
{
DBUG_PRINT("note",
("Found table '%s.%s' with different refresh version",
table_list->db, table_list->table_name));
+
if (flags & MYSQL_LOCK_IGNORE_FLUSH)
{
/* Force close at once after usage */
@@ -1774,7 +2500,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (table->open_placeholder && table->in_use == thd)
{
VOID(pthread_mutex_unlock(&LOCK_open));
- my_error(ER_UPDATE_TABLE_USED, MYF(0), table->s->table_name);
+ my_error(ER_UPDATE_TABLE_USED, MYF(0), table->s->table_name.str);
DBUG_RETURN(0);
}
@@ -1805,8 +2531,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
*/
if (table->in_use != thd)
{
- wait_for_refresh(thd);
- /* wait_for_refresh will unlock LOCK_open for us */
+ /* wait_for_conditionwill unlock LOCK_open for us */
+ wait_for_condition(thd, &LOCK_open, &COND_refresh);
}
else
{
@@ -1837,7 +2563,6 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
else
{
/* Insert a new TABLE instance into the open cache */
- TABLE_SHARE *share;
int error;
/* Free cache if too big */
while (open_cache.records > table_cache_size && unused_tables)
@@ -1883,18 +2608,12 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(NULL);
}
- error= open_unireg_entry(thd, table, table_list->db,
- table_list->table_name,
- alias, table_list, mem_root,
- (flags & OPEN_VIEW_NO_PARSE));
- if ((error > 0) ||
- (!table_list->view && !error &&
- !(table->s->table_cache_key= memdup_root(&table->mem_root,
- (char*) key,
- key_length))))
+
+ error= open_unireg_entry(thd, table, table_list, alias, key, key_length,
+ mem_root, (flags & OPEN_VIEW_NO_PARSE));
+ if (error > 0)
{
- table->next=table->prev=table;
- free_cache_entry(table);
+ my_free((gptr)table, MYF(0));
VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(NULL);
}
@@ -1911,12 +2630,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(0); // VIEW
}
- share= table->s;
- share->db= share->table_cache_key;
- share->key_length= key_length;
- share->version= refresh_version;
- share->flush_version= flush_version;
- DBUG_PRINT("info", ("inserting table %p into the cache", table));
+ DBUG_PRINT("info", ("inserting table 0x%lx into the cache", (long) table));
VOID(my_hash_insert(&open_cache,(byte*) table));
}
@@ -1931,9 +2645,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->reginfo.lock_type=TL_READ; /* Assume read */
reset:
+ DBUG_ASSERT(table->s->ref_count > 0 || table->s->tmp_table != NO_TMP_TABLE);
+
if (thd->lex->need_correct_ident())
table->alias_name_used= my_strcasecmp(table_alias_charset,
- table->s->table_name, alias);
+ table->s->table_name.str, alias);
/* Fix alias if table name changes */
if (strcmp(table->alias, alias))
{
@@ -1948,9 +2664,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->const_table=0;
table->null_row= table->maybe_null= table->force_index= 0;
table->status=STATUS_NO_RECORD;
- table->keys_in_use_for_query= table->s->keys_in_use;
table->insert_values= 0;
- table->used_keys= table->s->keys_for_keyread;
table->fulltext_searched= 0;
table->file->ft_handler= 0;
/* Catch wrong handling of the auto_increment_field_not_null. */
@@ -1960,6 +2674,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->timestamp_field_type= table->timestamp_field->get_auto_set_type();
table->pos_in_table_list= table_list;
table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
+ table->clear_column_bitmaps();
DBUG_ASSERT(table->key_read == 0);
DBUG_RETURN(table);
}
@@ -1972,63 +2687,63 @@ TABLE *find_locked_table(THD *thd, const char *db,const char *table_name)
for (TABLE *table=thd->open_tables; table ; table=table->next)
{
- if (table->s->key_length == key_length &&
- !memcmp(table->s->table_cache_key,key,key_length))
+ if (table->s->table_cache_key.length == key_length &&
+ !memcmp(table->s->table_cache_key.str, key, key_length))
return table;
}
return(0);
}
-/****************************************************************************
- Reopen an table because the definition has changed. The date file for the
- table is already closed.
+/*
+ Reopen an table because the definition has changed.
SYNOPSIS
reopen_table()
- table Table to be opened
- locked 1 if we have already a lock on LOCK_open
+ table Table object
NOTES
- table->query_id will be 0 if table was reopened
+ The data file for the table is already closed and the share is released
+ The table has a 'dummy' share that mainly contains database and table name.
- RETURN
- 0 ok
- 1 error ('table' is unchanged if table couldn't be reopened)
-****************************************************************************/
+ RETURN
+ 0 ok
+ 1 error. The old table object is not changed.
+*/
-bool reopen_table(TABLE *table,bool locked)
+bool reopen_table(TABLE *table)
{
TABLE tmp;
- char *db= table->s->table_cache_key;
- const char *table_name= table->s->table_name;
bool error= 1;
Field **field;
uint key,part;
+ TABLE_LIST table_list;
+ THD *thd= table->in_use;
DBUG_ENTER("reopen_table");
+ DBUG_ASSERT(table->s->ref_count == 0);
+ DBUG_ASSERT(!table->sort.io_cache);
+
#ifdef EXTRA_DEBUG
if (table->db_stat)
sql_print_error("Table %s had a open data handler in reopen_table",
table->alias);
#endif
- if (!locked)
- VOID(pthread_mutex_lock(&LOCK_open));
- safe_mutex_assert_owner(&LOCK_open);
-
- if (open_unireg_entry(table->in_use, &tmp, db, table_name,
- table->alias, 0, table->in_use->mem_root, 0))
+ table_list.db= table->s->db.str;
+ table_list.table_name= table->s->table_name.str;
+ table_list.table= table;
+ table_list.belong_to_view= 0;
+ table_list.next_local= 0;
+
+ if (wait_for_locked_table_names(thd, &table_list))
+ DBUG_RETURN(1); // Thread was killed
+
+ if (open_unireg_entry(thd, &tmp, &table_list,
+ table->alias,
+ table->s->table_cache_key.str,
+ table->s->table_cache_key.length,
+ thd->mem_root, 0))
goto end;
- free_io_cache(table);
-
- if (!(tmp.s->table_cache_key= memdup_root(&tmp.mem_root,db,
- table->s->key_length)))
- {
- delete tmp.triggers;
- closefrm(&tmp); // End of memory
- goto end;
- }
- tmp.s->db= tmp.s->table_cache_key;
/* This list copies variables set by open_table */
tmp.tablenr= table->tablenr;
@@ -2037,15 +2752,12 @@ bool reopen_table(TABLE *table,bool locked)
tmp.null_row= table->null_row;
tmp.maybe_null= table->maybe_null;
tmp.status= table->status;
- tmp.keys_in_use_for_query= tmp.s->keys_in_use;
- tmp.used_keys= tmp.s->keys_for_keyread;
+
+ tmp.s->table_map_id= table->s->table_map_id;
/* Get state */
- tmp.s->key_length= table->s->key_length;
- tmp.in_use= table->in_use;
+ tmp.in_use= thd;
tmp.reginfo.lock_type=table->reginfo.lock_type;
- tmp.s->version= refresh_version;
- tmp.s->tmp_table= table->s->tmp_table;
tmp.grant= table->grant;
/* Replace table in open list */
@@ -2054,11 +2766,11 @@ bool reopen_table(TABLE *table,bool locked)
delete table->triggers;
if (table->file)
- VOID(closefrm(table)); // close file, free everything
+ VOID(closefrm(table, 1)); // close file, free everything
*table= tmp;
- table->s= &table->share_not_to_be_used;
- table->file->change_table_ptr(table);
+ table->default_column_bitmaps();
+ table->file->change_table_ptr(table, table->s);
DBUG_ASSERT(table->alias != 0);
for (field=table->field ; *field ; field++)
@@ -2078,32 +2790,59 @@ bool reopen_table(TABLE *table,bool locked)
error=0;
end:
- if (!locked)
- VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(error);
}
-/*
- Used with ALTER TABLE:
- Close all instanses of table when LOCK TABLES is in used;
- Close first all instances of table and then reopen them
- */
+/**
+ @brief Close all instances of a table open by this thread and replace
+ them with exclusive name-locks.
+
+ @param thd Thread context
+ @param db Database name for the table to be closed
+ @param table_name Name of the table to be closed
+
+ @note This function assumes that if we are not under LOCK TABLES,
+ then there is only one table open and locked. This means that
+ the function probably has to be adjusted before it can be used
+ anywhere outside ALTER TABLE.
+*/
-bool close_data_tables(THD *thd,const char *db, const char *table_name)
+void close_data_files_and_morph_locks(THD *thd, const char *db,
+ const char *table_name)
{
TABLE *table;
+ DBUG_ENTER("close_data_files_and_morph_locks");
+
+ safe_mutex_assert_owner(&LOCK_open);
+
+ if (thd->lock)
+ {
+ /*
+ If we are not under LOCK TABLES we should have only one table
+ open and locked so it makes sense to remove the lock at once.
+ */
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock= 0;
+ }
+
+ /*
+ Note that open table list may contain a name-lock placeholder
+ for target table name if we process ALTER TABLE ... RENAME.
+ So loop below makes sense even if we are not under LOCK TABLES.
+ */
for (table=thd->open_tables; table ; table=table->next)
{
- if (!strcmp(table->s->table_name, table_name) &&
- !strcmp(table->s->db, db))
+ if (!strcmp(table->s->table_name.str, table_name) &&
+ !strcmp(table->s->db.str, db))
{
- mysql_lock_remove(thd, thd->locked_tables,table);
- table->file->close();
- table->db_stat=0;
+ if (thd->locked_tables)
+ mysql_lock_remove(thd, thd->locked_tables, table);
+ table->open_placeholder= 1;
+ close_handle_and_leave_table_as_lock(table);
}
}
- return 0; // For the future
+ DBUG_VOID_RETURN;
}
@@ -2129,20 +2868,21 @@ bool close_data_tables(THD *thd,const char *db, const char *table_name)
bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
{
+ TABLE *table,*next,**prev;
+ TABLE **tables,**tables_ptr; // For locks
+ bool error=0, not_used;
DBUG_ENTER("reopen_tables");
- safe_mutex_assert_owner(&LOCK_open);
if (!thd->open_tables)
DBUG_RETURN(0);
- TABLE *table,*next,**prev;
- TABLE **tables,**tables_ptr; // For locks
- bool error=0, not_used;
+ safe_mutex_assert_owner(&LOCK_open);
if (get_locks)
{
/* The ptr is checked later */
uint opens=0;
- for (table=thd->open_tables; table ; table=table->next) opens++;
+ for (table= thd->open_tables; table ; table=table->next)
+ opens++;
tables= (TABLE**) my_alloca(sizeof(TABLE*)*opens);
}
else
@@ -2154,7 +2894,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
{
uint db_stat=table->db_stat;
next=table->next;
- if (!tables || (!db_stat && reopen_table(table,1)))
+ if (!tables || (!db_stat && reopen_table(table)))
{
my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
VOID(hash_delete(&open_cache,(byte*) table));
@@ -2213,40 +2953,37 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
bool send_refresh)
{
+ bool found= send_refresh;
DBUG_ENTER("close_old_data_files");
- bool found=send_refresh;
+
for (; table ; table=table->next)
{
- if (table->needs_reopen_or_name_lock())
+ /*
+ Reopen marked for flush. But close log tables. They are flushed only
+ explicitly on FLUSH LOGS
+ */
+ if (table->needs_reopen_or_name_lock() && !table->s->log_table)
{
found=1;
- /*
- Note that it is safe to update version even for open placeholders
- as later in this function we reset TABLE::open_placeholder and thus
- effectively remove them from the table cache.
- */
- if (!morph_locks) // If not from flush tables
- table->s->version= refresh_version; // Let other threads use table
if (table->db_stat)
{
- if (morph_locks)
- {
+ if (morph_locks)
+ {
/*
Wake up threads waiting for table-level lock on this table
so they won't sneak in when we will temporarily remove our
lock on it. This will also give them a chance to close their
instances of this table.
*/
- mysql_lock_abort(thd, table);
+ mysql_lock_abort(thd, table, TRUE);
mysql_lock_remove(thd, thd->locked_tables, table);
/*
We want to protect the table from concurrent DDL operations
(like RENAME TABLE) until we will re-open and re-lock it.
*/
- table->open_placeholder= 1;
- }
- table->file->close();
- table->db_stat=0;
+ table->open_placeholder= 1;
+ }
+ close_handle_and_leave_table_as_lock(table);
}
else if (table->open_placeholder)
{
@@ -2272,14 +3009,21 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
Wait until all threads has closed the tables in the list
We have also to wait if there is thread that has a lock on this table even
if the table is closed
+ NOTE: log tables are handled differently by the logging routines.
+ E.g. general_log is always opened and locked by the logger
+ and the table handler used by the logger, will be skipped by
+ this check.
*/
bool table_is_used(TABLE *table, bool wait_for_name_lock)
{
+ DBUG_ENTER("table_is_used");
do
{
- char *key= table->s->table_cache_key;
- uint key_length= table->s->key_length;
+ char *key= table->s->table_cache_key.str;
+ uint key_length= table->s->table_cache_key.length;
+
+ DBUG_PRINT("loop", ("table_name: %s", table->alias));
HASH_SEARCH_STATE state;
for (TABLE *search= (TABLE*) hash_first(&open_cache, (byte*) key,
key_length, &state);
@@ -2287,12 +3031,31 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock)
search= (TABLE*) hash_next(&open_cache, (byte*) key,
key_length, &state))
{
- if (search->locked_by_name && wait_for_name_lock ||
- search->is_name_opened() && search->needs_reopen_or_name_lock())
- return 1; // Table is used
+ DBUG_PRINT("info", ("share: 0x%lx locked_by_logger: %d "
+ "open_placeholder: %d locked_by_name: %d "
+ "db_stat: %u version: %lu",
+ (ulong) search->s, search->locked_by_logger,
+ search->open_placeholder, search->locked_by_name,
+ search->db_stat,
+ search->s->version));
+ if (search->in_use == table->in_use)
+ continue; // Name locked by this thread
+ /*
+ We can't use the table under any of the following conditions:
+ - There is an name lock on it (Table is to be deleted or altered)
+ - If we are in flush table and we didn't execute the flush
+ - If the table engine is open and it's an old version
+ (We must wait until all engines are shut down to use the table)
+ However we fo not wait if we encountered a table, locked by the logger.
+ Log tables are managed separately by logging routines.
+ */
+ if (!search->locked_by_logger &&
+ (search->locked_by_name && wait_for_name_lock ||
+ (search->is_name_opened() && search->needs_reopen_or_name_lock())))
+ DBUG_RETURN(1);
}
} while ((table=table->next));
- return 0;
+ DBUG_RETURN(0);
}
@@ -2329,22 +3092,58 @@ bool wait_for_tables(THD *thd)
}
-/* drop tables from locked list */
+/*
+ drop tables from locked list
+
+ SYNOPSIS
+ drop_locked_tables()
+ thd Thread thandler
+ db Database
+ table_name Table name
+
+ INFORMATION
+ This is only called on drop tables
-bool drop_locked_tables(THD *thd,const char *db, const char *table_name)
+ The TABLE object for the dropped table is unlocked but still kept around
+ as a name lock, which means that the table will be available for other
+ thread as soon as we call unlock_table_names().
+ If there is multiple copies of the table locked, all copies except
+ the first, which acts as a name lock, is removed.
+
+ RETURN
+ # If table existed, return table
+ 0 Table was not locked
+*/
+
+
+TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name)
{
- TABLE *table,*next,**prev;
- bool found=0;
+ TABLE *table,*next,**prev, *found= 0;
prev= &thd->open_tables;
+ DBUG_ENTER("drop_locked_tables");
+
for (table= thd->open_tables; table ; table=next)
{
next=table->next;
- if (!strcmp(table->s->table_name, table_name) &&
- !strcmp(table->s->db, db))
+ if (!strcmp(table->s->table_name.str, table_name) &&
+ !strcmp(table->s->db.str, db))
{
mysql_lock_remove(thd, thd->locked_tables,table);
- VOID(hash_delete(&open_cache,(byte*) table));
- found=1;
+ if (!found)
+ {
+ found= table;
+ /* Close engine table, but keep object around as a name lock */
+ if (table->db_stat)
+ {
+ table->db_stat= 0;
+ table->file->close();
+ }
+ }
+ else
+ {
+ /* We already have a name lock, remove copy */
+ VOID(hash_delete(&open_cache,(byte*) table));
+ }
}
else
{
@@ -2360,7 +3159,7 @@ bool drop_locked_tables(THD *thd,const char *db, const char *table_name)
my_free((gptr) thd->locked_tables,MYF(0));
thd->locked_tables=0;
}
- return found;
+ DBUG_RETURN(found);
}
@@ -2375,10 +3174,10 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name)
TABLE *table;
for (table= thd->open_tables; table ; table= table->next)
{
- if (!strcmp(table->s->table_name,table_name) &&
- !strcmp(table->s->db, db))
+ if (!strcmp(table->s->table_name.str, table_name) &&
+ !strcmp(table->s->db.str, db))
{
- mysql_lock_abort(thd,table);
+ mysql_lock_abort(thd,table, TRUE);
break;
}
}
@@ -2386,146 +3185,216 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name)
/*
+ 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
+ shares open at the same time).
+
+ share->table_map_id is not ~0UL.
+ */
+void assign_new_table_id(TABLE_SHARE *share)
+{
+ static ulong last_table_id= ~0UL;
+
+ DBUG_ENTER("assign_new_table_id");
+
+ /* Preconditions */
+ DBUG_ASSERT(share != NULL);
+ safe_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;
+}
+
+/*
Load a table definition from file and open unireg table
SYNOPSIS
open_unireg_entry()
thd Thread handle
entry Store open table definition here
- db Database name
- name Table name
+ table_list TABLE_LIST with db, table_name & belong_to_view
alias Alias name
- table_desc TABLE_LIST descriptor (used with views)
+ cache_key Key for share_cache
+ cache_key_length length of cache_key
mem_root temporary mem_root for parsing
flags the OPEN_VIEW_NO_PARSE flag to be passed to
openfrm()/open_new_frm()
NOTES
Extra argument for open is taken from thd->open_options
+ One must have a lock on LOCK_open when calling this function
RETURN
0 ok
# Error
*/
-static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
- const char *name, const char *alias,
- TABLE_LIST *table_desc, MEM_ROOT *mem_root,
- uint flags)
+
+static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list,
+ const char *alias,
+ char *cache_key, uint cache_key_length,
+ MEM_ROOT *mem_root, uint flags)
{
- char path[FN_REFLEN];
int error;
+ TABLE_SHARE *share;
uint discover_retry_count= 0;
DBUG_ENTER("open_unireg_entry");
- strxmov(path, mysql_data_home, "/", db, "/", name, NullS);
- while ((error= openfrm(thd, path, alias,
- (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
- HA_GET_INDEX | HA_TRY_READ_ONLY |
- NO_ERR_ON_NEW_FRM),
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
- (flags & OPEN_VIEW_NO_PARSE),
- thd->open_options, entry)) &&
- (error != 5 ||
- (fn_format(path, path, 0, reg_ext, MY_UNPACK_FILENAME),
- open_new_frm(thd, path, alias, db, name,
- (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
- HA_GET_INDEX | HA_TRY_READ_ONLY),
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
- (flags & OPEN_VIEW_NO_PARSE),
- thd->open_options, entry, table_desc, mem_root))))
+ safe_mutex_assert_owner(&LOCK_open);
+
+retry:
+ if (!(share= get_table_share_with_create(thd, table_list, cache_key,
+ cache_key_length,
+ OPEN_VIEW, &error)))
+ DBUG_RETURN(1);
+ if (share->is_view)
{
- if (!entry->s || !entry->s->crashed)
+ /* Open view */
+ error= (int) open_new_frm(thd, share, alias,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+ HA_GET_INDEX | HA_TRY_READ_ONLY),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
+ (flags & OPEN_VIEW_NO_PARSE),
+ thd->open_options, entry, table_list,
+ mem_root);
+ if (error)
+ goto err;
+ /* TODO: Don't free this */
+ release_table_share(share, RELEASE_NORMAL);
+ DBUG_RETURN((flags & OPEN_VIEW_NO_PARSE)? -1 : 0);
+ }
+
+ while ((error= open_table_from_share(thd, share, alias,
+ (uint) (HA_OPEN_KEYFILE |
+ HA_OPEN_RNDFILE |
+ HA_GET_INDEX |
+ HA_TRY_READ_ONLY),
+ (READ_KEYINFO | COMPUTE_TYPES |
+ EXTRA_RECORD),
+ thd->open_options, entry, FALSE)))
+ {
+ if (error == 7) // Table def changed
{
+ share->version= 0; // Mark share as old
+ if (discover_retry_count++) // Retry once
+ goto err;
+
/*
- Frm file could not be found on disk
- Since it does not exist, no one can be using it
- LOCK_open has been locked to protect from someone else
- trying to discover the table at the same time.
+ TODO:
+ Here we should wait until all threads has released the table.
+ For now we do one retry. This may cause a deadlock if there
+ is other threads waiting for other tables used by this thread.
+
+ Proper fix would be to if the second retry failed:
+ - Mark that table def changed
+ - Return from open table
+ - Close all tables used by this thread
+ - Start waiting that the share is released
+ - Retry by opening all tables again
*/
- if (discover_retry_count++ != 0)
+ if (ha_create_table_from_engine(thd, table_list->db,
+ table_list->table_name))
goto err;
- if (ha_create_table_from_engine(thd, db, name) > 0)
- {
- /* Give right error message */
- thd->clear_error();
- DBUG_PRINT("error", ("Discovery of %s/%s failed", db, name));
- my_printf_error(ER_UNKNOWN_ERROR,
- "Failed to open '%-.64s', error while "
- "unpacking from engine",
- MYF(0), name);
-
+ /*
+ TO BE FIXED
+ To avoid deadlock, only wait for release if no one else is
+ using the share.
+ */
+ if (share->ref_count != 1)
goto err;
- }
-
- mysql_reset_errors(thd, 1); // Clear warnings
- thd->clear_error(); // Clear error message
- continue;
- }
-
- // Code below is for repairing a crashed file
- TABLE_LIST table_list;
- bzero((char*) &table_list, sizeof(table_list)); // just for safe
- table_list.db=(char*) db;
- table_list.table_name=(char*) name;
-
- safe_mutex_assert_owner(&LOCK_open);
-
- if ((error=lock_table_name(thd,&table_list)))
- {
- if (error < 0)
+ /* Free share and wait until it's released by all threads */
+ release_table_share(share, RELEASE_WAIT_FOR_DROP);
+ if (!thd->killed)
{
- goto err;
+ mysql_reset_errors(thd, 1); // Clear warnings
+ thd->clear_error(); // Clear error message
+ goto retry;
}
- if (wait_for_locked_table_names(thd,&table_list))
- {
- unlock_table_name(thd,&table_list);
- goto err;
- }
- }
- pthread_mutex_unlock(&LOCK_open);
- thd->clear_error(); // Clear error message
- error= 0;
- if (openfrm(thd, path, alias,
- (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX |
- HA_TRY_READ_ONLY),
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
- ha_open_options | HA_OPEN_FOR_REPAIR,
- entry) || ! entry->file ||
- (entry->file->is_crashed() && entry->file->check_and_repair(thd)))
- {
- /* Give right error message */
- thd->clear_error();
- my_error(ER_NOT_KEYFILE, MYF(0), name, my_errno);
- sql_print_error("Couldn't repair table: %s.%s",db,name);
- if (entry->file)
- closefrm(entry);
- error=1;
+ DBUG_RETURN(1);
}
- else
- thd->clear_error(); // Clear error message
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd,&table_list);
-
- if (error)
+ if (!entry->s || !entry->s->crashed)
goto err;
- break;
- }
-
- if (error == 5)
- DBUG_RETURN((flags & OPEN_VIEW_NO_PARSE)? -1 : 0); // we have just opened VIEW
-
- /*
- We can't mark all tables in 'mysql' database as system since we don't
- allow to lock such tables for writing with any other tables (even with
- other system tables) and some privilege tables need this.
- */
- if (!my_strcasecmp(system_charset_info, db, "mysql") &&
- !my_strcasecmp(system_charset_info, name, "proc"))
- entry->s->system_table= 1;
+ // Code below is for repairing a crashed file
+ if ((error= lock_table_name(thd, table_list, TRUE)))
+ {
+ if (error < 0)
+ goto err;
+ if (wait_for_locked_table_names(thd, table_list))
+ {
+ unlock_table_name(thd, table_list);
+ goto err;
+ }
+ }
+ pthread_mutex_unlock(&LOCK_open);
+ thd->clear_error(); // Clear error message
+ error= 0;
+ if (open_table_from_share(thd, share, alias,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+ HA_GET_INDEX |
+ HA_TRY_READ_ONLY),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+ ha_open_options | HA_OPEN_FOR_REPAIR,
+ entry, FALSE) || ! entry->file ||
+ (entry->file->is_crashed() && entry->file->check_and_repair(thd)))
+ {
+ /* Give right error message */
+ thd->clear_error();
+ my_error(ER_NOT_KEYFILE, MYF(0), share->table_name.str, my_errno);
+ sql_print_error("Couldn't repair table: %s.%s", share->db.str,
+ share->table_name.str);
+ if (entry->file)
+ closefrm(entry, 0);
+ error=1;
+ }
+ else
+ thd->clear_error(); // Clear error message
+ pthread_mutex_lock(&LOCK_open);
+ unlock_table_name(thd, table_list);
+
+ if (error)
+ goto err;
+ break;
+ }
- if (Table_triggers_list::check_n_load(thd, db, name, entry, 0))
+ if (Table_triggers_list::check_n_load(thd, share->db.str,
+ share->table_name.str, entry, 0))
+ {
+ closefrm(entry, 0);
goto err;
+ }
/*
If we are here, there was no fatal error (but error may be still
@@ -2537,13 +3406,14 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
if (mysql_bin_log.is_open())
{
char *query, *end;
- uint query_buf_size= 20 + 2*NAME_LEN + 1;
- if ((query= (char*)my_malloc(query_buf_size,MYF(MY_WME))))
+ uint query_buf_size= 20 + share->db.length + share->table_name.length +1;
+ if ((query= (char*) my_malloc(query_buf_size,MYF(MY_WME))))
{
+ /* this DELETE FROM is needed even with row-based binlogging */
end = strxmov(strmov(query, "DELETE FROM `"),
- db,"`.`",name,"`", NullS);
- Query_log_event qinfo(thd, query, (ulong)(end-query), 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ share->db.str,"`.`",share->table_name.str,"`", NullS);
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ query, (ulong)(end-query), FALSE, FALSE);
my_free(query, MYF(0));
}
else
@@ -2553,25 +3423,19 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
DBA on top of warning the client (which will automatically be done
because of MYF(MY_WME) in my_malloc() above).
*/
- sql_print_error("When opening HEAP table, could not allocate \
-memory to write 'DELETE FROM `%s`.`%s`' to the binary log",db,name);
+ sql_print_error("When opening HEAP table, could not allocate memory "
+ "to write 'DELETE FROM `%s`.`%s`' to the binary log",
+ table_list->db, table_list->table_name);
delete entry->triggers;
- if (entry->file)
- closefrm(entry);
+ closefrm(entry, 0);
goto err;
}
}
}
DBUG_RETURN(0);
+
err:
- /* Hide "Table doesn't exist" errors if table belong to view */
- if (thd->net.last_errno == ER_NO_SUCH_TABLE &&
- table_desc && table_desc->belong_to_view)
- {
- TABLE_LIST *view= table_desc->belong_to_view;
- thd->clear_error();
- my_error(ER_VIEW_INVALID, MYF(0), view->view_db.str, view->view_name.str);
- }
+ release_table_share(share, RELEASE_NORMAL);
DBUG_RETURN(1);
}
@@ -2632,25 +3496,18 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
statement for which table list for prelocking is already built, let
us cache routines and try to build such table list.
- NOTE: We will mark statement as requiring prelocking only if we will
- have non empty table list. But this does not guarantee that in prelocked
- mode we will have some locked tables, because queries which use only
- derived/information schema tables and views possible. Thus "counter"
- may be still zero for prelocked statement...
*/
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
thd->lex->uses_stored_routines())
{
- bool first_no_prelocking, need_prelocking, tabs_changed;
+ bool first_no_prelocking, need_prelocking;
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
DBUG_ASSERT(thd->lex->query_tables == *start);
sp_get_prelocking_info(thd, &need_prelocking, &first_no_prelocking);
- if (sp_cache_routines_and_add_tables(thd, thd->lex,
- first_no_prelocking,
- &tabs_changed))
+ if (sp_cache_routines_and_add_tables(thd, thd->lex, first_no_prelocking))
{
/*
Serious error during reading stored routines from mysql.proc table.
@@ -2660,7 +3517,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
result= -1;
goto err;
}
- else if ((tabs_changed || *start) && need_prelocking)
+ else if (need_prelocking)
{
query_tables_last_own= save_query_tables_last;
*start= thd->lex->query_tables;
@@ -2931,13 +3788,13 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
if (table)
{
-#if defined( __WIN__) || defined(OS2)
+#if defined( __WIN__)
/* Win32 can't drop a file that is open */
if (lock_type == TL_WRITE_ALLOW_READ)
{
lock_type= TL_WRITE;
}
-#endif /* __WIN__ || OS2 */
+#endif /* __WIN__ */
table_list->lock_type= lock_type;
table_list->table= table;
table->grant= table_list->grant;
@@ -3129,15 +3986,16 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
in prelocked mode.
*/
DBUG_ASSERT(!thd->prelocked_mode || !thd->lex->requires_prelocking());
- /*
- If statement requires prelocking then it has non-empty table list.
- So it is safe to shortcut.
- */
- DBUG_ASSERT(!thd->lex->requires_prelocking() || tables);
*need_reopen= FALSE;
- if (!tables)
+ /*
+ CREATE ... SELECT UUID() locks no tables, we have to test here.
+ */
+ if (thd->lex->binlog_row_based_if_mixed)
+ thd->set_current_stmt_binlog_row_based_if_mixed();
+
+ if (!tables && !thd->lex->requires_prelocking())
DBUG_RETURN(0);
/*
@@ -3167,6 +4025,17 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
{
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
+ /*
+ If we have >= 2 different tables to update with auto_inc columns,
+ statement-based binlogging won't work. We can solve this problem in
+ mixed mode by switching to row-based binlogging:
+ */
+ if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
+ has_two_write_locked_tables_with_auto_increment(tables))
+ {
+ thd->lex->binlog_row_based_if_mixed= TRUE;
+ thd->set_current_stmt_binlog_row_based_if_mixed();
+ }
}
if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
@@ -3280,8 +4149,22 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables)
/*
Open a single table without table caching and don't set it in open_list
- Used by alter_table to open a temporary table and when creating
- a temporary table with CREATE TEMPORARY ...
+
+ SYNPOSIS
+ open_temporary_table()
+ thd Thread object
+ path Path (without .frm)
+ db database
+ table_name Table name
+ link_in_list 1 if table should be linked into thd->temporary_tables
+
+ NOTES:
+ Used by alter_table to open a temporary table and when creating
+ a temporary table with CREATE TEMPORARY ...
+
+ RETURN
+ 0 Error
+ # TABLE object
*/
TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
@@ -3289,51 +4172,60 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
{
TABLE *tmp_table;
TABLE_SHARE *share;
+ char cache_key[MAX_DBKEY_LENGTH], *saved_cache_key, *tmp_path;
+ uint key_length;
+ TABLE_LIST table_list;
DBUG_ENTER("open_temporary_table");
-
- /*
- The extra size in my_malloc() is for table_cache_key
- 4 bytes for master thread id if we are in the slave
- 1 byte to terminate db
- 1 byte to terminate table_name
- total of 6 extra bytes in my_malloc in addition to table/db stuff
- */
- if (!(tmp_table=(TABLE*) my_malloc(sizeof(*tmp_table)+(uint) strlen(db)+
- (uint) strlen(table_name)+6+4,
- MYF(MY_WME))))
+ DBUG_PRINT("enter",
+ ("table: '%s'.'%s' path: '%s' server_id: %u "
+ "pseudo_thread_id: %lu",
+ db, table_name, path,
+ (uint) thd->server_id, (ulong) thd->variables.pseudo_thread_id));
+
+ 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);
+
+ if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table) + sizeof(*share) +
+ strlen(path)+1 + key_length,
+ MYF(MY_WME))))
DBUG_RETURN(0); /* purecov: inspected */
- if (openfrm(thd, path, table_name,
- (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX),
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
- ha_open_options,
- tmp_table))
+ share= (TABLE_SHARE*) (tmp_table+1);
+ tmp_path= (char*) (share+1);
+ saved_cache_key= strmov(tmp_path, path)+1;
+ memcpy(saved_cache_key, cache_key, key_length);
+
+ init_tmp_table_share(share, saved_cache_key, key_length,
+ strend(saved_cache_key)+1, tmp_path);
+
+ if (open_table_def(thd, share, 0) ||
+ open_table_from_share(thd, share, table_name,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+ HA_GET_INDEX),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+ ha_open_options,
+ tmp_table, FALSE))
{
+ /* No need to lock share->mutex as this is not needed for tmp tables */
+ free_table_share(share);
my_free((char*) tmp_table,MYF(0));
DBUG_RETURN(0);
}
- share= tmp_table->s;
- tmp_table->reginfo.lock_type=TL_WRITE; // Simulate locked
+ tmp_table->reginfo.lock_type= TL_WRITE; // Simulate locked
share->tmp_table= (tmp_table->file->has_transactions() ?
TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
- share->table_cache_key= (char*) (tmp_table+1);
- share->db= share->table_cache_key;
- share->key_length= (uint) (strmov(((char*) (share->table_name=
- strmov(share->table_cache_key,
- db)+1)),
- table_name) -
- share->table_cache_key) +1;
- int4store(share->table_cache_key + share->key_length, thd->server_id);
- share->key_length+= 4;
- int4store(share->table_cache_key + share->key_length,
- thd->variables.pseudo_thread_id);
- share->key_length+= 4;
if (link_in_list)
{
- tmp_table->next=thd->temporary_tables;
- thd->temporary_tables=tmp_table;
+ /* growing temp list at the head */
+ tmp_table->next= thd->temporary_tables;
+ if (tmp_table->next)
+ tmp_table->next->prev= tmp_table;
+ thd->temporary_tables= tmp_table;
+ thd->temporary_tables->prev= 0;
if (thd->slave_thread)
slave_open_temp_tables++;
}
@@ -3342,21 +4234,22 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
}
-bool rm_temporary_table(enum db_type base, char *path)
+bool rm_temporary_table(handlerton *base, char *path)
{
bool error=0;
+ handler *file;
+ char *ext;
DBUG_ENTER("rm_temporary_table");
- fn_format(path, path,"",reg_ext,4);
- unpack_filename(path,path);
+ strmov(ext= strend(path), reg_ext);
if (my_delete(path,MYF(0)))
error=1; /* purecov: inspected */
- *fn_ext(path)='\0'; // remove extension
- handler *file= get_new_handler((TABLE*) 0, current_thd->mem_root, base);
+ *ext= 0; // remove extension
+ file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base);
if (file && file->delete_table(path))
{
error=1;
- sql_print_warning("Could not remove tmp table: '%s', error: %d",
+ sql_print_warning("Could not remove temporary table: '%s', error: %d",
path, my_errno);
}
delete file;
@@ -3381,17 +4274,50 @@ Field *view_ref_found= (Field*) 0x2;
static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
{
- if (thd->set_query_id)
+ DBUG_ENTER("update_field_dependencies");
+ if (thd->mark_used_columns != MARK_COLUMNS_NONE)
{
- if (field->query_id != thd->query_id)
+ MY_BITMAP *current_bitmap, *other_bitmap;
+
+ /*
+ We always want to register the used keys, as the column bitmap may have
+ been set for all fields (for example for view).
+ */
+
+ table->covering_keys.intersect(field->part_of_key);
+ table->merge_keys.merge(field->part_of_key);
+
+ if (thd->mark_used_columns == MARK_COLUMNS_READ)
{
- field->query_id= thd->query_id;
- table->used_fields++;
- table->used_keys.intersect(field->part_of_key);
+ current_bitmap= table->read_set;
+ other_bitmap= table->write_set;
}
else
- thd->dupp_field= field;
+ {
+ current_bitmap= table->write_set;
+ other_bitmap= table->read_set;
+ }
+
+ if (bitmap_fast_test_and_set(current_bitmap, field->field_index))
+ {
+ if (thd->mark_used_columns == MARK_COLUMNS_WRITE)
+ {
+ DBUG_PRINT("warning", ("Found duplicated field"));
+ thd->dup_field= field;
+ }
+ else
+ {
+ DBUG_PRINT("note", ("Field found before"));
+ }
+ DBUG_VOID_RETURN;
+ }
+ if (table->get_fields_in_item_tree)
+ field->flags|= GET_FIXED_FIELDS_FLAG;
+ table->used_fields++;
}
+ else if (table->get_fields_in_item_tree)
+ field->flags|= GET_FIXED_FIELDS_FLAG;
+ DBUG_VOID_RETURN;
}
@@ -3429,6 +4355,7 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list,
Field_iterator_view field_it;
field_it.set(table_list);
Query_arena *arena, backup;
+ LINT_INIT(arena);
DBUG_ASSERT(table_list->schema_table_reformed ||
(ref != 0 && table_list->view != 0));
@@ -3518,6 +4445,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
DBUG_ASSERT(table_ref->is_natural_join && table_ref->join_columns);
DBUG_ASSERT(*actual_table == NULL);
+ LINT_INIT(arena);
LINT_INIT(found_field);
for (nj_col= NULL, curr_nj_col= field_it++; curr_nj_col;
@@ -3629,8 +4557,18 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
table->field[cached_field_index]->field_name, name))
field_ptr= table->field + cached_field_index;
else if (table->s->name_hash.records)
+ {
field_ptr= (Field**) hash_search(&table->s->name_hash, (byte*) name,
length);
+ if (field_ptr)
+ {
+ /*
+ field_ptr points to field in TABLE_SHARE. Convert it to the matching
+ field in table
+ */
+ field_ptr= (table->field + (field_ptr - table->s->field));
+ }
+ }
else
{
if (!(field_ptr= table->field))
@@ -3649,8 +4587,9 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
{
if (!allow_rowid ||
my_strcasecmp(system_charset_info, name, "_rowid") ||
- !(field=table->rowid_field))
+ table->s->rowid_field_offset == 0)
DBUG_RETURN((Field*) 0);
+ field= table->field[table->s->rowid_field_offset-1];
}
update_field_dependencies(thd, field, table);
@@ -3712,6 +4651,9 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
{
Field *fld;
DBUG_ENTER("find_field_in_table_ref");
+ DBUG_ASSERT(table_list->alias);
+ DBUG_ASSERT(name);
+ DBUG_ASSERT(item_name);
DBUG_PRINT("enter",
("table: '%s' field name: '%s' item name: '%s' ref 0x%lx",
table_list->alias, name, item_name, (ulong) ref));
@@ -3799,18 +4741,97 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
register_tree_change, actual_table);
}
+ if (fld)
+ {
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- /* Check if there are sufficient access rights to the found field. */
- if (fld && check_privileges &&
- check_column_grant_in_table_ref(thd, *actual_table, name, length))
- fld= WRONG_GRANT;
+ /* Check if there are sufficient access rights to the found field. */
+ if (check_privileges &&
+ check_column_grant_in_table_ref(thd, *actual_table, name, length))
+ fld= WRONG_GRANT;
+ else
#endif
-
+ if (thd->mark_used_columns != MARK_COLUMNS_NONE)
+ {
+ /*
+ Get rw_set correct for this field so that the handler
+ knows that this field is involved in the query and gets
+ retrieved/updated
+ */
+ Field *field_to_set= NULL;
+ if (fld == view_ref_found)
+ {
+ Item *it= (*ref)->real_item();
+ if (it->type() == Item::FIELD_ITEM)
+ field_to_set= ((Item_field*)it)->field;
+ else
+ {
+ if (thd->mark_used_columns == MARK_COLUMNS_READ)
+ it->walk(&Item::register_field_in_read_map, 1, (byte *) 0);
+ }
+ }
+ else
+ field_to_set= fld;
+ if (field_to_set)
+ {
+ TABLE *table= field_to_set->table;
+ if (thd->mark_used_columns == MARK_COLUMNS_READ)
+ bitmap_set_bit(table->read_set, field_to_set->field_index);
+ else
+ bitmap_set_bit(table->write_set, field_to_set->field_index);
+ }
+ }
+ }
DBUG_RETURN(fld);
}
/*
+ Find field in table, no side effects, only purpose is to check for field
+ in table object and get reference to the field if found.
+
+ SYNOPSIS
+ find_field_in_table_sef()
+
+ table table where to find
+ name Name of field searched for
+
+ RETURN
+ 0 field is not found
+ # pointer to field
+*/
+
+Field *find_field_in_table_sef(TABLE *table, const char *name)
+{
+ Field **field_ptr;
+ if (table->s->name_hash.records)
+ {
+ field_ptr= (Field**)hash_search(&table->s->name_hash,(byte*) name,
+ strlen(name));
+ if (field_ptr)
+ {
+ /*
+ field_ptr points to field in TABLE_SHARE. Convert it to the matching
+ field in table
+ */
+ field_ptr= (table->field + (field_ptr - table->s->field));
+ }
+ }
+ else
+ {
+ if (!(field_ptr= table->field))
+ return (Field *)0;
+ for (; *field_ptr; ++field_ptr)
+ if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name))
+ break;
+ }
+ if (field_ptr)
+ return *field_ptr;
+ else
+ return (Field *)0;
+}
+
+
+/*
Find field in table list.
SYNOPSIS
@@ -3963,8 +4984,8 @@ find_field_in_tables(THD *thd, Item_ident *item,
{
Field *nf=new Field_null(NULL,0,Field::NONE,
cur_field->field_name,
- cur_field->table,
&my_charset_bin);
+ nf->init(cur_table->table);
cur_field= nf;
}
}
@@ -4521,15 +5542,19 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
if (field_1)
{
+ TABLE *table_1= nj_col_1->table_ref->table;
/* Mark field_1 used for table cache. */
- field_1->query_id= thd->query_id;
- nj_col_1->table_ref->table->used_keys.intersect(field_1->part_of_key);
+ bitmap_set_bit(table_1->read_set, field_1->field_index);
+ table_1->covering_keys.intersect(field_1->part_of_key);
+ table_1->merge_keys.merge(field_1->part_of_key);
}
if (field_2)
{
+ TABLE *table_2= nj_col_2->table_ref->table;
/* Mark field_2 used for table cache. */
- field_2->query_id= thd->query_id;
- nj_col_2->table_ref->table->used_keys.intersect(field_2->part_of_key);
+ bitmap_set_bit(table_2->read_set, field_2->field_index);
+ table_2->covering_keys.intersect(field_2->part_of_key);
+ table_2->merge_keys.merge(field_2->part_of_key);
}
if (using_fields != NULL)
@@ -5014,17 +6039,18 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
****************************************************************************/
bool setup_fields(THD *thd, Item **ref_pointer_array,
- List<Item> &fields, bool set_query_id,
+ List<Item> &fields, enum_mark_columns mark_used_columns,
List<Item> *sum_func_list, bool allow_sum_func)
{
reg2 Item *item;
- bool save_set_query_id= thd->set_query_id;
+ enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
List_iterator<Item> it(fields);
bool save_is_item_list_lookup;
DBUG_ENTER("setup_fields");
- thd->set_query_id=set_query_id;
+ thd->mark_used_columns= mark_used_columns;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
if (allow_sum_func)
thd->lex->allow_sum_func|= 1 << thd->lex->current_select->nest_level;
thd->where= THD::DEFAULT_WHERE;
@@ -5054,7 +6080,8 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
{
thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup;
thd->lex->allow_sum_func= save_allow_sum_func;
- thd->set_query_id= save_set_query_id;
+ thd->mark_used_columns= save_mark_used_columns;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
DBUG_RETURN(TRUE); /* purecov: inspected */
}
if (ref)
@@ -5069,7 +6096,8 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS;
thd->lex->allow_sum_func= save_allow_sum_func;
- thd->set_query_id= save_set_query_id;
+ thd->mark_used_columns= save_mark_used_columns;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
DBUG_RETURN(test(thd->net.report_error));
}
@@ -5113,7 +6141,6 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables)
context name resolution contest to setup table list there
from_clause Top-level list of table references in the FROM clause
tables Table list (select_lex->table_list)
- conds Condition of current SELECT (can be changed by VIEW)
leaves List of join table leaves list (select_lex->leaf_tables)
refresh It is onle refresh for subquery
select_insert It is SELECT ... INSERT command
@@ -5135,7 +6162,7 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables)
bool setup_tables(THD *thd, Name_resolution_context *context,
List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
- Item **conds, TABLE_LIST **leaves, bool select_insert)
+ TABLE_LIST **leaves, bool select_insert)
{
uint tablenr= 0;
DBUG_ENTER("setup_tables");
@@ -5167,24 +6194,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
tablenr= 0;
}
setup_table_map(table, table_list, tablenr);
- table->used_keys= table->s->keys_for_keyread;
- if (table_list->use_index)
- {
- key_map map;
- get_key_map_from_key_list(&map, table, table_list->use_index);
- if (map.is_set_all())
- DBUG_RETURN(1);
- table->keys_in_use_for_query=map;
- }
- if (table_list->ignore_index)
- {
- key_map map;
- get_key_map_from_key_list(&map, table, table_list->ignore_index);
- if (map.is_set_all())
- DBUG_RETURN(1);
- table->keys_in_use_for_query.subtract(map);
- }
- table->used_keys.intersect(table->keys_in_use_for_query);
+ if (table_list->process_index_hints(table))
+ DBUG_RETURN(1);
}
if (tablenr > MAX_TABLES)
{
@@ -5248,31 +6259,31 @@ bool setup_tables_and_check_access(THD *thd,
Name_resolution_context *context,
List<TABLE_LIST> *from_clause,
TABLE_LIST *tables,
- Item **conds, TABLE_LIST **leaves,
+ TABLE_LIST **leaves,
bool select_insert,
ulong want_access_first,
ulong want_access)
{
- TABLE_LIST *leaves_tmp = NULL;
+ TABLE_LIST *leaves_tmp= NULL;
bool first_table= true;
- if (setup_tables (thd, context, from_clause, tables, conds,
- &leaves_tmp, select_insert))
+ if (setup_tables(thd, context, from_clause, tables,
+ &leaves_tmp, select_insert))
return TRUE;
if (leaves)
- *leaves = leaves_tmp;
+ *leaves= leaves_tmp;
for (; leaves_tmp; leaves_tmp= leaves_tmp->next_leaf)
{
if (leaves_tmp->belong_to_view &&
check_single_table_access(thd, first_table ? want_access_first :
- want_access, leaves_tmp))
+ want_access, leaves_tmp, FALSE))
{
tables->hide_view_error(thd);
return TRUE;
}
- first_table= false;
+ first_table= 0;
}
return FALSE;
}
@@ -5398,7 +6409,6 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
}
#endif
-
/*
Update the tables used in the query based on the referenced fields. For
views and natural joins this update is performed inside the loop below.
@@ -5464,17 +6474,13 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
if ((field= field_iterator.field()))
{
- /*
- Mark if field used before in this select.
- Used by 'insert' to verify if a field name is used twice.
- */
- if (field->query_id == thd->query_id)
- thd->dupp_field= field;
- field->query_id= thd->query_id;
-
+ /* Mark fields as used to allow storage engine to optimze access */
+ bitmap_set_bit(field->table->read_set, field->field_index);
if (table)
- table->used_keys.intersect(field->part_of_key);
-
+ {
+ table->covering_keys.intersect(field->part_of_key);
+ table->merge_keys.merge(field->part_of_key);
+ }
if (tables->is_natural_join)
{
TABLE *field_table;
@@ -5490,17 +6496,14 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
if (field_table)
{
thd->used_tables|= field_table->map;
- field_table->used_keys.intersect(field->part_of_key);
+ field_table->covering_keys.intersect(field->part_of_key);
+ field_table->merge_keys.merge(field->part_of_key);
field_table->used_fields++;
}
}
}
else
- {
thd->used_tables|= item->used_tables();
- item->walk(&Item::reset_query_id_processor,
- (byte *)(&thd->query_id));
- }
thd->lex->current_select->cur_pos_in_select_list++;
}
/*
@@ -5571,7 +6574,8 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
arena->is_conventional())
arena= 0; // For easier test
- thd->set_query_id=1;
+ thd->mark_used_columns= MARK_COLUMNS_READ;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
select_lex->cond_count= 0;
select_lex->between_count= 0;
@@ -5866,7 +6870,7 @@ my_bool mysql_rm_tmp_tables(void)
char filePath[FN_REFLEN], *tmpdir, filePathCopy[FN_REFLEN];
MY_DIR *dirp;
FILEINFO *file;
- TABLE tmp_table;
+ TABLE_SHARE share;
THD *thd;
DBUG_ENTER("mysql_rm_tmp_tables");
@@ -5878,50 +6882,53 @@ my_bool mysql_rm_tmp_tables(void)
for (i=0; i<=mysql_tmpdir_list.max; i++)
{
tmpdir=mysql_tmpdir_list.list[i];
- /* See if the directory exists */
+ /* See if the directory exists */
if (!(dirp = my_dir(tmpdir,MYF(MY_WME | MY_DONT_SORT))))
continue;
/* Remove all SQLxxx tables from directory */
- for (idx=0 ; idx < (uint) dirp->number_off_files ; idx++)
- {
- file=dirp->dir_entry+idx;
+ for (idx=0 ; idx < (uint) dirp->number_off_files ; idx++)
+ {
+ file=dirp->dir_entry+idx;
- /* skiping . and .. */
- if (file->name[0] == '.' && (!file->name[1] ||
- (file->name[1] == '.' && !file->name[2])))
- continue;
+ /* skiping . and .. */
+ if (file->name[0] == '.' && (!file->name[1] ||
+ (file->name[1] == '.' && !file->name[2])))
+ continue;
- if (!bcmp(file->name,tmp_file_prefix,tmp_file_prefix_length))
- {
- char *ext= fn_ext(file->name);
- uint ext_len= strlen(ext);
- uint filePath_len= my_snprintf(filePath, sizeof(filePath),
- "%s%s", tmpdir, file->name);
- if (!bcmp(reg_ext, ext, ext_len))
+ if (!bcmp(file->name,tmp_file_prefix,tmp_file_prefix_length))
{
- TABLE tmp_table;
- if (!openfrm(thd, filePath, "tmp_table", (uint) 0,
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
- 0, &tmp_table))
+ char *ext= fn_ext(file->name);
+ uint ext_len= strlen(ext);
+ uint filePath_len= my_snprintf(filePath, sizeof(filePath),
+ "%s%c%s", tmpdir, FN_LIBCHAR,
+ file->name);
+ if (!bcmp(reg_ext, ext, ext_len))
{
+ handler *handler_file= 0;
/* We should cut file extention before deleting of table */
memcpy(filePathCopy, filePath, filePath_len - ext_len);
filePathCopy[filePath_len - ext_len]= 0;
- tmp_table.file->delete_table(filePathCopy);
- closefrm(&tmp_table);
+ init_tmp_table_share(&share, "", 0, "", filePathCopy);
+ if (!open_table_def(thd, &share, 0) &&
+ ((handler_file= get_new_handler(&share, thd->mem_root,
+ share.db_type()))))
+ {
+ handler_file->delete_table(filePathCopy);
+ delete handler_file;
+ }
+ free_table_share(&share);
}
+ /*
+ File can be already deleted by tmp_table.file->delete_table().
+ So we hide error messages which happnes during deleting of these
+ files(MYF(0)).
+ */
+ VOID(my_delete(filePath, MYF(0)));
}
- /*
- File can be already deleted by tmp_table.file->delete_table().
- So we hide error messages which happnes during deleting of these
- files(MYF(0)).
- */
- VOID(my_delete(filePath, MYF(0)));
}
- }
- my_dirend(dirp);
+ my_dirend(dirp);
}
delete thd;
my_pthread_setspecific_ptr(THR_THD, 0);
@@ -5952,7 +6959,7 @@ void remove_db_from_cache(const char *db)
for (uint idx=0 ; idx < open_cache.records ; idx++)
{
TABLE *table=(TABLE*) hash_element(&open_cache,idx);
- if (!strcmp(table->s->db, db))
+ if (!strcmp(table->s->db.str, db))
{
table->s->version= 0L; /* Free when thread is ready */
if (!table->in_use)
@@ -5965,7 +6972,11 @@ void remove_db_from_cache(const char *db)
/*
-** free all unused tables
+ free all unused tables
+
+ NOTE
+ This is called by 'handle_manager' when one wants to periodicly flush
+ all not used tables.
*/
void flush_tables()
@@ -5998,7 +7009,8 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
char key[MAX_DBKEY_LENGTH];
uint key_length;
TABLE *table;
- bool result=0, signalled= 0;
+ TABLE_SHARE *share;
+ bool result= 0, signalled= 0;
DBUG_ENTER("remove_table_from_cache");
DBUG_PRINT("enter", ("Table: '%s.%s' flags: %u", db, table_name, flags));
@@ -6015,6 +7027,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
&state))
{
THD *in_use;
+
table->s->version=0L; /* Free when thread is ready */
if (!(in_use=table->in_use))
{
@@ -6023,6 +7036,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
}
else if (in_use != thd)
{
+ DBUG_PRINT("info", ("Table was in use by other thread"));
in_use->some_tables_deleted=1;
if (table->is_name_opened())
{
@@ -6057,10 +7071,30 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
}
}
else
+ {
+ DBUG_PRINT("info", ("Table was in use by current thread. db_stat: %u",
+ table->db_stat));
result= result || (flags & RTFC_OWNED_BY_THD_FLAG);
+ }
}
while (unused_tables && !unused_tables->s->version)
VOID(hash_delete(&open_cache,(byte*) unused_tables));
+
+ DBUG_PRINT("info", ("Removing table from table_def_cache"));
+ /* Remove table from table definition cache if it's not in use */
+ if ((share= (TABLE_SHARE*) hash_search(&table_def_cache,(byte*) key,
+ key_length)))
+ {
+ DBUG_PRINT("info", ("share version: %lu ref_count: %u",
+ share->version, share->ref_count));
+ share->version= 0; // Mark for delete
+ if (share->ref_count == 0)
+ {
+ pthread_mutex_lock(&share->mutex);
+ VOID(hash_delete(&table_def_cache, (byte*) share));
+ }
+ }
+
if (result && (flags & RTFC_WAIT_OTHER_THREAD_FLAG))
{
/*
@@ -6099,6 +7133,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
DBUG_RETURN(result);
}
+
int setup_ftfuncs(SELECT_LEX *select_lex)
{
List_iterator<Item_func_match> li(*(select_lex->ftfunc_list)),
@@ -6143,11 +7178,11 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
SYNOPSIS
open_new_frm()
THD thread handler
- path path to .frm
+ path path to .frm file (without extension)
alias alias for table
db database
table_name name of table
- db_stat open flags (for example HA_OPEN_KEYFILE|HA_OPEN_RNDFILE..)
+ db_stat open flags (for example ->OPEN_KEYFILE|HA_OPEN_RNDFILE..)
can be 0 (example in ha_example_table)
prgflag READ_ALL etc..
ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc..
@@ -6157,18 +7192,20 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
*/
static bool
-open_new_frm(THD *thd, const char *path, const char *alias,
- const char *db, const char *table_name,
+open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
uint db_stat, uint prgflag,
uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc,
MEM_ROOT *mem_root)
{
LEX_STRING pathstr;
File_parser *parser;
+ char path[FN_REFLEN];
DBUG_ENTER("open_new_frm");
- pathstr.str= (char*) path;
- pathstr.length= strlen(path);
+ /* Create path with extension */
+ pathstr.length= (uint) (strxmov(path, share->normalized_path.str, reg_ext,
+ NullS)- path);
+ pathstr.str= path;
if ((parser= sql_parse_prepare(&pathstr, mem_root, 1)))
{
@@ -6176,7 +7213,8 @@ open_new_frm(THD *thd, const char *path, const char *alias,
{
if (table_desc == 0 || table_desc->required_type == FRMTYPE_TABLE)
{
- my_error(ER_WRONG_OBJECT, MYF(0), db, table_name, "BASE TABLE");
+ my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str,
+ "BASE TABLE");
goto err;
}
if (mysql_make_view(thd, parser, table_desc,
@@ -6186,7 +7224,7 @@ open_new_frm(THD *thd, const char *path, const char *alias,
else
{
/* only VIEWs are supported now */
- my_error(ER_FRM_UNKNOWN_TYPE, MYF(0), path, parser->type()->str);
+ my_error(ER_FRM_UNKNOWN_TYPE, MYF(0), share->path, parser->type()->str);
goto err;
}
DBUG_RETURN(0);
@@ -6202,3 +7240,312 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b)
{
return a->length == b->length && !strncmp(a->str, b->str, a->length);
}
+
+
+/*
+ SYNOPSIS
+ abort_and_upgrade_lock()
+ lpt Parameter passing struct
+ All parameters passed through the ALTER_PARTITION_PARAM_TYPE object
+ RETURN VALUE
+ 0
+ DESCRIPTION
+ Remember old lock level (for possible downgrade later on), abort all
+ waiting threads and ensure that all keeping locks currently are
+ completed such that we own the lock exclusively and no other interaction
+ is ongoing.
+
+ thd Thread object
+ table Table object
+ db Database name
+ table_name Table name
+ old_lock_level Old lock level
+*/
+
+int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ uint flags= RTFC_WAIT_OTHER_THREAD_FLAG | RTFC_CHECK_KILLED_FLAG;
+ DBUG_ENTER("abort_and_upgrade_locks");
+
+ lpt->old_lock_type= lpt->table->reginfo.lock_type;
+ VOID(pthread_mutex_lock(&LOCK_open));
+ mysql_lock_abort(lpt->thd, lpt->table, TRUE);
+ VOID(remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, flags));
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(0);
+}
+
+
+/*
+ SYNOPSIS
+ close_open_tables_and_downgrade()
+ RESULT VALUES
+ NONE
+ DESCRIPTION
+ We need to ensure that any thread that has managed to open the table
+ but not yet encountered our lock on the table is also thrown out to
+ ensure that no threads see our frm changes premature to the final
+ version. The intermediate versions are only meant for use after a
+ crash and later REPAIR TABLE.
+ We also downgrade locks after the upgrade to WRITE_ONLY
+*/
+
+void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ VOID(pthread_mutex_lock(&LOCK_open));
+ remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name,
+ RTFC_WAIT_OTHER_THREAD_FLAG);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ mysql_lock_downgrade_write(lpt->thd, lpt->table, lpt->old_lock_type);
+}
+
+
+/*
+ SYNOPSIS
+ mysql_wait_completed_table()
+ lpt Parameter passing struct
+ my_table My table object
+ All parameters passed through the ALTER_PARTITION_PARAM object
+ RETURN VALUES
+ TRUE Failure
+ FALSE Success
+ DESCRIPTION
+ We have changed the frm file and now we want to wait for all users of
+ the old frm to complete before proceeding to ensure that no one
+ remains that uses the old frm definition.
+ Start by ensuring that all users of the table will be removed from cache
+ once they are done. Then abort all that have stumbled on locks and
+ haven't been started yet.
+
+ thd Thread object
+ table Table object
+ db Database name
+ table_name Table name
+*/
+
+void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ TABLE *table;
+ DBUG_ENTER("mysql_wait_completed_table");
+
+ key_length=(uint) (strmov(strmov(key,lpt->db)+1,lpt->table_name)-key)+1;
+ VOID(pthread_mutex_lock(&LOCK_open));
+ HASH_SEARCH_STATE state;
+ for (table= (TABLE*) hash_first(&open_cache,(byte*) key,key_length,
+ &state) ;
+ table;
+ table= (TABLE*) hash_next(&open_cache,(byte*) key,key_length,
+ &state))
+ {
+ THD *in_use= table->in_use;
+ table->s->version= 0L;
+ if (!in_use)
+ {
+ relink_unused(table);
+ }
+ else
+ {
+ /* Kill delayed insert threads */
+ if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
+ ! in_use->killed)
+ {
+ in_use->killed= THD::KILL_CONNECTION;
+ pthread_mutex_lock(&in_use->mysys_var->mutex);
+ if (in_use->mysys_var->current_cond)
+ {
+ pthread_mutex_lock(in_use->mysys_var->current_mutex);
+ pthread_cond_broadcast(in_use->mysys_var->current_cond);
+ pthread_mutex_unlock(in_use->mysys_var->current_mutex);
+ }
+ pthread_mutex_unlock(&in_use->mysys_var->mutex);
+ }
+ /*
+ Now we must abort all tables locks used by this thread
+ as the thread may be waiting to get a lock for another table
+ */
+ for (TABLE *thd_table= in_use->open_tables;
+ thd_table ;
+ thd_table= thd_table->next)
+ {
+ if (thd_table->db_stat) // If table is open
+ mysql_lock_abort_for_thread(lpt->thd, thd_table);
+ }
+ }
+ }
+ /*
+ We start by removing all unused objects from the cache and marking
+ those in use for removal after completion. Now we also need to abort
+ all that are locked and are not progressing due to being locked
+ by our lock. We don't upgrade our lock here.
+ */
+ mysql_lock_abort(lpt->thd, my_table, FALSE);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Tells if two (or more) tables have auto_increment columns and we want to
+ lock those tables with a write lock.
+
+ SYNOPSIS
+ has_two_write_locked_tables_with_auto_increment
+ tables Table list
+
+ NOTES:
+ Call this function only when you have established the list of all tables
+ which you'll want to update (including stored functions, triggers, views
+ inside your statement).
+
+ RETURN
+ 0 No
+ 1 Yes
+*/
+
+static bool
+has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables)
+{
+ char *first_table_name= NULL, *first_db;
+ LINT_INIT(first_db);
+
+ for (TABLE_LIST *table= tables; table; table= table->next_global)
+ {
+ /* we must do preliminary checks as table->table may be NULL */
+ if (!table->placeholder() &&
+ table->table->found_next_number_field &&
+ (table->lock_type >= TL_WRITE_ALLOW_WRITE))
+ {
+ if (first_table_name == NULL)
+ {
+ first_table_name= table->table_name;
+ first_db= table->db;
+ DBUG_ASSERT(first_db);
+ }
+ else if (strcmp(first_db, table->db) ||
+ strcmp(first_table_name, table->table_name))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ Open and lock system tables for read.
+
+ SYNOPSIS
+ open_system_tables_for_read()
+ thd Thread context.
+ table_list List of tables to open.
+ backup Pointer to Open_tables_state instance where
+ information about currently open tables will be
+ saved, and from which will be restored when we will
+ end work with system tables.
+
+ NOTES
+ Thanks to restrictions which we put on opening and locking of
+ system tables for writing, we can open and lock them for reading
+ even when we already have some other tables open and locked. One
+ must call close_system_tables() to close systems tables opened
+ with this call.
+
+ RETURN
+ FALSE Success
+ TRUE Error
+*/
+
+bool
+open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
+ Open_tables_state *backup)
+{
+ DBUG_ENTER("open_system_tables_for_read");
+
+ thd->reset_n_backup_open_tables_state(backup);
+
+ uint count= 0;
+ bool not_used;
+ for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global)
+ {
+ TABLE *table= open_table(thd, tables, thd->mem_root, &not_used,
+ MYSQL_LOCK_IGNORE_FLUSH);
+ if (!table)
+ goto error;
+
+ DBUG_ASSERT(table->s->system_table);
+
+ table->use_all_columns();
+ table->reginfo.lock_type= tables->lock_type;
+ tables->table= table;
+ count++;
+ }
+
+ {
+ TABLE **list= (TABLE**) thd->alloc(sizeof(TABLE*) * count);
+ TABLE **ptr= list;
+ for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global)
+ *(ptr++)= tables->table;
+
+ thd->lock= mysql_lock_tables(thd, list, count,
+ MYSQL_LOCK_IGNORE_FLUSH, &not_used);
+ }
+ if (thd->lock)
+ DBUG_RETURN(FALSE);
+
+error:
+ close_system_tables(thd, backup);
+
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Close system tables, opened with open_system_tables_for_read().
+
+ SYNOPSIS
+ close_system_tables()
+ thd Thread context
+ backup Pointer to Open_tables_state instance which holds
+ information about tables which were open before we
+ decided to access system tables.
+*/
+
+void
+close_system_tables(THD *thd, Open_tables_state *backup)
+{
+ close_thread_tables(thd);
+ thd->restore_backup_open_tables_state(backup);
+}
+
+
+/*
+ Open and lock one system table for update.
+
+ SYNOPSIS
+ open_system_table_for_update()
+ thd Thread context.
+ one_table Table to open.
+
+ NOTES
+ Table opened with this call should closed using close_thread_tables().
+
+ RETURN
+ 0 Error
+ # Pointer to TABLE object of system table
+*/
+
+TABLE *
+open_system_table_for_update(THD *thd, TABLE_LIST *one_table)
+{
+ DBUG_ENTER("open_system_table_for_update");
+
+ TABLE *table= open_ltable(thd, one_table, one_table->lock_type);
+ if (table)
+ {
+ DBUG_ASSERT(table->s->system_table);
+ table->use_all_columns();
+ }
+
+ DBUG_RETURN(table);
+}