diff options
-rw-r--r-- | sql/ha_ndbcluster.cc | 7 | ||||
-rw-r--r-- | sql/lock.cc | 7 | ||||
-rw-r--r-- | sql/lock.h | 15 | ||||
-rw-r--r-- | sql/mdl.h | 4 | ||||
-rw-r--r-- | sql/sp_head.cc | 11 | ||||
-rw-r--r-- | sql/sp_head.h | 4 | ||||
-rw-r--r-- | sql/sql_base.cc | 316 | ||||
-rw-r--r-- | sql/sql_class.h | 14 | ||||
-rw-r--r-- | sql/sql_db.cc | 8 | ||||
-rw-r--r-- | sql/sql_lex.cc | 1 | ||||
-rw-r--r-- | sql/sql_lex.h | 10 | ||||
-rw-r--r-- | sql/sql_parse.cc | 16 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 2 | ||||
-rw-r--r-- | sql/sql_show.cc | 4 | ||||
-rw-r--r-- | sql/sql_table.cc | 26 | ||||
-rw-r--r-- | sql/sql_trigger.cc | 6 | ||||
-rw-r--r-- | sql/sql_view.cc | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 103 | ||||
-rw-r--r-- | sql/table.cc | 7 | ||||
-rw-r--r-- | sql/table.h | 19 |
20 files changed, 357 insertions, 225 deletions
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 2d082cc71f6..2af2b064020 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -7408,9 +7408,10 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str)); // Delete the table and all related files TABLE_LIST table_list; - bzero((char*) &table_list,sizeof(table_list)); - table_list.db= (char*) db; - table_list.alias= table_list.table_name= (char*)file_name_str; + table_list.init_one_table(db, strlen(db), file_name_str, + strlen(file_name_str), file_name_str, + TL_WRITE); + table_list.mdl_request.set_tpye(MDL_EXCLUSIVE); (void)mysql_rm_table_part2(thd, &table_list, FALSE, /* if_exists */ FALSE, /* drop_temporary */ diff --git a/sql/lock.cc b/sql/lock.cc index 758ea6cf914..8e91bd9360e 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -880,6 +880,8 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, before calling it. Also it cannot be called while holding LOCK_open mutex. Both these invariants are enforced by asserts in MDL_context::acquire_locks(). + @note Initialization of MDL_request members of TABLE_LIST elements + is a responsibility of the caller. @retval FALSE Success. @retval TRUE Failure (OOM or thread was killed). @@ -894,12 +896,7 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list) global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) - { - lock_table->mdl_request.init(MDL_key::TABLE, - lock_table->db, lock_table->table_name, - MDL_EXCLUSIVE); mdl_requests.push_front(&lock_table->mdl_request); - } mdl_requests.push_front(&global_request); diff --git a/sql/lock.h b/sql/lock.h index 19b23f1f42b..5425a6ccb13 100644 --- a/sql/lock.h +++ b/sql/lock.h @@ -15,33 +15,32 @@ typedef struct st_mysql_lock MYSQL_LOCK; #define MYSQL_OPEN_TEMPORARY_ONLY 0x0004 #define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008 #define MYSQL_LOCK_LOG_TABLE 0x0010 -#define MYSQL_OPEN_TAKE_UPGRADABLE_MDL 0x0020 /** Do not try to acquire a metadata lock on the table: we already have one. */ -#define MYSQL_OPEN_HAS_MDL_LOCK 0x0040 +#define MYSQL_OPEN_HAS_MDL_LOCK 0x0020 /** If in locked tables mode, ignore the locked tables and get a new instance of the table. */ -#define MYSQL_OPEN_GET_NEW_TABLE 0x0080 +#define MYSQL_OPEN_GET_NEW_TABLE 0x0040 /** Don't look up the table in the list of temporary tables. */ -#define MYSQL_OPEN_SKIP_TEMPORARY 0x0100 +#define MYSQL_OPEN_SKIP_TEMPORARY 0x0080 /** Fail instead of waiting when conficting metadata lock is discovered. */ -#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT 0x0200 +#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT 0x0100 /** Open tables using MDL_SHARED lock instead of one specified in parser. */ -#define MYSQL_OPEN_FORCE_SHARED_MDL 0x0400 +#define MYSQL_OPEN_FORCE_SHARED_MDL 0x0200 /** Open tables using MDL_SHARED_HIGH_PRIO lock instead of one specified in parser. */ -#define MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL 0x0800 +#define MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL 0x0400 /** When opening or locking the table, use the maximum timeout (LONG_TIMEOUT = 1 year) rather than the user-supplied timeout value. */ -#define MYSQL_LOCK_IGNORE_TIMEOUT 0x1000 +#define MYSQL_LOCK_IGNORE_TIMEOUT 0x0800 /** Please refer to the internals manual. */ #define MYSQL_OPEN_REOPEN (MYSQL_OPEN_IGNORE_FLUSH |\ diff --git a/sql/mdl.h b/sql/mdl.h index 2fb21a5aa18..2e296b73057 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -308,6 +308,10 @@ public: MDL_key key; public: + static void *operator new(size_t size, MEM_ROOT *mem_root) throw () + { return alloc_root(mem_root, size); } + static void operator delete(void *ptr, MEM_ROOT *mem_root) {} + void init(MDL_key::enum_mdl_namespace namespace_arg, const char *db_arg, const char *name_arg, enum_mdl_type mdl_type_arg); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 2e66aec91e5..2d5b648e82e 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -4010,6 +4010,11 @@ sp_head::add_used_tables_to_table_list(THD *thd, table->prelocking_placeholder= 1; table->belong_to_view= belong_to_view; table->trg_event_map= stab->trg_event_map; + /* + Since we don't allow DDL on base tables in prelocked mode it + is safe to infer the type of metadata lock from the type of + table lock. + */ table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, table->lock_type >= TL_WRITE_ALLOW_WRITE ? MDL_SHARED_WRITE : MDL_SHARED_READ); @@ -4040,7 +4045,8 @@ sp_head::add_used_tables_to_table_list(THD *thd, TABLE_LIST * sp_add_to_query_tables(THD *thd, LEX *lex, const char *db, const char *name, - thr_lock_type locktype) + thr_lock_type locktype, + enum_mdl_type mdl_type) { TABLE_LIST *table; @@ -4055,8 +4061,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex, table->select_lex= lex->current_select; table->cacheable_table= 1; table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, - table->lock_type >= TL_WRITE_ALLOW_WRITE ? - MDL_SHARED_WRITE : MDL_SHARED_READ); + mdl_type); lex->add_to_query_tables(table); return table; diff --git a/sql/sp_head.h b/sql/sp_head.h index 165f88321a9..8975c239810 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -1346,7 +1346,9 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, TABLE_LIST * sp_add_to_query_tables(THD *thd, LEX *lex, const char *db, const char *name, - thr_lock_type locktype); + thr_lock_type locktype, + enum_mdl_type mdl_type); + Item * sp_prepare_func_item(THD* thd, Item **it_addr); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 28633365e28..85017886d24 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2358,82 +2358,90 @@ void table_share_release_hook(void *share) /** - A helper function that acquires an MDL lock for a table - being opened. + Try to acquire an MDL lock for a table being opened. + + @param[in,out] thd Session context, to report errors. + @param[out] ot_ctx Open table context, to hold the back off + state. If we failed to acquire a lock + due to a lock conflict, we add the + failed request to the open table context. + @param[in,out] mdl_request A request for an MDL lock. + If we managed to acquire a ticket + (no errors or lock conflicts occurred), + contains a reference to it on + return. However, is not modified if MDL + lock type- modifying flags were provided. + @param[in] flags flags MYSQL_OPEN_FORCE_SHARED_MDL, + MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL or + MYSQL_OPEN_FAIL_ON_MDL_CONFLICT + @sa open_table(). + @param[out] mdl_ticket Only modified if there was no error. + If we managed to acquire an MDL + lock, contains a reference to the + ticket, otherwise is set to NULL. + + @retval TRUE An error occurred. + @retval FALSE No error, but perhaps a lock conflict, check mdl_ticket. */ static bool -open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, +open_table_get_mdl_lock(THD *thd, Open_table_context *ot_ctx, MDL_request *mdl_request, - Open_table_context *ot_ctx, - uint flags) + uint flags, + MDL_ticket **mdl_ticket) { - if (table_list->lock_strategy) + if (flags & (MYSQL_OPEN_FORCE_SHARED_MDL | + MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)) { - MDL_request_list mdl_requests; - MDL_request *global_request; /* - In case of CREATE TABLE .. If NOT EXISTS .. SELECT, the table - may not yet exist. Let's acquire an exclusive lock for that - case. If later it turns out the table existsed, we will - downgrade the lock to shared. Note that, according to the - locking protocol, all exclusive locks must be acquired before - shared locks. This invariant is preserved here and is also - enforced by asserts in metadata locking subsystem. + MYSQL_OPEN_FORCE_SHARED_MDL flag means that we are executing + PREPARE for a prepared statement and want to override + the type-of-operation aware metadata lock which was set + in the parser/during view opening with a simple shared + metadata lock. + This is necessary to allow concurrent execution of PREPARE + and LOCK TABLES WRITE statement against the same table. + + MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL flag means that we open + the table in order to get information about it for one of I_S + queries and also want to override the type-of-operation aware + shared metadata lock which was set earlier (e.g. during view + opening) with a high-priority shared metadata lock. + This is necessary to avoid unnecessary waiting and extra + ER_WARN_I_S_SKIPPED_TABLE warnings when accessing I_S tables. + + These two flags are mutually exclusive. */ + DBUG_ASSERT(!(flags & MYSQL_OPEN_FORCE_SHARED_MDL) || + !(flags & MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)); - mdl_request->set_type(MDL_EXCLUSIVE); - DBUG_ASSERT(! thd->mdl_context.has_locks() || - thd->handler_tables_hash.records || - thd->global_read_lock.is_acquired()); - - if (!(global_request= ot_ctx->get_global_mdl_request(thd))) - return 1; - - mdl_requests.push_front(mdl_request); - mdl_requests.push_front(global_request); + mdl_request= new (thd->mem_root) MDL_request(mdl_request); + if (mdl_request == NULL) + return TRUE; - if (thd->mdl_context.acquire_locks(&mdl_requests, ot_ctx->get_timeout())) - return 1; + mdl_request->set_type((flags & MYSQL_OPEN_FORCE_SHARED_MDL) ? + MDL_SHARED : MDL_SHARED_HIGH_PRIO); } - else - { - if (flags & MYSQL_OPEN_FORCE_SHARED_MDL) - { - /* - While executing PREPARE for prepared statement we override - type-of-operation aware type of shared metadata lock which - was set in the parser with simple shared metadata lock. - This is necessary to allow concurrent execution of PREPARE - and LOCK TABLES WRITE statement which locks one of the tables - used in the statement being prepared. - */ - DBUG_ASSERT(!(flags & (MYSQL_OPEN_TAKE_UPGRADABLE_MDL | - MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL))); - mdl_request->set_type(MDL_SHARED); - } - else if (flags & MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL) - { - DBUG_ASSERT(!(flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL)); - mdl_request->set_type(MDL_SHARED_HIGH_PRIO); - } - - ot_ctx->add_request(mdl_request); + ot_ctx->add_request(mdl_request); - if (thd->mdl_context.try_acquire_lock(mdl_request)) - return 1; + if (thd->mdl_context.try_acquire_lock(mdl_request)) + return TRUE; - if (mdl_request->ticket == NULL) + if (mdl_request->ticket == NULL) + { + if (flags & MYSQL_OPEN_FAIL_ON_MDL_CONFLICT) { - if (flags & MYSQL_OPEN_FAIL_ON_MDL_CONFLICT) - my_error(ER_WARN_I_S_SKIPPED_TABLE, MYF(0), table_list->db, table_list->table_name); - else - ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK); - return 1; + my_error(ER_WARN_I_S_SKIPPED_TABLE, MYF(0), + mdl_request->key.db_name(), mdl_request->key.name()); + return TRUE; } + if (ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK, + mdl_request, NULL)) + return TRUE; } - return 0; + *mdl_ticket= mdl_request->ticket; + return FALSE; } @@ -2468,11 +2476,9 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, is never opened. In both cases, metadata locks are always taken according to the lock strategy. - This function will take a exclusive metadata lock on the table if - TABLE_LIST::lock_strategy is EXCLUSIVE_DOWNGRADABLE_MDL or EXCLUSIVE_MDL. - If the lock strategy is EXCLUSIVE_DOWNGRADABLE_MDL and opening the table - is successful, the exclusive metadata lock is downgraded to a shared - lock. + If the lock strategy is OTLS_DOWNGRADE_IF_EXISTS and opening the table + is successful, the exclusive metadata lock acquired by the caller + is downgraded to a shared lock. RETURN TRUE Open failed. "action" parameter may contain type of action @@ -2490,7 +2496,6 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, char key[MAX_DBKEY_LENGTH]; uint key_length; char *alias= table_list->alias; - MDL_request *mdl_request; MDL_ticket *mdl_ticket; int error; TABLE_SHARE *share; @@ -2528,7 +2533,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (thd->version != refresh_version) { - (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC); + (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC, + NULL, NULL); DBUG_RETURN(TRUE); } } @@ -2701,23 +2707,25 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, This is the normal use case. */ - mdl_request= &table_list->mdl_request; if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) { - if (open_table_get_mdl_lock(thd, table_list, mdl_request, ot_ctx, flags)) + if (open_table_get_mdl_lock(thd, ot_ctx, &table_list->mdl_request, + flags, &mdl_ticket) || + mdl_ticket == NULL) { DEBUG_SYNC(thd, "before_open_table_wait_refresh"); DBUG_RETURN(TRUE); } DEBUG_SYNC(thd, "after_open_table_mdl_shared"); } - - /* - Grab reference to the granted MDL lock ticket. Must be done after - open_table_get_mdl_lock as the lock on the table might have been - acquired previously (MYSQL_OPEN_HAS_MDL_LOCK). - */ - mdl_ticket= mdl_request->ticket; + else + { + /* + Grab reference to the MDL lock ticket that was acquired + by the caller. + */ + mdl_ticket= table_list->mdl_request.ticket; + } hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length); mysql_mutex_lock(&LOCK_open); @@ -2737,7 +2745,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { /* Someone did a refresh while thread was opening tables */ mysql_mutex_unlock(&LOCK_open); - (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC); + (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC, + NULL, NULL); DBUG_RETURN(TRUE); } @@ -2878,7 +2887,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, */ release_table_share(share); mysql_mutex_unlock(&LOCK_open); - (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC); + (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC, + NULL, NULL); DBUG_RETURN(TRUE); } /* Force close at once after usage */ @@ -2918,12 +2928,14 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (error == 7) { share->version= 0; - (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER); + (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER, + NULL, table_list); } else if (share->crashed) { share->version= 0; - (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR); + (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR, + NULL, table_list); } goto err_unlock; @@ -2947,7 +2959,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table exists now we should downgrade our exclusive metadata lock on this table to SW metadata lock. */ - if (table_list->lock_strategy == TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL && + if (table_list->lock_strategy == TABLE_LIST::OTLS_DOWNGRADE_IF_EXISTS && !(flags & MYSQL_OPEN_HAS_MDL_LOCK)) mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_WRITE); @@ -3782,6 +3794,8 @@ end_with_lock_open: Open_table_context::Open_table_context(THD *thd, ulong timeout) :m_action(OT_NO_ACTION), + m_failed_mdl_request(NULL), + m_failed_table(NULL), m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()), m_has_locks((thd->in_multi_stmt_transaction_mode() && thd->mdl_context.has_locks()) || @@ -3801,10 +3815,8 @@ MDL_request *Open_table_context::get_global_mdl_request(THD *thd) { if (! m_global_mdl_request) { - char *buff; - if ((buff= (char*)thd->alloc(sizeof(MDL_request)))) + if ((m_global_mdl_request= new (thd->mem_root) MDL_request())) { - m_global_mdl_request= new (buff) MDL_request(); m_global_mdl_request->init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); } @@ -3823,7 +3835,8 @@ MDL_request *Open_table_context::get_global_mdl_request(THD *thd) bool Open_table_context:: -request_backoff_action(enum_open_table_action action_arg) +request_backoff_action(enum_open_table_action action_arg, + MDL_request *mdl_request, TABLE_LIST *table) { /* We are inside a transaction that already holds locks and have @@ -3847,6 +3860,19 @@ request_backoff_action(enum_open_table_action action_arg) return TRUE; } m_action= action_arg; + /* + If waiting for metadata lock is requested, a pointer to + MDL_request object for which we were unable to acquire the + lock is required. + */ + DBUG_ASSERT(m_action != OT_WAIT_MDL_LOCK || mdl_request); + m_failed_mdl_request= mdl_request; + /* + If auto-repair or discovery are requested, a pointer to table + list element must be provided. + */ + DBUG_ASSERT((m_action != OT_DISCOVER && m_action != OT_REPAIR) || table); + m_failed_table= table; return FALSE; } @@ -3855,10 +3881,6 @@ request_backoff_action(enum_open_table_action action_arg) Recover from failed attempt of open table by performing requested action. @param thd Thread context - @param mdl_request MDL_request of the object that caused the problem. - @param table Optional (can be NULL). Used only if action is OT_REPAIR. - In that case a TABLE_LIST for the table to be repaired. - @todo: It's unnecessary and should be removed. @pre This function should be called only with "action" != OT_NO_ACTION and after having called @sa close_tables_for_reopen(). @@ -3869,8 +3891,7 @@ request_backoff_action(enum_open_table_action action_arg) bool Open_table_context:: -recover_from_failed_open(THD *thd, MDL_request *mdl_request, - TABLE_LIST *table) +recover_from_failed_open(THD *thd) { bool result= FALSE; /* @@ -3882,7 +3903,8 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, switch (m_action) { case OT_WAIT_MDL_LOCK: - result= thd->mdl_context.wait_for_lock(mdl_request, get_timeout()); + result= thd->mdl_context.wait_for_lock(m_failed_mdl_request, + get_timeout()); break; case OT_WAIT_TDC: result= tdc_wait_for_old_versions(thd, &m_mdl_requests, get_timeout()); @@ -3891,7 +3913,7 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, case OT_DISCOVER: { MDL_request mdl_global_request; - MDL_request mdl_xlock_request(mdl_request); + MDL_request mdl_xlock_request(&m_failed_table->mdl_request); MDL_request_list mdl_requests; mdl_global_request.init(MDL_key::GLOBAL, "", "", @@ -3905,14 +3927,11 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, thd->mdl_context.acquire_locks(&mdl_requests, get_timeout()))) break; - DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE); mysql_mutex_lock(&LOCK_open); - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, - mdl_request->key.db_name(), - mdl_request->key.name()); - ha_create_table_from_engine(thd, - mdl_request->key.db_name(), - mdl_request->key.name()); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db, + m_failed_table->table_name); + ha_create_table_from_engine(thd, m_failed_table->db, + m_failed_table->table_name); mysql_mutex_unlock(&LOCK_open); thd->warning_info->clear_warning_info(thd->query_id); @@ -3923,7 +3942,7 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, case OT_REPAIR: { MDL_request mdl_global_request; - MDL_request mdl_xlock_request(mdl_request); + MDL_request mdl_xlock_request(&m_failed_table->mdl_request); MDL_request_list mdl_requests; mdl_global_request.init(MDL_key::GLOBAL, "", "", @@ -3937,14 +3956,12 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, thd->mdl_context.acquire_locks(&mdl_requests, get_timeout()))) break; - DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE); mysql_mutex_lock(&LOCK_open); - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, - mdl_request->key.db_name(), - mdl_request->key.name()); + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db, + m_failed_table->table_name); mysql_mutex_unlock(&LOCK_open); - result= auto_repair_table(thd, table); + result= auto_repair_table(thd, m_failed_table); thd->mdl_context.release_transactional_locks(); break; } @@ -3953,6 +3970,13 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, } /* Remove all old requests, they will be re-added. */ m_mdl_requests.empty(); + /* + Reset the pointers to conflicting MDL request and the + TABLE_LIST element, set when we need auto-discovery or repair, + for safety. + */ + m_failed_mdl_request= NULL; + m_failed_table= NULL; /* Prepare for possible another back-off. */ m_action= OT_NO_ACTION; return result; @@ -4081,7 +4105,8 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, if (rt->mdl_request.ticket == NULL) { /* A lock conflict. Someone's trying to modify SP metadata. */ - ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK); + ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK, + &rt->mdl_request, NULL); DBUG_RETURN(TRUE); } DEBUG_SYNC(thd, "after_shared_lock_pname"); @@ -4228,12 +4253,14 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, */ if (tables->view) { + MDL_ticket *mdl_ticket; /* We still need to take a MDL lock on the merged view to protect it from concurrent changes. */ - if (!open_table_get_mdl_lock(thd, tables, &tables->mdl_request, - ot_ctx, flags)) + if (!open_table_get_mdl_lock(thd, ot_ctx, &tables->mdl_request, + flags, &mdl_ticket) && + mdl_ticket != NULL) goto process_view_routines; /* Fall-through to return error. */ } @@ -4423,6 +4450,8 @@ end: should be acquired. @param tables_end End of list of tables. @param ot_ctx Context of open_tables() operation. + @param flags Bitmap of flags to modify how the tables will be + open, see open_table() description for details. @retval FALSE Success. @retval TRUE Failure (e.g. connection was killed) @@ -4431,31 +4460,30 @@ end: static bool open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, TABLE_LIST *tables_end, - Open_table_context *ot_ctx) + Open_table_context *ot_ctx, + uint flags) { MDL_request_list mdl_requests; TABLE_LIST *table; DBUG_ASSERT(!thd->locked_tables_mode); - DEBUG_SYNC(thd, "open_tables_acquire_upgradable_mdl"); for (table= tables_start; table && table != tables_end; table= table->next_global) { - if (table->lock_type >= TL_WRITE_ALLOW_WRITE && + if (table->mdl_request.type >= MDL_SHARED_NO_WRITE && !(table->open_type == OT_TEMPORARY_ONLY || + (flags & MYSQL_OPEN_TEMPORARY_ONLY) || (table->open_type != OT_BASE_ONLY && + ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) && find_temporary_table(thd, table)))) - { - table->mdl_request.set_type(table->lock_type > TL_WRITE_ALLOW_READ ? - MDL_SHARED_NO_READ_WRITE : - MDL_SHARED_NO_WRITE); mdl_requests.push_front(&table->mdl_request); - } } if (! mdl_requests.is_empty()) { + DEBUG_SYNC(thd, "open_tables_acquire_upgradable_mdl"); + MDL_request *global_request= ot_ctx->get_global_mdl_request(thd); if (global_request == NULL) @@ -4469,11 +4497,8 @@ open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, for (table= tables_start; table && table != tables_end; table= table->next_global) { - if (table->lock_type >= TL_WRITE_ALLOW_WRITE) - { + if (table->mdl_request.type >= MDL_SHARED_NO_WRITE) table->mdl_request.ticket= NULL; - table->mdl_request.set_type(MDL_SHARED_WRITE); - } } return FALSE; @@ -4489,6 +4514,8 @@ open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, @param tables_start Start of list of tables on which upgradable locks should be searched for. @param tables_end End of list of tables. + @param flags Bitmap of flags to modify how the tables will be + open, see open_table() description for details. @retval FALSE Success. @retval TRUE Failure (e.g. connection was killed) @@ -4496,7 +4523,7 @@ open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, static bool open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, - TABLE_LIST *tables_end) + TABLE_LIST *tables_end, uint flags) { TABLE_LIST *table; @@ -4505,9 +4532,11 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, for (table= tables_start; table && table != tables_end; table= table->next_global) { - if (table->lock_type >= TL_WRITE_ALLOW_WRITE && + if (table->mdl_request.type >= MDL_SHARED_NO_WRITE && !(table->open_type == OT_TEMPORARY_ONLY || + (flags & MYSQL_OPEN_TEMPORARY_ONLY) || (table->open_type != OT_BASE_ONLY && + ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) && find_temporary_table(thd, table)))) { /* @@ -4519,8 +4548,14 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, lock, all other instances of TABLE for the same table will have the same ticket. - Note that find_table_for_mdl_upgrade() will report an error if a - ticket is not found. + Note that this works OK even for CREATE TABLE statements which + request X type of metadata lock. This is because under LOCK TABLES + such statements don't create the table but only check if it exists + or, in most complex case, only insert into it. + Thus SNRW lock should be enough. + + Note that find_table_for_mdl_upgrade() will report an error if + no suitable ticket is found. */ if (!find_table_for_mdl_upgrade(thd->open_tables, table->db, table->table_name, FALSE)) @@ -4612,21 +4647,19 @@ restart: (in non-LOCK TABLES mode) we might have to acquire upgradable semi-exclusive metadata locks (SNW or SNRW) on some of the tables to be opened. - So we acquire all such locks at once here as doing this in one + When executing CREATE TABLE .. If NOT EXISTS .. SELECT, the + table may not yet exist, in which case we acquire an exclusive + lock. + We acquire all such locks at once here as doing this in one by one fashion may lead to deadlocks or starvation. Later when we will be opening corresponding table pre-acquired metadata lock will be reused (thanks to the fact that in recursive case metadata locks are acquired without waiting). */ - if (flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL) + if (! (flags & (MYSQL_OPEN_HAS_MDL_LOCK | + MYSQL_OPEN_FORCE_SHARED_MDL | + MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL))) { - /* - open_tables_acquire_upgradable_mdl() does not currenly handle - these two flags. At this point, that does not matter as they - are not used together with MYSQL_OPEN_TAKE_UPGRADABLE_MDL. - */ - DBUG_ASSERT(!(flags & (MYSQL_OPEN_SKIP_TEMPORARY | - MYSQL_OPEN_TEMPORARY_ONLY))); if (thd->locked_tables_mode) { /* @@ -4634,7 +4667,8 @@ restart: need to check if appropriate locks were pre-acquired. */ if (open_tables_check_upgradable_mdl(thd, *start, - thd->lex->first_not_own_table())) + thd->lex->first_not_own_table(), + flags)) { error= TRUE; goto err; @@ -4642,7 +4676,7 @@ restart: } else if (open_tables_acquire_upgradable_mdl(thd, *start, thd->lex->first_not_own_table(), - &ot_ctx)) + &ot_ctx, flags)) { error= TRUE; goto err; @@ -4688,7 +4722,6 @@ restart: have failed to open since closing tables can trigger removal of elements from the table list (if MERGE tables are involved), */ - TABLE_LIST *failed_table= *table_to_open; close_tables_for_reopen(thd, start, ot_ctx.start_of_statement_svp()); /* @@ -4696,8 +4729,7 @@ restart: TABLE_LIST element. Altough currently this assumption is valid it may change in future. */ - if (ot_ctx.recover_from_failed_open(thd, &failed_table->mdl_request, - failed_table)) + if (ot_ctx.recover_from_failed_open(thd)) goto err; error= FALSE; @@ -4741,7 +4773,7 @@ restart: { close_tables_for_reopen(thd, start, ot_ctx.start_of_statement_svp()); - if (ot_ctx.recover_from_failed_open(thd, &rt->mdl_request, NULL)) + if (ot_ctx.recover_from_failed_open(thd)) goto err; error= FALSE; @@ -5163,6 +5195,9 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, /* open_ltable can be used only for BASIC TABLEs */ table_list->required_type= FRMTYPE_TABLE; + /* This function can't properly handle requests for such metadata locks. */ + DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_NO_WRITE); + while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, lock_flags)) && ot_ctx.can_recover_from_failed_open()) { @@ -5173,8 +5208,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, */ thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp()); table_list->mdl_request.ticket= 0; - if (ot_ctx.recover_from_failed_open(thd, &table_list->mdl_request, - table_list)) + if (ot_ctx.recover_from_failed_open(thd)) break; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 916b79f8353..d494fdf86b5 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1329,9 +1329,9 @@ public: }; Open_table_context(THD *thd, ulong timeout); - bool recover_from_failed_open(THD *thd, MDL_request *mdl_request, - TABLE_LIST *table); - bool request_backoff_action(enum_open_table_action action_arg); + bool recover_from_failed_open(THD *thd); + bool request_backoff_action(enum_open_table_action action_arg, + MDL_request *mdl_request, TABLE_LIST *table); void add_request(MDL_request *request) { m_mdl_requests.push_front(request); } @@ -1362,6 +1362,14 @@ private: MDL_request_list m_mdl_requests; /** Back off action. */ enum enum_open_table_action m_action; + /** For OT_WAIT_MDL_LOCK action, the request for which we should wait. */ + MDL_request *m_failed_mdl_request; + /** + For OT_DISCOVER and OT_REPAIR actions, the table list element for + the table which definition should be re-discovered or which + should be repaired. + */ + TABLE_LIST *m_failed_table; MDL_ticket *m_start_of_statement_svp; /** Whether we had any locks when this context was created. diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 15fdd842e34..2e48475f298 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1203,6 +1203,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, table_list->alias= table_list->table_name; // If lower_case_table_names=2 table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix); + table_list->mdl_request.init(MDL_key::TABLE, table_list->db, + table_list->table_name, MDL_EXCLUSIVE); /* Link into list */ (*tot_list_next)= table_list; tot_list_next= &table_list->next_local; @@ -1918,9 +1920,11 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db) Table_ident *new_ident= new Table_ident(thd, new_db, table_str, 0); if (!old_ident || !new_ident || !sl->add_table_to_list(thd, old_ident, NULL, - TL_OPTION_UPDATING, TL_IGNORE) || + TL_OPTION_UPDATING, TL_IGNORE, + MDL_EXCLUSIVE) || !sl->add_table_to_list(thd, new_ident, NULL, - TL_OPTION_UPDATING, TL_IGNORE)) + TL_OPTION_UPDATING, TL_IGNORE, + MDL_EXCLUSIVE)) { error= 1; my_dirend(dirp); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 1795bc272f1..48474609b9b 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1978,6 +1978,7 @@ TABLE_LIST *st_select_lex_node::add_table_to_list (THD *thd, Table_ident *table, LEX_STRING *alias, ulong table_join_options, thr_lock_type flags, + enum_mdl_type mdl_type, List<Index_hint> *hints, LEX_STRING *option) { diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 2ce6bdeed42..38aac9a1042 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -502,6 +502,7 @@ public: LEX_STRING *alias, ulong table_options, thr_lock_type flags= TL_UNLOCK, + enum_mdl_type mdl_type= MDL_SHARED_READ, List<Index_hint> *hints= 0, LEX_STRING *option= 0); virtual void set_lock_for_tables(thr_lock_type lock_type) {} @@ -799,6 +800,7 @@ public: LEX_STRING *alias, ulong table_options, thr_lock_type flags= TL_UNLOCK, + enum_mdl_type mdl_type= MDL_SHARED_READ, List<Index_hint> *hints= 0, LEX_STRING *option= 0); TABLE_LIST* get_table_list(); @@ -2249,6 +2251,7 @@ public: yacc_yyvs= NULL; m_set_signal_info.clear(); m_lock_type= TL_READ_DEFAULT; + m_mdl_type= MDL_SHARED_READ; } ~Yacc_state(); @@ -2260,6 +2263,7 @@ public: void reset_before_substatement() { m_lock_type= TL_READ_DEFAULT; + m_mdl_type= MDL_SHARED_READ; } /** @@ -2299,6 +2303,12 @@ public: */ thr_lock_type m_lock_type; + /** + The type of requested metadata lock for tables added to + the statement table list. + */ + enum_mdl_type m_mdl_type; + /* TODO: move more attributes from the LEX structure here. */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 60b2ffa4179..4c3f44ea75c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1606,7 +1606,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, /* 'parent_lex' is used in init_query() so it must be before it. */ schema_select_lex->parent_lex= lex; schema_select_lex->init_query(); - if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ)) + if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ, + MDL_SHARED_READ)) DBUG_RETURN(1); lex->query_tables_last= query_tables_last; break; @@ -2544,7 +2545,7 @@ case SQLCOM_PREPARE: /* Set strategies: reset default or 'prepared' values. */ create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; - create_table->lock_strategy= TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL; + create_table->lock_strategy= TABLE_LIST::OTLS_DOWNGRADE_IF_EXISTS; /* Close any open handlers for the table @@ -3502,16 +3503,13 @@ end_with_restore_list: thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE)) goto error; - init_mdl_requests(all_tables); - thd->variables.option_bits|= OPTION_TABLE_LOCK; thd->in_lock_tables=1; { Lock_tables_prelocking_strategy lock_tables_prelocking_strategy; - res= (open_and_lock_tables(thd, all_tables, FALSE, - MYSQL_OPEN_TAKE_UPGRADABLE_MDL, + res= (open_and_lock_tables(thd, all_tables, FALSE, 0, &lock_tables_prelocking_strategy) || thd->locked_tables_list.init_locked_tables(thd)); } @@ -6014,6 +6012,7 @@ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc) - TL_OPTION_FORCE_INDEX : Force usage of index - TL_OPTION_ALIAS : an alias in multi table DELETE @param lock_type How table should be locked + @param mdl_type Type of metadata lock to acquire on the table. @param use_index List of indexed used in USE INDEX @param ignore_index List of indexed used in IGNORE INDEX @@ -6028,6 +6027,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, LEX_STRING *alias, ulong table_options, thr_lock_type lock_type, + enum_mdl_type mdl_type, List<Index_hint> *index_hints_arg, LEX_STRING *option) { @@ -6175,9 +6175,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->next_name_resolution_table= NULL; /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); - ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, - (ptr->lock_type >= TL_WRITE_ALLOW_WRITE) ? - MDL_SHARED_WRITE : MDL_SHARED_READ); + ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type); DBUG_RETURN(ptr); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index c2d3c595d95..e5d7514d9f5 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1690,7 +1690,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt) for the prepare phase. */ create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; - create_table->lock_strategy= TABLE_LIST::SHARED_MDL; + create_table->lock_strategy= TABLE_LIST::OTLS_NONE; if (select_lex->item_list.elements) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f1db513d0e2..a33bc5943da 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2358,7 +2358,7 @@ int make_table_list(THD *thd, SELECT_LEX *sel, Table_ident *table_ident; table_ident= new Table_ident(thd, *db_name, *table_name, 1); sel->init_query(); - if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ)) + if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, MDL_SHARED_READ)) return 1; return 0; } @@ -6582,7 +6582,7 @@ int make_schema_select(THD *thd, SELECT_LEX *sel, strlen(schema_table->table_name), 0); if (schema_table->old_format(thd, schema_table) || /* Handle old syntax */ !sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0), - 0, 0, TL_READ)) + 0, 0, TL_READ, MDL_SHARED_READ)) { DBUG_RETURN(1); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 2b8e7de3a60..b2a950ca4b0 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4656,6 +4656,14 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, strxmov(table_name, db, ".", table->table_name, NullS); thd->open_options|= extra_open_options; table->lock_type= lock_type; + /* + To make code safe for re-execution we need to reset type of MDL + request as code below may change it. + To allow concurrent execution of read-only operations we acquire + weak metadata lock for them. + */ + table->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_READ) ? + MDL_SHARED_NO_READ_WRITE : MDL_SHARED_READ); /* open only one table from local list of command */ { TABLE_LIST *save_next_global, *save_next_local; @@ -4677,8 +4685,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (view_operator_func == NULL) table->required_type=FRMTYPE_TABLE; - open_error= open_and_lock_tables(thd, table, TRUE, - MYSQL_OPEN_TAKE_UPGRADABLE_MDL); + open_error= open_and_lock_tables(thd, table, TRUE, 0); thd->no_warnings_for_error= 0; table->next_global= save_next_global; table->next_local= save_next_local; @@ -5024,6 +5031,7 @@ send_result_message: /* Clear the ticket released in close_thread_tables(). */ table->mdl_request.ticket= NULL; DEBUG_SYNC(thd, "ha_admin_open_ltable"); + table->mdl_request.set_type(MDL_SHARED_WRITE); if ((table->table= open_ltable(thd, table, lock_type, 0))) { result_code= table->table->file->ha_analyze(thd, check_opt); @@ -5461,6 +5469,7 @@ mysql_discard_or_import_tablespace(THD *thd, not complain when we lock the table */ thd->tablespace_op= TRUE; + table_list->mdl_request.set_type(MDL_SHARED_WRITE); if (!(table=open_ltable(thd, table_list, TL_WRITE, 0))) { thd->tablespace_op=FALSE; @@ -6568,6 +6577,12 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE)) DBUG_RETURN(TRUE); + + /* + TODO/FIXME: Get rid of this code branch if possible. To add insult + to injury it breaks locking protocol. + */ + table_list->mdl_request.set_type(MDL_EXCLUSIVE); if (lock_table_names(thd, table_list)) { error= 1; @@ -6608,8 +6623,7 @@ view_err: Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info); - error= open_and_lock_tables(thd, table_list, FALSE, - MYSQL_OPEN_TAKE_UPGRADABLE_MDL, + error= open_and_lock_tables(thd, table_list, FALSE, 0, &alter_prelocking_strategy); if (error) @@ -7904,6 +7918,10 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list) table_list->table= NULL; /* Same applies to MDL ticket. */ table_list->mdl_request.ticket= NULL; + /* Set lock type which is appropriate for ALTER TABLE. */ + table_list->lock_type= TL_WRITE_ALLOW_READ; + /* Same applies to MDL request. */ + table_list->mdl_request.set_type(MDL_SHARED_NO_WRITE); bzero((char*) &create_info, sizeof(create_info)); create_info.row_type=ROW_TYPE_NOT_USED; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 9ce62d9f2a4..ae09898ada2 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -489,8 +489,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) else { tables->table= open_n_lock_single_table(thd, tables, - TL_WRITE_ALLOW_READ, - MYSQL_OPEN_TAKE_UPGRADABLE_MDL); + TL_WRITE_ALLOW_READ, 0); if (! tables->table) goto end; tables->table->use_all_columns(); @@ -1667,7 +1666,8 @@ bool add_table_for_trigger(THD *thd, DBUG_RETURN(TRUE); *table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str, - tbl_name.str, TL_IGNORE); + tbl_name.str, TL_IGNORE, + MDL_SHARED_NO_WRITE); DBUG_RETURN(*table ? FALSE : TRUE); } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 3a6866f4a7e..4eee9502177 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -433,7 +433,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, lex->link_first_table_back(view, link_to_local); view->open_strategy= TABLE_LIST::OPEN_STUB; - view->lock_strategy= TABLE_LIST::EXCLUSIVE_MDL; + view->lock_strategy= TABLE_LIST::OTLS_NONE; view->open_type= OT_BASE_ONLY; if (open_and_lock_tables(thd, lex->query_tables, TRUE, 0)) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index aa336f3c072..e5875663d4e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -697,7 +697,8 @@ static bool add_create_index_prepare (LEX *lex, Table_ident *table) lex->sql_command= SQLCOM_CREATE_INDEX; if (!lex->current_select->add_table_to_list(lex->thd, table, NULL, TL_OPTION_UPDATING, - TL_WRITE_ALLOW_READ)) + TL_WRITE_ALLOW_READ, + MDL_SHARED_NO_WRITE)) return TRUE; lex->alter_info.reset(); lex->alter_info.flags= ALTER_ADD_INDEX; @@ -2023,7 +2024,7 @@ create: lex->sql_command= SQLCOM_CREATE_TABLE; if (!lex->select_lex.add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING, - TL_WRITE)) + TL_WRITE, MDL_EXCLUSIVE)) MYSQL_YYABORT; lex->alter_info.reset(); lex->col_list.empty(); @@ -4213,7 +4214,8 @@ create2: lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE; src_table= lex->select_lex.add_table_to_list(thd, $2, NULL, 0, - TL_READ); + TL_READ, + MDL_SHARED_READ); if (! src_table) MYSQL_YYABORT; /* CREATE TABLE ... LIKE is not allowed for views. */ @@ -4227,7 +4229,8 @@ create2: lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE; src_table= lex->select_lex.add_table_to_list(thd, $3, NULL, 0, - TL_READ); + TL_READ, + MDL_SHARED_READ); if (! src_table) MYSQL_YYABORT; /* CREATE TABLE ... LIKE is not allowed for views. */ @@ -6154,7 +6157,8 @@ alter: lex->duplicates= DUP_ERROR; if (!lex->select_lex.add_table_to_list(thd, $4, NULL, TL_OPTION_UPDATING, - TL_WRITE_ALLOW_READ)) + TL_WRITE_ALLOW_READ, + MDL_SHARED_NO_WRITE)) MYSQL_YYABORT; lex->col_list.empty(); lex->select_lex.init_order(); @@ -6847,6 +6851,8 @@ checksum: { LEX *lex=Lex; lex->sql_command = SQLCOM_CHECKSUM; + /* Will be overriden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; } table_list opt_checksum_type {} @@ -6866,6 +6872,8 @@ repair: lex->no_write_to_binlog= $2; lex->check_opt.init(); lex->alter_info.reset(); + /* Will be overriden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; } table_list opt_mi_repair_type {} @@ -6895,6 +6903,8 @@ analyze: lex->no_write_to_binlog= $2; lex->check_opt.init(); lex->alter_info.reset(); + /* Will be overriden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; } table_list {} @@ -6921,6 +6931,8 @@ check: lex->sql_command = SQLCOM_CHECK; lex->check_opt.init(); lex->alter_info.reset(); + /* Will be overriden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; } table_list opt_mi_check_type {} @@ -6953,6 +6965,8 @@ optimize: lex->no_write_to_binlog= $2; lex->check_opt.init(); lex->alter_info.reset(); + /* Will be overriden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; } table_list {} @@ -7001,9 +7015,9 @@ table_to_table: LEX *lex=Lex; SELECT_LEX *sl= lex->current_select; if (!sl->add_table_to_list(lex->thd, $1,NULL,TL_OPTION_UPDATING, - TL_IGNORE) || + TL_IGNORE, MDL_EXCLUSIVE) || !sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING, - TL_IGNORE)) + TL_IGNORE, MDL_EXCLUSIVE)) MYSQL_YYABORT; } ; @@ -7034,7 +7048,8 @@ keycache_list: assign_to_keycache: table_ident cache_keys_spec { - if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ, + if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ, + MDL_SHARED_READ, Select->pop_index_hints())) MYSQL_YYABORT; } @@ -7044,6 +7059,7 @@ assign_to_keycache_parts: table_ident adm_partition cache_keys_spec { if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ, + MDL_SHARED_READ, Select->pop_index_hints())) MYSQL_YYABORT; } @@ -7079,6 +7095,7 @@ preload_keys: table_ident cache_keys_spec opt_ignore_leaves { if (!Select->add_table_to_list(YYTHD, $1, NULL, $3, TL_READ, + MDL_SHARED_READ, Select->pop_index_hints())) MYSQL_YYABORT; } @@ -7088,6 +7105,7 @@ preload_keys_parts: table_ident adm_partition cache_keys_spec opt_ignore_leaves { if (!Select->add_table_to_list(YYTHD, $1, NULL, $4, TL_READ, + MDL_SHARED_READ, Select->pop_index_hints())) MYSQL_YYABORT; } @@ -9220,6 +9238,7 @@ table_factor: if (!($$= Select->add_table_to_list(YYTHD, $2, $3, Select->get_table_join_options(), YYPS->m_lock_type, + YYPS->m_mdl_type, Select->pop_index_hints()))) MYSQL_YYABORT; Select->add_joined_table($$); @@ -9291,7 +9310,7 @@ table_factor: MYSQL_YYABORT; if (!($$= sel->add_table_to_list(lex->thd, new Table_ident(unit), $5, 0, - TL_READ))) + TL_READ, MDL_SHARED_READ))) MYSQL_YYABORT; sel->add_joined_table($$); @@ -10126,13 +10145,17 @@ do: */ drop: - DROP opt_temporary table_or_tables if_exists table_list opt_restrict + DROP opt_temporary table_or_tables if_exists { LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_TABLE; lex->drop_temporary= $2; lex->drop_if_exists= $4; + YYPS->m_lock_type= TL_IGNORE; + YYPS->m_mdl_type= MDL_EXCLUSIVE; } + table_list opt_restrict + {} | DROP INDEX_SYM ident ON table_ident {} { LEX *lex=Lex; @@ -10145,7 +10168,8 @@ drop: lex->alter_info.drop_list.push_back(ad); if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL, TL_OPTION_UPDATING, - TL_WRITE_ALLOW_READ)) + TL_WRITE_ALLOW_READ, + MDL_SHARED_NO_WRITE)) MYSQL_YYABORT; } | DROP DATABASE if_exists ident @@ -10215,12 +10239,16 @@ drop: { Lex->sql_command = SQLCOM_DROP_USER; } - | DROP VIEW_SYM if_exists table_list opt_restrict + | DROP VIEW_SYM if_exists { LEX *lex= Lex; lex->sql_command= SQLCOM_DROP_VIEW; lex->drop_if_exists= $3; + YYPS->m_lock_type= TL_IGNORE; + YYPS->m_mdl_type= MDL_EXCLUSIVE; } + table_list opt_restrict + {} | DROP EVENT_SYM if_exists sp_name { Lex->drop_if_exists= $3; @@ -10261,7 +10289,10 @@ table_list: table_name: table_ident { - if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING)) + if (!Select->add_table_to_list(YYTHD, $1, NULL, + TL_OPTION_UPDATING, + YYPS->m_lock_type, + YYPS->m_mdl_type)) MYSQL_YYABORT; } ; @@ -10276,7 +10307,8 @@ table_alias_ref: { if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING | TL_OPTION_ALIAS, - YYPS->m_lock_type)) + YYPS->m_lock_type, + YYPS->m_mdl_type)) MYSQL_YYABORT; } ; @@ -10558,6 +10590,8 @@ delete: lex->sql_command= SQLCOM_DELETE; mysql_init_select(lex); YYPS->m_lock_type= TL_WRITE_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_WRITE; + lex->ignore= 0; lex->select_lex.init_order(); } @@ -10568,9 +10602,11 @@ single_multi: FROM table_ident { if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING, - YYPS->m_lock_type)) + YYPS->m_lock_type, + YYPS->m_mdl_type)) MYSQL_YYABORT; YYPS->m_lock_type= TL_READ_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_READ; } where_clause opt_order_clause delete_limit_clause {} @@ -10578,6 +10614,7 @@ single_multi: { mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_READ; } FROM join_table_list where_clause { @@ -10588,6 +10625,7 @@ single_multi: { mysql_init_multi_delete(Lex); YYPS->m_lock_type= TL_READ_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_READ; } USING join_table_list where_clause { @@ -10611,7 +10649,8 @@ table_wild_one: ti, NULL, TL_OPTION_UPDATING | TL_OPTION_ALIAS, - YYPS->m_lock_type)) + YYPS->m_lock_type, + YYPS->m_mdl_type)) MYSQL_YYABORT; } | ident '.' ident opt_wild @@ -10623,7 +10662,8 @@ table_wild_one: ti, NULL, TL_OPTION_UPDATING | TL_OPTION_ALIAS, - YYPS->m_lock_type)) + YYPS->m_lock_type, + YYPS->m_mdl_type)) MYSQL_YYABORT; } ; @@ -11137,7 +11177,15 @@ flush: flush_options: table_or_tables - { Lex->type|= REFRESH_TABLES; } + { + Lex->type|= REFRESH_TABLES; + /* + Set type of metadata and table locks for + FLUSH TABLES table_list WITH READ LOCK. + */ + YYPS->m_lock_type= TL_READ_NO_INSERT; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } opt_table_list {} opt_with_read_lock {} | flush_options_list @@ -11301,7 +11349,7 @@ load: { LEX *lex=Lex; if (!Select->add_table_to_list(YYTHD, $12, NULL, TL_OPTION_UPDATING, - $4)) + $4, MDL_SHARED_WRITE)) MYSQL_YYABORT; lex->field_list.empty(); lex->update_list.empty(); @@ -13007,10 +13055,14 @@ table_lock: table_ident opt_table_alias lock_option { thr_lock_type lock_type= (thr_lock_type) $3; - if (!Select->add_table_to_list(YYTHD, $1, $2, 0, lock_type)) + bool lock_for_write= (lock_type >= TL_WRITE_ALLOW_WRITE); + if (!Select->add_table_to_list(YYTHD, $1, $2, 0, lock_type, + (lock_for_write ? + MDL_SHARED_NO_READ_WRITE : + MDL_SHARED_READ))) MYSQL_YYABORT; /* If table is to be write locked, protect from a impending GRL. */ - if (lock_type >= TL_WRITE_ALLOW_WRITE) + if (lock_for_write) Lex->protect_against_global_read_lock= TRUE; } ; @@ -13765,6 +13817,7 @@ query_expression_option: if (check_simple_select()) MYSQL_YYABORT; YYPS->m_lock_type= TL_READ_HIGH_PRIORITY; + YYPS->m_mdl_type= MDL_SHARED_READ; Select->options|= SELECT_HIGH_PRIORITY; } | DISTINCT { Select->options|= SELECT_DISTINCT; } @@ -13894,7 +13947,10 @@ view_tail: LEX *lex= thd->lex; lex->sql_command= SQLCOM_CREATE_VIEW; /* first table in list is target VIEW name */ - if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING)) + if (!lex->select_lex.add_table_to_list(thd, $3, NULL, + TL_OPTION_UPDATING, + TL_IGNORE, + MDL_EXCLUSIVE)) MYSQL_YYABORT; } view_list_opt AS view_select @@ -14034,7 +14090,8 @@ trigger_tail: if (!lex->select_lex.add_table_to_list(YYTHD, $9, (LEX_STRING*) 0, TL_OPTION_UPDATING, - TL_IGNORE)) + TL_WRITE_ALLOW_READ, + MDL_SHARED_NO_WRITE)) MYSQL_YYABORT; } ; diff --git a/sql/table.cc b/sql/table.cc index 65918dd58f9..1e7cb747bc6 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4659,13 +4659,6 @@ void TABLE_LIST::reinit_before_use(THD *thd) parent_embedding->nested_join->join_list.head() == embedded); mdl_request.ticket= NULL; - /* - Since we manipulate with the metadata lock type in open_table(), - we need to reset it to the parser default, to restore things back - to first-execution state. - */ - mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ? - MDL_SHARED_WRITE : MDL_SHARED_READ); } /* diff --git a/sql/table.h b/sql/table.h index fcff0cfb0d5..ea585208b83 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1581,20 +1581,21 @@ struct TABLE_LIST OPEN_STUB } open_strategy; /** - Indicates the locking strategy for the object being opened: - whether the associated metadata lock is shared or exclusive. + Indicates the locking strategy for the object being opened. */ enum { - /* Take a shared metadata lock before the object is opened. */ - SHARED_MDL= 0, /* - Take a exclusive metadata lock before the object is opened. - If opening is successful, downgrade to a shared lock. + Take metadata lock specified by 'mdl_request' member before + the object is opened. Do nothing after that. */ - EXCLUSIVE_DOWNGRADABLE_MDL, - /* Take a exclusive metadata lock before the object is opened. */ - EXCLUSIVE_MDL + OTLS_NONE= 0, + /* + Take (exclusive) metadata lock specified by 'mdl_request' member + before object is opened. If opening is successful, downgrade to + a shared lock. + */ + OTLS_DOWNGRADE_IF_EXISTS } lock_strategy; /* For transactional locking. */ int lock_timeout; /* NOWAIT or WAIT [X] */ |