Pre-requisite patch for bug #51263 "Deadlock between

transactional SELECT and ALTER TABLE ... REBUILD PARTITION".

The goal of this patch is to decouple type of metadata
lock acquired for table by open_tables() from type of
table-level lock to be acquired on it.

To achieve this we change approach to how we determine what
type of metadata lock should be acquired on table to be open.
Now instead of inferring it at open_tables() time from flags
and type of table-level lock we rely on that type of metadata
lock is properly set at parsing time and is not changed
further.
This commit is contained in:
Dmitry Lenev 2010-05-25 16:35:01 +04:00
parent 6ceacd4fb9
commit a3c080be7a
20 changed files with 359 additions and 227 deletions

View File

@ -7408,9 +7408,10 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str)); DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str));
// Delete the table and all related files // Delete the table and all related files
TABLE_LIST table_list; TABLE_LIST table_list;
bzero((char*) &table_list,sizeof(table_list)); table_list.init_one_table(db, strlen(db), file_name_str,
table_list.db= (char*) db; strlen(file_name_str), file_name_str,
table_list.alias= table_list.table_name= (char*)file_name_str; TL_WRITE);
table_list.mdl_request.set_tpye(MDL_EXCLUSIVE);
(void)mysql_rm_table_part2(thd, &table_list, (void)mysql_rm_table_part2(thd, &table_list,
FALSE, /* if_exists */ FALSE, /* if_exists */
FALSE, /* drop_temporary */ FALSE, /* drop_temporary */

View File

@ -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 before calling it. Also it cannot be called while holding
LOCK_open mutex. Both these invariants are enforced by asserts LOCK_open mutex. Both these invariants are enforced by asserts
in MDL_context::acquire_locks(). 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 FALSE Success.
@retval TRUE Failure (OOM or thread was killed). @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); global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) 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(&lock_table->mdl_request);
}
mdl_requests.push_front(&global_request); mdl_requests.push_front(&global_request);

View File

@ -15,33 +15,32 @@ typedef struct st_mysql_lock MYSQL_LOCK;
#define MYSQL_OPEN_TEMPORARY_ONLY 0x0004 #define MYSQL_OPEN_TEMPORARY_ONLY 0x0004
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008 #define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008
#define MYSQL_LOCK_LOG_TABLE 0x0010 #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 Do not try to acquire a metadata lock on the table: we
already have one. 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 If in locked tables mode, ignore the locked tables and get
a new instance of the table. 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. */ /** 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. */ /** 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. */ /** 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 Open tables using MDL_SHARED_HIGH_PRIO lock instead of one specified
in parser. 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 When opening or locking the table, use the maximum timeout
(LONG_TIMEOUT = 1 year) rather than the user-supplied timeout value. (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. */ /** Please refer to the internals manual. */
#define MYSQL_OPEN_REOPEN (MYSQL_OPEN_IGNORE_FLUSH |\ #define MYSQL_OPEN_REOPEN (MYSQL_OPEN_IGNORE_FLUSH |\

View File

@ -308,6 +308,10 @@ public:
MDL_key key; MDL_key key;
public: 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, void init(MDL_key::enum_mdl_namespace namespace_arg,
const char *db_arg, const char *name_arg, const char *db_arg, const char *name_arg,
enum_mdl_type mdl_type_arg); enum_mdl_type mdl_type_arg);

View File

@ -4010,6 +4010,11 @@ sp_head::add_used_tables_to_table_list(THD *thd,
table->prelocking_placeholder= 1; table->prelocking_placeholder= 1;
table->belong_to_view= belong_to_view; table->belong_to_view= belong_to_view;
table->trg_event_map= stab->trg_event_map; 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->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
table->lock_type >= TL_WRITE_ALLOW_WRITE ? table->lock_type >= TL_WRITE_ALLOW_WRITE ?
MDL_SHARED_WRITE : MDL_SHARED_READ); MDL_SHARED_WRITE : MDL_SHARED_READ);
@ -4040,7 +4045,8 @@ sp_head::add_used_tables_to_table_list(THD *thd,
TABLE_LIST * TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex, sp_add_to_query_tables(THD *thd, LEX *lex,
const char *db, const char *name, const char *db, const char *name,
thr_lock_type locktype) thr_lock_type locktype,
enum_mdl_type mdl_type)
{ {
TABLE_LIST *table; TABLE_LIST *table;
@ -4055,8 +4061,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
table->select_lex= lex->current_select; table->select_lex= lex->current_select;
table->cacheable_table= 1; table->cacheable_table= 1;
table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
table->lock_type >= TL_WRITE_ALLOW_WRITE ? mdl_type);
MDL_SHARED_WRITE : MDL_SHARED_READ);
lex->add_to_query_tables(table); lex->add_to_query_tables(table);
return table; return table;

View File

@ -1346,7 +1346,9 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
TABLE_LIST * TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex, sp_add_to_query_tables(THD *thd, LEX *lex,
const char *db, const char *name, const char *db, const char *name,
thr_lock_type locktype); thr_lock_type locktype,
enum_mdl_type mdl_type);
Item * Item *
sp_prepare_func_item(THD* thd, Item **it_addr); sp_prepare_func_item(THD* thd, Item **it_addr);

View File

@ -2358,82 +2358,90 @@ void table_share_release_hook(void *share)
/** /**
A helper function that acquires an MDL lock for a table Try to acquire an MDL lock for a table being opened.
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 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, 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 MYSQL_OPEN_FORCE_SHARED_MDL flag means that we are executing
may not yet exist. Let's acquire an exclusive lock for that PREPARE for a prepared statement and want to override
case. If later it turns out the table existsed, we will the type-of-operation aware metadata lock which was set
downgrade the lock to shared. Note that, according to the in the parser/during view opening with a simple shared
locking protocol, all exclusive locks must be acquired before metadata lock.
shared locks. This invariant is preserved here and is also This is necessary to allow concurrent execution of PREPARE
enforced by asserts in metadata locking subsystem. 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); mdl_request= new (thd->mem_root) MDL_request(mdl_request);
DBUG_ASSERT(! thd->mdl_context.has_locks() || if (mdl_request == NULL)
thd->handler_tables_hash.records || return TRUE;
thd->global_read_lock.is_acquired());
if (!(global_request= ot_ctx->get_global_mdl_request(thd))) mdl_request->set_type((flags & MYSQL_OPEN_FORCE_SHARED_MDL) ?
return 1; MDL_SHARED : MDL_SHARED_HIGH_PRIO);
mdl_requests.push_front(mdl_request);
mdl_requests.push_front(global_request);
if (thd->mdl_context.acquire_locks(&mdl_requests, ot_ctx->get_timeout()))
return 1;
} }
else
ot_ctx->add_request(mdl_request);
if (thd->mdl_context.try_acquire_lock(mdl_request))
return TRUE;
if (mdl_request->ticket == NULL)
{ {
if (flags & MYSQL_OPEN_FORCE_SHARED_MDL) if (flags & MYSQL_OPEN_FAIL_ON_MDL_CONFLICT)
{ {
/* my_error(ER_WARN_I_S_SKIPPED_TABLE, MYF(0),
While executing PREPARE for prepared statement we override mdl_request->key.db_name(), mdl_request->key.name());
type-of-operation aware type of shared metadata lock which return TRUE;
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);
if (thd->mdl_context.try_acquire_lock(mdl_request))
return 1;
if (mdl_request->ticket == NULL)
{
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;
} }
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 is never opened. In both cases, metadata locks are always taken according
to the lock strategy. to the lock strategy.
This function will take a exclusive metadata lock on the table if If the lock strategy is OTLS_DOWNGRADE_IF_EXISTS and opening the table
TABLE_LIST::lock_strategy is EXCLUSIVE_DOWNGRADABLE_MDL or EXCLUSIVE_MDL. is successful, the exclusive metadata lock acquired by the caller
If the lock strategy is EXCLUSIVE_DOWNGRADABLE_MDL and opening the table is downgraded to a shared lock.
is successful, the exclusive metadata lock is downgraded to a shared
lock.
RETURN RETURN
TRUE Open failed. "action" parameter may contain type of action 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]; char key[MAX_DBKEY_LENGTH];
uint key_length; uint key_length;
char *alias= table_list->alias; char *alias= table_list->alias;
MDL_request *mdl_request;
MDL_ticket *mdl_ticket; MDL_ticket *mdl_ticket;
int error; int error;
TABLE_SHARE *share; 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) 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); 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. This is the normal use case.
*/ */
mdl_request= &table_list->mdl_request;
if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) 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"); DEBUG_SYNC(thd, "before_open_table_wait_refresh");
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
DEBUG_SYNC(thd, "after_open_table_mdl_shared"); DEBUG_SYNC(thd, "after_open_table_mdl_shared");
} }
else
/* {
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 Grab reference to the MDL lock ticket that was acquired
acquired previously (MYSQL_OPEN_HAS_MDL_LOCK). by the caller.
*/ */
mdl_ticket= mdl_request->ticket; mdl_ticket= table_list->mdl_request.ticket;
}
hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length); hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
mysql_mutex_lock(&LOCK_open); 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 */ /* Someone did a refresh while thread was opening tables */
mysql_mutex_unlock(&LOCK_open); 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); DBUG_RETURN(TRUE);
} }
@ -2878,7 +2887,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
*/ */
release_table_share(share); release_table_share(share);
mysql_mutex_unlock(&LOCK_open); 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); DBUG_RETURN(TRUE);
} }
/* Force close at once after usage */ /* 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) if (error == 7)
{ {
share->version= 0; 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) else if (share->crashed)
{ {
share->version= 0; 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; 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 table exists now we should downgrade our exclusive metadata
lock on this table to SW metadata lock. 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)) !(flags & MYSQL_OPEN_HAS_MDL_LOCK))
mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_WRITE); 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) Open_table_context::Open_table_context(THD *thd, ulong timeout)
:m_action(OT_NO_ACTION), :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_start_of_statement_svp(thd->mdl_context.mdl_savepoint()),
m_has_locks((thd->in_multi_stmt_transaction_mode() && m_has_locks((thd->in_multi_stmt_transaction_mode() &&
thd->mdl_context.has_locks()) || 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) if (! m_global_mdl_request)
{ {
char *buff; if ((m_global_mdl_request= new (thd->mem_root) MDL_request()))
if ((buff= (char*)thd->alloc(sizeof(MDL_request))))
{ {
m_global_mdl_request= new (buff) MDL_request();
m_global_mdl_request->init(MDL_key::GLOBAL, "", "", m_global_mdl_request->init(MDL_key::GLOBAL, "", "",
MDL_INTENTION_EXCLUSIVE); MDL_INTENTION_EXCLUSIVE);
} }
@ -3823,7 +3835,8 @@ MDL_request *Open_table_context::get_global_mdl_request(THD *thd)
bool bool
Open_table_context:: 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 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; return TRUE;
} }
m_action= action_arg; 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; 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. Recover from failed attempt of open table by performing requested action.
@param thd Thread context @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 @pre This function should be called only with "action" != OT_NO_ACTION
and after having called @sa close_tables_for_reopen(). and after having called @sa close_tables_for_reopen().
@ -3869,8 +3891,7 @@ request_backoff_action(enum_open_table_action action_arg)
bool bool
Open_table_context:: Open_table_context::
recover_from_failed_open(THD *thd, MDL_request *mdl_request, recover_from_failed_open(THD *thd)
TABLE_LIST *table)
{ {
bool result= FALSE; bool result= FALSE;
/* /*
@ -3882,7 +3903,8 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
switch (m_action) switch (m_action)
{ {
case OT_WAIT_MDL_LOCK: 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; break;
case OT_WAIT_TDC: case OT_WAIT_TDC:
result= tdc_wait_for_old_versions(thd, &m_mdl_requests, get_timeout()); 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: case OT_DISCOVER:
{ {
MDL_request mdl_global_request; 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_request_list mdl_requests;
mdl_global_request.init(MDL_key::GLOBAL, "", "", 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()))) thd->mdl_context.acquire_locks(&mdl_requests, get_timeout())))
break; break;
DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE);
mysql_mutex_lock(&LOCK_open); mysql_mutex_lock(&LOCK_open);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
mdl_request->key.db_name(), m_failed_table->table_name);
mdl_request->key.name()); ha_create_table_from_engine(thd, m_failed_table->db,
ha_create_table_from_engine(thd, m_failed_table->table_name);
mdl_request->key.db_name(),
mdl_request->key.name());
mysql_mutex_unlock(&LOCK_open); mysql_mutex_unlock(&LOCK_open);
thd->warning_info->clear_warning_info(thd->query_id); 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: case OT_REPAIR:
{ {
MDL_request mdl_global_request; 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_request_list mdl_requests;
mdl_global_request.init(MDL_key::GLOBAL, "", "", 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()))) thd->mdl_context.acquire_locks(&mdl_requests, get_timeout())))
break; break;
DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE);
mysql_mutex_lock(&LOCK_open); mysql_mutex_lock(&LOCK_open);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
mdl_request->key.db_name(), m_failed_table->table_name);
mdl_request->key.name());
mysql_mutex_unlock(&LOCK_open); 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(); thd->mdl_context.release_transactional_locks();
break; break;
} }
@ -3953,6 +3970,13 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
} }
/* Remove all old requests, they will be re-added. */ /* Remove all old requests, they will be re-added. */
m_mdl_requests.empty(); 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. */ /* Prepare for possible another back-off. */
m_action= OT_NO_ACTION; m_action= OT_NO_ACTION;
return result; return result;
@ -4081,7 +4105,8 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
if (rt->mdl_request.ticket == NULL) if (rt->mdl_request.ticket == NULL)
{ {
/* A lock conflict. Someone's trying to modify SP metadata. */ /* 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); DBUG_RETURN(TRUE);
} }
DEBUG_SYNC(thd, "after_shared_lock_pname"); 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) if (tables->view)
{ {
MDL_ticket *mdl_ticket;
/* /*
We still need to take a MDL lock on the merged view to protect We still need to take a MDL lock on the merged view to protect
it from concurrent changes. it from concurrent changes.
*/ */
if (!open_table_get_mdl_lock(thd, tables, &tables->mdl_request, if (!open_table_get_mdl_lock(thd, ot_ctx, &tables->mdl_request,
ot_ctx, flags)) flags, &mdl_ticket) &&
mdl_ticket != NULL)
goto process_view_routines; goto process_view_routines;
/* Fall-through to return error. */ /* Fall-through to return error. */
} }
@ -4423,6 +4450,8 @@ end:
should be acquired. should be acquired.
@param tables_end End of list of tables. @param tables_end End of list of tables.
@param ot_ctx Context of open_tables() operation. @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 FALSE Success.
@retval TRUE Failure (e.g. connection was killed) @retval TRUE Failure (e.g. connection was killed)
@ -4431,31 +4460,30 @@ end:
static bool static bool
open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
TABLE_LIST *tables_end, TABLE_LIST *tables_end,
Open_table_context *ot_ctx) Open_table_context *ot_ctx,
uint flags)
{ {
MDL_request_list mdl_requests; MDL_request_list mdl_requests;
TABLE_LIST *table; TABLE_LIST *table;
DBUG_ASSERT(!thd->locked_tables_mode); DBUG_ASSERT(!thd->locked_tables_mode);
DEBUG_SYNC(thd, "open_tables_acquire_upgradable_mdl");
for (table= tables_start; table && table != tables_end; for (table= tables_start; table && table != tables_end;
table= table->next_global) 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 || !(table->open_type == OT_TEMPORARY_ONLY ||
(flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
(table->open_type != OT_BASE_ONLY && (table->open_type != OT_BASE_ONLY &&
! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
find_temporary_table(thd, table)))) 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); mdl_requests.push_front(&table->mdl_request);
}
} }
if (! mdl_requests.is_empty()) if (! mdl_requests.is_empty())
{ {
DEBUG_SYNC(thd, "open_tables_acquire_upgradable_mdl");
MDL_request *global_request= ot_ctx->get_global_mdl_request(thd); MDL_request *global_request= ot_ctx->get_global_mdl_request(thd);
if (global_request == NULL) 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; for (table= tables_start; table && table != tables_end;
table= table->next_global) 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.ticket= NULL;
table->mdl_request.set_type(MDL_SHARED_WRITE);
}
} }
return FALSE; 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 @param tables_start Start of list of tables on which upgradable locks
should be searched for. should be searched for.
@param tables_end End of list of tables. @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 FALSE Success.
@retval TRUE Failure (e.g. connection was killed) @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 static bool
open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
TABLE_LIST *tables_end) TABLE_LIST *tables_end, uint flags)
{ {
TABLE_LIST *table; 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; for (table= tables_start; table && table != tables_end;
table= table->next_global) 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 || !(table->open_type == OT_TEMPORARY_ONLY ||
(flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
(table->open_type != OT_BASE_ONLY && (table->open_type != OT_BASE_ONLY &&
! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
find_temporary_table(thd, table)))) 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 lock, all other instances of TABLE for the same table will have the
same ticket. same ticket.
Note that find_table_for_mdl_upgrade() will report an error if a Note that this works OK even for CREATE TABLE statements which
ticket is not found. 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, if (!find_table_for_mdl_upgrade(thd->open_tables, table->db,
table->table_name, FALSE)) table->table_name, FALSE))
@ -4612,21 +4647,19 @@ restart:
(in non-LOCK TABLES mode) we might have to acquire upgradable (in non-LOCK TABLES mode) we might have to acquire upgradable
semi-exclusive metadata locks (SNW or SNRW) on some of the semi-exclusive metadata locks (SNW or SNRW) on some of the
tables to be opened. 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 by one fashion may lead to deadlocks or starvation. Later when
we will be opening corresponding table pre-acquired metadata we will be opening corresponding table pre-acquired metadata
lock will be reused (thanks to the fact that in recursive case lock will be reused (thanks to the fact that in recursive case
metadata locks are acquired without waiting). 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) if (thd->locked_tables_mode)
{ {
/* /*
@ -4634,7 +4667,8 @@ restart:
need to check if appropriate locks were pre-acquired. need to check if appropriate locks were pre-acquired.
*/ */
if (open_tables_check_upgradable_mdl(thd, *start, if (open_tables_check_upgradable_mdl(thd, *start,
thd->lex->first_not_own_table())) thd->lex->first_not_own_table(),
flags))
{ {
error= TRUE; error= TRUE;
goto err; goto err;
@ -4642,7 +4676,7 @@ restart:
} }
else if (open_tables_acquire_upgradable_mdl(thd, *start, else if (open_tables_acquire_upgradable_mdl(thd, *start,
thd->lex->first_not_own_table(), thd->lex->first_not_own_table(),
&ot_ctx)) &ot_ctx, flags))
{ {
error= TRUE; error= TRUE;
goto err; goto err;
@ -4688,7 +4722,6 @@ restart:
have failed to open since closing tables can trigger removal of have failed to open since closing tables can trigger removal of
elements from the table list (if MERGE tables are involved), 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()); 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 TABLE_LIST element. Altough currently this assumption is valid
it may change in future. it may change in future.
*/ */
if (ot_ctx.recover_from_failed_open(thd, &failed_table->mdl_request, if (ot_ctx.recover_from_failed_open(thd))
failed_table))
goto err; goto err;
error= FALSE; error= FALSE;
@ -4741,7 +4773,7 @@ restart:
{ {
close_tables_for_reopen(thd, start, close_tables_for_reopen(thd, start,
ot_ctx.start_of_statement_svp()); 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; goto err;
error= FALSE; 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 */ /* open_ltable can be used only for BASIC TABLEs */
table_list->required_type= FRMTYPE_TABLE; 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)) && while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, lock_flags)) &&
ot_ctx.can_recover_from_failed_open()) 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()); thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp());
table_list->mdl_request.ticket= 0; table_list->mdl_request.ticket= 0;
if (ot_ctx.recover_from_failed_open(thd, &table_list->mdl_request, if (ot_ctx.recover_from_failed_open(thd))
table_list))
break; break;
} }

View File

@ -1329,9 +1329,9 @@ public:
}; };
Open_table_context(THD *thd, ulong timeout); Open_table_context(THD *thd, ulong timeout);
bool recover_from_failed_open(THD *thd, MDL_request *mdl_request, bool recover_from_failed_open(THD *thd);
TABLE_LIST *table); bool request_backoff_action(enum_open_table_action action_arg,
bool request_backoff_action(enum_open_table_action action_arg); MDL_request *mdl_request, TABLE_LIST *table);
void add_request(MDL_request *request) void add_request(MDL_request *request)
{ m_mdl_requests.push_front(request); } { m_mdl_requests.push_front(request); }
@ -1362,6 +1362,14 @@ private:
MDL_request_list m_mdl_requests; MDL_request_list m_mdl_requests;
/** Back off action. */ /** Back off action. */
enum enum_open_table_action m_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; MDL_ticket *m_start_of_statement_svp;
/** /**
Whether we had any locks when this context was created. Whether we had any locks when this context was created.

View File

@ -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->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->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 */ /* Link into list */
(*tot_list_next)= table_list; (*tot_list_next)= table_list;
tot_list_next= &table_list->next_local; 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); Table_ident *new_ident= new Table_ident(thd, new_db, table_str, 0);
if (!old_ident || !new_ident || if (!old_ident || !new_ident ||
!sl->add_table_to_list(thd, old_ident, NULL, !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, !sl->add_table_to_list(thd, new_ident, NULL,
TL_OPTION_UPDATING, TL_IGNORE)) TL_OPTION_UPDATING, TL_IGNORE,
MDL_EXCLUSIVE))
{ {
error= 1; error= 1;
my_dirend(dirp); my_dirend(dirp);

View File

@ -1978,6 +1978,7 @@ TABLE_LIST *st_select_lex_node::add_table_to_list (THD *thd, Table_ident *table,
LEX_STRING *alias, LEX_STRING *alias,
ulong table_join_options, ulong table_join_options,
thr_lock_type flags, thr_lock_type flags,
enum_mdl_type mdl_type,
List<Index_hint> *hints, List<Index_hint> *hints,
LEX_STRING *option) LEX_STRING *option)
{ {

View File

@ -502,6 +502,7 @@ public:
LEX_STRING *alias, LEX_STRING *alias,
ulong table_options, ulong table_options,
thr_lock_type flags= TL_UNLOCK, thr_lock_type flags= TL_UNLOCK,
enum_mdl_type mdl_type= MDL_SHARED_READ,
List<Index_hint> *hints= 0, List<Index_hint> *hints= 0,
LEX_STRING *option= 0); LEX_STRING *option= 0);
virtual void set_lock_for_tables(thr_lock_type lock_type) {} virtual void set_lock_for_tables(thr_lock_type lock_type) {}
@ -799,6 +800,7 @@ public:
LEX_STRING *alias, LEX_STRING *alias,
ulong table_options, ulong table_options,
thr_lock_type flags= TL_UNLOCK, thr_lock_type flags= TL_UNLOCK,
enum_mdl_type mdl_type= MDL_SHARED_READ,
List<Index_hint> *hints= 0, List<Index_hint> *hints= 0,
LEX_STRING *option= 0); LEX_STRING *option= 0);
TABLE_LIST* get_table_list(); TABLE_LIST* get_table_list();
@ -2249,6 +2251,7 @@ public:
yacc_yyvs= NULL; yacc_yyvs= NULL;
m_set_signal_info.clear(); m_set_signal_info.clear();
m_lock_type= TL_READ_DEFAULT; m_lock_type= TL_READ_DEFAULT;
m_mdl_type= MDL_SHARED_READ;
} }
~Yacc_state(); ~Yacc_state();
@ -2260,6 +2263,7 @@ public:
void reset_before_substatement() void reset_before_substatement()
{ {
m_lock_type= TL_READ_DEFAULT; m_lock_type= TL_READ_DEFAULT;
m_mdl_type= MDL_SHARED_READ;
} }
/** /**
@ -2299,6 +2303,12 @@ public:
*/ */
thr_lock_type m_lock_type; 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. TODO: move more attributes from the LEX structure here.
*/ */

View File

@ -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. */ /* 'parent_lex' is used in init_query() so it must be before it. */
schema_select_lex->parent_lex= lex; schema_select_lex->parent_lex= lex;
schema_select_lex->init_query(); 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); DBUG_RETURN(1);
lex->query_tables_last= query_tables_last; lex->query_tables_last= query_tables_last;
break; break;
@ -2544,7 +2545,7 @@ case SQLCOM_PREPARE:
/* Set strategies: reset default or 'prepared' values. */ /* Set strategies: reset default or 'prepared' values. */
create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; 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 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)) thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
goto error; goto error;
init_mdl_requests(all_tables);
thd->variables.option_bits|= OPTION_TABLE_LOCK; thd->variables.option_bits|= OPTION_TABLE_LOCK;
thd->in_lock_tables=1; thd->in_lock_tables=1;
{ {
Lock_tables_prelocking_strategy lock_tables_prelocking_strategy; Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
res= (open_and_lock_tables(thd, all_tables, FALSE, res= (open_and_lock_tables(thd, all_tables, FALSE, 0,
MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
&lock_tables_prelocking_strategy) || &lock_tables_prelocking_strategy) ||
thd->locked_tables_list.init_locked_tables(thd)); 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_FORCE_INDEX : Force usage of index
- TL_OPTION_ALIAS : an alias in multi table DELETE - TL_OPTION_ALIAS : an alias in multi table DELETE
@param lock_type How table should be locked @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 use_index List of indexed used in USE INDEX
@param ignore_index List of indexed used in IGNORE 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, LEX_STRING *alias,
ulong table_options, ulong table_options,
thr_lock_type lock_type, thr_lock_type lock_type,
enum_mdl_type mdl_type,
List<Index_hint> *index_hints_arg, List<Index_hint> *index_hints_arg,
LEX_STRING *option) LEX_STRING *option)
{ {
@ -6175,9 +6175,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->next_name_resolution_table= NULL; ptr->next_name_resolution_table= NULL;
/* Link table in global list (all used tables) */ /* Link table in global list (all used tables) */
lex->add_to_query_tables(ptr); lex->add_to_query_tables(ptr);
ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type);
(ptr->lock_type >= TL_WRITE_ALLOW_WRITE) ?
MDL_SHARED_WRITE : MDL_SHARED_READ);
DBUG_RETURN(ptr); DBUG_RETURN(ptr);
} }

View File

@ -1690,7 +1690,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
for the prepare phase. for the prepare phase.
*/ */
create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; 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) if (select_lex->item_list.elements)
{ {

View File

@ -2358,7 +2358,7 @@ int make_table_list(THD *thd, SELECT_LEX *sel,
Table_ident *table_ident; Table_ident *table_ident;
table_ident= new Table_ident(thd, *db_name, *table_name, 1); table_ident= new Table_ident(thd, *db_name, *table_name, 1);
sel->init_query(); 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 1;
return 0; return 0;
} }
@ -6582,7 +6582,7 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
strlen(schema_table->table_name), 0); strlen(schema_table->table_name), 0);
if (schema_table->old_format(thd, schema_table) || /* Handle old syntax */ if (schema_table->old_format(thd, schema_table) || /* Handle old syntax */
!sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0), !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); DBUG_RETURN(1);
} }

View File

@ -4656,6 +4656,14 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
strxmov(table_name, db, ".", table->table_name, NullS); strxmov(table_name, db, ".", table->table_name, NullS);
thd->open_options|= extra_open_options; thd->open_options|= extra_open_options;
table->lock_type= lock_type; 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 */ /* open only one table from local list of command */
{ {
TABLE_LIST *save_next_global, *save_next_local; 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) if (view_operator_func == NULL)
table->required_type=FRMTYPE_TABLE; table->required_type=FRMTYPE_TABLE;
open_error= open_and_lock_tables(thd, table, TRUE, open_error= open_and_lock_tables(thd, table, TRUE, 0);
MYSQL_OPEN_TAKE_UPGRADABLE_MDL);
thd->no_warnings_for_error= 0; thd->no_warnings_for_error= 0;
table->next_global= save_next_global; table->next_global= save_next_global;
table->next_local= save_next_local; table->next_local= save_next_local;
@ -5024,6 +5031,7 @@ send_result_message:
/* Clear the ticket released in close_thread_tables(). */ /* Clear the ticket released in close_thread_tables(). */
table->mdl_request.ticket= NULL; table->mdl_request.ticket= NULL;
DEBUG_SYNC(thd, "ha_admin_open_ltable"); 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))) if ((table->table= open_ltable(thd, table, lock_type, 0)))
{ {
result_code= table->table->file->ha_analyze(thd, check_opt); 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 not complain when we lock the table
*/ */
thd->tablespace_op= TRUE; thd->tablespace_op= TRUE;
table_list->mdl_request.set_type(MDL_SHARED_WRITE);
if (!(table=open_ltable(thd, table_list, TL_WRITE, 0))) if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
{ {
thd->tablespace_op=FALSE; 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)) if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
DBUG_RETURN(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)) if (lock_table_names(thd, table_list))
{ {
error= 1; error= 1;
@ -6608,8 +6623,7 @@ view_err:
Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info); Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info);
error= open_and_lock_tables(thd, table_list, FALSE, error= open_and_lock_tables(thd, table_list, FALSE, 0,
MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
&alter_prelocking_strategy); &alter_prelocking_strategy);
if (error) if (error)
@ -7904,6 +7918,10 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
table_list->table= NULL; table_list->table= NULL;
/* Same applies to MDL ticket. */ /* Same applies to MDL ticket. */
table_list->mdl_request.ticket= NULL; 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)); bzero((char*) &create_info, sizeof(create_info));
create_info.row_type=ROW_TYPE_NOT_USED; create_info.row_type=ROW_TYPE_NOT_USED;

View File

@ -489,8 +489,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
else else
{ {
tables->table= open_n_lock_single_table(thd, tables, tables->table= open_n_lock_single_table(thd, tables,
TL_WRITE_ALLOW_READ, TL_WRITE_ALLOW_READ, 0);
MYSQL_OPEN_TAKE_UPGRADABLE_MDL);
if (! tables->table) if (! tables->table)
goto end; goto end;
tables->table->use_all_columns(); tables->table->use_all_columns();
@ -1667,7 +1666,8 @@ bool add_table_for_trigger(THD *thd,
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
*table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str, *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); DBUG_RETURN(*table ? FALSE : TRUE);
} }

View File

@ -433,7 +433,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
lex->link_first_table_back(view, link_to_local); lex->link_first_table_back(view, link_to_local);
view->open_strategy= TABLE_LIST::OPEN_STUB; 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; view->open_type= OT_BASE_ONLY;
if (open_and_lock_tables(thd, lex->query_tables, TRUE, 0)) if (open_and_lock_tables(thd, lex->query_tables, TRUE, 0))

View File

@ -697,7 +697,8 @@ static bool add_create_index_prepare (LEX *lex, Table_ident *table)
lex->sql_command= SQLCOM_CREATE_INDEX; lex->sql_command= SQLCOM_CREATE_INDEX;
if (!lex->current_select->add_table_to_list(lex->thd, table, NULL, if (!lex->current_select->add_table_to_list(lex->thd, table, NULL,
TL_OPTION_UPDATING, TL_OPTION_UPDATING,
TL_WRITE_ALLOW_READ)) TL_WRITE_ALLOW_READ,
MDL_SHARED_NO_WRITE))
return TRUE; return TRUE;
lex->alter_info.reset(); lex->alter_info.reset();
lex->alter_info.flags= ALTER_ADD_INDEX; lex->alter_info.flags= ALTER_ADD_INDEX;
@ -2023,7 +2024,7 @@ create:
lex->sql_command= SQLCOM_CREATE_TABLE; lex->sql_command= SQLCOM_CREATE_TABLE;
if (!lex->select_lex.add_table_to_list(thd, $5, NULL, if (!lex->select_lex.add_table_to_list(thd, $5, NULL,
TL_OPTION_UPDATING, TL_OPTION_UPDATING,
TL_WRITE)) TL_WRITE, MDL_EXCLUSIVE))
MYSQL_YYABORT; MYSQL_YYABORT;
lex->alter_info.reset(); lex->alter_info.reset();
lex->col_list.empty(); lex->col_list.empty();
@ -4213,7 +4214,8 @@ create2:
lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE; lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
src_table= lex->select_lex.add_table_to_list(thd, $2, NULL, 0, src_table= lex->select_lex.add_table_to_list(thd, $2, NULL, 0,
TL_READ); TL_READ,
MDL_SHARED_READ);
if (! src_table) if (! src_table)
MYSQL_YYABORT; MYSQL_YYABORT;
/* CREATE TABLE ... LIKE is not allowed for views. */ /* CREATE TABLE ... LIKE is not allowed for views. */
@ -4227,7 +4229,8 @@ create2:
lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE; lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
src_table= lex->select_lex.add_table_to_list(thd, $3, NULL, 0, src_table= lex->select_lex.add_table_to_list(thd, $3, NULL, 0,
TL_READ); TL_READ,
MDL_SHARED_READ);
if (! src_table) if (! src_table)
MYSQL_YYABORT; MYSQL_YYABORT;
/* CREATE TABLE ... LIKE is not allowed for views. */ /* CREATE TABLE ... LIKE is not allowed for views. */
@ -6154,7 +6157,8 @@ alter:
lex->duplicates= DUP_ERROR; lex->duplicates= DUP_ERROR;
if (!lex->select_lex.add_table_to_list(thd, $4, NULL, if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
TL_OPTION_UPDATING, TL_OPTION_UPDATING,
TL_WRITE_ALLOW_READ)) TL_WRITE_ALLOW_READ,
MDL_SHARED_NO_WRITE))
MYSQL_YYABORT; MYSQL_YYABORT;
lex->col_list.empty(); lex->col_list.empty();
lex->select_lex.init_order(); lex->select_lex.init_order();
@ -6847,6 +6851,8 @@ checksum:
{ {
LEX *lex=Lex; LEX *lex=Lex;
lex->sql_command = SQLCOM_CHECKSUM; lex->sql_command = SQLCOM_CHECKSUM;
/* Will be overriden during execution. */
YYPS->m_lock_type= TL_UNLOCK;
} }
table_list opt_checksum_type table_list opt_checksum_type
{} {}
@ -6866,6 +6872,8 @@ repair:
lex->no_write_to_binlog= $2; lex->no_write_to_binlog= $2;
lex->check_opt.init(); lex->check_opt.init();
lex->alter_info.reset(); lex->alter_info.reset();
/* Will be overriden during execution. */
YYPS->m_lock_type= TL_UNLOCK;
} }
table_list opt_mi_repair_type table_list opt_mi_repair_type
{} {}
@ -6895,6 +6903,8 @@ analyze:
lex->no_write_to_binlog= $2; lex->no_write_to_binlog= $2;
lex->check_opt.init(); lex->check_opt.init();
lex->alter_info.reset(); lex->alter_info.reset();
/* Will be overriden during execution. */
YYPS->m_lock_type= TL_UNLOCK;
} }
table_list table_list
{} {}
@ -6921,6 +6931,8 @@ check:
lex->sql_command = SQLCOM_CHECK; lex->sql_command = SQLCOM_CHECK;
lex->check_opt.init(); lex->check_opt.init();
lex->alter_info.reset(); lex->alter_info.reset();
/* Will be overriden during execution. */
YYPS->m_lock_type= TL_UNLOCK;
} }
table_list opt_mi_check_type table_list opt_mi_check_type
{} {}
@ -6953,6 +6965,8 @@ optimize:
lex->no_write_to_binlog= $2; lex->no_write_to_binlog= $2;
lex->check_opt.init(); lex->check_opt.init();
lex->alter_info.reset(); lex->alter_info.reset();
/* Will be overriden during execution. */
YYPS->m_lock_type= TL_UNLOCK;
} }
table_list table_list
{} {}
@ -7001,9 +7015,9 @@ table_to_table:
LEX *lex=Lex; LEX *lex=Lex;
SELECT_LEX *sl= lex->current_select; SELECT_LEX *sl= lex->current_select;
if (!sl->add_table_to_list(lex->thd, $1,NULL,TL_OPTION_UPDATING, 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, !sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING,
TL_IGNORE)) TL_IGNORE, MDL_EXCLUSIVE))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
; ;
@ -7035,6 +7049,7 @@ assign_to_keycache:
table_ident cache_keys_spec 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())) Select->pop_index_hints()))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
@ -7044,6 +7059,7 @@ assign_to_keycache_parts:
table_ident adm_partition cache_keys_spec table_ident adm_partition 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())) Select->pop_index_hints()))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
@ -7079,6 +7095,7 @@ preload_keys:
table_ident cache_keys_spec opt_ignore_leaves table_ident cache_keys_spec opt_ignore_leaves
{ {
if (!Select->add_table_to_list(YYTHD, $1, NULL, $3, TL_READ, if (!Select->add_table_to_list(YYTHD, $1, NULL, $3, TL_READ,
MDL_SHARED_READ,
Select->pop_index_hints())) Select->pop_index_hints()))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
@ -7088,6 +7105,7 @@ preload_keys_parts:
table_ident adm_partition cache_keys_spec opt_ignore_leaves table_ident adm_partition cache_keys_spec opt_ignore_leaves
{ {
if (!Select->add_table_to_list(YYTHD, $1, NULL, $4, TL_READ, if (!Select->add_table_to_list(YYTHD, $1, NULL, $4, TL_READ,
MDL_SHARED_READ,
Select->pop_index_hints())) Select->pop_index_hints()))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
@ -9220,6 +9238,7 @@ table_factor:
if (!($$= Select->add_table_to_list(YYTHD, $2, $3, if (!($$= Select->add_table_to_list(YYTHD, $2, $3,
Select->get_table_join_options(), Select->get_table_join_options(),
YYPS->m_lock_type, YYPS->m_lock_type,
YYPS->m_mdl_type,
Select->pop_index_hints()))) Select->pop_index_hints())))
MYSQL_YYABORT; MYSQL_YYABORT;
Select->add_joined_table($$); Select->add_joined_table($$);
@ -9291,7 +9310,7 @@ table_factor:
MYSQL_YYABORT; MYSQL_YYABORT;
if (!($$= sel->add_table_to_list(lex->thd, if (!($$= sel->add_table_to_list(lex->thd,
new Table_ident(unit), $5, 0, new Table_ident(unit), $5, 0,
TL_READ))) TL_READ, MDL_SHARED_READ)))
MYSQL_YYABORT; MYSQL_YYABORT;
sel->add_joined_table($$); sel->add_joined_table($$);
@ -10126,13 +10145,17 @@ do:
*/ */
drop: 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 *lex=Lex;
lex->sql_command = SQLCOM_DROP_TABLE; lex->sql_command = SQLCOM_DROP_TABLE;
lex->drop_temporary= $2; lex->drop_temporary= $2;
lex->drop_if_exists= $4; 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 {} | DROP INDEX_SYM ident ON table_ident {}
{ {
LEX *lex=Lex; LEX *lex=Lex;
@ -10145,7 +10168,8 @@ drop:
lex->alter_info.drop_list.push_back(ad); lex->alter_info.drop_list.push_back(ad);
if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL, if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
TL_OPTION_UPDATING, TL_OPTION_UPDATING,
TL_WRITE_ALLOW_READ)) TL_WRITE_ALLOW_READ,
MDL_SHARED_NO_WRITE))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| DROP DATABASE if_exists ident | DROP DATABASE if_exists ident
@ -10215,12 +10239,16 @@ drop:
{ {
Lex->sql_command = SQLCOM_DROP_USER; Lex->sql_command = SQLCOM_DROP_USER;
} }
| DROP VIEW_SYM if_exists table_list opt_restrict | DROP VIEW_SYM if_exists
{ {
LEX *lex= Lex; LEX *lex= Lex;
lex->sql_command= SQLCOM_DROP_VIEW; lex->sql_command= SQLCOM_DROP_VIEW;
lex->drop_if_exists= $3; 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 | DROP EVENT_SYM if_exists sp_name
{ {
Lex->drop_if_exists= $3; Lex->drop_if_exists= $3;
@ -10261,7 +10289,10 @@ table_list:
table_name: table_name:
table_ident 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; MYSQL_YYABORT;
} }
; ;
@ -10276,7 +10307,8 @@ table_alias_ref:
{ {
if (!Select->add_table_to_list(YYTHD, $1, NULL, if (!Select->add_table_to_list(YYTHD, $1, NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS, TL_OPTION_UPDATING | TL_OPTION_ALIAS,
YYPS->m_lock_type)) YYPS->m_lock_type,
YYPS->m_mdl_type))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
; ;
@ -10558,6 +10590,8 @@ delete:
lex->sql_command= SQLCOM_DELETE; lex->sql_command= SQLCOM_DELETE;
mysql_init_select(lex); mysql_init_select(lex);
YYPS->m_lock_type= TL_WRITE_DEFAULT; YYPS->m_lock_type= TL_WRITE_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_WRITE;
lex->ignore= 0; lex->ignore= 0;
lex->select_lex.init_order(); lex->select_lex.init_order();
} }
@ -10568,9 +10602,11 @@ single_multi:
FROM table_ident FROM table_ident
{ {
if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING, 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; MYSQL_YYABORT;
YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_lock_type= TL_READ_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_READ;
} }
where_clause opt_order_clause where_clause opt_order_clause
delete_limit_clause {} delete_limit_clause {}
@ -10578,6 +10614,7 @@ single_multi:
{ {
mysql_init_multi_delete(Lex); mysql_init_multi_delete(Lex);
YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_lock_type= TL_READ_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_READ;
} }
FROM join_table_list where_clause FROM join_table_list where_clause
{ {
@ -10588,6 +10625,7 @@ single_multi:
{ {
mysql_init_multi_delete(Lex); mysql_init_multi_delete(Lex);
YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_lock_type= TL_READ_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_READ;
} }
USING join_table_list where_clause USING join_table_list where_clause
{ {
@ -10611,7 +10649,8 @@ table_wild_one:
ti, ti,
NULL, NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS, TL_OPTION_UPDATING | TL_OPTION_ALIAS,
YYPS->m_lock_type)) YYPS->m_lock_type,
YYPS->m_mdl_type))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| ident '.' ident opt_wild | ident '.' ident opt_wild
@ -10623,7 +10662,8 @@ table_wild_one:
ti, ti,
NULL, NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS, TL_OPTION_UPDATING | TL_OPTION_ALIAS,
YYPS->m_lock_type)) YYPS->m_lock_type,
YYPS->m_mdl_type))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
; ;
@ -11137,7 +11177,15 @@ flush:
flush_options: flush_options:
table_or_tables 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_table_list {}
opt_with_read_lock {} opt_with_read_lock {}
| flush_options_list | flush_options_list
@ -11301,7 +11349,7 @@ load:
{ {
LEX *lex=Lex; LEX *lex=Lex;
if (!Select->add_table_to_list(YYTHD, $12, NULL, TL_OPTION_UPDATING, if (!Select->add_table_to_list(YYTHD, $12, NULL, TL_OPTION_UPDATING,
$4)) $4, MDL_SHARED_WRITE))
MYSQL_YYABORT; MYSQL_YYABORT;
lex->field_list.empty(); lex->field_list.empty();
lex->update_list.empty(); lex->update_list.empty();
@ -13007,10 +13055,14 @@ table_lock:
table_ident opt_table_alias lock_option table_ident opt_table_alias lock_option
{ {
thr_lock_type lock_type= (thr_lock_type) $3; 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; MYSQL_YYABORT;
/* If table is to be write locked, protect from a impending GRL. */ /* 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; Lex->protect_against_global_read_lock= TRUE;
} }
; ;
@ -13765,6 +13817,7 @@ query_expression_option:
if (check_simple_select()) if (check_simple_select())
MYSQL_YYABORT; MYSQL_YYABORT;
YYPS->m_lock_type= TL_READ_HIGH_PRIORITY; YYPS->m_lock_type= TL_READ_HIGH_PRIORITY;
YYPS->m_mdl_type= MDL_SHARED_READ;
Select->options|= SELECT_HIGH_PRIORITY; Select->options|= SELECT_HIGH_PRIORITY;
} }
| DISTINCT { Select->options|= SELECT_DISTINCT; } | DISTINCT { Select->options|= SELECT_DISTINCT; }
@ -13894,7 +13947,10 @@ view_tail:
LEX *lex= thd->lex; LEX *lex= thd->lex;
lex->sql_command= SQLCOM_CREATE_VIEW; lex->sql_command= SQLCOM_CREATE_VIEW;
/* first table in list is target VIEW name */ /* 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; MYSQL_YYABORT;
} }
view_list_opt AS view_select view_list_opt AS view_select
@ -14034,7 +14090,8 @@ trigger_tail:
if (!lex->select_lex.add_table_to_list(YYTHD, $9, if (!lex->select_lex.add_table_to_list(YYTHD, $9,
(LEX_STRING*) 0, (LEX_STRING*) 0,
TL_OPTION_UPDATING, TL_OPTION_UPDATING,
TL_IGNORE)) TL_WRITE_ALLOW_READ,
MDL_SHARED_NO_WRITE))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
; ;

View File

@ -4659,13 +4659,6 @@ void TABLE_LIST::reinit_before_use(THD *thd)
parent_embedding->nested_join->join_list.head() == embedded); parent_embedding->nested_join->join_list.head() == embedded);
mdl_request.ticket= NULL; 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);
} }
/* /*

View File

@ -1581,20 +1581,21 @@ struct TABLE_LIST
OPEN_STUB OPEN_STUB
} open_strategy; } open_strategy;
/** /**
Indicates the locking strategy for the object being opened: Indicates the locking strategy for the object being opened.
whether the associated metadata lock is shared or exclusive.
*/ */
enum enum
{ {
/* Take a shared metadata lock before the object is opened. */
SHARED_MDL= 0,
/* /*
Take a exclusive metadata lock before the object is opened. Take metadata lock specified by 'mdl_request' member before
If opening is successful, downgrade to a shared lock. the object is opened. Do nothing after that.
*/ */
EXCLUSIVE_DOWNGRADABLE_MDL, OTLS_NONE= 0,
/* Take a exclusive metadata lock before the object is opened. */ /*
EXCLUSIVE_MDL 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; } lock_strategy;
/* For transactional locking. */ /* For transactional locking. */
int lock_timeout; /* NOWAIT or WAIT [X] */ int lock_timeout; /* NOWAIT or WAIT [X] */