Commit aeebede1 authored by Konstantin Osipov's avatar Konstantin Osipov

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.


sql/ha_ndbcluster_binlog.cc:
  lock_tables() now also accepts set of options to be passed to
  mysql_lock_tables().
sql/lock.cc:
  Changed mysql_lock_tables() always requests caller to reopen
  table instead doing this on its own when waiting for lock was
  aborted. This allows us to get rid of several functions which
  were used in rare cases and significantly complicated our life.
sql/log_event_old.cc:
  lock_tables() now also accepts set of options to be passed
  to mysql_lock_tables().
sql/mysql_priv.h:
  Now mysql_lock_tables() always requests caller to reopen
  table instead doing this on its own when waiting for lock was
  aborted. So we no longer need wait_for_tables() and
  table_is_used() functions and MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN
  flag.
  open_and_lock_table_derived() and lock_tables() now accept
  options to be passed to open_tables() and mysql_lock_tables()
  calls.
sql/sql_base.cc:
  Since now mysql_lock_tables() always requests caller to
  reopen table instead doing this on its own when waiting for
  lock was aborted we no longer need wait_for_tables(),
  table_is_used() and close_old_data_files() functions.
  open_and_lock_table_derived() and lock_tables() now accept
  options to be passed to open_tables() and mysql_lock_tables()
  calls. This was needed in order to get rid of redundant code
  in open_system_tables_for_read() function.
sql/sql_handler.cc:
  mysql_lock_tables() is now always requests reopen if waiting
  for lock is aborted. MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN flag
  was removed.
sql/sql_insert.cc:
  handle_delayed_insert():
  Since mysql_lock_tables() is no longer responsible for
  reopening tables when waiting for lock was aborted we have
  to handle such situation outside of this function. To simplify
  this extracted code opening table for delayed insert thread to
  separate method of Delayed_insert class.
sql/sql_update.cc:
  lock_tables() now also accepts set of options to be passed
  to mysql_lock_tables().
parent de1979d3
......@@ -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)
{
......
......@@ -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);
......
This diff is collapsed.
......@@ -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
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.
*/
if (di->open_and_lock_table())
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,
&not_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, &not_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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment