diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index bf76960201d..f272526edb8 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -2391,7 +2391,7 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row) goto add_ndb_binlog_index_err; } - if (lock_tables(thd, &binlog_tables, 1, &need_reopen)) + if (lock_tables(thd, &binlog_tables, 1, 0, &need_reopen)) { if (need_reopen) { diff --git a/sql/lock.cc b/sql/lock.cc index 0c8c3095844..38b2d22f91f 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -94,31 +94,6 @@ static int lock_external(THD *thd, TABLE **table,uint count); static int unlock_external(THD *thd, TABLE **table,uint count); static void print_lock_error(int error, const char *); -/* - Lock tables. - - SYNOPSIS - mysql_lock_tables() - thd The current thread. - tables An array of pointers to the tables to lock. - count The number of tables to lock. - flags Options: - MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock - MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY - MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables. - MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered - or dropped tables by itself, - mysql_lock_tables() should - notify upper level and rely - on caller doing this. - need_reopen Out parameter, TRUE if some tables were altered - or deleted and should be reopened by caller. - - RETURN - A lock structure pointer on success. - NULL on error or if some tables should be reopen. -*/ - /* Map the return value of thr_lock to an error from errmsg.txt */ static int thr_lock_errno_to_mysql[]= { 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; @@ -247,6 +222,28 @@ static void reset_lock_data_and_free(MYSQL_LOCK **mysql_lock) } +/** + Lock tables. + + @param thd The current thread. + @param tables An array of pointers to the tables to lock. + @param count The number of tables to lock. + @param flags Options: + MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock + MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY + MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables. + @param need_reopen Out parameter, TRUE if some tables were altered + or deleted and should be reopened by caller. + + @note Caller of this function should always be ready to handle request to + reopen table unless there are external invariants which guarantee + that such thing won't be needed (for example we are obtaining lock + on table on which we already have exclusive metadata lock). + + @retval A lock structure pointer on success. + @retval NULL on error or if some tables should be reopen. +*/ + MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags, bool *need_reopen) { @@ -330,7 +327,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, my_error(rc, MYF(0)); break; } - else if (rc == 1) /* aborted */ + else if (rc == 1) /* aborted or killed */ { thd->some_tables_deleted=1; // Try again sql_lock->lock_count= 0; // Locks are already freed @@ -339,8 +336,9 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH)) { /* - Thread was killed or lock aborted. Let upper level close all - used tables and retry or give error. + Success and nobody set thd->some_tables_deleted to force reopen + or we were called with MYSQL_LOCK_IGNORE_FLUSH so such attempts + should be ignored. */ break; } @@ -366,13 +364,9 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, */ reset_lock_data_and_free(&sql_lock); retry: - if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN) - { - *need_reopen= TRUE; - break; - } - if (wait_for_tables(thd)) - break; // Couldn't open tables + /* Let upper level close all used tables and retry or give error. */ + *need_reopen= TRUE; + break; } thd_proc_info(thd, 0); if (thd->killed) diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 030d51b3618..3dcf19f6b32 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1461,7 +1461,8 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) lex_start(thd); while ((error= lock_tables(thd, rli->tables_to_lock, - rli->tables_to_lock_count, &need_reopen))) + rli->tables_to_lock_count, 0, + &need_reopen))) { if (!need_reopen) { diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index be03759b383..ab692c3ab22 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1241,8 +1241,6 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table); void close_data_files_and_morph_locks(THD *thd, const char *db, const char *table_name); void close_handle_and_leave_table_as_lock(TABLE *table); -bool wait_for_tables(THD *thd); -bool table_is_used(TABLE *table, bool wait_for_name_lock); void unlock_locked_tables(THD *thd); void execute_init_command(THD *thd, sys_var_str *init_command_var, rw_lock_t *var_mutex); @@ -1476,22 +1474,24 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond); int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags); /* open_and_lock_tables with optional derived handling */ -int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived); +int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived, + uint flags); /* simple open_and_lock_tables without derived handling */ inline int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) { - return open_and_lock_tables_derived(thd, tables, FALSE); + return open_and_lock_tables_derived(thd, tables, FALSE, 0); } /* open_and_lock_tables with derived handling */ inline int open_and_lock_tables(THD *thd, TABLE_LIST *tables) { - return open_and_lock_tables_derived(thd, tables, TRUE); + return open_and_lock_tables_derived(thd, tables, TRUE, 0); } /* simple open_and_lock_tables without derived handling for single table */ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, thr_lock_type lock_type); bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags); -int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen); +int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags, + bool *need_reopen); int decide_logging_format(THD *thd, TABLE_LIST *tables); TABLE *open_temporary_table(THD *thd, const char *path, const char *db, const char *table_name, bool link_in_list); @@ -2045,10 +2045,9 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, /* mysql_lock_tables() and open_table() flags bits */ #define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001 #define MYSQL_LOCK_IGNORE_FLUSH 0x0002 -#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN 0x0004 -#define MYSQL_OPEN_TEMPORARY_ONLY 0x0008 -#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0010 -#define MYSQL_LOCK_PERF_SCHEMA 0x0020 +#define MYSQL_OPEN_TEMPORARY_ONLY 0x0004 +#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008 +#define MYSQL_LOCK_PERF_SCHEMA 0x0010 void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index de4aaac633e..6b0efadb712 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -127,8 +127,6 @@ 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 tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context); static bool has_write_table_with_auto_increment(TABLE_LIST *tables); @@ -3439,9 +3437,6 @@ bool reopen_tables(THD *thd, bool get_locks) TABLE *err_tables= NULL, *err_tab_tmp; bool error=0, not_used; bool merge_table_found= FALSE; - const uint flags= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN | - MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK | - MYSQL_LOCK_IGNORE_FLUSH; DBUG_ENTER("reopen_tables"); @@ -3534,10 +3529,14 @@ bool reopen_tables(THD *thd, bool get_locks) if (tables != tables_ptr) // Should we get back old locks { MYSQL_LOCK *lock; + const uint flags= MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK | + MYSQL_LOCK_IGNORE_FLUSH; /* - We should always get these locks. Anyway, we must not go into - wait_for_tables() as it tries to acquire LOCK_open, which is - already locked. + Since we have exclusive metadata locks on tables which we + are reopening we should always get these locks (We won't + wait on table level locks so can't get aborted and we ignore + other threads that set THD::some_tables_deleted by using + MYSQL_LOCK_IGNORE_FLUSH flag). */ thd->some_tables_deleted=0; if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables), @@ -3565,165 +3564,6 @@ bool reopen_tables(THD *thd, bool get_locks) } -/** - Close handlers for tables in list, but leave the TABLE structure - intact so that we can re-open these quickly. - - @param thd Thread context - @param table Head of the list of TABLE objects - @param morph_locks TRUE - remove locks which we have on tables being closed - but ensure that no DML or DDL will sneak in before - we will re-open the table (i.e. temporarily morph - our table-level locks into name-locks). - FALSE - otherwise - @param send_refresh Should we awake waiters even if we didn't close any tables? -*/ - -static 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"); - - for (; table ; table=table->next) - { - DBUG_PRINT("tcache", ("checking table: '%s'.'%s' 0x%lx", - table->s->db.str, table->s->table_name.str, - (long) table)); - DBUG_PRINT("tcache", ("needs refresh: %d is open: %u", - table->needs_reopen_or_name_lock(), table->db_stat)); - /* - Reopen marked for flush. - */ - if (table->needs_reopen_or_name_lock()) - { - found=1; - if (table->db_stat) - { - if (morph_locks) - { - /* - Forward lock handling to MERGE parent. But unlock parent - once only. - */ - TABLE *ulcktbl= table->parent ? table->parent : table; - if (ulcktbl->lock_count) - { - /* - 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, ulcktbl, TRUE); - mysql_lock_remove(thd, thd->locked_tables, ulcktbl, TRUE); - ulcktbl->lock_count= 0; - } - if ((ulcktbl != table) && ulcktbl->db_stat) - { - /* - Close the parent too. Note that parent can come later in - the list of tables. It will then be noticed as closed and - as a placeholder. When this happens, do not clear the - placeholder flag. See the branch below ("***"). - */ - ulcktbl->open_placeholder= 1; - close_handle_and_leave_table_as_lock(ulcktbl); - } - /* - 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; - } - close_handle_and_leave_table_as_lock(table); - } - else if (table->open_placeholder && !morph_locks) - { - /* - We come here only in close-for-back-off scenario. So we have to - "close" create placeholder here to avoid deadlocks (for example, - in case of concurrent execution of CREATE TABLE t1 SELECT * FROM t2 - and RENAME TABLE t2 TO t1). In close-for-re-open scenario we will - probably want to let it stay. - - Note "***": We must not enter this branch if the placeholder - flag has been set because of a former close through a child. - See above the comment that refers to this note. - */ - table->open_placeholder= 0; - } - } - } - if (found) - broadcast_refresh(); - DBUG_VOID_RETURN; -} - - -/* - 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 -*/ - -bool table_is_used(TABLE *table, bool wait_for_name_lock) -{ - DBUG_ENTER("table_is_used"); - do - { - char *key= table->s->table_cache_key.str; - uint key_length= table->s->table_cache_key.length; - /* Note that 'table' can use artificial TABLE_SHARE object. */ - TABLE_SHARE *share= (TABLE_SHARE*)my_hash_search(&table_def_cache, - (uchar*) key, key_length); - if (share && !share->used_tables.is_empty() && - share->version != refresh_version) - DBUG_RETURN(1); - } while ((table=table->next)); - DBUG_RETURN(0); -} - - -/* - Wait until all used tables are refreshed. - - FIXME We should remove this function since for several functions which - are invoked by it new scenarios of usage are introduced, while - this function implements optimization useful only in rare cases. -*/ - -bool wait_for_tables(THD *thd) -{ - bool result; - DBUG_ENTER("wait_for_tables"); - - thd_proc_info(thd, "Waiting for tables"); - pthread_mutex_lock(&LOCK_open); - while (!thd->killed) - { - thd->some_tables_deleted=0; - close_old_data_files(thd,thd->open_tables,0,dropping_tables != 0); - mysql_ha_flush(thd); - if (!table_is_used(thd->open_tables,1)) - break; - (void) pthread_cond_wait(&COND_refresh,&LOCK_open); - } - if (thd->killed) - result= 1; // aborted - else - { - /* Now we can open all tables without any interference */ - thd_proc_info(thd, "Reopen tables"); - thd->version= refresh_version; - result=reopen_tables(thd, 0); - } - pthread_mutex_unlock(&LOCK_open); - thd_proc_info(thd, 0); - DBUG_RETURN(result); -} - - /** Unlock and close tables open and locked by LOCK TABLES statement. @@ -5190,14 +5030,8 @@ retry: DBUG_ASSERT(thd->lock == 0); // You must lock everything at once if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK) if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, - (lock_flags | - MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN), - &refresh))) + lock_flags, &refresh))) { - /* - FIXME: Actually we should get rid of MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN option - as all reopening should happen outside of mysql_lock_tables() code. - */ if (refresh) { close_thread_tables(thd); @@ -5222,6 +5056,8 @@ retry: open_and_lock_tables_derived() thd - thread handler tables - list of tables for open&locking + flags - set of options to be used to open and lock tables (see + open_tables() and mysql_lock_tables() for details). derived - if to handle derived tables RETURN @@ -5239,7 +5075,8 @@ retry: the third argument set appropriately. */ -int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) +int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived, + uint flags) { uint counter; bool need_reopen; @@ -5248,7 +5085,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) for ( ; ; ) { - if (open_tables(thd, &tables, &counter, 0)) + if (open_tables(thd, &tables, &counter, flags)) DBUG_RETURN(-1); DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", { @@ -5257,7 +5094,8 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) my_sleep(6000000); thd->proc_info= old_proc_info;}); - if (!lock_tables(thd, tables, counter, &need_reopen)) + if (!lock_tables(thd, tables, counter, flags, + &need_reopen)) break; if (!need_reopen) DBUG_RETURN(-1); @@ -5492,6 +5330,7 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) thd Thread handler tables Tables to lock count Number of opened tables + flags Options (see mysql_lock_tables() for details) need_reopen Out parameter which if TRUE indicates that some tables were dropped or altered during this call and therefore invoker should reopen tables and @@ -5512,7 +5351,8 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) -1 Error */ -int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) +int lock_tables(THD *thd, TABLE_LIST *tables, uint count, + uint flags, bool *need_reopen) { TABLE_LIST *table; @@ -5540,7 +5380,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) { DBUG_ASSERT(thd->lock == 0); // You must lock everything at once TABLE **start,**ptr; - uint lock_flag= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN; if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count))) DBUG_RETURN(-1); @@ -5573,7 +5412,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) DEBUG_SYNC(thd, "before_lock_tables_takes_lock"); if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), - lock_flag, need_reopen))) + flags, need_reopen))) { if (thd->lex->requires_prelocking()) { @@ -9190,41 +9029,38 @@ bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, Open_tables_state *backup) { + Query_tables_list query_tables_list_backup; + LEX *lex= thd->lex; + DBUG_ENTER("open_system_tables_for_read"); alloc_mdl_locks(table_list, thd->mem_root); + /* + Besides using new Open_tables_state for opening system tables, + we also have to backup and reset/and then restore part of LEX + which is accessed by open_tables() in order to determine if + prelocking is needed and what tables should be added for it. + close_system_tables() doesn't require such treatment. + */ + lex->reset_n_backup_query_tables_list(&query_tables_list_backup); thd->reset_n_backup_open_tables_state(backup); - uint count= 0; - enum_open_table_action not_used; - bool not_used_2; + if (open_and_lock_tables_derived(thd, table_list, FALSE, + MYSQL_LOCK_IGNORE_FLUSH)) + { + lex->restore_backup_query_tables_list(&query_tables_list_backup); + goto error; + } + for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global) { - TABLE *table= open_table(thd, tables, thd->mem_root, ¬_used, - MYSQL_LOCK_IGNORE_FLUSH); - if (!table) - goto error; - - DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM); - - table->use_all_columns(); - table->reginfo.lock_type= tables->lock_type; - tables->table= table; - count++; + DBUG_ASSERT(tables->table->s->table_category == TABLE_CATEGORY_SYSTEM); + tables->table->use_all_columns(); } + lex->restore_backup_query_tables_list(&query_tables_list_backup); - { - 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, ¬_used_2); - } - if (thd->lock) - DBUG_RETURN(FALSE); + DBUG_RETURN(FALSE); error: close_system_tables(thd, backup); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index c8a66073a67..5c034a0452a 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -519,8 +519,7 @@ retry: */ thd->open_tables= thd->handler_tables; - lock= mysql_lock_tables(thd, &tables->table, 1, - MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen); + lock= mysql_lock_tables(thd, &tables->table, 1, 0, &need_reopen); /* restore previous context */ thd->open_tables= backup_open_tables; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index eb4eee9abb5..525f6b9a4d8 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1817,6 +1817,7 @@ public: inline uint lock_count() { return locks_in_memory; } TABLE* get_local_table(THD* client_thd); + bool open_and_lock_table(); bool handle_inserts(void); }; @@ -2292,6 +2293,42 @@ void kill_delayed_threads(void) } +/** + Open and lock table for use by delayed thread and check that + this table is suitable for delayed inserts. + + @retval FALSE - Success. + @retval TRUE - Failure. +*/ + +bool Delayed_insert::open_and_lock_table() +{ + if (!(table= open_n_lock_single_table(&thd, &table_list, + TL_WRITE_DELAYED))) + { + thd.fatal_error(); // Abort waiting inserts + return TRUE; + } + if (!(table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED)) + { + my_error(ER_DELAYED_NOT_SUPPORTED, MYF(ME_FATALERROR), + table_list.table_name); + return TRUE; + } + if (table->triggers) + { + /* + Table has triggers. This is not an error, but we do + not support triggers with delayed insert. Terminate the delayed + thread without an error and thus request lock upgrade. + */ + return TRUE; + } + table->copy_blobs= 1; + return FALSE; +} + + /* * Create a new delayed insert thread */ @@ -2354,29 +2391,8 @@ pthread_handler_t handle_delayed_insert(void *arg) alloc_mdl_locks(&di->table_list, thd->mem_root); - /* Open table */ - if (!(di->table= open_n_lock_single_table(thd, &di->table_list, - TL_WRITE_DELAYED))) - { - thd->fatal_error(); // Abort waiting inserts + if (di->open_and_lock_table()) goto err; - } - if (!(di->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED)) - { - my_error(ER_DELAYED_NOT_SUPPORTED, MYF(ME_FATALERROR), - di->table_list.table_name); - goto err; - } - if (di->table->triggers) - { - /* - Table has triggers. This is not an error, but we do - not support triggers with delayed insert. Terminate the delayed - thread without an error and thus request lock upgrade. - */ - goto err; - } - di->table->copy_blobs=1; /* Tell client that the thread is initialized */ pthread_cond_signal(&di->cond_client); @@ -2450,7 +2466,7 @@ pthread_handler_t handle_delayed_insert(void *arg) if (di->tables_in_use && ! thd->lock) { - bool not_used; + bool need_reopen; /* Request for new delayed insert. Lock the table, but avoid to be blocked by a global read lock. @@ -2463,11 +2479,29 @@ pthread_handler_t handle_delayed_insert(void *arg) */ if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1, MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK, - ¬_used))) + &need_reopen))) { - /* Fatal error */ - di->dead= 1; - thd->killed= THD::KILL_CONNECTION; + if (need_reopen) + { + /* + We were waiting to obtain TL_WRITE_DELAYED (probably due to + someone having or requesting TL_WRITE_ALLOW_READ) and got + aborted. Try to reopen table and if it fails die. + */ + close_thread_tables(thd); + di->table= 0; + if (di->open_and_lock_table()) + { + di->dead= 1; + thd->killed= THD::KILL_CONNECTION; + } + } + else + { + /* Fatal error */ + di->dead= 1; + thd->killed= THD::KILL_CONNECTION; + } } pthread_cond_broadcast(&di->cond_client); } @@ -3533,6 +3567,12 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, table->reginfo.lock_type=TL_WRITE; hooks->prelock(&table, 1); // Call prelock hooks + /* + mysql_lock_tables() below should never fail with request to reopen table + since it won't wait for the table lock (we have exclusive metadata lock on + the table) and thus can't get aborted and since it ignores other threads + setting THD::some_tables_deleted thanks to MYSQL_LOCK_IGNORE_FLUSH. + */ if (! ((*lock)= mysql_lock_tables(thd, &table, 1, MYSQL_LOCK_IGNORE_FLUSH, ¬_used)) || hooks->postlock(&table, 1)) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 1ad39a2f838..5cf6ceaa394 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -222,7 +222,7 @@ int mysql_update(THD *thd, /* convert to multiupdate */ DBUG_RETURN(2); } - if (!lock_tables(thd, table_list, table_count, &need_reopen)) + if (!lock_tables(thd, table_list, table_count, 0, &need_reopen)) break; if (!need_reopen) DBUG_RETURN(1); @@ -1099,7 +1099,7 @@ reopen_tables: /* now lock and fill tables */ if (!thd->stmt_arena->is_stmt_prepare() && - lock_tables(thd, table_list, table_count, &need_reopen)) + lock_tables(thd, table_list, table_count, 0, &need_reopen)) { if (!need_reopen) DBUG_RETURN(TRUE);