#At file:///opt/local/work/6.0-46224-1/ based on revid:kostja@stripped
3700 Konstantin Osipov 2009-11-12
A fix for Bug#46224 "HANDLER statements within
a transaction might lead to deadlocks"
modified:
sql/lock.cc
sql/mdl.cc
sql/mdl.h
sql/sql_base.cc
sql/sql_class.cc
sql/sql_class.h
sql/sql_delete.cc
sql/sql_handler.cc
sql/sql_insert.cc
sql/sql_parse.cc
sql/sql_plist.h
sql/sql_table.cc
=== modified file 'sql/lock.cc'
--- a/sql/lock.cc 2009-10-12 09:08:34 +0000
+++ b/sql/lock.cc 2009-11-11 22:40:17 +0000
@@ -336,23 +336,12 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,
preserved.
*/
reset_lock_data(sql_lock);
- thd->some_tables_deleted=1; // Try again
sql_lock->lock_count= 0; // Locks are already freed
// Fall through: unlock, reset lock data, free and retry
}
- else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
- {
- /*
- Success and nobody set thd->some_tables_deleted to force reopen
- or we were called with MYSQL_LOCK_IGNORE_FLUSH so such attempts
- should be ignored.
- */
- break;
- }
- else if (!thd->open_tables)
+ else
{
- // Only using temporary tables, no need to unlock
- thd->some_tables_deleted=0;
+ /* Success */
break;
}
thd_proc_info(thd, 0);
=== modified file 'sql/mdl.cc'
--- a/sql/mdl.cc 2009-11-09 22:43:35 +0000
+++ b/sql/mdl.cc 2009-11-11 22:40:17 +0000
@@ -219,80 +219,6 @@ void MDL_context::destroy()
/**
- Backup and reset state of meta-data locking context.
-
- mdl_context_backup_and_reset(), mdl_context_restore() and
- mdl_context_merge() are used by HANDLER implementation which
- needs to open table for new HANDLER independently of already
- open HANDLERs and add this table/metadata lock to the set of
- tables open/metadata locks for HANDLERs afterwards.
-*/
-
-void MDL_context::backup_and_reset(MDL_context *backup)
-{
- DBUG_ASSERT(backup->m_tickets.is_empty());
-
- m_tickets.swap(backup->m_tickets);
-
- backup->m_has_global_shared_lock= m_has_global_shared_lock;
- backup->m_lt_or_ha_sentinel= m_lt_or_ha_sentinel;
- /*
- When the main context is swapped out, one can not take
- the global shared lock, and one can not rely on it:
- the functionality in this mode is reduced, since it exists as
- a temporary hack to support ad-hoc opening of system tables.
- */
- m_has_global_shared_lock= FALSE;
- m_lt_or_ha_sentinel= NULL;
-}
-
-
-/**
- Restore state of meta-data locking context from backup.
-*/
-
-void MDL_context::restore_from_backup(MDL_context *backup)
-{
- DBUG_ASSERT(m_tickets.is_empty());
- DBUG_ASSERT(m_has_global_shared_lock == FALSE);
-
- m_tickets.swap(backup->m_tickets);
- m_has_global_shared_lock= backup->m_has_global_shared_lock;
- m_lt_or_ha_sentinel= backup->m_lt_or_ha_sentinel;
-}
-
-
-/**
- Merge meta-data locks from one context into another.
-*/
-
-void MDL_context::merge(MDL_context *src)
-{
- MDL_ticket *ticket;
-
- DBUG_ASSERT(m_thd == src->m_thd);
-
- if (!src->m_tickets.is_empty())
- {
- Ticket_iterator it(src->m_tickets);
- while ((ticket= it++))
- {
- DBUG_ASSERT(ticket->m_ctx);
- ticket->m_ctx= this;
- m_tickets.push_front(ticket);
- }
- src->m_tickets.empty();
- }
- /*
- MDL_context::merge() is a hack used in one place only: to open
- an SQL handler. We never acquire the global shared lock there.
- */
- DBUG_ASSERT(! src->m_has_global_shared_lock);
- DBUG_ASSERT(! src->m_lt_or_ha_sentinel);
-}
-
-
-/**
Initialize a lock request.
This is to be used for every lock request.
@@ -611,7 +537,7 @@ MDL_lock::can_grant_lock(const MDL_conte
if (waiting.is_empty() || type_arg == MDL_SHARED_HIGH_PRIO)
can_grant= TRUE;
}
- else if (granted.head()->get_ctx() == requestor_ctx)
+ else if (granted.front()->get_ctx() == requestor_ctx)
{
/*
When exclusive lock comes from the same context we can satisfy our
@@ -664,6 +590,9 @@ MDL_lock::can_grant_lock(const MDL_conte
/**
Check whether the context already holds a compatible lock ticket
on an object.
+ Start searching the transactional locks. If not
+ found in the list of transactional locks, look at LOCK TABLES
+ and HANDLER locks.
@param mdl_request Lock request object for lock to be acquired
@@ -671,13 +600,19 @@ MDL_lock::can_grant_lock(const MDL_conte
*/
MDL_ticket *
-MDL_context::find_ticket(MDL_request *mdl_request)
+MDL_context::find_ticket(MDL_request *mdl_request,
+ bool *is_lt_or_ha)
{
MDL_ticket *ticket;
Ticket_iterator it(m_tickets);
+ *is_lt_or_ha= FALSE;
+
while ((ticket= it++))
{
+ if (ticket == m_lt_or_ha_sentinel)
+ *is_lt_or_ha= TRUE;
+
if (mdl_request->type == ticket->m_type &&
mdl_request->key.is_equal(&ticket->m_lock->key))
break;
@@ -714,6 +649,7 @@ MDL_context::try_acquire_shared_lock(MDL
MDL_lock *lock;
MDL_key *key= &mdl_request->key;
MDL_ticket *ticket;
+ bool is_lt_or_ha;
DBUG_ASSERT(mdl_request->is_shared() && mdl_request->ticket == NULL);
@@ -729,10 +665,13 @@ MDL_context::try_acquire_shared_lock(MDL
}
/*
- Check whether the context already holds a shared lock on the object,
- and if so, grant the request.
+ Check whether the context already holds a shared lock on the
+ object, and if so, grant the request.
+ If the existing ticket is a HANDLER or LOCK TABLES ticket,
+ or if we're acquiring a HANDLER ticket, we will create a
+ separate ticket for it, see below.
*/
- if ((ticket= find_ticket(mdl_request)))
+ if ((ticket= find_ticket(mdl_request, &is_lt_or_ha)) && !is_lt_or_ha)
{
DBUG_ASSERT(ticket->m_state == MDL_ACQUIRED);
/* Only shared locks can be recursive. */
@@ -769,7 +708,12 @@ MDL_context::try_acquire_shared_lock(MDL
}
}
- if (lock->can_grant_lock(this, mdl_request->type, FALSE))
+ /*
+ If we already have such ticket, just
+ create another MDL_ticket object and grant it.
+ Otherwise check if we can grant the lock.
+ */
+ if (is_lt_or_ha || lock->can_grant_lock(this, mdl_request->type, FALSE))
{
mdl_request->ticket= ticket;
lock->granted.push_front(ticket);
@@ -792,6 +736,43 @@ MDL_context::try_acquire_shared_lock(MDL
/**
+ Create a copy of a granted ticket.
+ This is used to make sure that HANDLER ticket
+ is never shared with a ticket that belongs to
+ a transaction, so that when we HANDLER CLOSE,
+ we don't release a transactional ticket, and
+ vice versa -- when we COMMIT, we don't mistakenly
+ release a ticket for an open HANDLER.
+*/
+
+bool
+MDL_context::clone_ticket(MDL_request *mdl_request)
+{
+ MDL_ticket *ticket;
+
+ safe_mutex_assert_not_owner(&LOCK_open);
+ /* Only used for HANDLER. */
+ DBUG_ASSERT(mdl_request->ticket && mdl_request->ticket->is_shared());
+
+ if (!(ticket= MDL_ticket::create(this, mdl_request->type)))
+ return TRUE;
+
+ ticket->m_state= MDL_ACQUIRED;
+ ticket->m_lock= mdl_request->ticket->m_lock;
+ mdl_request->ticket= ticket;
+
+ pthread_mutex_lock(&LOCK_mdl);
+ ticket->m_lock->granted.push_front(ticket);
+ if (mdl_request->type == MDL_SHARED_UPGRADABLE)
+ global_lock.active_intention_exclusive++;
+ pthread_mutex_unlock(&LOCK_mdl);
+
+ m_tickets.push_front(ticket);
+
+ return FALSE;
+}
+
+/**
Notify a thread holding a shared metadata lock which
conflicts with a pending exclusive lock.
@@ -855,7 +836,10 @@ bool MDL_context::acquire_exclusive_lock
safe_mutex_assert_not_owner(&LOCK_open);
/* Exclusive locks must always be acquired first, all at once. */
- DBUG_ASSERT(! has_locks());
+ DBUG_ASSERT(! has_locks() ||
+ (m_lt_or_ha_sentinel &&
+ m_tickets.front() == m_lt_or_ha_sentinel &&
+ ! thd_in_lock_tables(m_thd)));
if (m_has_global_shared_lock)
{
@@ -1465,8 +1449,9 @@ MDL_context::is_exclusive_lock_owner(MDL
const char *db, const char *name)
{
MDL_request mdl_request;
+ bool unused;
mdl_request.init(mdl_namespace, db, name, MDL_EXCLUSIVE);
- MDL_ticket *ticket= find_ticket(&mdl_request);
+ MDL_ticket *ticket= find_ticket(&mdl_request, &unused);
DBUG_ASSERT(ticket == NULL || ticket->m_state == MDL_ACQUIRED);
@@ -1637,3 +1622,52 @@ void MDL_context::release_all_locks()
DBUG_VOID_RETURN;
}
+
+
+/**
+ Does this savepoint has this lock?
+
+ @retval TRUE if the ticket belongs to a savepoint taken.
+ @retval FALSE The ticket is either LT/HA ticket,
+ or is taken after the savepoint.
+*/
+
+bool MDL_context::has_lock(MDL_ticket *mdl_savepoint,
+ MDL_ticket *mdl_ticket)
+{
+ MDL_ticket *ticket;
+ MDL_context::Ticket_iterator it(m_tickets);
+
+ while ((ticket= it++) && ticket != m_lt_or_ha_sentinel)
+ {
+ /* First met the savepoint. Then ticket must be sometimes after it. */
+ if (ticket == mdl_savepoint)
+ return TRUE;
+ /* First met the ticket. */
+ if (ticket == mdl_ticket)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+
+/**
+ Rearrange the ticket to reside in the part of the list that's
+ beyong m_lt_or_ha_sentinel. This effectively changes the ticket
+ life cycle, from automatic to manual: i.e. the ticket is no
+ longer released by MDL_context::commit_transaction() or
+ MDL_context::rollback_to_savepoint(), it must be released manually.
+*/
+
+void MDL_context::move_lt_or_ha_ticket(MDL_ticket *mdl_ticket)
+{
+ m_tickets.remove(mdl_ticket);
+ if (m_lt_or_ha_sentinel == NULL)
+ {
+ m_lt_or_ha_sentinel= mdl_ticket;
+ /* sic: linear from the number of transactional tickets acquired so-far! */
+ m_tickets.push_back(mdl_ticket);
+ }
+ else
+ m_tickets.insert_after(m_lt_or_ha_sentinel, mdl_ticket);
+}
=== modified file 'sql/mdl.h'
--- a/sql/mdl.h 2009-11-09 22:43:35 +0000
+++ b/sql/mdl.h 2009-11-11 22:40:17 +0000
@@ -327,15 +327,12 @@ public:
void init(THD *thd);
void destroy();
- void backup_and_reset(MDL_context *backup);
- void restore_from_backup(MDL_context *backup);
- void merge(MDL_context *source);
-
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 clone_ticket(MDL_request *mdl_request);
bool wait_for_locks(MDL_request_list *requests);
@@ -349,6 +346,9 @@ public:
bool is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
const char *db, const char *name);
+
+ bool has_lock(MDL_ticket *mdl_savepoint, MDL_ticket *mdl_ticket);
+
inline bool has_locks() const
{
return !m_tickets.is_empty();
@@ -356,7 +356,7 @@ public:
inline MDL_ticket *mdl_savepoint()
{
- return m_tickets.head();
+ return m_tickets.front();
}
void set_lt_or_ha_sentinel()
@@ -369,6 +369,8 @@ public:
{
m_lt_or_ha_sentinel= NULL;
}
+ void move_lt_or_ha_ticket(MDL_ticket *mdl_ticket);
+
void commit_transaction();
void release_all_locks();
@@ -387,7 +389,8 @@ private:
THD *m_thd;
private:
void release_ticket(MDL_ticket *ticket);
- MDL_ticket *find_ticket(MDL_request *mdl_req);
+ MDL_ticket *find_ticket(MDL_request *mdl_req,
+ bool *is_lt_or_ha);
void release_all_locks_after(MDL_ticket *sentinel);
};
=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc 2009-11-09 22:43:35 +0000
+++ b/sql/sql_base.cc 2009-11-11 22:40:17 +0000
@@ -1258,7 +1258,6 @@ static void close_open_tables(THD *thd)
while (thd->open_tables)
found_old_table|= close_thread_table(thd, &thd->open_tables);
- thd->some_tables_deleted= 0;
/* Free tables to hold down open files */
while (table_cache_count > table_cache_size && unused_tables)
@@ -2343,7 +2342,8 @@ open_table_get_mdl_lock(THD *thd, TABLE_
enforced by asserts in metadata locking subsystem.
*/
mdl_request->set_type(MDL_EXCLUSIVE);
- DBUG_ASSERT(! thd->mdl_context.has_locks());
+ DBUG_ASSERT(! thd->mdl_context.has_locks() ||
+ thd->handler_tables_hash.records);
if (thd->mdl_context.acquire_exclusive_lock(mdl_request))
return 1;
@@ -2801,7 +2801,7 @@ bool open_table(THD *thd, TABLE_LIST *ta
if (!share->free_tables.is_empty())
{
- table= share->free_tables.head();
+ table= share->free_tables.front();
table_def_use_table(thd, table);
/* We need to release share as we have EXTRA reference to it in our hands. */
release_table_share(share);
@@ -4145,8 +4145,7 @@ bool open_tables(THD *thd, TABLE_LIST **
even if they don't create problems for current thread (i.e. to avoid
having DDL blocked by HANDLERs opened for long time).
*/
- if (thd->handler_tables)
- mysql_ha_flush(thd);
+ mysql_ha_flush(thd);
/*
temporary mem_root for new .frm parsing.
=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc 2009-10-26 14:02:26 +0000
+++ b/sql/sql_class.cc 2009-11-11 22:40:17 +0000
@@ -492,7 +492,7 @@ THD::THD()
catalog= (char*)"std"; // the only catalog we have for now
main_security_ctx.init();
security_ctx= &main_security_ctx;
- some_tables_deleted=no_errors=password= 0;
+ no_errors=password= 0;
query_start_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
killed= NOT_KILLED;
@@ -1006,6 +1006,7 @@ void THD::cleanup(void)
}
locked_tables_list.unlock_locked_tables(this);
+ mysql_ha_cleanup(this);
/*
If the thread was in the middle of an ongoing transaction (rolled
@@ -1022,7 +1023,6 @@ void THD::cleanup(void)
#endif /* defined(ENABLED_DEBUG_SYNC) */
wt_thd_destroy(&transaction.wt);
- mysql_ha_cleanup(this);
delete_dynamic(&user_var_events);
my_hash_free(&user_vars);
close_temporary_tables(this);
@@ -1099,7 +1099,6 @@ THD::~THD()
cleanup();
mdl_context.destroy();
- handler_mdl_context.destroy();
ha_close_connection(this);
mysql_audit_release(this);
plugin_thdvar_cleanup(this);
@@ -3134,12 +3133,11 @@ void THD::restore_backup_open_tables_sta
to be sure that it was properly cleaned up.
*/
DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 &&
- handler_tables == 0 && derived_tables == 0 &&
+ derived_tables == 0 &&
lock == 0 &&
locked_tables_mode == LTM_NONE &&
m_reprepare_observer == NULL);
mdl_context.destroy();
- handler_mdl_context.destroy();
set_open_tables_state(backup);
DBUG_VOID_RETURN;
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2009-11-09 22:43:35 +0000
+++ b/sql/sql_class.h 2009-11-11 22:40:17 +0000
@@ -935,11 +935,6 @@ public:
XXX Why are internal temporary tables added to this list?
*/
TABLE *temporary_tables;
- /**
- List of tables that were opened with HANDLER OPEN and are
- still in use by this thread.
- */
- TABLE *handler_tables;
TABLE *derived_tables;
/*
During a MySQL session, one can lock tables in two modes: automatic
@@ -1005,8 +1000,6 @@ public:
uint state_flags;
MDL_context mdl_context;
- MDL_context handler_mdl_context;
-
/**
This constructor initializes Open_tables_state instance which can only
be used as backup storage. To prepare Open_tables_state instance for
@@ -1031,13 +1024,12 @@ public:
void reset_open_tables_state(THD *thd)
{
- open_tables= temporary_tables= handler_tables= derived_tables= 0;
+ open_tables= temporary_tables= derived_tables= 0;
extra_lock= lock= 0;
locked_tables_mode= LTM_NONE;
state_flags= 0U;
m_reprepare_observer= NULL;
mdl_context.init(thd);
- handler_mdl_context.init(thd);
}
void enter_locked_tables_mode(enum_locked_tables_mode mode_arg)
{
@@ -1955,7 +1947,6 @@ public:
bool slave_thread, one_shot_set;
/* tells if current statement should binlog row-based(1) or stmt-based(0) */
bool current_stmt_binlog_row_based;
- bool some_tables_deleted;
bool last_cuted_field;
bool no_errors, password;
/**
=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc 2009-10-23 06:24:37 +0000
+++ b/sql/sql_delete.cc 2009-11-11 22:40:17 +0000
@@ -1129,8 +1129,6 @@ bool mysql_truncate(THD *thd, TABLE_LIST
Ha_global_schema_lock_guard global_schema_lock_guard(thd);
DBUG_ENTER("mysql_truncate");
- mysql_ha_rm_tables(thd, table_list);
-
bzero((char*) &create_info,sizeof(create_info));
/* Remove tables from the HANDLER's hash. */
=== modified file 'sql/sql_handler.cc'
--- a/sql/sql_handler.cc 2009-10-25 13:41:27 +0000
+++ b/sql/sql_handler.cc 2009-11-11 22:40:17 +0000
@@ -124,32 +124,19 @@ static void mysql_ha_hash_free(TABLE_LIS
static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables)
{
- TABLE **table_ptr;
- MDL_ticket *mdl_ticket;
- /*
- Though we could take the table pointer from hash_tables->table,
- we must follow the thd->handler_tables chain anyway, as we need the
- address of the 'next' pointer referencing this table
- for close_thread_table().
- */
- for (table_ptr= &(thd->handler_tables);
- *table_ptr && (*table_ptr != tables->table);
- table_ptr= &(*table_ptr)->next)
- ;
-
- if (*table_ptr)
+ if (tables->table && !tables->table->s->tmp_table)
{
- (*table_ptr)->file->ha_index_or_rnd_end();
- mdl_ticket= (*table_ptr)->mdl_ticket;
+ /* Non temporary table. */
+ tables->table->file->ha_index_or_rnd_end();
pthread_mutex_lock(&LOCK_open);
- if (close_thread_table(thd, table_ptr))
+ if (close_thread_table(thd, &tables->table))
{
/* Tell threads waiting for refresh that something has happened */
broadcast_refresh();
}
pthread_mutex_unlock(&LOCK_open);
- thd->handler_mdl_context.release_lock(mdl_ticket);
+ thd->mdl_context.release_lock(tables->mdl_request.ticket);
}
else if (tables->table)
{
@@ -195,7 +182,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
uint dblen, namelen, aliaslen, counter;
bool error;
TABLE *backup_open_tables;
- MDL_context backup_mdl_context;
+ MDL_ticket *mdl_savepoint;
DBUG_ENTER("mysql_ha_open");
DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
tables->db, tables->table_name, tables->alias,
@@ -265,6 +252,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
memcpy(hash_tables->table_name, tables->table_name, namelen);
memcpy(hash_tables->alias, tables->alias, aliaslen);
hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED);
+ /* for now HANDLER can be used only for real TABLES */
+ hash_tables->required_type= FRMTYPE_TABLE;
/* add to hash */
if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
@@ -292,7 +281,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
*/
backup_open_tables= thd->open_tables;
thd->open_tables= NULL;
- thd->mdl_context.backup_and_reset(&backup_mdl_context);
+ mdl_savepoint= thd->mdl_context.mdl_savepoint();
/*
open_tables() will set 'hash_tables->table' if successful.
@@ -300,53 +289,44 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
*/
DBUG_ASSERT(! hash_tables->table);
- /* for now HANDLER can be used only for real TABLES */
- hash_tables->required_type= FRMTYPE_TABLE;
/*
We use open_tables() here, rather than, say,
open_ltable() or open_table() because we would like to be able
to open a temporary table.
*/
error= open_tables(thd, &hash_tables, &counter, 0);
- if (thd->open_tables)
+
+ if (! error &&
+ hash_tables->table &&
+ ! (hash_tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
{
- if (thd->open_tables->next)
- {
- /*
- We opened something that is more than a single table.
- This happens with MERGE engine. Don't try to link
- this mess into thd->handler_tables list, close it
- and report an error. We must do it right away
- because mysql_ha_close_table(), called down the road,
- can close a single table only.
- */
- close_thread_tables(thd);
- thd->mdl_context.release_all_locks();
- my_error(ER_ILLEGAL_HA, MYF(0), hash_tables->alias);
- error= TRUE;
- }
- else
- {
- /* Merge the opened table into handler_tables list. */
- thd->open_tables->next= thd->handler_tables;
- thd->handler_tables= thd->open_tables;
- }
+ my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
+ error= TRUE;
+ }
+ if (!error &&
+ hash_tables->mdl_request.ticket &&
+ thd->mdl_context.has_lock(mdl_savepoint,
+ hash_tables->mdl_request.ticket))
+ {
+ /* The ticket returned is within a savepoint. Make a copy. */
+ error= thd->mdl_context.clone_ticket(&hash_tables->mdl_request);
+ hash_tables->table->mdl_ticket= hash_tables->mdl_request.ticket;
}
- thd->handler_mdl_context.merge(&thd->mdl_context);
-
- /* Restore the state. */
- thd->open_tables= backup_open_tables;
- thd->mdl_context.restore_from_backup(&backup_mdl_context);
-
if (error)
- goto err;
-
- /* There can be only one table in '*tables'. */
- if (! (hash_tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
{
- my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
- goto err;
+ close_thread_tables(thd);
+ thd->open_tables= backup_open_tables;
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ if (!reopen)
+ my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
+ else
+ hash_tables->table= NULL;
+ DBUG_PRINT("exit",("ERROR"));
+ DBUG_RETURN(TRUE);
}
+ thd->open_tables= backup_open_tables;
+ if (hash_tables->mdl_request.ticket)
+ thd->mdl_context.move_lt_or_ha_ticket(hash_tables->mdl_request.ticket);
/*
If it's a temp table, don't reset table->query_id as the table is
@@ -358,14 +338,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
my_ok(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
-
-err:
- if (hash_tables->table)
- mysql_ha_close_table(thd, hash_tables);
- if (!reopen)
- my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
- DBUG_PRINT("exit",("ERROR"));
- DBUG_RETURN(TRUE);
}
@@ -497,31 +469,13 @@ retry:
hash_tables->db, hash_tables->table_name,
hash_tables->alias, table));
}
-
-#if MYSQL_VERSION_ID < 40100
- if (*tables->db && strcmp(table->table_cache_key, tables->db))
- {
- DBUG_PRINT("info",("wrong db"));
- table= NULL;
- }
-#endif
}
else
table= NULL;
if (!table)
{
-#if MYSQL_VERSION_ID < 40100
- char buff[MAX_DBKEY_LENGTH];
- if (*tables->db)
- strxnmov(buff, sizeof(buff)-1, tables->db, ".", tables->table_name,
- NullS);
- else
- strncpy(buff, tables->alias, sizeof(buff));
- my_error(ER_UNKNOWN_TABLE, MYF(0), buff, "HANDLER");
-#else
my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
-#endif
goto err0;
}
tables->table=table;
@@ -530,11 +484,9 @@ retry:
backup_open_tables= thd->open_tables;
/*
mysql_lock_tables() needs thd->open_tables to be set correctly to
- be able to handle aborts properly. When the abort happens, it's
- safe to not protect thd->handler_tables because it won't close any
- tables.
+ be able to handle aborts properly.
*/
- thd->open_tables= thd->handler_tables;
+ thd->open_tables= NULL;
lock= mysql_lock_tables(thd, &tables->table, 1, 0, &need_reopen);
@@ -544,12 +496,6 @@ retry:
if (need_reopen)
{
mysql_ha_close_table(thd, hash_tables);
- /*
- The lock might have been aborted, we need to manually reset
- thd->some_tables_deleted because handler's tables are closed
- in a non-standard way. Otherwise we might loop indefinitely.
- */
- thd->some_tables_deleted= 0;
goto retry;
}
@@ -821,7 +767,8 @@ void mysql_ha_flush(THD *thd)
if (hash_tables->table &&
((hash_tables->table->mdl_ticket &&
hash_tables->table->mdl_ticket->has_pending_conflicting_lock()) ||
- hash_tables->table->s->needs_reopen()))
+ (!hash_tables->table->s->tmp_table &&
+ hash_tables->table->s->needs_reopen())))
mysql_ha_close_table(thd, hash_tables);
}
=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc 2009-10-25 13:41:27 +0000
+++ b/sql/sql_insert.cc 2009-11-11 22:40:17 +0000
@@ -3658,8 +3658,7 @@ static TABLE *create_table_from_items(TH
/*
mysql_lock_tables() below should never fail with request to reopen table
since it won't wait for the table lock (we have exclusive metadata lock on
- the table) and thus can't get aborted and since it ignores other threads
- setting THD::some_tables_deleted thanks to MYSQL_LOCK_IGNORE_FLUSH.
+ the table) and thus can't get aborted.
*/
if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
MYSQL_LOCK_IGNORE_FLUSH, ¬_used)) ||
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2009-11-09 22:43:35 +0000
+++ b/sql/sql_parse.cc 2009-11-11 22:40:17 +0000
@@ -3636,6 +3636,12 @@ end_with_restore_list:
if (trans_commit_implicit(thd))
goto error;
/* release transactional metadata locks. */
+ /*
+ As of 5.6, entering LOCK TABLES mode
+ implicitly closes all open HANDLERs. This is
+ to avoid deadlocks.
+ */
+ mysql_ha_cleanup(thd);
thd->mdl_context.commit_transaction();
if (lex->protect_against_global_read_lock &&
wait_if_global_read_lock(thd, 0, 1))
@@ -6886,7 +6892,9 @@ bool reload_acl_and_cache(THD *thd, ulon
}
#endif /*HAVE_QUERY_CACHE*/
- DBUG_ASSERT(!thd || thd->locked_tables_mode || !thd->mdl_context.has_locks());
+ DBUG_ASSERT(!thd || thd->locked_tables_mode ||
+ !thd->mdl_context.has_locks() ||
+ thd->handler_tables_hash.records);
/*
Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too
=== modified file 'sql/sql_plist.h'
--- a/sql/sql_plist.h 2009-03-04 13:31:31 +0000
+++ b/sql/sql_plist.h 2009-11-11 22:40:17 +0000
@@ -71,6 +71,36 @@ public:
first= a;
*B::prev_ptr(a)= &first;
}
+ inline void push_back(T *a)
+ {
+ insert_after(back(), a);
+ }
+ inline T *back()
+ {
+ T *t= front();
+ if (t)
+ {
+ while (*B::next_ptr(t))
+ t= *B::next_ptr(t);
+ }
+ return t;
+ }
+ inline void insert_after(T *pos, T *a)
+ {
+ if (pos == NULL)
+ push_front(a);
+ else
+ {
+ *B::next_ptr(a)= *B::next_ptr(pos);
+ *B::prev_ptr(a)= B::next_ptr(pos);
+ *B::next_ptr(pos)= a;
+ if (*B::next_ptr(a))
+ {
+ T *old_next= *B::next_ptr(a);
+ *B::prev_ptr(old_next)= B::next_ptr(a);
+ }
+ }
+ }
inline void remove(T *a)
{
T *next= *B::next_ptr(a);
@@ -78,8 +108,8 @@ public:
*B::prev_ptr(next)= *B::prev_ptr(a);
**B::prev_ptr(a)= next;
}
- inline T* head() { return first; }
- inline const T *head() const { return first; }
+ inline T* front() { return first; }
+ inline const T *front() const { return first; }
void swap(I_P_List<T,B> &rhs)
{
swap_variables(T *, first, rhs.first);
=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc 2009-11-09 22:43:35 +0000
+++ b/sql/sql_table.cc 2009-11-11 22:40:17 +0000
@@ -7630,7 +7630,6 @@ end_temporary:
(ulong) (copied + deleted), (ulong) deleted,
(ulong) thd->warning_info->statement_warn_count());
my_ok(thd, copied + deleted, 0L, tmp_name);
- thd->some_tables_deleted=0;
DBUG_RETURN(FALSE);
err_new_table_cleanup:
Attachment: [text/bzr-bundle] bzr/kostja@sun.com-20091111224017-9xkjtftddjq1g4eb.bundle
| Thread |
|---|
| • bzr commit into mysql-6.0-codebase-bugfixing branch (kostja:3700)Bug#46224 | Konstantin Osipov | 11 Nov |