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