Backport of:
---------------------------------------------------------- revno: 2617.69.20 committer: Konstantin Osipov <kostja@sun.com> branch nick: 5.4-4284-1-assert timestamp: Thu 2009-08-13 18:29:55 +0400 message: WL#4284 "Transactional DDL locking" A review fix. Since WL#4284 implementation separated MDL_request and MDL_ticket, MDL_request becamse a utility object necessary only to get a ticket. Store it by-value in TABLE_LIST with the intent to merge MDL_request::key with table_list->table_name and table_list->db in future. Change the MDL subsystem to not require MDL_requests to stay around till close_thread_tables(). Remove the list of requests from the MDL context. Requests for shared metadata locks acquired in open_tables() are only used as a list in recover_from_failed_open_table_attempt(), which calls mdl_context.wait_for_locks() for this list. To keep such list for recover_from_failed_open_table_attempt(), introduce a context class (Open_table_context), that collects all requests. A lot of minor cleanups and simplications that became possible with this change. sql/event_db_repository.cc: Remove alloc_mdl_requests(). Now MDL_request instance is a member of TABLE_LIST, and init_one_table() initializes it. sql/ha_ndbcluster_binlog.cc: Remove now unnecessary declaration and initialization of binlog_mdl_request. sql/lock.cc: No need to allocate MDL requests in lock_table_names() now. sql/log.cc: Use init_one_table() method, remove alloc_mdl_requests(), which is now unnecessary. sql/log_event.cc: No need to allocate mdl_request separately now. Use init_one_table() method. sql/log_event_old.cc: Update to the new signature of close_tables_for_reopen(). sql/mdl.cc: Update try_acquire_exclusive_lock() to be more easy to use. Function lock_table_name_if_not_cached() has been removed. Make acquire_shared_lock() signature consistent with try_acquire_exclusive_lock() signature. Remove methods that are no longer used. Update comments. sql/mdl.h: Implement an assignment operator that doesn't copy MDL_key (MDL_key::operator= is private and should remain private). This is a hack to work-around assignment of TABLE_LIST by value in several places. Such assignments violate encapsulation, since only perform a shallow copy. In most cases these assignments are a hack on their own. sql/mysql_priv.h: Update signatures of close_thread_tables() and close_tables_for_reopen(). sql/sp.cc: Allocate TABLE_LIST in thd->mem_root. Use init_one_table(). sql/sp_head.cc: Use init_one_table(). Remove thd->locked_tables_root, it's no longer needed. sql/sql_acl.cc: Use init_mdl_requests() and init_one_table(). sql/sql_base.cc: Update to new signatures of try_acquire_shared_lock() and try_acquire_exclusive_lock(). Remove lock_table_name_if_not_cached(). Fix a bug in open_ltable() that would not return ER_LOCK_DEADLOCK in case of a failed lock_tables() and a multi-statement transaction. Fix a bug in open_and_lock_tables_derived() that would not return ER_LOCK_DEADLOCK in case of a multi-statement transaction and a failure of lock_tables(). Move assignment of enum_open_table_action to a method of Open_table_context, a new class that maintains information for backoff actions. Minor rearrangements of the code. Remove alloc_mdl_requests() in functions that work with system tables: instead the patch ensures that callers always initialize TABLE_LIST argument. sql/sql_class.cc: THD::locked_tables_root is no more. sql/sql_class.h: THD::locked_tables_root is no more. Add a declaration for Open_table_context class. sql/sql_delete.cc: Update to use the simplified MDL API. sql/sql_handler.cc: TABLE_LIST::mdl_request is stored by-value now. Ensure that mdl_request.ticket is NULL for every request that is passed into MDL, to satisfy MDL asserts. @ sql/sql_help.cc Function open_system_tables_for_read() no longer initializes mdl_requests. Move TABLE_LIST::mdl_request initialization closer to TABLE_LIST initialization. sql/sql_help.cc: Function open_system_tables_for_read() no longer initializes mdl_requests. Move TABLE_LIST::mdl_request initialization closer to TABLE_LIST initialization. sql/sql_insert.cc: Remove assignment by-value of TABLE_LIST in TABLEOP_HOOKS. We can't carry over a granted MDL ticket from one table list to another. sql/sql_parse.cc: Change alloc_mdl_requests() -> init_mdl_requests(). @todo We can remove init_mdl_requests() altogether in some places: all places that call add_table_to_list() already have mdl requests initialized. sql/sql_plugin.cc: Use init_one_table(). THD::locked_tables_root is no more. sql/sql_servers.cc: Use init_one_table(). sql/sql_show.cc: Update acquire_high_priority_shared_lock() to use TABLE_LIST::mdl_request rather than allocate an own. Fix get_trigger_table_impl() to use init_one_table(), check for out of memory, follow the coding style. sql/sql_table.cc: Update to work with TABLE_LIST::mdl_request by-value. Remove lock_table_name_if_not_cached(). The code that used to delegate to it is quite simple and concise without it now. sql/sql_udf.cc: Use init_one_table(). sql/sql_update.cc: Update to use the new signature of close_tables_for_reopen(). sql/table.cc: Move re-setting of mdl_requests for prepared statements and stored procedures from close_thread_tables() to reinit_stmt_before_use(). Change alloc_mdl_requests() to init_mdl_requests(). init_mdl_requests() is a hack that can't be deleted until we don't have a list-aware TABLE_LIST constructor. Hopefully its use will be minimal sql/table.h: Change alloc_mdl_requests() to init_mdl_requests() TABLE_LIST::mdl_request is stored by value. sql/tztime.cc: We no longer initialize mdl requests in open_system_tables_for*() functions. Move this initialization closer to initialization of the rest of TABLE_LIST members. storage/myisammrg/ha_myisammrg.cc: Simplify mdl_request initialization.
This commit is contained in:
parent
478e09609c
commit
ce5c87a3d3
@ -555,7 +555,6 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
|
||||
DBUG_ENTER("Event_db_repository::open_event_table");
|
||||
|
||||
tables.init_one_table("mysql", 5, "event", 5, "event", lock_type);
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
|
||||
if (simple_open_n_lock_tables(thd, &tables))
|
||||
{
|
||||
@ -1110,7 +1109,6 @@ Event_db_repository::check_system_tables(THD *thd)
|
||||
|
||||
/* Check mysql.db */
|
||||
tables.init_one_table("mysql", 5, "db", 2, "db", TL_READ);
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
|
||||
if (simple_open_n_lock_tables(thd, &tables))
|
||||
{
|
||||
@ -1128,7 +1126,6 @@ Event_db_repository::check_system_tables(THD *thd)
|
||||
}
|
||||
/* Check mysql.user */
|
||||
tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ);
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
|
||||
if (simple_open_n_lock_tables(thd, &tables))
|
||||
{
|
||||
@ -1149,7 +1146,6 @@ Event_db_repository::check_system_tables(THD *thd)
|
||||
}
|
||||
/* Check mysql.event */
|
||||
tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
|
||||
if (simple_open_n_lock_tables(thd, &tables))
|
||||
{
|
||||
|
@ -140,7 +140,6 @@ static Uint64 *p_latest_trans_gci= 0;
|
||||
*/
|
||||
static TABLE *ndb_binlog_index= 0;
|
||||
static TABLE_LIST binlog_tables;
|
||||
static MDL_request binlog_mdl_request;
|
||||
|
||||
/*
|
||||
Helper functions
|
||||
@ -2337,13 +2336,10 @@ static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index)
|
||||
const char *save_proc_info= thd->proc_info;
|
||||
TABLE_LIST *tables= &binlog_tables;
|
||||
|
||||
bzero((char*) tables, sizeof(*tables));
|
||||
tables->db= repdb;
|
||||
tables->alias= tables->table_name= reptable;
|
||||
tables->lock_type= TL_WRITE;
|
||||
tables->init_one_table(repdb, strlen(repdb), reptable, strlen(reptable),
|
||||
reptable, TL_WRITE);
|
||||
thd->proc_info= "Opening " NDB_REP_DB "." NDB_REP_TABLE;
|
||||
binlog_mdl_request.init(0, tables->db, tables->table_name);
|
||||
tables->mdl_request= &binlog_mdl_request;
|
||||
|
||||
tables->required_type= FRMTYPE_TABLE;
|
||||
uint counter;
|
||||
thd->clear_error();
|
||||
|
21
sql/lock.cc
21
sql/lock.cc
@ -952,26 +952,18 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
|
||||
|
||||
bool lock_table_names(THD *thd, TABLE_LIST *table_list)
|
||||
{
|
||||
MDL_request_list mdl_requests;
|
||||
TABLE_LIST *lock_table;
|
||||
MDL_request *mdl_request;
|
||||
|
||||
for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
|
||||
{
|
||||
mdl_request= MDL_request::create(0, lock_table->db, lock_table->table_name,
|
||||
thd->mem_root);
|
||||
if (!mdl_request)
|
||||
goto end;
|
||||
mdl_request->set_type(MDL_EXCLUSIVE);
|
||||
thd->mdl_context.add_request(mdl_request);
|
||||
lock_table->mdl_request= mdl_request;
|
||||
lock_table->mdl_request.init(0, lock_table->db, lock_table->table_name,
|
||||
MDL_EXCLUSIVE);
|
||||
mdl_requests.push_front(&lock_table->mdl_request);
|
||||
}
|
||||
if (thd->mdl_context.acquire_exclusive_locks())
|
||||
goto end;
|
||||
if (thd->mdl_context.acquire_exclusive_locks(&mdl_requests))
|
||||
return 1;
|
||||
return 0;
|
||||
|
||||
end:
|
||||
thd->mdl_context.remove_all_requests();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -987,7 +979,6 @@ void unlock_table_names(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("unlock_table_names");
|
||||
thd->mdl_context.release_all_locks();
|
||||
thd->mdl_context.remove_all_requests();
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
42
sql/log.cc
42
sql/log.cc
@ -423,14 +423,10 @@ bool Log_to_csv_event_handler::
|
||||
save_thd_options= thd->options;
|
||||
thd->options&= ~OPTION_BIN_LOG;
|
||||
|
||||
bzero(& table_list, sizeof(TABLE_LIST));
|
||||
table_list.alias= table_list.table_name= GENERAL_LOG_NAME.str;
|
||||
table_list.table_name_length= GENERAL_LOG_NAME.length;
|
||||
|
||||
table_list.lock_type= TL_WRITE_CONCURRENT_INSERT;
|
||||
|
||||
table_list.db= MYSQL_SCHEMA_NAME.str;
|
||||
table_list.db_length= MYSQL_SCHEMA_NAME.length;
|
||||
table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
|
||||
GENERAL_LOG_NAME.str, GENERAL_LOG_NAME.length,
|
||||
GENERAL_LOG_NAME.str,
|
||||
TL_WRITE_CONCURRENT_INSERT);
|
||||
|
||||
/*
|
||||
1) open_performance_schema_table generates an error of the
|
||||
@ -588,14 +584,10 @@ bool Log_to_csv_event_handler::
|
||||
*/
|
||||
save_time_zone_used= thd->time_zone_used;
|
||||
|
||||
bzero(& table_list, sizeof(TABLE_LIST));
|
||||
table_list.alias= table_list.table_name= SLOW_LOG_NAME.str;
|
||||
table_list.table_name_length= SLOW_LOG_NAME.length;
|
||||
|
||||
table_list.lock_type= TL_WRITE_CONCURRENT_INSERT;
|
||||
|
||||
table_list.db= MYSQL_SCHEMA_NAME.str;
|
||||
table_list.db_length= MYSQL_SCHEMA_NAME.length;
|
||||
table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
|
||||
SLOW_LOG_NAME.str, SLOW_LOG_NAME.length,
|
||||
SLOW_LOG_NAME.str,
|
||||
TL_WRITE_CONCURRENT_INSERT);
|
||||
|
||||
if (!(table= open_performance_schema_table(thd, & table_list,
|
||||
& open_tables_backup)))
|
||||
@ -733,29 +725,25 @@ int Log_to_csv_event_handler::
|
||||
{
|
||||
TABLE_LIST table_list;
|
||||
TABLE *table;
|
||||
LEX_STRING *UNINIT_VAR(log_name);
|
||||
int result;
|
||||
Open_tables_state open_tables_backup;
|
||||
|
||||
DBUG_ENTER("Log_to_csv_event_handler::activate_log");
|
||||
|
||||
bzero(& table_list, sizeof(TABLE_LIST));
|
||||
|
||||
if (log_table_type == QUERY_LOG_GENERAL)
|
||||
{
|
||||
table_list.alias= table_list.table_name= GENERAL_LOG_NAME.str;
|
||||
table_list.table_name_length= GENERAL_LOG_NAME.length;
|
||||
log_name= &GENERAL_LOG_NAME;
|
||||
}
|
||||
else
|
||||
{
|
||||
DBUG_ASSERT(log_table_type == QUERY_LOG_SLOW);
|
||||
table_list.alias= table_list.table_name= SLOW_LOG_NAME.str;
|
||||
table_list.table_name_length= SLOW_LOG_NAME.length;
|
||||
|
||||
log_name= &SLOW_LOG_NAME;
|
||||
}
|
||||
|
||||
table_list.lock_type= TL_WRITE_CONCURRENT_INSERT;
|
||||
|
||||
table_list.db= MYSQL_SCHEMA_NAME.str;
|
||||
table_list.db_length= MYSQL_SCHEMA_NAME.length;
|
||||
table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
|
||||
log_name->str, log_name->length, log_name->str,
|
||||
TL_WRITE_CONCURRENT_INSERT);
|
||||
|
||||
table= open_performance_schema_table(thd, & table_list,
|
||||
& open_tables_backup);
|
||||
|
@ -8067,7 +8067,6 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
|
||||
{
|
||||
RPL_TABLE_LIST *table_list;
|
||||
char *db_mem, *tname_mem;
|
||||
MDL_request *mdl_request;
|
||||
size_t dummy_len;
|
||||
void *memory;
|
||||
DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)");
|
||||
@ -8082,21 +8081,18 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
|
||||
&table_list, (uint) sizeof(RPL_TABLE_LIST),
|
||||
&db_mem, (uint) NAME_LEN + 1,
|
||||
&tname_mem, (uint) NAME_LEN + 1,
|
||||
&mdl_request, sizeof(MDL_request),
|
||||
NullS)))
|
||||
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
|
||||
|
||||
bzero(table_list, sizeof(*table_list));
|
||||
table_list->db = db_mem;
|
||||
table_list->alias= table_list->table_name = tname_mem;
|
||||
table_list->lock_type= TL_WRITE;
|
||||
table_list->next_global= table_list->next_local= 0;
|
||||
strmov(db_mem, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len));
|
||||
strmov(tname_mem, m_tblnam);
|
||||
|
||||
table_list->init_one_table(db_mem, strlen(db_mem),
|
||||
tname_mem, strlen(tname_mem),
|
||||
tname_mem, TL_WRITE);
|
||||
|
||||
table_list->table_id= m_table_id;
|
||||
table_list->updating= 1;
|
||||
strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len));
|
||||
strmov(table_list->table_name, m_tblnam);
|
||||
mdl_request->init(0, table_list->db, table_list->table_name);
|
||||
table_list->mdl_request= mdl_request;
|
||||
|
||||
int error= 0;
|
||||
|
||||
|
@ -1506,7 +1506,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
|
||||
*/
|
||||
thd->binlog_flush_pending_rows_event(false);
|
||||
TABLE_LIST *tables= rli->tables_to_lock;
|
||||
close_tables_for_reopen(thd, &tables, FALSE);
|
||||
close_tables_for_reopen(thd, &tables);
|
||||
|
||||
uint tables_count= rli->tables_to_lock_count;
|
||||
if ((error= open_tables(thd, &tables, &tables_count, 0)))
|
||||
|
242
sql/mdl.cc
242
sql/mdl.cc
@ -194,7 +194,6 @@ void MDL_context::init(THD *thd_arg)
|
||||
rely here on the default constructors of I_P_List
|
||||
to empty the list.
|
||||
*/
|
||||
m_requests.empty();
|
||||
m_tickets.empty();
|
||||
}
|
||||
|
||||
@ -213,7 +212,6 @@ void MDL_context::init(THD *thd_arg)
|
||||
|
||||
void MDL_context::destroy()
|
||||
{
|
||||
DBUG_ASSERT(m_requests.is_empty());
|
||||
DBUG_ASSERT(m_tickets.is_empty());
|
||||
DBUG_ASSERT(! m_has_global_shared_lock);
|
||||
}
|
||||
@ -231,10 +229,8 @@ void MDL_context::destroy()
|
||||
|
||||
void MDL_context::backup_and_reset(MDL_context *backup)
|
||||
{
|
||||
DBUG_ASSERT(backup->m_requests.is_empty());
|
||||
DBUG_ASSERT(backup->m_tickets.is_empty());
|
||||
|
||||
m_requests.swap(backup->m_requests);
|
||||
m_tickets.swap(backup->m_tickets);
|
||||
|
||||
backup->m_has_global_shared_lock= m_has_global_shared_lock;
|
||||
@ -254,11 +250,9 @@ void MDL_context::backup_and_reset(MDL_context *backup)
|
||||
|
||||
void MDL_context::restore_from_backup(MDL_context *backup)
|
||||
{
|
||||
DBUG_ASSERT(m_requests.is_empty());
|
||||
DBUG_ASSERT(m_tickets.is_empty());
|
||||
DBUG_ASSERT(m_has_global_shared_lock == FALSE);
|
||||
|
||||
m_requests.swap(backup->m_requests);
|
||||
m_tickets.swap(backup->m_tickets);
|
||||
m_has_global_shared_lock= backup->m_has_global_shared_lock;
|
||||
}
|
||||
@ -271,18 +265,9 @@ void MDL_context::restore_from_backup(MDL_context *backup)
|
||||
void MDL_context::merge(MDL_context *src)
|
||||
{
|
||||
MDL_ticket *ticket;
|
||||
MDL_request *mdl_request;
|
||||
|
||||
DBUG_ASSERT(m_thd == src->m_thd);
|
||||
|
||||
if (!src->m_requests.is_empty())
|
||||
{
|
||||
Request_iterator it(src->m_requests);
|
||||
while ((mdl_request= it++))
|
||||
m_requests.push_front(mdl_request);
|
||||
src->m_requests.empty();
|
||||
}
|
||||
|
||||
if (!src->m_tickets.is_empty())
|
||||
{
|
||||
Ticket_iterator it(src->m_tickets);
|
||||
@ -315,15 +300,11 @@ void MDL_context::merge(MDL_context *src)
|
||||
for example in the grant subsystem, to lock privilege tables.
|
||||
|
||||
The MDL subsystem does not own or manage memory of lock requests.
|
||||
Instead it assumes that the life time of every lock request (including
|
||||
encompassed members db/name) encloses calls to MDL_context::add_request()
|
||||
and MDL_context::remove_request() or MDL_context::remove_all_requests().
|
||||
|
||||
@param type Id of type of object to be locked
|
||||
@param db Name of database to which the object belongs
|
||||
@param name Name of of the object
|
||||
|
||||
The initialized lock request will have MDL_SHARED type.
|
||||
@param mdl_type The MDL lock type for the request.
|
||||
|
||||
Suggested lock types: TABLE - 0 PROCEDURE - 1 FUNCTION - 2
|
||||
Note that tables and views must have the same lock type, since
|
||||
@ -332,10 +313,11 @@ void MDL_context::merge(MDL_context *src)
|
||||
|
||||
void MDL_request::init(unsigned char type_arg,
|
||||
const char *db_arg,
|
||||
const char *name_arg)
|
||||
const char *name_arg,
|
||||
enum enum_mdl_type mdl_type_arg)
|
||||
{
|
||||
key.mdl_key_init(type_arg, db_arg, name_arg);
|
||||
type= MDL_SHARED;
|
||||
type= mdl_type_arg;
|
||||
ticket= NULL;
|
||||
}
|
||||
|
||||
@ -360,91 +342,20 @@ void MDL_request::init(unsigned char type_arg,
|
||||
|
||||
MDL_request *
|
||||
MDL_request::create(unsigned char type, const char *db,
|
||||
const char *name, MEM_ROOT *root)
|
||||
const char *name, enum_mdl_type mdl_type,
|
||||
MEM_ROOT *root)
|
||||
{
|
||||
MDL_request *mdl_request;
|
||||
|
||||
if (!(mdl_request= (MDL_request*) alloc_root(root, sizeof(MDL_request))))
|
||||
return NULL;
|
||||
|
||||
mdl_request->init(type, db, name);
|
||||
mdl_request->init(type, db, name, mdl_type);
|
||||
|
||||
return mdl_request;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Add a lock request to the list of lock requests of the context.
|
||||
|
||||
The procedure to acquire metadata locks is:
|
||||
- allocate and initialize lock requests
|
||||
(MDL_request::create())
|
||||
- associate them with a context (MDL_context::add_request())
|
||||
- call MDL_context::acquire_shared_lock() and
|
||||
MDL_context::release_lock() (maybe repeatedly).
|
||||
|
||||
Associates a lock request with the given context.
|
||||
There should be no more than one context per connection, to
|
||||
avoid deadlocks.
|
||||
|
||||
@param mdl_request The lock request to be added.
|
||||
*/
|
||||
|
||||
void MDL_context::add_request(MDL_request *mdl_request)
|
||||
{
|
||||
DBUG_ENTER("MDL_context::add_request");
|
||||
DBUG_ASSERT(mdl_request->ticket == NULL);
|
||||
m_requests.push_front(mdl_request);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Remove a lock request from the list of lock requests.
|
||||
|
||||
Disassociates a lock request from the given context.
|
||||
|
||||
@param mdl_request The lock request to be removed.
|
||||
|
||||
@pre The lock request being removed should correspond to a ticket that
|
||||
was released or was not acquired.
|
||||
|
||||
@note Resets lock request back to its initial state
|
||||
(i.e. sets type to MDL_SHARED).
|
||||
*/
|
||||
|
||||
void MDL_context::remove_request(MDL_request *mdl_request)
|
||||
{
|
||||
DBUG_ENTER("MDL_context::remove_request");
|
||||
/* Reset lock request back to its initial state. */
|
||||
mdl_request->type= MDL_SHARED;
|
||||
mdl_request->ticket= NULL;
|
||||
m_requests.remove(mdl_request);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Clear all lock requests in the context.
|
||||
Disassociates lock requests from the context.
|
||||
|
||||
Also resets lock requests back to their initial state (i.e. MDL_SHARED).
|
||||
*/
|
||||
|
||||
void MDL_context::remove_all_requests()
|
||||
{
|
||||
MDL_request *mdl_request;
|
||||
Request_iterator it(m_requests);
|
||||
while ((mdl_request= it++))
|
||||
{
|
||||
/* Reset lock request back to its initial state. */
|
||||
mdl_request->type= MDL_SHARED;
|
||||
mdl_request->ticket= NULL;
|
||||
}
|
||||
m_requests.empty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Auxiliary functions needed for creation/destruction of MDL_lock objects.
|
||||
|
||||
@ -636,8 +547,10 @@ MDL_global_lock::is_lock_type_compatible(enum_mdl_type type,
|
||||
/**
|
||||
Check if request for the lock can be satisfied given current state of lock.
|
||||
|
||||
@param lock Lock.
|
||||
@param mdl_request Request for lock.
|
||||
@param requestor_ctx The context that identifies the owner of the request.
|
||||
@param type_arg The requested lock type.
|
||||
@param is_upgrade Must be set to TRUE when we are upgrading
|
||||
a shared upgradable lock to exclusive.
|
||||
|
||||
@retval TRUE Lock request can be satisfied
|
||||
@retval FALSE There is some conflicting lock.
|
||||
@ -731,7 +644,7 @@ MDL_lock::can_grant_lock(const MDL_context *requestor_ctx, enum_mdl_type type_ar
|
||||
|
||||
/**
|
||||
Check whether the context already holds a compatible lock ticket
|
||||
on a object. Only shared locks can be recursive.
|
||||
on an object.
|
||||
|
||||
@param mdl_request Lock request object for lock to be acquired
|
||||
|
||||
@ -744,8 +657,6 @@ MDL_context::find_ticket(MDL_request *mdl_request)
|
||||
MDL_ticket *ticket;
|
||||
Ticket_iterator it(m_tickets);
|
||||
|
||||
DBUG_ASSERT(mdl_request->is_shared());
|
||||
|
||||
while ((ticket= it++))
|
||||
{
|
||||
if (mdl_request->type == ticket->m_type &&
|
||||
@ -762,35 +673,33 @@ MDL_context::find_ticket(MDL_request *mdl_request)
|
||||
|
||||
Unlike exclusive locks, shared locks are acquired one by
|
||||
one. This is interface is chosen to simplify introduction of
|
||||
the new locking API to the system. MDL_context::acquire_shared_lock()
|
||||
the new locking API to the system. MDL_context::try_acquire_shared_lock()
|
||||
is currently used from open_table(), and there we have only one
|
||||
table to work with.
|
||||
|
||||
In future we may consider allocating multiple shared locks at once.
|
||||
|
||||
This function must be called after the lock is added to a context.
|
||||
@param mdl_request [in/out] Lock request object for lock to be acquired
|
||||
|
||||
@param mdl_request [in] Lock request object for lock to be acquired
|
||||
@param retry [out] Indicates that conflicting lock exists and another
|
||||
attempt should be made after releasing all current
|
||||
locks and waiting for conflicting lock go away
|
||||
(using MDL_context::wait_for_locks()).
|
||||
|
||||
@retval FALSE Success.
|
||||
@retval TRUE Failure. Either error occurred or conflicting lock exists.
|
||||
In the latter case "retry" parameter is set to TRUE.
|
||||
@retval FALSE Success. The lock may have not been acquired.
|
||||
Check the ticket, if it's NULL, a conflicting lock
|
||||
exists and another attempt should be made after releasing
|
||||
all current locks and waiting for conflicting lock go
|
||||
away (using MDL_context::wait_for_locks()).
|
||||
@retval TRUE Out of resources, an error has been reported.
|
||||
*/
|
||||
|
||||
bool
|
||||
MDL_context::acquire_shared_lock(MDL_request *mdl_request, bool *retry)
|
||||
MDL_context::try_acquire_shared_lock(MDL_request *mdl_request)
|
||||
{
|
||||
MDL_lock *lock;
|
||||
MDL_key *key= &mdl_request->key;
|
||||
MDL_ticket *ticket;
|
||||
*retry= FALSE;
|
||||
|
||||
DBUG_ASSERT(mdl_request->is_shared() && mdl_request->ticket == NULL);
|
||||
|
||||
/* Don't take chances in production. */
|
||||
mdl_request->ticket= NULL;
|
||||
safe_mutex_assert_not_owner(&LOCK_open);
|
||||
|
||||
if (m_has_global_shared_lock &&
|
||||
@ -807,6 +716,8 @@ MDL_context::acquire_shared_lock(MDL_request *mdl_request, bool *retry)
|
||||
if ((ticket= find_ticket(mdl_request)))
|
||||
{
|
||||
DBUG_ASSERT(ticket->m_state == MDL_ACQUIRED);
|
||||
/* Only shared locks can be recursive. */
|
||||
DBUG_ASSERT(ticket->is_shared());
|
||||
mdl_request->ticket= ticket;
|
||||
return FALSE;
|
||||
}
|
||||
@ -816,8 +727,7 @@ MDL_context::acquire_shared_lock(MDL_request *mdl_request, bool *retry)
|
||||
if (!global_lock.is_lock_type_compatible(mdl_request->type, FALSE))
|
||||
{
|
||||
pthread_mutex_unlock(&LOCK_mdl);
|
||||
*retry= TRUE;
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(ticket= MDL_ticket::create(this, mdl_request->type)))
|
||||
@ -854,13 +764,11 @@ MDL_context::acquire_shared_lock(MDL_request *mdl_request, bool *retry)
|
||||
{
|
||||
/* We can't get here if we allocated a new lock. */
|
||||
DBUG_ASSERT(! lock->is_empty());
|
||||
*retry= TRUE;
|
||||
MDL_ticket::destroy(ticket);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&LOCK_mdl);
|
||||
|
||||
return *retry;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
@ -888,6 +796,19 @@ static bool notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Acquire a single exclusive lock. A convenience
|
||||
wrapper around the method acquiring a list of locks.
|
||||
*/
|
||||
|
||||
bool MDL_context::acquire_exclusive_lock(MDL_request *mdl_request)
|
||||
{
|
||||
MDL_request_list mdl_requests;
|
||||
mdl_requests.push_front(mdl_request);
|
||||
return acquire_exclusive_locks(&mdl_requests);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Acquire exclusive locks. The context must contain the list of
|
||||
locks to be acquired. There must be no granted locks in the
|
||||
@ -903,7 +824,7 @@ static bool notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket)
|
||||
@retval TRUE Failure
|
||||
*/
|
||||
|
||||
bool MDL_context::acquire_exclusive_locks()
|
||||
bool MDL_context::acquire_exclusive_locks(MDL_request_list *mdl_requests)
|
||||
{
|
||||
MDL_lock *lock;
|
||||
bool signalled= FALSE;
|
||||
@ -911,9 +832,11 @@ bool MDL_context::acquire_exclusive_locks()
|
||||
MDL_request *mdl_request;
|
||||
MDL_ticket *ticket;
|
||||
st_my_thread_var *mysys_var= my_thread_var;
|
||||
Request_iterator it(m_requests);
|
||||
MDL_request_list::Iterator it(*mdl_requests);
|
||||
|
||||
safe_mutex_assert_not_owner(&LOCK_open);
|
||||
/* Exclusive locks must always be acquired first, all at once. */
|
||||
DBUG_ASSERT(! has_locks());
|
||||
|
||||
if (m_has_global_shared_lock)
|
||||
{
|
||||
@ -931,6 +854,9 @@ bool MDL_context::acquire_exclusive_locks()
|
||||
DBUG_ASSERT(mdl_request->type == MDL_EXCLUSIVE &&
|
||||
mdl_request->ticket == NULL);
|
||||
|
||||
/* Don't take chances in production. */
|
||||
mdl_request->ticket= NULL;
|
||||
|
||||
/* Early allocation: ticket is used as a shortcut to the lock. */
|
||||
if (!(ticket= MDL_ticket::create(this, mdl_request->type)))
|
||||
goto err;
|
||||
@ -1024,10 +950,7 @@ bool MDL_context::acquire_exclusive_locks()
|
||||
return FALSE;
|
||||
|
||||
err:
|
||||
/*
|
||||
Remove our pending lock requests from the locks.
|
||||
Ignore those lock requests which were not made MDL_PENDING.
|
||||
*/
|
||||
/* Remove our pending tickets from the locks. */
|
||||
it.rewind();
|
||||
while ((mdl_request= it++) && mdl_request->ticket)
|
||||
{
|
||||
@ -1163,17 +1086,16 @@ MDL_ticket::upgrade_shared_lock_to_exclusive()
|
||||
@param mdl_request [in] The lock request
|
||||
@param conflict [out] Indicates that conflicting lock exists
|
||||
|
||||
@retval TRUE Failure either conflicting lock exists or some error
|
||||
occurred (probably OOM).
|
||||
@retval FALSE Success, lock was acquired.
|
||||
@retval TRUE Failure: some error occurred (probably OOM).
|
||||
@retval FALSE Success: the lock might have not been acquired,
|
||||
check request.ticket to find out.
|
||||
|
||||
FIXME: Compared to lock_table_name_if_not_cached()
|
||||
it gives slightly more false negatives.
|
||||
*/
|
||||
|
||||
bool
|
||||
MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request,
|
||||
bool *conflict)
|
||||
MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request)
|
||||
{
|
||||
MDL_lock *lock;
|
||||
MDL_ticket *ticket;
|
||||
@ -1184,7 +1106,7 @@ MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request,
|
||||
|
||||
safe_mutex_assert_not_owner(&LOCK_open);
|
||||
|
||||
*conflict= FALSE;
|
||||
mdl_request->ticket= NULL;
|
||||
|
||||
pthread_mutex_lock(&LOCK_mdl);
|
||||
|
||||
@ -1197,7 +1119,8 @@ MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request,
|
||||
{
|
||||
MDL_ticket::destroy(ticket);
|
||||
MDL_lock::destroy(lock);
|
||||
goto err;
|
||||
pthread_mutex_unlock(&LOCK_mdl);
|
||||
return TRUE;
|
||||
}
|
||||
mdl_request->ticket= ticket;
|
||||
lock->type= MDL_lock::MDL_LOCK_EXCLUSIVE;
|
||||
@ -1206,16 +1129,9 @@ MDL_context::try_acquire_exclusive_lock(MDL_request *mdl_request,
|
||||
ticket->m_state= MDL_ACQUIRED;
|
||||
ticket->m_lock= lock;
|
||||
global_lock.active_intention_exclusive++;
|
||||
pthread_mutex_unlock(&LOCK_mdl);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* There is some lock for the object. */
|
||||
*conflict= TRUE;
|
||||
|
||||
err:
|
||||
pthread_mutex_unlock(&LOCK_mdl);
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
@ -1262,7 +1178,7 @@ bool MDL_context::acquire_global_shared_lock()
|
||||
|
||||
/**
|
||||
Wait until there will be no locks that conflict with lock requests
|
||||
in the context.
|
||||
in the given list.
|
||||
|
||||
This is a part of the locking protocol and must be used by the
|
||||
acquirer of shared locks after a back-off.
|
||||
@ -1274,11 +1190,11 @@ bool MDL_context::acquire_global_shared_lock()
|
||||
*/
|
||||
|
||||
bool
|
||||
MDL_context::wait_for_locks()
|
||||
MDL_context::wait_for_locks(MDL_request_list *mdl_requests)
|
||||
{
|
||||
MDL_lock *lock;
|
||||
MDL_request *mdl_request;
|
||||
Request_iterator it(m_requests);
|
||||
MDL_request_list::Iterator it(*mdl_requests);
|
||||
const char *old_msg;
|
||||
st_my_thread_var *mysys_var= my_thread_var;
|
||||
|
||||
@ -1380,8 +1296,7 @@ void MDL_context::release_ticket(MDL_ticket *ticket)
|
||||
|
||||
|
||||
/**
|
||||
Release all locks associated with the context, but leave them
|
||||
in the context as lock requests.
|
||||
Release all locks associated with the context.
|
||||
|
||||
This function is used to back off in case of a lock conflict.
|
||||
It is also used to release shared locks in the end of an SQL
|
||||
@ -1396,15 +1311,6 @@ void MDL_context::release_all_locks()
|
||||
|
||||
safe_mutex_assert_not_owner(&LOCK_open);
|
||||
|
||||
/* Detach lock tickets from the requests for back off. */
|
||||
{
|
||||
MDL_request *mdl_request;
|
||||
Request_iterator it(m_requests);
|
||||
|
||||
while ((mdl_request= it++))
|
||||
mdl_request->ticket= NULL;
|
||||
}
|
||||
|
||||
if (m_tickets.is_empty())
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
@ -1444,7 +1350,7 @@ void MDL_context::release_lock(MDL_ticket *ticket)
|
||||
|
||||
/**
|
||||
Release all locks in the context which correspond to the same name/
|
||||
object as this lock request, remove lock requests from the context.
|
||||
object as this lock request.
|
||||
|
||||
@param ticket One of the locks for the name/object for which all
|
||||
locks should be released.
|
||||
@ -1455,19 +1361,6 @@ void MDL_context::release_all_locks_for_name(MDL_ticket *name)
|
||||
/* Use MDL_ticket::lock to identify other locks for the same object. */
|
||||
MDL_lock *lock= name->m_lock;
|
||||
|
||||
/* Remove matching lock requests from the context. */
|
||||
MDL_request *mdl_request;
|
||||
Request_iterator it_mdl_request(m_requests);
|
||||
|
||||
while ((mdl_request= it_mdl_request++))
|
||||
{
|
||||
DBUG_ASSERT(mdl_request->ticket &&
|
||||
mdl_request->ticket->m_state == MDL_ACQUIRED);
|
||||
|
||||
if (mdl_request->ticket->m_lock == lock)
|
||||
remove_request(mdl_request);
|
||||
}
|
||||
|
||||
/* Remove matching lock tickets from the context. */
|
||||
MDL_ticket *ticket;
|
||||
Ticket_iterator it_ticket(m_tickets);
|
||||
@ -1537,16 +1430,11 @@ bool
|
||||
MDL_context::is_exclusive_lock_owner(unsigned char type,
|
||||
const char *db, const char *name)
|
||||
{
|
||||
MDL_key key(type, db, name);
|
||||
MDL_ticket *ticket;
|
||||
MDL_context::Ticket_iterator it(m_tickets);
|
||||
MDL_request mdl_request;
|
||||
mdl_request.init(type, db, name, MDL_EXCLUSIVE);
|
||||
MDL_ticket *ticket= find_ticket(&mdl_request);
|
||||
|
||||
while ((ticket= it++))
|
||||
{
|
||||
if (ticket->m_lock->type == MDL_lock::MDL_LOCK_EXCLUSIVE &&
|
||||
ticket->m_lock->key.is_equal(&key))
|
||||
break;
|
||||
}
|
||||
DBUG_ASSERT(ticket == NULL || ticket->m_state == MDL_ACQUIRED);
|
||||
|
||||
return ticket;
|
||||
}
|
||||
|
85
sql/mdl.h
85
sql/mdl.h
@ -109,7 +109,6 @@ public:
|
||||
mdl_key_init(type_arg, db_arg, name_arg);
|
||||
}
|
||||
MDL_key() {} /* To use when part of MDL_request. */
|
||||
|
||||
private:
|
||||
char m_ptr[MAX_MDLKEY_LENGTH];
|
||||
uint m_length;
|
||||
@ -157,12 +156,20 @@ public:
|
||||
/**
|
||||
Pointers for participating in the list of lock requests for this context.
|
||||
*/
|
||||
MDL_request *next_in_context;
|
||||
MDL_request **prev_in_context;
|
||||
MDL_request *next_in_list;
|
||||
MDL_request **prev_in_list;
|
||||
/**
|
||||
Pointer to the lock ticket object for this lock request.
|
||||
Valid only if this lock request is satisfied.
|
||||
*/
|
||||
MDL_ticket *ticket;
|
||||
|
||||
/** A lock is requested based on a fully qualified name and type. */
|
||||
MDL_key key;
|
||||
|
||||
void init(unsigned char type_arg, const char *db_arg, const char *name_arg);
|
||||
public:
|
||||
void init(unsigned char type_arg, const char *db_arg, const char *name_arg,
|
||||
enum_mdl_type mdl_type_arg);
|
||||
/** Set type of lock request. Can be only applied to pending locks. */
|
||||
inline void set_type(enum_mdl_type type_arg)
|
||||
{
|
||||
@ -171,15 +178,37 @@ public:
|
||||
}
|
||||
bool is_shared() const { return type < MDL_EXCLUSIVE; }
|
||||
|
||||
/**
|
||||
Pointer to the lock ticket object for this lock request.
|
||||
Valid only if this lock request is satisfied.
|
||||
*/
|
||||
MDL_ticket *ticket;
|
||||
|
||||
static MDL_request *create(unsigned char type, const char *db,
|
||||
const char *name, MEM_ROOT *root);
|
||||
const char *name, enum_mdl_type mdl_type,
|
||||
MEM_ROOT *root);
|
||||
|
||||
/*
|
||||
This is to work around the ugliness of TABLE_LIST
|
||||
compiler-generated assignment operator. It is currently used
|
||||
in several places to quickly copy "most" of the members of the
|
||||
table list. These places currently never assume that the mdl
|
||||
request is carried over to the new TABLE_LIST, or shared
|
||||
between lists.
|
||||
|
||||
This method does not initialize the instance being assigned!
|
||||
Use of init() for initialization after this assignment operator
|
||||
is mandatory. Can only be used before the request has been
|
||||
granted.
|
||||
*/
|
||||
MDL_request& operator=(const MDL_request &rhs)
|
||||
{
|
||||
ticket= NULL;
|
||||
/* Do nothing, in particular, don't try to copy the key. */
|
||||
return *this;
|
||||
}
|
||||
/* Another piece of ugliness for TABLE_LIST constructor */
|
||||
MDL_request() {}
|
||||
|
||||
MDL_request(const MDL_request *rhs)
|
||||
:type(rhs->type),
|
||||
ticket(NULL),
|
||||
key(&rhs->key)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@ -248,6 +277,11 @@ private:
|
||||
};
|
||||
|
||||
|
||||
typedef I_P_List<MDL_request, I_P_List_adapter<MDL_request,
|
||||
&MDL_request::next_in_list,
|
||||
&MDL_request::prev_in_list> >
|
||||
MDL_request_list;
|
||||
|
||||
/**
|
||||
Context of the owner of metadata locks. I.e. each server
|
||||
connection has such a context.
|
||||
@ -256,14 +290,6 @@ private:
|
||||
class MDL_context
|
||||
{
|
||||
public:
|
||||
typedef I_P_List<MDL_request,
|
||||
I_P_List_adapter<MDL_request,
|
||||
&MDL_request::next_in_context,
|
||||
&MDL_request::prev_in_context> >
|
||||
Request_list;
|
||||
|
||||
typedef Request_list::Iterator Request_iterator;
|
||||
|
||||
typedef I_P_List<MDL_ticket,
|
||||
I_P_List_adapter<MDL_ticket,
|
||||
&MDL_ticket::next_in_context,
|
||||
@ -279,16 +305,13 @@ public:
|
||||
void restore_from_backup(MDL_context *backup);
|
||||
void merge(MDL_context *source);
|
||||
|
||||
void add_request(MDL_request *mdl_request);
|
||||
void remove_request(MDL_request *mdl_request);
|
||||
void remove_all_requests();
|
||||
|
||||
bool acquire_shared_lock(MDL_request *mdl_request, bool *retry);
|
||||
bool acquire_exclusive_locks();
|
||||
bool try_acquire_exclusive_lock(MDL_request *mdl_request, bool *conflict);
|
||||
bool try_acquire_shared_lock(MDL_request *mdl_request);
|
||||
bool acquire_exclusive_lock(MDL_request *mdl_request);
|
||||
bool acquire_exclusive_locks(MDL_request_list *requests);
|
||||
bool try_acquire_exclusive_lock(MDL_request *mdl_request);
|
||||
bool acquire_global_shared_lock();
|
||||
|
||||
bool wait_for_locks();
|
||||
bool wait_for_locks(MDL_request_list *requests);
|
||||
|
||||
void release_all_locks();
|
||||
void release_all_locks_for_name(MDL_ticket *ticket);
|
||||
@ -312,16 +335,8 @@ public:
|
||||
|
||||
void rollback_to_savepoint(MDL_ticket *mdl_savepoint);
|
||||
|
||||
/**
|
||||
Get iterator for walking through all lock requests in the context.
|
||||
*/
|
||||
inline Request_iterator get_requests()
|
||||
{
|
||||
return Request_iterator(m_requests);
|
||||
}
|
||||
inline THD *get_thd() const { return m_thd; }
|
||||
private:
|
||||
Request_list m_requests;
|
||||
Ticket_list m_tickets;
|
||||
bool m_has_global_shared_lock;
|
||||
THD *m_thd;
|
||||
|
@ -827,7 +827,7 @@ extern my_decimal decimal_zero;
|
||||
void free_items(Item *item);
|
||||
void cleanup_items(Item *item);
|
||||
class THD;
|
||||
void close_thread_tables(THD *thd, bool is_back_off= 0);
|
||||
void close_thread_tables(THD *thd);
|
||||
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables);
|
||||
@ -1232,10 +1232,8 @@ void release_table_share(TABLE_SHARE *share);
|
||||
TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name);
|
||||
TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
|
||||
uint lock_flags);
|
||||
enum enum_open_table_action {OT_NO_ACTION= 0, OT_BACK_OFF_AND_RETRY,
|
||||
OT_DISCOVER, OT_REPAIR};
|
||||
bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
|
||||
enum_open_table_action *action, uint flags);
|
||||
Open_table_context *backoff, uint flags);
|
||||
bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
|
||||
char *cache_key, uint cache_key_length,
|
||||
MEM_ROOT *mem_root, uint flags);
|
||||
@ -1504,7 +1502,7 @@ void free_io_cache(TABLE *entry);
|
||||
void intern_close_table(TABLE *entry);
|
||||
bool close_thread_table(THD *thd, TABLE **table_ptr);
|
||||
void close_temporary_tables(THD *thd);
|
||||
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool skip_mdl);
|
||||
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables);
|
||||
TABLE_LIST *find_table_in_list(TABLE_LIST *table,
|
||||
TABLE_LIST *TABLE_LIST::*link,
|
||||
const char *db_name,
|
||||
|
15
sql/sp.cc
15
sql/sp.cc
@ -263,13 +263,11 @@ Stored_routine_creation_ctx::load_from_db(THD *thd,
|
||||
|
||||
TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
|
||||
{
|
||||
TABLE_LIST table;
|
||||
|
||||
DBUG_ENTER("open_proc_table_for_read");
|
||||
|
||||
TABLE_LIST table;
|
||||
bzero((char*) &table, sizeof(table));
|
||||
table.db= (char*) "mysql";
|
||||
table.table_name= table.alias= (char*)"proc";
|
||||
table.lock_type= TL_READ;
|
||||
table.init_one_table("mysql", 5, "proc", 4, "proc", TL_READ);
|
||||
|
||||
if (!open_system_tables_for_read(thd, &table, backup))
|
||||
DBUG_RETURN(table.table);
|
||||
@ -294,13 +292,10 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
|
||||
|
||||
static TABLE *open_proc_table_for_update(THD *thd)
|
||||
{
|
||||
TABLE_LIST table;
|
||||
DBUG_ENTER("open_proc_table_for_update");
|
||||
|
||||
TABLE_LIST table;
|
||||
bzero((char*) &table, sizeof(table));
|
||||
table.db= (char*) "mysql";
|
||||
table.table_name= table.alias= (char*)"proc";
|
||||
table.lock_type= TL_WRITE;
|
||||
table.init_one_table("mysql", 5, "proc", 4, "proc", TL_WRITE);
|
||||
|
||||
DBUG_RETURN(open_system_table_for_update(thd, &table));
|
||||
}
|
||||
|
@ -3981,10 +3981,7 @@ 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;
|
||||
table->mdl_request= MDL_request::create(0, table->db, table->table_name,
|
||||
thd->locked_tables_root ?
|
||||
thd->locked_tables_root :
|
||||
thd->mem_root);
|
||||
table->mdl_request.init(0, table->db, table->table_name, MDL_SHARED);
|
||||
|
||||
/* Everyting else should be zeroed */
|
||||
|
||||
@ -4026,10 +4023,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
|
||||
table->lock_type= locktype;
|
||||
table->select_lex= lex->current_select;
|
||||
table->cacheable_table= 1;
|
||||
table->mdl_request= MDL_request::create(0, table->db, table->table_name,
|
||||
thd->locked_tables_root ?
|
||||
thd->locked_tables_root :
|
||||
thd->mem_root);
|
||||
table->mdl_request.init(0, table->db, table->table_name, MDL_SHARED);
|
||||
|
||||
lex->add_to_query_tables(table);
|
||||
return table;
|
||||
|
@ -691,7 +691,7 @@ my_bool acl_reload(THD *thd)
|
||||
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
|
||||
tables[0].skip_temporary= tables[1].skip_temporary=
|
||||
tables[2].skip_temporary= TRUE;
|
||||
alloc_mdl_requests(tables, thd->mem_root);
|
||||
init_mdl_requests(tables);
|
||||
|
||||
if (simple_open_n_lock_tables(thd, tables))
|
||||
{
|
||||
@ -1596,10 +1596,7 @@ bool change_password(THD *thd, const char *host, const char *user,
|
||||
if (check_change_password(thd, host, user, new_password, new_password_len))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
bzero((char*) &tables, sizeof(tables));
|
||||
tables.alias= tables.table_name= (char*) "user";
|
||||
tables.db= (char*) "mysql";
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
|
||||
|
||||
#ifdef HAVE_REPLICATION
|
||||
/*
|
||||
@ -3111,7 +3108,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
|
||||
? tables+2 : 0);
|
||||
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE;
|
||||
tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
|
||||
alloc_mdl_requests(tables, thd->mem_root);
|
||||
init_mdl_requests(tables);
|
||||
|
||||
/*
|
||||
This statement will be replicated as a statement, even when using
|
||||
@ -3329,7 +3326,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
|
||||
tables[0].next_local= tables[0].next_global= tables+1;
|
||||
tables[0].lock_type=tables[1].lock_type=TL_WRITE;
|
||||
tables[0].db=tables[1].db=(char*) "mysql";
|
||||
alloc_mdl_requests(tables, thd->mem_root);
|
||||
init_mdl_requests(tables);
|
||||
|
||||
/*
|
||||
This statement will be replicated as a statement, even when using
|
||||
@ -3468,7 +3465,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
|
||||
tables[0].next_local= tables[0].next_global= tables+1;
|
||||
tables[0].lock_type=tables[1].lock_type=TL_WRITE;
|
||||
tables[0].db=tables[1].db=(char*) "mysql";
|
||||
alloc_mdl_requests(tables, thd->mem_root);
|
||||
init_mdl_requests(tables);
|
||||
|
||||
/*
|
||||
This statement will be replicated as a statement, even when using
|
||||
@ -3797,12 +3794,10 @@ static my_bool grant_reload_procs_priv(THD *thd)
|
||||
my_bool return_val= FALSE;
|
||||
DBUG_ENTER("grant_reload_procs_priv");
|
||||
|
||||
bzero((char*) &table, sizeof(table));
|
||||
table.alias= table.table_name= (char*) "procs_priv";
|
||||
table.db= (char *) "mysql";
|
||||
table.lock_type= TL_READ;
|
||||
table.init_one_table("mysql", 5, "procs_priv",
|
||||
strlen("procs_priv"), "procs_priv",
|
||||
TL_READ);
|
||||
table.skip_temporary= 1;
|
||||
alloc_mdl_requests(&table, thd->mem_root);
|
||||
|
||||
if (simple_open_n_lock_tables(thd, &table))
|
||||
{
|
||||
@ -3869,7 +3864,7 @@ my_bool grant_reload(THD *thd)
|
||||
tables[0].next_local= tables[0].next_global= tables+1;
|
||||
tables[0].lock_type= tables[1].lock_type= TL_READ;
|
||||
tables[0].skip_temporary= tables[1].skip_temporary= TRUE;
|
||||
alloc_mdl_requests(tables, thd->mem_root);
|
||||
init_mdl_requests(tables);
|
||||
|
||||
/*
|
||||
To avoid deadlocks we should obtain table locks before
|
||||
@ -5219,7 +5214,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
|
||||
(tables+4)->lock_type= TL_WRITE;
|
||||
tables->db= (tables+1)->db= (tables+2)->db=
|
||||
(tables+3)->db= (tables+4)->db= (char*) "mysql";
|
||||
alloc_mdl_requests(tables, thd->mem_root);
|
||||
init_mdl_requests(tables);
|
||||
|
||||
#ifdef HAVE_REPLICATION
|
||||
/*
|
||||
|
283
sql/sql_base.cc
283
sql/sql_base.cc
@ -125,7 +125,8 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
|
||||
uint db_stat, uint prgflag,
|
||||
uint ha_open_flags, TABLE *outparam,
|
||||
TABLE_LIST *table_desc, MEM_ROOT *mem_root);
|
||||
static bool tdc_wait_for_old_versions(THD *thd, MDL_context *context);
|
||||
static bool tdc_wait_for_old_versions(THD *thd,
|
||||
MDL_request_list *mdl_requests);
|
||||
static bool
|
||||
has_write_table_with_auto_increment(TABLE_LIST *tables);
|
||||
|
||||
@ -1360,8 +1361,7 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
|
||||
leave prelocked mode if needed.
|
||||
*/
|
||||
|
||||
void close_thread_tables(THD *thd,
|
||||
bool is_back_off)
|
||||
void close_thread_tables(THD *thd)
|
||||
{
|
||||
TABLE *table;
|
||||
DBUG_ENTER("close_thread_tables");
|
||||
@ -1471,10 +1471,6 @@ void close_thread_tables(THD *thd,
|
||||
|
||||
thd->locked_tables_mode= LTM_NONE;
|
||||
|
||||
/*
|
||||
Note that we are leaving prelocked mode so we don't need
|
||||
to care about THD::locked_tables_root.
|
||||
*/
|
||||
/* Fallthrough */
|
||||
}
|
||||
|
||||
@ -1503,11 +1499,6 @@ void close_thread_tables(THD *thd,
|
||||
if (thd->open_tables)
|
||||
close_open_tables(thd);
|
||||
|
||||
if (!is_back_off)
|
||||
{
|
||||
thd->mdl_context.remove_all_requests();
|
||||
}
|
||||
|
||||
/*
|
||||
Defer the release of metadata locks until the current transaction
|
||||
is either committed or rolled back. This prevents other statements
|
||||
@ -2316,10 +2307,10 @@ void table_share_release_hook(void *share)
|
||||
static bool
|
||||
open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
|
||||
MDL_request *mdl_request,
|
||||
uint flags,
|
||||
enum_open_table_action *action)
|
||||
Open_table_context *ot_ctx,
|
||||
uint flags)
|
||||
{
|
||||
thd->mdl_context.add_request(mdl_request);
|
||||
ot_ctx->add_request(mdl_request);
|
||||
|
||||
if (table_list->lock_strategy)
|
||||
{
|
||||
@ -2333,16 +2324,13 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
|
||||
enforced by asserts in metadata locking subsystem.
|
||||
*/
|
||||
mdl_request->set_type(MDL_EXCLUSIVE);
|
||||
if (thd->mdl_context.acquire_exclusive_locks())
|
||||
{
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
DBUG_ASSERT(! thd->mdl_context.has_locks());
|
||||
|
||||
if (thd->mdl_context.acquire_exclusive_lock(mdl_request))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool retry;
|
||||
|
||||
/*
|
||||
There is no MDL_SHARED_UPGRADABLE_HIGH_PRIO type of metadata lock so we
|
||||
want to be sure that caller doesn't pass us both flags simultaneously.
|
||||
@ -2356,12 +2344,11 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
|
||||
if (flags & MYSQL_LOCK_IGNORE_FLUSH)
|
||||
mdl_request->set_type(MDL_SHARED_HIGH_PRIO);
|
||||
|
||||
if (thd->mdl_context.acquire_shared_lock(mdl_request, &retry))
|
||||
if (thd->mdl_context.try_acquire_shared_lock(mdl_request))
|
||||
return 1;
|
||||
if (mdl_request->ticket == NULL)
|
||||
{
|
||||
if (retry)
|
||||
*action= OT_BACK_OFF_AND_RETRY;
|
||||
else
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
(void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -2416,7 +2403,7 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
|
||||
|
||||
|
||||
bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
||||
enum_open_table_action *action, uint flags)
|
||||
Open_table_context *ot_ctx, uint flags)
|
||||
{
|
||||
reg1 TABLE *table;
|
||||
char key[MAX_DBKEY_LENGTH];
|
||||
@ -2428,11 +2415,6 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
||||
TABLE_SHARE *share;
|
||||
DBUG_ENTER("open_table");
|
||||
|
||||
/* Parsing of partitioning information from .frm needs thd->lex set up. */
|
||||
DBUG_ASSERT(thd->lex->is_lex_started);
|
||||
|
||||
*action= OT_NO_ACTION;
|
||||
|
||||
/* an open table operation needs a lot of the stack space */
|
||||
if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias))
|
||||
DBUG_RETURN(TRUE);
|
||||
@ -2602,12 +2584,15 @@ 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;
|
||||
mdl_request= &table_list->mdl_request;
|
||||
if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
|
||||
{
|
||||
if (open_table_get_mdl_lock(thd, table_list, mdl_request, flags,
|
||||
action))
|
||||
if (open_table_get_mdl_lock(thd, table_list, mdl_request, ot_ctx, flags))
|
||||
{
|
||||
DEBUG_SYNC(thd, "before_open_table_wait_refresh");
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
DEBUG_SYNC(thd, "after_open_table_mdl_shared");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2633,8 +2618,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
||||
! (flags & MYSQL_LOCK_IGNORE_FLUSH))
|
||||
{
|
||||
/* Someone did a refresh while thread was opening tables */
|
||||
*action= OT_BACK_OFF_AND_RETRY;
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
(void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
@ -2766,9 +2751,9 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
||||
separately in the caller for old table versions to go away
|
||||
(see tdc_wait_for_old_versions()).
|
||||
*/
|
||||
*action= OT_BACK_OFF_AND_RETRY;
|
||||
release_table_share(share);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
(void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
/* Force close at once after usage */
|
||||
@ -2808,12 +2793,12 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
||||
if (error == 7)
|
||||
{
|
||||
share->version= 0;
|
||||
*action= OT_DISCOVER;
|
||||
(void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER);
|
||||
}
|
||||
else if (share->crashed)
|
||||
{
|
||||
share->version= 0;
|
||||
*action= OT_REPAIR;
|
||||
(void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR);
|
||||
}
|
||||
|
||||
goto err_unlock;
|
||||
@ -2891,10 +2876,8 @@ err_unlock:
|
||||
err_unlock2:
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
|
||||
{
|
||||
thd->mdl_context.release_lock(mdl_ticket);
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
}
|
||||
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
@ -3004,6 +2987,9 @@ Locked_tables_list::init_locked_tables(THD *thd)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
memcpy(db, src_table_list->db, db_len + 1);
|
||||
memcpy(table_name, src_table_list->table_name, table_name_len + 1);
|
||||
memcpy(alias, src_table_list->alias, alias_len + 1);
|
||||
/**
|
||||
Sic: remember the *actual* table level lock type taken, to
|
||||
acquire the exact same type in reopen_tables().
|
||||
@ -3014,11 +3000,9 @@ Locked_tables_list::init_locked_tables(THD *thd)
|
||||
dst_table_list->init_one_table(db, db_len, table_name, table_name_len,
|
||||
alias,
|
||||
src_table_list->table->reginfo.lock_type);
|
||||
dst_table_list->mdl_request= src_table_list->mdl_request;
|
||||
dst_table_list->table= table;
|
||||
memcpy(db, src_table_list->db, db_len + 1);
|
||||
memcpy(table_name, src_table_list->table_name, table_name_len + 1);
|
||||
memcpy(alias, src_table_list->alias, alias_len + 1);
|
||||
dst_table_list->mdl_request.ticket= src_table_list->mdl_request.ticket;
|
||||
|
||||
/* Link last into the list of tables */
|
||||
*(dst_table_list->prev_global= m_locked_tables_last)= dst_table_list;
|
||||
m_locked_tables_last= &dst_table_list->next_global;
|
||||
@ -3227,7 +3211,7 @@ unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count)
|
||||
bool
|
||||
Locked_tables_list::reopen_tables(THD *thd)
|
||||
{
|
||||
enum enum_open_table_action ot_action_unused;
|
||||
Open_table_context ot_ctx_unused(thd);
|
||||
bool lt_refresh_unused;
|
||||
size_t reopen_count= 0;
|
||||
MYSQL_LOCK *lock;
|
||||
@ -3240,7 +3224,7 @@ Locked_tables_list::reopen_tables(THD *thd)
|
||||
continue;
|
||||
|
||||
/* Links into thd->open_tables upon success */
|
||||
if (open_table(thd, table_list, thd->mem_root, &ot_action_unused,
|
||||
if (open_table(thd, table_list, thd->mem_root, &ot_ctx_unused,
|
||||
MYSQL_OPEN_REOPEN))
|
||||
{
|
||||
unlink_all_closed_tables(thd, 0, reopen_count);
|
||||
@ -3591,6 +3575,43 @@ end_with_lock_open:
|
||||
}
|
||||
|
||||
|
||||
/** Open_table_context */
|
||||
|
||||
Open_table_context::Open_table_context(THD *thd)
|
||||
:m_action(OT_NO_ACTION),
|
||||
m_can_deadlock(thd->in_multi_stmt_transaction() &&
|
||||
thd->mdl_context.has_locks())
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
Check if we can back-off and set back off action if we can.
|
||||
Otherwise report and return error.
|
||||
|
||||
@retval TRUE if back-off is impossible.
|
||||
@retval FALSE if we can back off. Back off action has been set.
|
||||
*/
|
||||
|
||||
bool
|
||||
Open_table_context::
|
||||
request_backoff_action(enum_open_table_action action_arg)
|
||||
{
|
||||
/*
|
||||
We have met a exclusive metadata lock or a old version of
|
||||
table and we are inside a transaction that already hold locks.
|
||||
We can't follow the locking protocol in this scenario as it
|
||||
might lead to deadlocks.
|
||||
*/
|
||||
if (m_can_deadlock)
|
||||
{
|
||||
my_error(ER_LOCK_DEADLOCK, MYF(0));
|
||||
return TRUE;
|
||||
}
|
||||
m_action= action_arg;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Recover from failed attempt ot open table by performing requested action.
|
||||
|
||||
@ -3604,57 +3625,58 @@ end_with_lock_open:
|
||||
@retval TRUE - Error
|
||||
*/
|
||||
|
||||
static bool
|
||||
recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table,
|
||||
enum_open_table_action action)
|
||||
bool
|
||||
Open_table_context::
|
||||
recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table)
|
||||
{
|
||||
bool result= FALSE;
|
||||
MDL_request *mdl_request= table->mdl_request;
|
||||
|
||||
switch (action)
|
||||
/* Execute the action. */
|
||||
switch (m_action)
|
||||
{
|
||||
case OT_BACK_OFF_AND_RETRY:
|
||||
result= (thd->mdl_context.wait_for_locks() ||
|
||||
tdc_wait_for_old_versions(thd, &thd->mdl_context));
|
||||
thd->mdl_context.remove_all_requests();
|
||||
case OT_WAIT:
|
||||
result= (thd->mdl_context.wait_for_locks(&m_mdl_requests) ||
|
||||
tdc_wait_for_old_versions(thd, &m_mdl_requests));
|
||||
break;
|
||||
case OT_DISCOVER:
|
||||
mdl_request->set_type(MDL_EXCLUSIVE);
|
||||
thd->mdl_context.add_request(mdl_request);
|
||||
if (thd->mdl_context.acquire_exclusive_locks())
|
||||
{
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
return TRUE;
|
||||
}
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
|
||||
ha_create_table_from_engine(thd, table->db, table->table_name);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
MDL_request mdl_xlock_request(&table->mdl_request);
|
||||
mdl_xlock_request.set_type(MDL_EXCLUSIVE);
|
||||
if ((result=
|
||||
thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request)))
|
||||
break;
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
|
||||
ha_create_table_from_engine(thd, table->db, table->table_name);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
|
||||
thd->warning_info->clear_warning_info(thd->query_id);
|
||||
thd->clear_error(); // Clear error message
|
||||
thd->mdl_context.release_lock(mdl_request->ticket);
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
break;
|
||||
thd->warning_info->clear_warning_info(thd->query_id);
|
||||
thd->clear_error(); // Clear error message
|
||||
thd->mdl_context.release_lock(mdl_xlock_request.ticket);
|
||||
break;
|
||||
}
|
||||
case OT_REPAIR:
|
||||
mdl_request->set_type(MDL_EXCLUSIVE);
|
||||
thd->mdl_context.add_request(mdl_request);
|
||||
if (thd->mdl_context.acquire_exclusive_locks())
|
||||
{
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
return TRUE;
|
||||
}
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
MDL_request mdl_xlock_request(&table->mdl_request);
|
||||
mdl_xlock_request.set_type(MDL_EXCLUSIVE);
|
||||
if ((result=
|
||||
thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request)))
|
||||
break;
|
||||
|
||||
result= auto_repair_table(thd, table);
|
||||
thd->mdl_context.release_lock(mdl_request->ticket);
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
break;
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
|
||||
result= auto_repair_table(thd, table);
|
||||
thd->mdl_context.release_lock(mdl_xlock_request.ticket);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
/* Remove all old requests, they will be re-added. */
|
||||
m_mdl_requests.empty();
|
||||
/* Prepare for possible another back-off. */
|
||||
m_action= OT_NO_ACTION;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -3722,14 +3744,13 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table)
|
||||
int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
|
||||
{
|
||||
TABLE_LIST *tables= NULL;
|
||||
enum_open_table_action action;
|
||||
Open_table_context ot_ctx(thd);
|
||||
int result=0;
|
||||
bool error;
|
||||
MEM_ROOT new_frm_mem;
|
||||
/* Also used for indicating that prelocking is need */
|
||||
TABLE_LIST **query_tables_last_own;
|
||||
bool safe_to_ignore_table;
|
||||
bool has_locks= thd->mdl_context.has_locks();
|
||||
DBUG_ENTER("open_tables");
|
||||
/*
|
||||
temporary mem_root for new .frm parsing.
|
||||
@ -3856,31 +3877,19 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
|
||||
*/
|
||||
Prelock_error_handler prelock_handler;
|
||||
thd->push_internal_handler(& prelock_handler);
|
||||
error= open_table(thd, tables, &new_frm_mem, &action, flags);
|
||||
error= open_table(thd, tables, &new_frm_mem, &ot_ctx, flags);
|
||||
thd->pop_internal_handler();
|
||||
safe_to_ignore_table= prelock_handler.safely_trapped_errors();
|
||||
}
|
||||
else
|
||||
error= open_table(thd, tables, &new_frm_mem, &action, flags);
|
||||
error= open_table(thd, tables, &new_frm_mem, &ot_ctx, flags);
|
||||
|
||||
free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (action)
|
||||
if (ot_ctx.can_recover_from_failed_open_table())
|
||||
{
|
||||
/*
|
||||
We have met a exclusive metadata lock or a old version of table and
|
||||
we are inside a transaction that already hold locks. We can't follow
|
||||
the locking protocol in this scenario as it might lead to deadlocks.
|
||||
*/
|
||||
if (thd->in_multi_stmt_transaction() && has_locks)
|
||||
{
|
||||
my_error(ER_LOCK_DEADLOCK, MYF(0));
|
||||
result= -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
We have met exclusive metadata lock or old version of table. Now we
|
||||
have to close all tables which are not up to date/release metadata
|
||||
@ -3897,13 +3906,13 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
|
||||
*/
|
||||
if (query_tables_last_own)
|
||||
thd->lex->mark_as_requiring_prelocking(query_tables_last_own);
|
||||
close_tables_for_reopen(thd, start, (action == OT_BACK_OFF_AND_RETRY));
|
||||
close_tables_for_reopen(thd, start);
|
||||
/*
|
||||
Here we rely on the fact that 'tables' still points to the valid
|
||||
TABLE_LIST element. Altough currently this assumption is valid
|
||||
it may change in future.
|
||||
*/
|
||||
if (recover_from_failed_open_table_attempt(thd, tables, action))
|
||||
if (ot_ctx.recover_from_failed_open_table_attempt(thd, tables))
|
||||
{
|
||||
result= -1;
|
||||
goto err;
|
||||
@ -4210,7 +4219,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
|
||||
uint lock_flags)
|
||||
{
|
||||
TABLE *table;
|
||||
enum_open_table_action action;
|
||||
Open_table_context ot_ctx(thd);
|
||||
bool refresh;
|
||||
bool error;
|
||||
DBUG_ENTER("open_ltable");
|
||||
@ -4224,16 +4233,18 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
|
||||
table_list->required_type= FRMTYPE_TABLE;
|
||||
|
||||
retry:
|
||||
while ((error= open_table(thd, table_list, thd->mem_root, &action, 0)) &&
|
||||
action)
|
||||
while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, 0)) &&
|
||||
ot_ctx.can_recover_from_failed_open_table())
|
||||
{
|
||||
/*
|
||||
Even altough we have failed to open table we still need to
|
||||
call close_thread_tables() to release metadata locks which
|
||||
Even though we have failed to open table we still need to
|
||||
call release_all_locks() to release metadata locks which
|
||||
might have been acquired successfully.
|
||||
*/
|
||||
close_thread_tables(thd, (action == OT_BACK_OFF_AND_RETRY));
|
||||
if (recover_from_failed_open_table_attempt(thd, table_list, action))
|
||||
if (! thd->locked_tables_mode)
|
||||
thd->mdl_context.release_all_locks();
|
||||
table_list->mdl_request.ticket= 0;
|
||||
if (ot_ctx.recover_from_failed_open_table_attempt(thd, table_list))
|
||||
break;
|
||||
}
|
||||
|
||||
@ -4272,8 +4283,20 @@ retry:
|
||||
{
|
||||
if (refresh)
|
||||
{
|
||||
close_thread_tables(thd);
|
||||
goto retry;
|
||||
if (ot_ctx.can_deadlock())
|
||||
{
|
||||
my_error(ER_LOCK_DEADLOCK, MYF(0));
|
||||
table= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
close_thread_tables(thd);
|
||||
table_list->table= NULL;
|
||||
table_list->mdl_request.ticket= NULL;
|
||||
if (! thd->locked_tables_mode)
|
||||
thd->mdl_context.release_all_locks();
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
else
|
||||
table= 0;
|
||||
@ -4283,7 +4306,7 @@ retry:
|
||||
else
|
||||
table= 0;
|
||||
|
||||
end:
|
||||
end:
|
||||
thd_proc_info(thd, 0);
|
||||
DBUG_RETURN(table);
|
||||
}
|
||||
@ -4320,6 +4343,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
|
||||
{
|
||||
uint counter;
|
||||
bool need_reopen;
|
||||
bool has_locks= thd->mdl_context.has_locks();
|
||||
DBUG_ENTER("open_and_lock_tables_derived");
|
||||
DBUG_PRINT("enter", ("derived handling: %d", derived));
|
||||
|
||||
@ -4339,7 +4363,12 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
|
||||
break;
|
||||
if (!need_reopen)
|
||||
DBUG_RETURN(-1);
|
||||
close_tables_for_reopen(thd, &tables, FALSE);
|
||||
if (thd->in_multi_stmt_transaction() && has_locks)
|
||||
{
|
||||
my_error(ER_LOCK_DEADLOCK, MYF(0));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
close_tables_for_reopen(thd, &tables);
|
||||
}
|
||||
if (derived &&
|
||||
(mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
|
||||
@ -4677,10 +4706,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count,
|
||||
table->table->query_id= thd->query_id;
|
||||
if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
|
||||
{
|
||||
/*
|
||||
This was an attempt to enter prelocked mode so there is no
|
||||
need to care about THD::locked_tables_root here.
|
||||
*/
|
||||
mysql_unlock_tables(thd, thd->lock);
|
||||
thd->lock= 0;
|
||||
DBUG_RETURN(-1);
|
||||
@ -4767,7 +4792,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count,
|
||||
|
||||
*/
|
||||
|
||||
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool is_back_off)
|
||||
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables)
|
||||
{
|
||||
/*
|
||||
If table list consists only from tables from prelocking set, table list
|
||||
@ -4778,8 +4803,11 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool is_back_off)
|
||||
thd->lex->chop_off_not_own_tables();
|
||||
sp_remove_not_own_routines(thd->lex);
|
||||
for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global)
|
||||
{
|
||||
tmp->table= 0;
|
||||
close_thread_tables(thd, is_back_off);
|
||||
tmp->mdl_request.ticket= NULL;
|
||||
}
|
||||
close_thread_tables(thd);
|
||||
if (!thd->locked_tables_mode)
|
||||
thd->mdl_context.release_all_locks();
|
||||
}
|
||||
@ -7855,7 +7883,8 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
|
||||
@param context Metadata locking context with locks.
|
||||
*/
|
||||
|
||||
static bool tdc_wait_for_old_versions(THD *thd, MDL_context *mdl_context)
|
||||
static bool
|
||||
tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests)
|
||||
{
|
||||
TABLE_SHARE *share;
|
||||
const char *old_msg;
|
||||
@ -7872,7 +7901,7 @@ static bool tdc_wait_for_old_versions(THD *thd, MDL_context *mdl_context)
|
||||
mysql_ha_flush(thd);
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
|
||||
MDL_context::Request_iterator it= mdl_context->get_requests();
|
||||
MDL_request_list::Iterator it(*mdl_requests);
|
||||
while ((mdl_request= it++))
|
||||
{
|
||||
if ((share= get_cached_table_share(mdl_request->key.db_name(),
|
||||
@ -8095,8 +8124,6 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
|
||||
|
||||
DBUG_ENTER("open_system_tables_for_read");
|
||||
|
||||
alloc_mdl_requests(table_list, thd->mem_root);
|
||||
|
||||
/*
|
||||
Besides using new Open_tables_state for opening system tables,
|
||||
we also have to backup and reset/and then restore part of LEX
|
||||
@ -8170,8 +8197,6 @@ open_system_table_for_update(THD *thd, TABLE_LIST *one_table)
|
||||
{
|
||||
DBUG_ENTER("open_system_table_for_update");
|
||||
|
||||
alloc_mdl_requests(one_table, thd->mem_root);
|
||||
|
||||
TABLE *table= open_ltable(thd, one_table, one_table->lock_type, 0);
|
||||
if (table)
|
||||
{
|
||||
@ -8208,7 +8233,6 @@ open_performance_schema_table(THD *thd, TABLE_LIST *one_table,
|
||||
|
||||
thd->reset_n_backup_open_tables_state(backup);
|
||||
|
||||
alloc_mdl_requests(one_table, thd->mem_root);
|
||||
if ((table= open_ltable(thd, one_table, one_table->lock_type, flags)))
|
||||
{
|
||||
DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_PERFORMANCE);
|
||||
@ -8286,7 +8310,6 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup)
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
|
||||
thd->mdl_context.release_all_locks();
|
||||
thd->mdl_context.remove_all_requests();
|
||||
|
||||
thd->restore_backup_open_tables_state(backup);
|
||||
}
|
||||
|
@ -466,8 +466,7 @@ THD::THD()
|
||||
#if defined(ENABLED_DEBUG_SYNC)
|
||||
debug_sync_control(0),
|
||||
#endif /* defined(ENABLED_DEBUG_SYNC) */
|
||||
main_warning_info(0),
|
||||
locked_tables_root(NULL)
|
||||
main_warning_info(0)
|
||||
{
|
||||
ulong tmp;
|
||||
|
||||
|
@ -1180,6 +1180,49 @@ private:
|
||||
Internal_error_handler *m_err_handler;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
A context of open_tables() function, used to recover
|
||||
from a failed open_table() attempt.
|
||||
|
||||
Implemented in sql_base.cc.
|
||||
*/
|
||||
|
||||
class Open_table_context
|
||||
{
|
||||
public:
|
||||
enum enum_open_table_action
|
||||
{
|
||||
OT_NO_ACTION= 0,
|
||||
OT_WAIT,
|
||||
OT_DISCOVER,
|
||||
OT_REPAIR
|
||||
};
|
||||
Open_table_context(THD *thd);
|
||||
|
||||
bool recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *tables);
|
||||
bool request_backoff_action(enum_open_table_action action_arg);
|
||||
|
||||
void add_request(MDL_request *request)
|
||||
{ m_mdl_requests.push_front(request); }
|
||||
|
||||
bool can_recover_from_failed_open_table() const
|
||||
{ return m_action != OT_NO_ACTION; }
|
||||
bool can_deadlock() const { return m_can_deadlock; }
|
||||
private:
|
||||
/** List of requests for all locks taken so far. Used for waiting on locks. */
|
||||
MDL_request_list m_mdl_requests;
|
||||
/** Back off action. */
|
||||
enum enum_open_table_action m_action;
|
||||
/**
|
||||
Whether we had any locks when this context was created.
|
||||
If we did, they are from the previous statement of a transaction,
|
||||
and we can't safely do back-off (and release them).
|
||||
*/
|
||||
bool m_can_deadlock;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Tables that were locked with LOCK TABLES statement.
|
||||
|
||||
@ -1236,7 +1279,6 @@ public:
|
||||
}
|
||||
bool init_locked_tables(THD *thd);
|
||||
TABLE_LIST *locked_tables() { return m_locked_tables; }
|
||||
MEM_ROOT *locked_tables_root() { return &m_locked_tables_root; }
|
||||
void unlink_from_list(THD *thd, TABLE_LIST *table_list,
|
||||
bool remove_from_locked_tables);
|
||||
void unlink_all_closed_tables(THD *thd,
|
||||
@ -1895,15 +1937,6 @@ public:
|
||||
/* Debug Sync facility. See debug_sync.cc. */
|
||||
struct st_debug_sync_control *debug_sync_control;
|
||||
#endif /* defined(ENABLED_DEBUG_SYNC) */
|
||||
|
||||
/**
|
||||
Points to the memory root of Locked_tables_list if
|
||||
we're locking the tables for LOCK TABLES. Otherwise is NULL.
|
||||
This is necessary to ensure that metadata locks allocated for
|
||||
tables used in triggers will persist after statement end.
|
||||
*/
|
||||
MEM_ROOT *locked_tables_root;
|
||||
|
||||
THD();
|
||||
~THD();
|
||||
|
||||
|
@ -1100,7 +1100,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
|
||||
TABLE *table;
|
||||
bool error;
|
||||
uint path_length;
|
||||
MDL_request *mdl_request= NULL;
|
||||
MDL_request mdl_request;
|
||||
bool has_mdl_lock= FALSE;
|
||||
DBUG_ENTER("mysql_truncate");
|
||||
|
||||
bzero((char*) &create_info,sizeof(create_info));
|
||||
@ -1145,6 +1146,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
|
||||
if (!dont_send_ok)
|
||||
{
|
||||
enum legacy_db_type table_type;
|
||||
/*
|
||||
FIXME: Code of TRUNCATE breaks the meta-data
|
||||
locking protocol since it tries to find out the table storage
|
||||
engine and therefore accesses table in some way without holding
|
||||
any kind of meta-data lock.
|
||||
*/
|
||||
mysql_frm_type(thd, path, &table_type);
|
||||
if (table_type == DB_TYPE_UNKNOWN)
|
||||
{
|
||||
@ -1170,20 +1177,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
|
||||
thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
|
||||
goto trunc_by_del;
|
||||
|
||||
/*
|
||||
FIXME: Actually code of TRUNCATE breaks meta-data locking protocol since
|
||||
tries to get table enging and therefore accesses table in some way
|
||||
without holding any kind of meta-data lock.
|
||||
*/
|
||||
mdl_request= MDL_request::create(0, table_list->db,
|
||||
table_list->table_name, thd->mem_root);
|
||||
mdl_request->set_type(MDL_EXCLUSIVE);
|
||||
thd->mdl_context.add_request(mdl_request);
|
||||
if (thd->mdl_context.acquire_exclusive_locks())
|
||||
{
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
mdl_request.init(0, table_list->db, table_list->table_name, MDL_EXCLUSIVE);
|
||||
if (thd->mdl_context.acquire_exclusive_lock(&mdl_request))
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
has_mdl_lock= TRUE;
|
||||
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db,
|
||||
table_list->table_name);
|
||||
@ -1212,19 +1211,13 @@ end:
|
||||
write_bin_log(thd, TRUE, thd->query(), thd->query_length());
|
||||
my_ok(thd); // This should return record count
|
||||
}
|
||||
if (mdl_request)
|
||||
{
|
||||
thd->mdl_context.release_lock(mdl_request->ticket);
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
}
|
||||
if (has_mdl_lock)
|
||||
thd->mdl_context.release_lock(mdl_request.ticket);
|
||||
}
|
||||
else if (error)
|
||||
{
|
||||
if (mdl_request)
|
||||
{
|
||||
thd->mdl_context.release_lock(mdl_request->ticket);
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
}
|
||||
if (has_mdl_lock)
|
||||
thd->mdl_context.release_lock(mdl_request.ticket);
|
||||
}
|
||||
DBUG_RETURN(error);
|
||||
|
||||
|
@ -150,7 +150,6 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables)
|
||||
}
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
thd->handler_mdl_context.release_lock(mdl_ticket);
|
||||
thd->handler_mdl_context.remove_request(tables->mdl_request);
|
||||
}
|
||||
else if (tables->table)
|
||||
{
|
||||
@ -163,6 +162,8 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables)
|
||||
|
||||
/* Mark table as closed, ready for re-open if necessary. */
|
||||
tables->table= NULL;
|
||||
/* Safety, cleanup the pointer to satisfy MDL assertions. */
|
||||
tables->mdl_request.ticket= NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -195,7 +196,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
|
||||
int error;
|
||||
TABLE *backup_open_tables;
|
||||
MDL_context backup_mdl_context;
|
||||
MDL_request *mdl_request;
|
||||
DBUG_ENTER("mysql_ha_open");
|
||||
DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
|
||||
tables->db, tables->table_name, tables->alias,
|
||||
@ -246,7 +246,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
|
||||
&db, (uint) dblen,
|
||||
&name, (uint) namelen,
|
||||
&alias, (uint) aliaslen,
|
||||
&mdl_request, sizeof(MDL_request),
|
||||
NullS)))
|
||||
{
|
||||
DBUG_PRINT("exit",("ERROR"));
|
||||
@ -260,8 +259,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
|
||||
memcpy(hash_tables->db, tables->db, dblen);
|
||||
memcpy(hash_tables->table_name, tables->table_name, namelen);
|
||||
memcpy(hash_tables->alias, tables->alias, aliaslen);
|
||||
mdl_request->init(0, db, name);
|
||||
hash_tables->mdl_request= mdl_request;
|
||||
hash_tables->mdl_request.init(0, db, name, MDL_SHARED);
|
||||
|
||||
/* add to hash */
|
||||
if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
|
||||
|
@ -653,6 +653,7 @@ bool mysqld_help(THD *thd, const char *mask)
|
||||
tables[3].alias= tables[3].table_name= (char*) "help_keyword";
|
||||
tables[3].lock_type= TL_READ;
|
||||
tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql";
|
||||
init_mdl_requests(tables);
|
||||
|
||||
Open_tables_state open_tables_state_backup;
|
||||
if (open_system_tables_for_read(thd, tables, &open_tables_state_backup))
|
||||
|
@ -2399,7 +2399,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
|
||||
thd->lex->set_stmt_unsafe();
|
||||
thd->set_current_stmt_binlog_row_based_if_mixed();
|
||||
|
||||
alloc_mdl_requests(&di->table_list, thd->mem_root);
|
||||
init_mdl_requests(&di->table_list);
|
||||
|
||||
if (di->open_and_lock_table())
|
||||
goto err;
|
||||
@ -3464,7 +3464,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
||||
Item *item;
|
||||
Field *tmp_field;
|
||||
bool not_used;
|
||||
enum_open_table_action not_used2;
|
||||
DBUG_ENTER("create_table_from_items");
|
||||
|
||||
tmp_table.alias= 0;
|
||||
@ -3543,13 +3542,13 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
||||
|
||||
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
|
||||
{
|
||||
enum enum_open_table_action ot_action_unused;
|
||||
Open_table_context ot_ctx_unused(thd);
|
||||
/*
|
||||
Here we open the destination table, on which we already have
|
||||
an exclusive metadata lock.
|
||||
*/
|
||||
if (open_table(thd, create_table, thd->mem_root,
|
||||
&ot_action_unused, MYSQL_OPEN_REOPEN))
|
||||
&ot_ctx_unused, MYSQL_OPEN_REOPEN))
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
quick_rm_table(create_info->db_type, create_table->db,
|
||||
@ -3562,7 +3561,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (open_table(thd, create_table, thd->mem_root, ¬_used2,
|
||||
Open_table_context ot_ctx_unused(thd);
|
||||
if (open_table(thd, create_table, thd->mem_root, &ot_ctx_unused,
|
||||
MYSQL_OPEN_TEMPORARY_ONLY) &&
|
||||
!create_info->table_existed)
|
||||
{
|
||||
@ -3637,18 +3637,28 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
|
||||
*/
|
||||
class MY_HOOKS : public TABLEOP_HOOKS {
|
||||
public:
|
||||
MY_HOOKS(select_create *x, TABLE_LIST *create_table,
|
||||
TABLE_LIST *select_tables)
|
||||
: ptr(x), all_tables(*create_table)
|
||||
MY_HOOKS(select_create *x, TABLE_LIST *create_table_arg,
|
||||
TABLE_LIST *select_tables_arg)
|
||||
: ptr(x),
|
||||
create_table(create_table_arg),
|
||||
select_tables(select_tables_arg)
|
||||
{
|
||||
all_tables.next_global= select_tables;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual int do_postlock(TABLE **tables, uint count)
|
||||
{
|
||||
int error;
|
||||
THD *thd= const_cast<THD*>(ptr->get_thd());
|
||||
if (int error= decide_logging_format(thd, &all_tables))
|
||||
TABLE_LIST *save_next_global= create_table->next_global;
|
||||
|
||||
create_table->next_global= select_tables;
|
||||
|
||||
error= decide_logging_format(thd, create_table);
|
||||
|
||||
create_table->next_global= save_next_global;
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
TABLE const *const table = *tables;
|
||||
@ -3662,7 +3672,8 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
|
||||
}
|
||||
|
||||
select_create *ptr;
|
||||
TABLE_LIST all_tables;
|
||||
TABLE_LIST *create_table;
|
||||
TABLE_LIST *select_tables;
|
||||
};
|
||||
|
||||
MY_HOOKS hooks(this, create_table, select_tables);
|
||||
|
@ -1107,7 +1107,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
select_lex.table_list.link_in_list((uchar*) &table_list,
|
||||
(uchar**) &table_list.next_local);
|
||||
thd->lex->add_to_query_tables(&table_list);
|
||||
alloc_mdl_requests(&table_list, thd->mem_root);
|
||||
init_mdl_requests(&table_list);
|
||||
|
||||
/* switch on VIEW optimisation: do not fill temporary tables */
|
||||
thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
|
||||
@ -3337,18 +3337,16 @@ end_with_restore_list:
|
||||
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
||||
goto error;
|
||||
|
||||
alloc_mdl_requests(all_tables, thd->locked_tables_list.locked_tables_root());
|
||||
init_mdl_requests(all_tables);
|
||||
|
||||
thd->options|= OPTION_TABLE_LOCK;
|
||||
thd->in_lock_tables=1;
|
||||
thd->locked_tables_root= thd->locked_tables_list.locked_tables_root();
|
||||
|
||||
res= (open_and_lock_tables_derived(thd, all_tables, FALSE,
|
||||
MYSQL_OPEN_TAKE_UPGRADABLE_MDL) ||
|
||||
thd->locked_tables_list.init_locked_tables(thd));
|
||||
|
||||
thd->in_lock_tables= 0;
|
||||
thd->locked_tables_root= NULL;
|
||||
|
||||
if (res)
|
||||
{
|
||||
@ -6021,9 +6019,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=
|
||||
MDL_request::create(0, ptr->db, ptr->table_name, thd->locked_tables_root ?
|
||||
thd->locked_tables_root : thd->mem_root);
|
||||
ptr->mdl_request.init(0, ptr->db, ptr->table_name, MDL_SHARED);
|
||||
DBUG_RETURN(ptr);
|
||||
}
|
||||
|
||||
|
@ -1349,7 +1349,6 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
|
||||
#ifdef EMBEDDED_LIBRARY
|
||||
bool table_exists;
|
||||
#endif /* EMBEDDED_LIBRARY */
|
||||
MDL_request mdl_request;
|
||||
DBUG_ENTER("plugin_load");
|
||||
|
||||
if (!(new_thd= new THD))
|
||||
@ -1363,12 +1362,7 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
|
||||
lex_start(new_thd);
|
||||
new_thd->db= my_strdup("mysql", MYF(0));
|
||||
new_thd->db_length= 5;
|
||||
bzero((uchar*)&tables, sizeof(tables));
|
||||
tables.alias= tables.table_name= (char*)"plugin";
|
||||
tables.lock_type= TL_READ;
|
||||
tables.db= new_thd->db;
|
||||
tables.mdl_request= &mdl_request;
|
||||
mdl_request.init(0, tables.db, tables.table_name);
|
||||
tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_READ);
|
||||
|
||||
#ifdef EMBEDDED_LIBRARY
|
||||
/*
|
||||
@ -1656,14 +1650,10 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl
|
||||
struct st_plugin_int *tmp;
|
||||
DBUG_ENTER("mysql_install_plugin");
|
||||
|
||||
bzero(&tables, sizeof(tables));
|
||||
tables.db= (char *)"mysql";
|
||||
tables.table_name= tables.alias= (char *)"plugin";
|
||||
tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
|
||||
if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
|
||||
/* need to open before acquiring LOCK_plugin or it will deadlock */
|
||||
if (! (table = open_ltable(thd, &tables, TL_WRITE, 0)))
|
||||
DBUG_RETURN(TRUE);
|
||||
@ -1734,10 +1724,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name)
|
||||
struct st_plugin_int *plugin;
|
||||
DBUG_ENTER("mysql_uninstall_plugin");
|
||||
|
||||
bzero(&tables, sizeof(tables));
|
||||
tables.db= (char *)"mysql";
|
||||
tables.table_name= tables.alias= (char *)"plugin";
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
|
||||
|
||||
/* need to open before acquiring LOCK_plugin or it will deadlock */
|
||||
if (! (table= open_ltable(thd, &tables, TL_WRITE, 0)))
|
||||
|
@ -228,11 +228,7 @@ bool servers_reload(THD *thd)
|
||||
DBUG_PRINT("info", ("locking servers_cache"));
|
||||
rw_wrlock(&THR_LOCK_servers);
|
||||
|
||||
bzero((char*) tables, sizeof(tables));
|
||||
tables[0].alias= tables[0].table_name= (char*) "servers";
|
||||
tables[0].db= (char*) "mysql";
|
||||
tables[0].lock_type= TL_READ;
|
||||
alloc_mdl_requests(tables, thd->mem_root);
|
||||
tables[0].init_one_table("mysql", 5, "servers", 7, "servers", TL_READ);
|
||||
|
||||
if (simple_open_n_lock_tables(thd, tables))
|
||||
{
|
||||
@ -363,10 +359,7 @@ insert_server(THD *thd, FOREIGN_SERVER *server)
|
||||
|
||||
DBUG_ENTER("insert_server");
|
||||
|
||||
bzero((char*) &tables, sizeof(tables));
|
||||
tables.db= (char*) "mysql";
|
||||
tables.alias= tables.table_name= (char*) "servers";
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
tables.init_one_table("mysql", 5, "servers", 7, "servers", TL_WRITE);
|
||||
|
||||
/* need to open before acquiring THR_LOCK_plugin or it will deadlock */
|
||||
if (! (table= open_ltable(thd, &tables, TL_WRITE, 0)))
|
||||
@ -582,10 +575,7 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
|
||||
DBUG_PRINT("info", ("server name server->server_name %s",
|
||||
server_options->server_name));
|
||||
|
||||
bzero((char*) &tables, sizeof(tables));
|
||||
tables.db= (char*) "mysql";
|
||||
tables.alias= tables.table_name= (char*) "servers";
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
tables.init_one_table("mysql", 5, "servers", 7, "servers", TL_WRITE);
|
||||
|
||||
rw_wrlock(&THR_LOCK_servers);
|
||||
|
||||
@ -707,10 +697,8 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered)
|
||||
TABLE_LIST tables;
|
||||
DBUG_ENTER("update_server");
|
||||
|
||||
bzero((char*) &tables, sizeof(tables));
|
||||
tables.db= (char*)"mysql";
|
||||
tables.alias= tables.table_name= (char*)"servers";
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
tables.init_one_table("mysql", 5, "servers", 7, "servers",
|
||||
TL_WRITE);
|
||||
|
||||
if (!(table= open_ltable(thd, &tables, TL_WRITE, 0)))
|
||||
{
|
||||
|
@ -2934,7 +2934,7 @@ fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables,
|
||||
table, res, db_name,
|
||||
table_name));
|
||||
thd->temporary_tables= 0;
|
||||
close_tables_for_reopen(thd, &show_table_list, FALSE);
|
||||
close_tables_for_reopen(thd, &show_table_list);
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
@ -3065,30 +3065,21 @@ uint get_table_open_method(TABLE_LIST *tables,
|
||||
*/
|
||||
|
||||
static bool
|
||||
acquire_high_prio_shared_mdl_lock(THD *thd, MDL_request *mdl_request,
|
||||
TABLE_LIST *table)
|
||||
acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table)
|
||||
{
|
||||
bool retry;
|
||||
|
||||
mdl_request->init(0, table->db, table->table_name);
|
||||
table->mdl_request= mdl_request;
|
||||
thd->mdl_context.add_request(mdl_request);
|
||||
mdl_request->set_type(MDL_SHARED_HIGH_PRIO);
|
||||
|
||||
while (1)
|
||||
bool error;
|
||||
table->mdl_request.init(0, table->db, table->table_name,
|
||||
MDL_SHARED_HIGH_PRIO);
|
||||
while (!(error=
|
||||
thd->mdl_context.try_acquire_shared_lock(&table->mdl_request)) &&
|
||||
!table->mdl_request.ticket)
|
||||
{
|
||||
if (thd->mdl_context.acquire_shared_lock(mdl_request, &retry))
|
||||
{
|
||||
if (!retry || thd->mdl_context.wait_for_locks())
|
||||
{
|
||||
thd->mdl_context.remove_all_requests();
|
||||
return TRUE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
MDL_request_list mdl_requests;
|
||||
mdl_requests.push_front(&table->mdl_request);
|
||||
if ((error= thd->mdl_context.wait_for_locks(&mdl_requests)))
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@ -3123,7 +3114,6 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
|
||||
char key[MAX_DBKEY_LENGTH];
|
||||
uint key_length;
|
||||
char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1];
|
||||
MDL_request mdl_request;
|
||||
|
||||
bzero((char*) &table_list, sizeof(TABLE_LIST));
|
||||
bzero((char*) &tbl, sizeof(TABLE));
|
||||
@ -3153,7 +3143,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
|
||||
simply obtaining internal lock of data-dictionary (ATM it
|
||||
is LOCK_open) instead of obtaning full-blown metadata lock.
|
||||
*/
|
||||
if (acquire_high_prio_shared_mdl_lock(thd, &mdl_request, &table_list))
|
||||
if (acquire_high_prio_shared_mdl_lock(thd, &table_list))
|
||||
{
|
||||
/*
|
||||
Some error occured (most probably we have been killed while
|
||||
@ -3213,8 +3203,7 @@ err_share:
|
||||
err_unlock:
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
|
||||
thd->mdl_context.release_lock(mdl_request.ticket);
|
||||
thd->mdl_context.remove_request(&mdl_request);
|
||||
thd->mdl_context.release_lock(table_list.mdl_request.ticket);
|
||||
thd->clear_error();
|
||||
return res;
|
||||
}
|
||||
@ -3462,7 +3451,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
||||
res= schema_table->process_table(thd, show_table_list, table,
|
||||
res, &orig_db_name,
|
||||
&tmp_lex_string);
|
||||
close_tables_for_reopen(thd, &show_table_list, FALSE);
|
||||
close_tables_for_reopen(thd, &show_table_list);
|
||||
}
|
||||
DBUG_ASSERT(!lex->query_tables_own_last);
|
||||
if (res)
|
||||
@ -7199,14 +7188,14 @@ static bool show_create_trigger_impl(THD *thd,
|
||||
- do not update Lex::query_tables in add_table_to_list().
|
||||
*/
|
||||
|
||||
static TABLE_LIST *get_trigger_table_impl(
|
||||
THD *thd,
|
||||
const sp_name *trg_name)
|
||||
static
|
||||
TABLE_LIST *get_trigger_table_impl(THD *thd, const sp_name *trg_name)
|
||||
{
|
||||
char trn_path_buff[FN_REFLEN];
|
||||
|
||||
LEX_STRING trn_path= { trn_path_buff, 0 };
|
||||
LEX_STRING db;
|
||||
LEX_STRING tbl_name;
|
||||
TABLE_LIST *table;
|
||||
|
||||
build_trn_path(thd, trg_name, &trn_path);
|
||||
|
||||
@ -7220,25 +7209,19 @@ static TABLE_LIST *get_trigger_table_impl(
|
||||
return NULL;
|
||||
|
||||
/* We need to reset statement table list to be PS/SP friendly. */
|
||||
|
||||
TABLE_LIST *table;
|
||||
|
||||
if (!(table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST))))
|
||||
{
|
||||
my_error(ER_OUTOFMEMORY, MYF(0), sizeof(TABLE_LIST));
|
||||
if (!(table= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST))))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
table->db_length= trg_name->m_db.length;
|
||||
table->db= thd->strmake(trg_name->m_db.str, trg_name->m_db.length);
|
||||
db= trg_name->m_db;
|
||||
|
||||
table->table_name_length= tbl_name.length;
|
||||
table->table_name= thd->strmake(tbl_name.str, tbl_name.length);
|
||||
db.str= thd->strmake(db.str, db.length);
|
||||
tbl_name.str= thd->strmake(tbl_name.str, tbl_name.length);
|
||||
|
||||
table->alias= thd->strmake(tbl_name.str, tbl_name.length);
|
||||
if (db.str == NULL || tbl_name.str == NULL)
|
||||
return NULL;
|
||||
|
||||
table->lock_type= TL_IGNORE;
|
||||
table->cacheable_table= 0;
|
||||
table->init_one_table(db.str, db.length, tbl_name.str, tbl_name.length,
|
||||
tbl_name.str, TL_IGNORE);
|
||||
|
||||
return table;
|
||||
}
|
||||
@ -7254,7 +7237,8 @@ static TABLE_LIST *get_trigger_table_impl(
|
||||
@return TABLE_LIST object corresponding to the base table.
|
||||
*/
|
||||
|
||||
static TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
|
||||
static
|
||||
TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
|
||||
{
|
||||
/* Acquire LOCK_open (stop the server). */
|
||||
|
||||
@ -7310,8 +7294,6 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name)
|
||||
|
||||
uint num_tables; /* NOTE: unused, only to pass to open_tables(). */
|
||||
|
||||
alloc_mdl_requests(lst, thd->mem_root);
|
||||
|
||||
if (open_tables(thd, &lst, &num_tables, 0))
|
||||
{
|
||||
my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0),
|
||||
|
204
sql/sql_table.cc
204
sql/sql_table.cc
@ -16,6 +16,7 @@
|
||||
/* drop and alter of tables */
|
||||
|
||||
#include "mysql_priv.h"
|
||||
#include "debug_sync.h"
|
||||
#include <hash.h>
|
||||
#include <myisam.h>
|
||||
#include <my_dir.h>
|
||||
@ -1907,14 +1908,25 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
||||
if (find_temporary_table(thd, table->db, table->table_name))
|
||||
{
|
||||
/*
|
||||
Since we don't acquire metadata lock if we have found temporary
|
||||
table, we should do something to avoid releasing it at the end.
|
||||
A temporary table.
|
||||
|
||||
Don't try to find a corresponding MDL lock or assign it
|
||||
to table->mdl_request.ticket. There can't be metadata
|
||||
locks for temporary tables: they are local to the session.
|
||||
|
||||
Later in this function we release the MDL lock only if
|
||||
table->mdl_requeset.ticket is not NULL. Thus here we
|
||||
ensure that we won't release the metadata lock on the base
|
||||
table locked with LOCK TABLES as a side effect of temporary
|
||||
table drop.
|
||||
*/
|
||||
table->mdl_request= NULL;
|
||||
DBUG_ASSERT(table->mdl_request.ticket == NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Not a temporary table.
|
||||
|
||||
Since 'tables' list can't contain duplicates (this is ensured
|
||||
by parser) it is safe to cache pointer to the TABLE instances
|
||||
in its elements.
|
||||
@ -1923,7 +1935,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
||||
table->table_name);
|
||||
if (!table->table)
|
||||
DBUG_RETURN(1);
|
||||
table->mdl_request->ticket= table->table->mdl_ticket;
|
||||
table->mdl_request.ticket= table->table->mdl_ticket;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2188,7 +2200,7 @@ err:
|
||||
Under LOCK TABLES we should release meta-data locks on the tables
|
||||
which were dropped. Otherwise we can rely on close_thread_tables()
|
||||
doing this. Unfortunately in this case we are likely to get more
|
||||
false positives in lock_table_name_if_not_cached() function. So
|
||||
false positives in try_acquire_exclusive_lock() function. So
|
||||
it makes sense to remove exclusive meta-data locks in all cases.
|
||||
|
||||
Leave LOCK TABLES mode if we managed to drop all tables which were
|
||||
@ -2203,14 +2215,14 @@ err:
|
||||
}
|
||||
for (table= tables; table; table= table->next_local)
|
||||
{
|
||||
if (table->mdl_request)
|
||||
if (table->mdl_request.ticket)
|
||||
{
|
||||
/*
|
||||
Under LOCK TABLES we may have several instances of table open
|
||||
and locked and therefore have to remove several metadata lock
|
||||
requests associated with them.
|
||||
*/
|
||||
thd->mdl_context.release_all_locks_for_name(table->mdl_request->ticket);
|
||||
thd->mdl_context.release_all_locks_for_name(table->mdl_request.ticket);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4094,47 +4106,6 @@ warn:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Auxiliary function which obtains exclusive meta-data lock on the
|
||||
table if there are no shared or exclusive on it already.
|
||||
|
||||
See mdl_try_acquire_exclusive_lock() function for more info.
|
||||
|
||||
TODO: This function is here mostly to simplify current patch
|
||||
and probably should be removed.
|
||||
TODO: Investigate if it is kosher to leave lock request in the
|
||||
context in the case when we fail to obtain the lock.
|
||||
*/
|
||||
|
||||
static bool lock_table_name_if_not_cached(THD *thd, const char *db,
|
||||
const char *table_name,
|
||||
MDL_request **mdl_request)
|
||||
{
|
||||
bool conflict;
|
||||
|
||||
if (!(*mdl_request= MDL_request::create(0, db, table_name, thd->mem_root)))
|
||||
return TRUE;
|
||||
(*mdl_request)->set_type(MDL_EXCLUSIVE);
|
||||
thd->mdl_context.add_request(*mdl_request);
|
||||
if (thd->mdl_context.try_acquire_exclusive_lock(*mdl_request, &conflict))
|
||||
{
|
||||
/*
|
||||
To simplify our life under LOCK TABLES we remove unsatisfied
|
||||
lock request from the context.
|
||||
*/
|
||||
thd->mdl_context.remove_request(*mdl_request);
|
||||
if (!conflict)
|
||||
{
|
||||
/* Probably OOM. */
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
*mdl_request= NULL;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Database and name-locking aware wrapper for mysql_create_table_no_lock(),
|
||||
*/
|
||||
@ -4145,7 +4116,8 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
|
||||
bool internal_tmp_table,
|
||||
uint select_field_count)
|
||||
{
|
||||
MDL_request *target_mdl_request= NULL;
|
||||
MDL_request target_mdl_request;
|
||||
bool has_target_mdl_lock= FALSE;
|
||||
bool result;
|
||||
DBUG_ENTER("mysql_create_table");
|
||||
|
||||
@ -4168,13 +4140,15 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
|
||||
|
||||
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
|
||||
{
|
||||
if (lock_table_name_if_not_cached(thd, db, table_name, &target_mdl_request))
|
||||
target_mdl_request.init(0, db, table_name, MDL_EXCLUSIVE);
|
||||
if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request))
|
||||
{
|
||||
result= TRUE;
|
||||
goto unlock;
|
||||
}
|
||||
if (!target_mdl_request)
|
||||
if (target_mdl_request.ticket == NULL)
|
||||
{
|
||||
/* Table exists and is locked by some other thread. */
|
||||
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
@ -4191,6 +4165,9 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
|
||||
}
|
||||
goto unlock;
|
||||
}
|
||||
/* Got lock. */
|
||||
DEBUG_SYNC(thd, "locked_table_name");
|
||||
has_target_mdl_lock= TRUE;
|
||||
}
|
||||
|
||||
result= mysql_create_table_no_lock(thd, db, table_name, create_info,
|
||||
@ -4199,11 +4176,9 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
|
||||
select_field_count);
|
||||
|
||||
unlock:
|
||||
if (target_mdl_request)
|
||||
{
|
||||
thd->mdl_context.release_lock(target_mdl_request->ticket);
|
||||
thd->mdl_context.remove_request(target_mdl_request);
|
||||
}
|
||||
if (has_target_mdl_lock)
|
||||
thd->mdl_context.release_lock(target_mdl_request.ticket);
|
||||
|
||||
pthread_mutex_lock(&LOCK_lock_db);
|
||||
if (!--creating_table && creating_database)
|
||||
pthread_cond_signal(&COND_refresh);
|
||||
@ -4364,11 +4339,11 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
|
||||
int error= 0;
|
||||
TABLE tmp_table, *table;
|
||||
TABLE_SHARE *share;
|
||||
bool has_mdl_lock= FALSE;
|
||||
char from[FN_REFLEN],tmp[FN_REFLEN+32];
|
||||
const char **ext;
|
||||
MY_STAT stat_info;
|
||||
MDL_request *mdl_request= NULL;
|
||||
enum enum_open_table_action ot_action_unused;
|
||||
Open_table_context ot_ctx_unused(thd);
|
||||
DBUG_ENTER("prepare_for_repair");
|
||||
uint reopen_for_repair_flags= (MYSQL_LOCK_IGNORE_FLUSH |
|
||||
MYSQL_OPEN_HAS_MDL_LOCK);
|
||||
@ -4386,15 +4361,11 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
|
||||
uint key_length;
|
||||
|
||||
key_length= create_table_def_key(thd, key, table_list, 0);
|
||||
mdl_request= MDL_request::create(0, table_list->db,
|
||||
table_list->table_name, thd->mem_root);
|
||||
mdl_request->set_type(MDL_EXCLUSIVE);
|
||||
thd->mdl_context.add_request(mdl_request);
|
||||
if (thd->mdl_context.acquire_exclusive_locks())
|
||||
{
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
table_list->mdl_request.init(0, table_list->db, table_list->table_name,
|
||||
MDL_EXCLUSIVE);
|
||||
if (thd->mdl_context.acquire_exclusive_lock(&table_list->mdl_request))
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
has_mdl_lock= TRUE;
|
||||
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
if (!(share= (get_table_share(thd, table_list, key, key_length, 0,
|
||||
@ -4412,7 +4383,6 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
|
||||
}
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
table= &tmp_table;
|
||||
table_list->mdl_request= mdl_request;
|
||||
}
|
||||
|
||||
/* A MERGE table must not come here. */
|
||||
@ -4507,7 +4477,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
|
||||
to finish the repair in the handler later on.
|
||||
*/
|
||||
if (open_table(thd, table_list, thd->mem_root,
|
||||
&ot_action_unused, reopen_for_repair_flags))
|
||||
&ot_ctx_unused, reopen_for_repair_flags))
|
||||
{
|
||||
error= send_check_errmsg(thd, table_list, "repair",
|
||||
"Failed to open partially repaired table");
|
||||
@ -4523,11 +4493,9 @@ end:
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
}
|
||||
/* In case of a temporary table there will be no metadata lock. */
|
||||
if (error && mdl_request)
|
||||
{
|
||||
thd->mdl_context.release_lock(mdl_request->ticket);
|
||||
thd->mdl_context.remove_request(mdl_request);
|
||||
}
|
||||
if (error && has_mdl_lock)
|
||||
thd->mdl_context.release_lock(table_list->mdl_request.ticket);
|
||||
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
@ -4938,6 +4906,8 @@ send_result_message:
|
||||
thd->mdl_context.release_all_locks();
|
||||
if (!result_code) // recreation went ok
|
||||
{
|
||||
/* Clear the ticket released in close_thread_tables(). */
|
||||
table->mdl_request.ticket= NULL;
|
||||
if ((table->table= open_ltable(thd, table, lock_type, 0)) &&
|
||||
((result_code= table->table->file->ha_analyze(thd, check_opt)) > 0))
|
||||
result_code= 0; // analyze went ok
|
||||
@ -5241,9 +5211,9 @@ bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table,
|
||||
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
|
||||
HA_CREATE_INFO *create_info)
|
||||
{
|
||||
MDL_request *target_mdl_request= NULL;
|
||||
char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1];
|
||||
char src_path[FN_REFLEN + 1], dst_path[FN_REFLEN + 1];
|
||||
uint dst_path_length;
|
||||
bool has_mdl_lock= FALSE;
|
||||
char *db= table->db;
|
||||
char *table_name= table->table_name;
|
||||
int err;
|
||||
@ -5298,19 +5268,20 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lock_table_name_if_not_cached(thd, db, table_name, &target_mdl_request))
|
||||
goto err;
|
||||
if (!target_mdl_request)
|
||||
table->mdl_request.init(0, db, table_name, MDL_EXCLUSIVE);
|
||||
if (thd->mdl_context.try_acquire_exclusive_lock(&table->mdl_request))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (table->mdl_request.ticket == NULL)
|
||||
goto table_exists;
|
||||
|
||||
DEBUG_SYNC(thd, "locked_table_name");
|
||||
has_mdl_lock= TRUE;
|
||||
|
||||
dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1,
|
||||
db, table_name, reg_ext, 0);
|
||||
if (!access(dst_path, F_OK))
|
||||
goto table_exists;
|
||||
/*
|
||||
Make the metadata lock available to open_table() called to
|
||||
reopen the table down the road.
|
||||
*/
|
||||
table->mdl_request= target_mdl_request;
|
||||
}
|
||||
|
||||
DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););
|
||||
@ -5440,14 +5411,14 @@ binlog:
|
||||
char buf[2048];
|
||||
String query(buf, sizeof(buf), system_charset_info);
|
||||
query.length(0); // Have to zero it since constructor doesn't
|
||||
enum enum_open_table_action ot_action_unused;
|
||||
Open_table_context ot_ctx_unused(thd);
|
||||
/*
|
||||
Here we open the destination table, on which we already have
|
||||
exclusive metadata lock. This is needed for store_create_info()
|
||||
to work. The table will be closed by close_thread_table() at
|
||||
the end of this branch.
|
||||
*/
|
||||
if (open_table(thd, table, thd->mem_root, &ot_action_unused,
|
||||
if (open_table(thd, table, thd->mem_root, &ot_ctx_unused,
|
||||
MYSQL_OPEN_REOPEN))
|
||||
goto err;
|
||||
|
||||
@ -5481,11 +5452,9 @@ binlog:
|
||||
res= FALSE;
|
||||
|
||||
err:
|
||||
if (target_mdl_request)
|
||||
{
|
||||
thd->mdl_context.release_lock(target_mdl_request->ticket);
|
||||
thd->mdl_context.remove_request(target_mdl_request);
|
||||
}
|
||||
if (has_mdl_lock)
|
||||
thd->mdl_context.release_lock(table->mdl_request.ticket);
|
||||
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
@ -5969,7 +5938,6 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Prepare column and key definitions for CREATE TABLE in ALTER TABLE.
|
||||
|
||||
@ -6424,7 +6392,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
{
|
||||
TABLE *table, *new_table= 0;
|
||||
MDL_ticket *mdl_ticket;
|
||||
MDL_request *target_mdl_request= NULL;
|
||||
MDL_request target_mdl_request;
|
||||
bool has_target_mdl_lock= FALSE;
|
||||
int error= 0;
|
||||
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
|
||||
char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
|
||||
@ -6647,15 +6616,21 @@ view_err:
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lock_table_name_if_not_cached(thd, new_db, new_name,
|
||||
&target_mdl_request))
|
||||
target_mdl_request.init(0, new_db, new_name, MDL_EXCLUSIVE);
|
||||
if (thd->mdl_context.try_acquire_exclusive_lock(&target_mdl_request))
|
||||
DBUG_RETURN(TRUE);
|
||||
if (!target_mdl_request)
|
||||
if (target_mdl_request.ticket == NULL)
|
||||
{
|
||||
/* Table exists and is locked by some thread. */
|
||||
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
DEBUG_SYNC(thd, "locked_table_name");
|
||||
has_target_mdl_lock= TRUE;
|
||||
/*
|
||||
Table maybe does not exist, but we got an exclusive lock
|
||||
on the name, now we can safely try to find out for sure.
|
||||
*/
|
||||
build_table_filename(new_name_buff, sizeof(new_name_buff) - 1,
|
||||
new_db, new_name_buff, reg_ext, 0);
|
||||
if (!access(new_name_buff, F_OK))
|
||||
@ -6843,8 +6818,7 @@ view_err:
|
||||
*/
|
||||
if (new_name != table_name || new_db != db)
|
||||
{
|
||||
thd->mdl_context.release_lock(target_mdl_request->ticket);
|
||||
thd->mdl_context.remove_request(target_mdl_request);
|
||||
thd->mdl_context.release_lock(target_mdl_request.ticket);
|
||||
thd->mdl_context.release_all_locks_for_name(mdl_ticket);
|
||||
}
|
||||
else
|
||||
@ -7081,7 +7055,6 @@ view_err:
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
if (fast_alter_partition)
|
||||
{
|
||||
DBUG_ASSERT(!target_mdl_request);
|
||||
DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
|
||||
create_info, table_list,
|
||||
db, table_name,
|
||||
@ -7161,13 +7134,13 @@ view_err:
|
||||
{
|
||||
if (table->s->tmp_table)
|
||||
{
|
||||
enum_open_table_action not_used;
|
||||
Open_table_context ot_ctx_unused(thd);
|
||||
TABLE_LIST tbl;
|
||||
bzero((void*) &tbl, sizeof(tbl));
|
||||
tbl.db= new_db;
|
||||
tbl.table_name= tbl.alias= tmp_name;
|
||||
/* Table is in thd->temporary_tables */
|
||||
(void) open_table(thd, &tbl, thd->mem_root, ¬_used,
|
||||
(void) open_table(thd, &tbl, thd->mem_root, &ot_ctx_unused,
|
||||
MYSQL_LOCK_IGNORE_FLUSH);
|
||||
new_table= tbl.table;
|
||||
}
|
||||
@ -7439,7 +7412,7 @@ view_err:
|
||||
To do this we need to obtain a handler object for it.
|
||||
NO need to tamper with MERGE tables. The real open is done later.
|
||||
*/
|
||||
enum enum_open_table_action ot_action_unused;
|
||||
Open_table_context ot_ctx_unused(thd);
|
||||
TABLE *t_table;
|
||||
if (new_name != table_name || new_db != db)
|
||||
{
|
||||
@ -7448,7 +7421,7 @@ view_err:
|
||||
table_list->table_name_length= strlen(new_name);
|
||||
table_list->db= new_db;
|
||||
table_list->db_length= strlen(new_db);
|
||||
table_list->mdl_request= target_mdl_request;
|
||||
table_list->mdl_request.ticket= target_mdl_request.ticket;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -7457,10 +7430,10 @@ view_err:
|
||||
points to a different instance than the one set initially
|
||||
to request the lock.
|
||||
*/
|
||||
table_list->mdl_request->ticket= mdl_ticket;
|
||||
table_list->mdl_request.ticket= mdl_ticket;
|
||||
}
|
||||
if (open_table(thd, table_list, thd->mem_root,
|
||||
&ot_action_unused, MYSQL_OPEN_REOPEN))
|
||||
&ot_ctx_unused, MYSQL_OPEN_REOPEN))
|
||||
{
|
||||
goto err_with_mdl;
|
||||
}
|
||||
@ -7523,8 +7496,7 @@ view_err:
|
||||
{
|
||||
if ((new_name != table_name || new_db != db))
|
||||
{
|
||||
thd->mdl_context.release_lock(target_mdl_request->ticket);
|
||||
thd->mdl_context.remove_request(target_mdl_request);
|
||||
thd->mdl_context.release_lock(target_mdl_request.ticket);
|
||||
thd->mdl_context.release_all_locks_for_name(mdl_ticket);
|
||||
}
|
||||
else
|
||||
@ -7583,11 +7555,9 @@ err:
|
||||
alter_info->datetime_field->field_name);
|
||||
thd->abort_on_warning= save_abort_on_warning;
|
||||
}
|
||||
if (target_mdl_request)
|
||||
{
|
||||
thd->mdl_context.release_lock(target_mdl_request->ticket);
|
||||
thd->mdl_context.remove_request(target_mdl_request);
|
||||
}
|
||||
if (has_target_mdl_lock)
|
||||
thd->mdl_context.release_lock(target_mdl_request.ticket);
|
||||
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
err_with_mdl:
|
||||
@ -7598,11 +7568,9 @@ err_with_mdl:
|
||||
tables and release the exclusive metadata lock.
|
||||
*/
|
||||
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
|
||||
if (target_mdl_request)
|
||||
{
|
||||
thd->mdl_context.release_lock(target_mdl_request->ticket);
|
||||
thd->mdl_context.remove_request(target_mdl_request);
|
||||
}
|
||||
if (has_target_mdl_lock)
|
||||
thd->mdl_context.release_lock(target_mdl_request.ticket);
|
||||
|
||||
thd->mdl_context.release_all_locks_for_name(mdl_ticket);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
@ -7851,6 +7819,8 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
|
||||
uninitialized data. open_tables() could fail.
|
||||
*/
|
||||
table_list->table= NULL;
|
||||
/* Same applies to MDL ticket. */
|
||||
table_list->mdl_request.ticket= NULL;
|
||||
|
||||
bzero((char*) &create_info, sizeof(create_info));
|
||||
create_info.row_type=ROW_TYPE_NOT_USED;
|
||||
|
@ -138,11 +138,7 @@ void udf_init()
|
||||
lex_start(new_thd);
|
||||
new_thd->set_db(db, sizeof(db)-1);
|
||||
|
||||
bzero((uchar*) &tables,sizeof(tables));
|
||||
tables.alias= tables.table_name= (char*) "func";
|
||||
tables.lock_type = TL_READ;
|
||||
tables.db= db;
|
||||
alloc_mdl_requests(&tables, new_thd->mem_root);
|
||||
tables.init_one_table(db, sizeof(db)-1, "func", 4, "func", TL_READ);
|
||||
|
||||
if (simple_open_n_lock_tables(new_thd, &tables))
|
||||
{
|
||||
@ -483,10 +479,7 @@ int mysql_create_function(THD *thd,udf_func *udf)
|
||||
|
||||
/* create entry in mysql.func table */
|
||||
|
||||
bzero((char*) &tables,sizeof(tables));
|
||||
tables.db= (char*) "mysql";
|
||||
tables.table_name= tables.alias= (char*) "func";
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE);
|
||||
/* Allow creation of functions even if we can't open func table */
|
||||
if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
|
||||
goto err;
|
||||
@ -562,10 +555,8 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
|
||||
if (udf->dlhandle && !find_udf_dl(udf->dl))
|
||||
dlclose(udf->dlhandle);
|
||||
|
||||
bzero((char*) &tables,sizeof(tables));
|
||||
tables.db=(char*) "mysql";
|
||||
tables.table_name= tables.alias= (char*) "func";
|
||||
alloc_mdl_requests(&tables, thd->mem_root);
|
||||
tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE);
|
||||
|
||||
if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
|
||||
goto err;
|
||||
table->use_all_columns();
|
||||
@ -579,15 +570,16 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
|
||||
if ((error = table->file->ha_delete_row(table->record[0])))
|
||||
table->file->print_error(error, MYF(0));
|
||||
}
|
||||
close_thread_tables(thd);
|
||||
|
||||
rw_unlock(&THR_LOCK_udf);
|
||||
|
||||
/* Binlog the drop function. */
|
||||
/*
|
||||
Binlog the drop function. Keep the table open and locked
|
||||
while binlogging, to avoid binlog inconsistency.
|
||||
*/
|
||||
write_bin_log(thd, TRUE, thd->query(), thd->query_length());
|
||||
|
||||
DBUG_RETURN(0);
|
||||
err:
|
||||
err:
|
||||
rw_unlock(&THR_LOCK_udf);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ int mysql_update(THD *thd,
|
||||
break;
|
||||
if (!need_reopen)
|
||||
DBUG_RETURN(1);
|
||||
close_tables_for_reopen(thd, &table_list, FALSE);
|
||||
close_tables_for_reopen(thd, &table_list);
|
||||
}
|
||||
|
||||
if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
|
||||
@ -1149,7 +1149,7 @@ reopen_tables:
|
||||
*/
|
||||
cleanup_items(thd->free_list);
|
||||
|
||||
close_tables_for_reopen(thd, &table_list, FALSE);
|
||||
close_tables_for_reopen(thd, &table_list);
|
||||
goto reopen_tables;
|
||||
}
|
||||
|
||||
|
14
sql/table.cc
14
sql/table.cc
@ -4583,6 +4583,14 @@ void TABLE_LIST::reinit_before_use(THD *thd)
|
||||
}
|
||||
while (parent_embedding &&
|
||||
parent_embedding->nested_join->join_list.head() == embedded);
|
||||
|
||||
mdl_request.ticket= NULL;
|
||||
/*
|
||||
Not strictly necessary, but we manipulate with the type in open_table(),
|
||||
so it's "safe" to reset the lock request type to the parser default, to
|
||||
restore things back to first-execution state.
|
||||
*/
|
||||
mdl_request.set_type(MDL_SHARED);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4811,11 +4819,11 @@ size_t max_row_length(TABLE *table, const uchar *data)
|
||||
objects for all elements of table list.
|
||||
*/
|
||||
|
||||
void alloc_mdl_requests(TABLE_LIST *table_list, MEM_ROOT *root)
|
||||
void init_mdl_requests(TABLE_LIST *table_list)
|
||||
{
|
||||
for ( ; table_list ; table_list= table_list->next_global)
|
||||
table_list->mdl_request=
|
||||
MDL_request::create(0, table_list->db, table_list->table_name, root);
|
||||
table_list->mdl_request.init(0, table_list->db, table_list->table_name,
|
||||
MDL_SHARED);
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
|
||||
#include "sql_plist.h"
|
||||
#include "mdl.h"
|
||||
|
||||
/* Structs that defines the TABLE */
|
||||
|
||||
@ -30,8 +31,6 @@ class st_select_lex;
|
||||
class partition_info;
|
||||
class COND_EQUAL;
|
||||
class Security_context;
|
||||
class MDL_request;
|
||||
class MDL_ticket;
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
@ -1126,6 +1125,7 @@ struct TABLE_LIST
|
||||
table_name_length= table_name_length_arg;
|
||||
alias= (char*) alias_arg;
|
||||
lock_type= lock_type_arg;
|
||||
mdl_request.init(0, db, table_name, MDL_SHARED);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1429,7 +1429,7 @@ struct TABLE_LIST
|
||||
uint table_open_method;
|
||||
enum enum_schema_table_state schema_table_state;
|
||||
|
||||
MDL_request *mdl_request;
|
||||
MDL_request mdl_request;
|
||||
|
||||
void calc_md5(char *buffer);
|
||||
void set_underlying_merge();
|
||||
@ -1798,6 +1798,6 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set,
|
||||
size_t max_row_length(TABLE *table, const uchar *data);
|
||||
|
||||
|
||||
void alloc_mdl_requests(TABLE_LIST *table_list, MEM_ROOT *root);
|
||||
void init_mdl_requests(TABLE_LIST *table_list);
|
||||
|
||||
#endif /* TABLE_INCLUDED */
|
||||
|
@ -1637,6 +1637,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
|
||||
tz_init_table_list(tz_tables+1);
|
||||
tz_tables[0].next_global= tz_tables[0].next_local= &tz_tables[1];
|
||||
tz_tables[1].prev_global= &tz_tables[0].next_global;
|
||||
init_mdl_requests(tz_tables);
|
||||
|
||||
/*
|
||||
We need to open only mysql.time_zone_leap_second, but we try to
|
||||
@ -2296,6 +2297,7 @@ my_tz_find(THD *thd, const String *name)
|
||||
Open_tables_state open_tables_state_backup;
|
||||
|
||||
tz_init_table_list(tz_tables);
|
||||
init_mdl_requests(tz_tables);
|
||||
if (!open_system_tables_for_read(thd, tz_tables,
|
||||
&open_tables_state_backup))
|
||||
{
|
||||
|
@ -388,7 +388,6 @@ int ha_myisammrg::add_children_list(void)
|
||||
{
|
||||
TABLE_LIST *parent_l= this->table->pos_in_table_list;
|
||||
TABLE_LIST *child_l;
|
||||
THD *thd= current_thd;
|
||||
DBUG_ENTER("ha_myisammrg::add_children_list");
|
||||
DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", this->table->s->db.str,
|
||||
this->table->s->table_name.str, (long) this->table));
|
||||
@ -434,15 +433,12 @@ int ha_myisammrg::add_children_list(void)
|
||||
/* Copy select_lex. Used in unique_table() at least. */
|
||||
child_l->select_lex= parent_l->select_lex;
|
||||
|
||||
child_l->mdl_request= NULL; /* Safety, if alloc_mdl_requests fails. */
|
||||
|
||||
/* Break when this was the last child. */
|
||||
if (&child_l->next_global == this->children_last_l)
|
||||
break;
|
||||
}
|
||||
|
||||
alloc_mdl_requests(children_l, thd->locked_tables_root ?
|
||||
thd->locked_tables_root : thd->mem_root);
|
||||
init_mdl_requests(children_l);
|
||||
|
||||
/* Insert children into the table list. */
|
||||
if (parent_l->next_global)
|
||||
@ -819,6 +815,8 @@ int ha_myisammrg::detach_children(void)
|
||||
Clear the table reference.
|
||||
*/
|
||||
child_l->table= NULL;
|
||||
/* Similarly, clear the ticket reference. */
|
||||
child_l->mdl_request.ticket= NULL;
|
||||
|
||||
/* Break when this was the last child. */
|
||||
if (&child_l->next_global == this->children_last_l)
|
||||
|
Loading…
x
Reference in New Issue
Block a user