Backport of:

----------------------------------------------------------
revno: 2630.10.1
committer: Konstantin Osipov <konstantin@mysql.com>
branch nick: mysql-6.0-lock-tables-tidyup
timestamp: Wed 2008-06-11 15:49:58 +0400
message:
  WL#3726, review fixes.
  Now that we have metadata locks, we don't need to keep a crippled
  TABLE instance in the table cache to indicate that a table is locked.
  Remove all code that used this technique. Instead, rely on metadata
  locks and use the standard open_table() and close_thread_table()
  to manipulate with the table cache tables.
  Removes a list of functions that have become unused (see the comment
  for sql_base.cc for details).
  Under LOCK TABLES, keep a TABLE_LIST instance for each table
  that may be temporarily closed. For that, implement an own class for
  LOCK TABLES mode, Locked_tables_list.

This is a pre-requisite patch for WL#4144.
This is not exactly a backport: there is no new 
online ALTER table in Celosia, so the old alter table
code was changed to work with the new table cache API.


mysql-test/r/lock.result:
  Update results (WL#3726 post-review patch).
mysql-test/r/trigger-compat.result:
  We take the table from the table cache now, thus no warning.
mysql-test/suite/rpl/r/rpl_trigger.result:
  We take the table from the table cache now, thus no warning.
mysql-test/t/lock.test:
  Additional tests for LOCK TABLES mode (previously not covered by
  the test suite (WL#3726).
sql/field.h:
  Remove reopen_table().
sql/lock.cc:
  Remove an obsolete parameter of mysql_lock_remove().
  It's not used anywhere now either.
sql/mysql_priv.h:
  Add 4 new open_table() flags.
  Remove declarations of removed functions.
sql/sp_head.cc:
  Rename thd->mdl_el_root to thd->locked_tables_root.
sql/sql_acl.cc:
  Use the new implementation of unlock_locked_tables().
sql/sql_base.cc:
  Implement class Locked_tables_list.
  Implement close_all_tables_for_name().
  Rewrite close_cached_tables() to use the new reopen_tables().
  Remove reopen_table(), reopen_tables(), reopen_table_entry() 
  (ex. open_unireg_entry()), close_data_files_and_leave_as_placeholders(),
  close_handle_and_leave_table_as_placeholder(), close_cached_table(),
  table_def_change_share(), reattach_merge(), reopen_name_locked_table(),
  unlink_open_table().
  
  Move acquisition of a metadata lock into an own function
  - open_table_get_mdl_lock().
sql/sql_class.cc:
  Deploy class Locked_tables_list.
sql/sql_class.h:
  Declare class Locked_tables_list.
  Keep one instance of this class in class THD.
  Rename mdl_el_root to locked_tables_root.
sql/sql_db.cc:
  Update a comment.
sql/sql_insert.cc:
  Use the plain open_table() to open a just created table in
  CREATE TABLE .. SELECT.
sql/sql_parse.cc:
  Use thd->locked_tables_list to enter and leave LTM_LOCK_TABLES mode.
sql/sql_partition.cc:
  Deploy the new method of working with partitioned table locks.
sql/sql_servers.cc:
  Update to the new signature of unlock_locked_tables().
sql/sql_table.cc:
  In mysql_rm_table_part2(), the branch that removes a table under
  LOCK TABLES, make sure that the table being dropped
  is also removed from THD::locked_tables_list.
  Update ALTER TABLE and CREATE TABLE LIKE implementation to use
  open_table() and close_all_tables_for_name() instead of 
  reopen_tables().
sql/sql_trigger.cc:
  Use the new locking way.
sql/table.h:
  Add TABLE::pos_in_locked_tables, which is used only under
  LOCK TABLES.
This commit is contained in:
Konstantin Osipov 2009-12-02 18:22:15 +03:00
parent e8a9191e64
commit 3104af49cd
20 changed files with 767 additions and 1199 deletions

View file

@ -201,3 +201,48 @@ ERROR HY000: Table 't2' was locked with a READ lock and can't be updated
UNLOCK TABLES;
DROP TABLE t1,t2;
End of 5.1 tests.
#
# Ensure that FLUSH TABLES doesn't substitute a base locked table
# with a temporary one.
#
drop table if exists t1, t2;
create table t1 (a int);
create table t2 (a int);
lock table t1 write, t2 write;
create temporary table t1 (a int);
flush table t1;
drop temporary table t1;
select * from t1;
a
unlock tables;
drop table t1, t2;
#
# Ensure that REPAIR .. USE_FRM works under LOCK TABLES.
#
drop table if exists t1, t2;
create table t1 (a int);
create table t2 (a int);
lock table t1 write, t2 write;
repair table t1 use_frm;
Table Op Msg_type Msg_text
test.t1 repair status OK
repair table t1 use_frm;
Table Op Msg_type Msg_text
test.t1 repair status OK
select * from t1;
a
select * from t2;
a
repair table t2 use_frm;
Table Op Msg_type Msg_text
test.t2 repair status OK
repair table t2 use_frm;
Table Op Msg_type Msg_text
test.t2 repair status OK
select * from t1;
a
unlock tables;
drop table t1, t2;
#
# End of 6.0 tests.
#

View file

@ -34,8 +34,6 @@ TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_MANIPULATION EVENT_OBJECT_CATA
def mysqltest_db1 wl2818_trg1 INSERT def mysqltest_db1 t1 0 NULL INSERT INTO t2 VALUES(CURRENT_USER()) ROW BEFORE NULL NULL OLD NEW NULL latin1 latin1_swedish_ci latin1_swedish_ci
def mysqltest_db1 wl2818_trg2 INSERT def mysqltest_db1 t1 0 NULL INSERT INTO t2 VALUES(CURRENT_USER()) ROW AFTER NULL NULL OLD NEW NULL mysqltest_dfn@localhost latin1 latin1_swedish_ci latin1_swedish_ci
DROP TRIGGER wl2818_trg1;
Warnings:
Warning 1454 No definer attribute for trigger 'mysqltest_db1'.'wl2818_trg1'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger.
DROP TRIGGER wl2818_trg2;
use mysqltest_db1;
DROP TABLE t1;

View file

@ -893,8 +893,6 @@ s
@
root@localhost
DROP TRIGGER trg1;
Warnings:
Warning 1454 No definer attribute for trigger 'test'.'trg1'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger.
DROP TABLE t1;
DROP TABLE t2;
STOP SLAVE;

View file

@ -252,3 +252,46 @@ UNLOCK TABLES;
DROP TABLE t1,t2;
--echo End of 5.1 tests.
--echo #
--echo # Ensure that FLUSH TABLES doesn't substitute a base locked table
--echo # with a temporary one.
--echo #
--disable_warnings
drop table if exists t1, t2;
--enable_warnings
create table t1 (a int);
create table t2 (a int);
lock table t1 write, t2 write;
create temporary table t1 (a int);
flush table t1;
drop temporary table t1;
select * from t1;
unlock tables;
drop table t1, t2;
--echo #
--echo # Ensure that REPAIR .. USE_FRM works under LOCK TABLES.
--echo #
--disable_warnings
drop table if exists t1, t2;
--enable_warnings
create table t1 (a int);
create table t2 (a int);
lock table t1 write, t2 write;
repair table t1 use_frm;
repair table t1 use_frm;
select * from t1;
select * from t2;
repair table t2 use_frm;
repair table t2 use_frm;
select * from t1;
unlock tables;
drop table t1, t2;
--echo #
--echo # End of 6.0 tests.
--echo #

View file

@ -478,7 +478,6 @@ public:
}
/* Hash value */
virtual void hash(ulong *nr, ulong *nr2);
friend bool reopen_table(THD *,struct st_table *,bool);
friend int cre_myisam(char * name, register TABLE *form, uint options,
ulonglong auto_increment_value);
friend class Copy_field;

View file

@ -512,28 +512,15 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
/**
Try to find the table in the list of locked tables.
In case of success, unlock the table and remove it from this list.
@note This function has a legacy side effect: the table is
unlocked even if it is not found in the locked list.
It's not clear if this side effect is intentional or still
desirable. It might lead to unmatched calls to
unlock_external(). Moreover, a discrepancy can be left
unnoticed by the storage engine, because in
unlock_external() we call handler::external_lock(F_UNLCK) only
if table->current_lock is not F_UNLCK.
If a table has more than one lock instance, removes them all.
@param thd thread context
@param locked list of locked tables
@param table the table to unlock
@param always_unlock specify explicitly if the legacy side
effect is desired.
*/
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
bool always_unlock)
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
{
if (always_unlock == TRUE)
mysql_unlock_some_tables(thd, &table, /* table count */ 1);
if (locked)
{
reg1 uint i;
@ -547,9 +534,8 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
DBUG_ASSERT(table->lock_position == i);
/* Unlock if not yet unlocked */
if (always_unlock == FALSE)
mysql_unlock_some_tables(thd, &table, /* table count */ 1);
/* Unlock the table. */
mysql_unlock_some_tables(thd, &table, /* table count */ 1);
/* Decrement table_count in advance, making below expressions easier */
old_tables= --locked->table_count;

View file

@ -1225,20 +1225,13 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
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);
bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list);
TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name);
TABLE *find_write_locked_table(TABLE *list, const char *db,
const char *table_name);
void detach_merge_children(TABLE *table, bool clear_refs);
bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
TABLE_LIST *new_child_list, TABLE_LIST **new_last);
bool reopen_table(TABLE *table);
bool reopen_tables(THD *thd, bool get_locks);
thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table);
void close_data_files_and_leave_as_placeholders(THD *thd, const char *db,
const char *table_name);
void close_handle_and_leave_table_as_placeholder(TABLE *table);
void unlock_locked_tables(THD *thd);
void execute_init_command(THD *thd, sys_var_str *init_command_var,
rw_lock_t *var_mutex);
extern Field *not_found_field;
@ -1388,12 +1381,12 @@ void add_join_on(TABLE_LIST *b,Item *expr);
void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields,
SELECT_LEX *lex);
bool add_proc_to_list(THD *thd, Item *item);
bool close_cached_table(THD *thd, TABLE *table);
bool wait_while_table_is_used(THD *thd, TABLE *table,
enum ha_extra_function function);
void unlink_open_table(THD *thd, TABLE *find, bool unlock);
void drop_open_table(THD *thd, TABLE *table, const char *db_name,
const char *table_name);
void close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
bool remove_from_locked_tables);
void update_non_unique_table_error(TABLE_LIST *update,
const char *operation,
TABLE_LIST *duplicate);
@ -1515,7 +1508,6 @@ void close_temporary_table(THD *thd, TABLE *table, bool free_share,
void close_temporary(TABLE *table, bool free_share, bool delete_table);
bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db,
const char *table_name);
void remove_db_from_cache(const char *db);
void flush_tables();
bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
char *make_default_log_name(char *buff,const char* log_ext);
@ -1561,7 +1553,7 @@ void create_subpartition_name(char *out, const char *in1,
typedef struct st_lock_param_type
{
TABLE_LIST table_list;
TABLE_LIST *table_list;
ulonglong copied;
ulonglong deleted;
THD *thd;
@ -1572,7 +1564,6 @@ typedef struct st_lock_param_type
const char *db;
const char *table_name;
uchar *pack_frm_data;
enum thr_lock_type old_lock_type;
uint key_count;
uint db_options;
size_t pack_frm_len;
@ -2044,12 +2035,31 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008
#define MYSQL_LOCK_PERF_SCHEMA 0x0010
#define MYSQL_OPEN_TAKE_UPGRADABLE_MDL 0x0020
/**
Do not try to acquire a metadata lock on the table: we
already have one.
*/
#define MYSQL_OPEN_HAS_MDL_LOCK 0x0040
/**
If in locked tables mode, ignore the locked tables and get
a new instance of the table.
*/
#define MYSQL_OPEN_GET_NEW_TABLE 0x0080
/** Don't look up the table in the list of temporary tables. */
#define MYSQL_OPEN_SKIP_TEMPORARY 0x0100
/** Please refer to the internals manual. */
#define MYSQL_OPEN_REOPEN (MYSQL_LOCK_IGNORE_FLUSH |\
MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK |\
MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |\
MYSQL_OPEN_GET_NEW_TABLE |\
MYSQL_OPEN_SKIP_TEMPORARY |\
MYSQL_OPEN_HAS_MDL_LOCK)
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
bool always_unlock);
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table);
void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock);
void mysql_lock_downgrade_write(THD *thd, TABLE *table,
thr_lock_type new_lock_type);

View file

@ -3985,8 +3985,8 @@ sp_head::add_used_tables_to_table_list(THD *thd,
table->belong_to_view= belong_to_view;
table->trg_event_map= stab->trg_event_map;
table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name,
thd->mdl_el_root ?
thd->mdl_el_root :
thd->locked_tables_root ?
thd->locked_tables_root :
thd->mem_root);
/* Everyting else should be zeroed */
@ -4030,8 +4030,9 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
table->select_lex= lex->current_select;
table->cacheable_table= 1;
table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name,
thd->mdl_el_root ? thd->mdl_el_root :
thd->mem_root);
thd->locked_tables_root ?
thd->locked_tables_root : thd->mem_root);
lex->add_to_query_tables(table);
return table;
}

View file

@ -676,7 +676,8 @@ my_bool acl_reload(THD *thd)
my_bool return_val= 1;
DBUG_ENTER("acl_reload");
unlock_locked_tables(thd); // Can't have locked tables here
/* Can't have locked tables here. */
thd->locked_tables_list.unlock_locked_tables(thd);
/*
To avoid deadlocks we should obtain table locks before

File diff suppressed because it is too large Load diff

View file

@ -469,7 +469,7 @@ THD::THD()
debug_sync_control(0),
#endif /* defined(ENABLED_DEBUG_SYNC) */
main_warning_info(0),
mdl_el_root(NULL)
locked_tables_root(NULL)
{
ulong tmp;
@ -574,8 +574,6 @@ THD::THD()
thr_lock_owner_init(&main_lock_id, &lock_info);
m_internal_handler= NULL;
init_sql_alloc(&locked_tables_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
}
@ -995,7 +993,7 @@ void THD::cleanup(void)
ha_rollback(this);
xid_cache_delete(&transaction.xid_state);
}
unlock_locked_tables(this);
locked_tables_list.unlock_locked_tables(this);
#if defined(ENABLED_DEBUG_SYNC)
/* End the Debug Sync Facility. See debug_sync.cc. */
@ -1074,7 +1072,6 @@ THD::~THD()
#endif
free_root(&main_mem_root, MYF(0));
free_root(&locked_tables_root, MYF(0));
DBUG_VOID_RETURN;
}

View file

@ -1169,6 +1169,58 @@ private:
Internal_error_handler *m_err_handler;
};
/**
Tables that were locked with LOCK TABLES statement.
Encapsulates a list of TABLE_LIST instances for tables
locked by LOCK TABLES statement, memory root for metadata locks,
and, generally, the context of LOCK TABLES statement.
In LOCK TABLES mode, the locked tables are kept open between
statements.
Therefore, we can't allocate metadata locks on execution memory
root -- as well as tables, the locks need to stay around till
UNLOCK TABLES is called.
The locks are allocated in the memory root encapsulate in this
class.
Some SQL commands, like FLUSH TABLE or ALTER TABLE, demand that
the tables they operate on are closed, at least temporarily.
This class encapsulates a list of TABLE_LIST instances, one
for each base table from LOCK TABLES list,
which helps conveniently close the TABLEs when it's necessary
and later reopen them.
Implemented in sql_base.cc
*/
class Locked_tables_list
{
private:
MEM_ROOT m_locked_tables_root;
TABLE_LIST *m_locked_tables;
TABLE_LIST **m_locked_tables_last;
public:
Locked_tables_list()
:m_locked_tables(NULL),
m_locked_tables_last(&m_locked_tables)
{
init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0);
}
void unlock_locked_tables(THD *thd);
~Locked_tables_list()
{
unlock_locked_tables(0);
}
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();
bool reopen_tables(THD *thd);
};
/**
Storage engine specific thread local data.
@ -1810,6 +1862,8 @@ public:
*/
Parser_state *m_parser_state;
Locked_tables_list locked_tables_list;
#ifdef WITH_PARTITION_STORAGE_ENGINE
partition_info *work_part_info;
#endif
@ -1819,8 +1873,13 @@ public:
struct st_debug_sync_control *debug_sync_control;
#endif /* defined(ENABLED_DEBUG_SYNC) */
MEM_ROOT *mdl_el_root;
MEM_ROOT locked_tables_root;
/**
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();

View file

@ -1956,8 +1956,8 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
/*
Step7: drop the old database.
remove_db_from_cache(olddb) and query_cache_invalidate(olddb)
are done inside mysql_rm_db(), no needs to execute them again.
query_cache_invalidate(olddb) is done inside mysql_rm_db(), no need
to execute them again.
mysql_rm_db() also "unuses" if we drop the current database.
*/
error= mysql_rm_db(thd, old_db->str, 0, 1);

View file

@ -3542,16 +3542,22 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
pthread_mutex_lock(&LOCK_open);
if (reopen_name_locked_table(thd, create_table))
enum enum_open_table_action ot_action_unused;
/*
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))
{
pthread_mutex_lock(&LOCK_open);
quick_rm_table(create_info->db_type, create_table->db,
table_case_name(create_info, create_table->table_name),
0);
pthread_mutex_unlock(&LOCK_open);
}
else
table= create_table->table;
pthread_mutex_unlock(&LOCK_open);
}
else
{

View file

@ -184,7 +184,7 @@ bool begin_trans(THD *thd)
return 1;
}
unlock_locked_tables(thd);
thd->locked_tables_list.unlock_locked_tables(thd);
if (end_active_trans(thd))
error= -1;
@ -3583,7 +3583,7 @@ end_with_restore_list:
done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes
false, mysqldump will not work.
*/
unlock_locked_tables(thd);
thd->locked_tables_list.unlock_locked_tables(thd);
if (thd->options & OPTION_TABLE_LOCK)
{
end_active_trans(thd);
@ -3594,7 +3594,7 @@ end_with_restore_list:
my_ok(thd);
break;
case SQLCOM_LOCK_TABLES:
unlock_locked_tables(thd);
thd->locked_tables_list.unlock_locked_tables(thd);
/* we must end the trasaction first, regardless of anything */
if (end_active_trans(thd))
goto error;
@ -3604,24 +3604,23 @@ end_with_restore_list:
if (lex->protect_against_global_read_lock &&
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
goto error;
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
alloc_mdl_locks(all_tables, &thd->locked_tables_root);
thd->mdl_el_root= &thd->locked_tables_root;
if (!(res= open_and_lock_tables_derived(thd, all_tables, FALSE,
MYSQL_OPEN_TAKE_UPGRADABLE_MDL)))
alloc_mdl_locks(all_tables, thd->locked_tables_list.locked_tables_root());
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)
{
#ifdef HAVE_QUERY_CACHE
if (thd->variables.query_cache_wlock_invalidate)
query_cache.invalidate_locked_for_write(first_table);
#endif /*HAVE_QUERY_CACHE*/
thd->locked_tables_mode= LTM_LOCK_TABLES;
my_ok(thd);
}
else
{
/*
/*
Need to end the current transaction, so the storage engine (InnoDB)
can free its locks if LOCK TABLES locked some tables before finding
that it can't lock a table in its list
@ -3630,8 +3629,14 @@ end_with_restore_list:
end_active_trans(thd);
thd->options&= ~(OPTION_TABLE_LOCK);
}
thd->in_lock_tables=0;
thd->mdl_el_root= 0;
else
{
#ifdef HAVE_QUERY_CACHE
if (thd->variables.query_cache_wlock_invalidate)
query_cache.invalidate_locked_for_write(first_table);
#endif /*HAVE_QUERY_CACHE*/
my_ok(thd);
}
break;
case SQLCOM_CREATE_DB:
{
@ -6534,8 +6539,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
/* Link table in global list (all used tables) */
lex->add_to_query_tables(ptr);
ptr->mdl_lock_data= mdl_alloc_lock(0 , ptr->db, ptr->table_name,
thd->mdl_el_root ? thd->mdl_el_root :
thd->mem_root);
thd->locked_tables_root ?
thd->locked_tables_root : thd->mem_root);
DBUG_RETURN(ptr);
}

View file

@ -4314,7 +4314,6 @@ set_engine_all_partitions(partition_info *part_info,
static int fast_end_partition(THD *thd, ulonglong copied,
ulonglong deleted,
TABLE *table,
TABLE_LIST *table_list, bool is_empty,
ALTER_PARTITION_PARAM_TYPE *lpt,
bool written_bin_log)
@ -4333,11 +4332,7 @@ static int fast_end_partition(THD *thd, ulonglong copied,
error= 1;
if (error)
{
/* If error during commit, no need to rollback, it's done. */
table->file->print_error(error, MYF(0));
DBUG_RETURN(TRUE);
}
DBUG_RETURN(TRUE); /* The error has been reported */
if ((!is_empty) && (!written_bin_log) &&
(!thd->lex->no_write_to_binlog))
@ -6215,30 +6210,13 @@ static void release_log_entries(partition_info *part_info)
*/
static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
{
int err;
if (lpt->thd->locked_tables_mode)
{
/*
When we have the table locked, it is necessary to reopen the table
since all table objects were closed and removed as part of the
ALTER TABLE of partitioning structure.
*/
pthread_mutex_lock(&LOCK_open);
lpt->thd->in_lock_tables= 1;
err= reopen_tables(lpt->thd, 1);
lpt->thd->in_lock_tables= 0;
if (err)
{
/*
Issue a warning since we weren't able to regain the lock again.
We also need to unlink table from thread's open list and from
table_cache
*/
unlink_open_table(lpt->thd, lpt->table, FALSE);
sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
}
pthread_mutex_unlock(&LOCK_open);
}
THD *thd= lpt->thd;
close_all_tables_for_name(thd, lpt->table->s, FALSE);
lpt->table= 0;
lpt->table_list->table= 0;
if (thd->locked_tables_list.reopen_tables(thd))
sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
}
/*
@ -6252,17 +6230,37 @@ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
{
TABLE_SHARE *share= lpt->table->s;
THD *thd= lpt->thd;
const char *db= lpt->db;
const char *table_name= lpt->table_name;
TABLE *table;
DBUG_ENTER("alter_close_tables");
/*
We need to also unlock tables and close all handlers.
We set lock to zero to ensure we don't do this twice
and we set db_stat to zero to ensure we don't close twice.
We must keep LOCK_open while manipulating with thd->open_tables.
Another thread may be working on it.
*/
pthread_mutex_lock(&LOCK_open);
close_data_files_and_leave_as_placeholders(thd, db, table_name);
/*
We can safely remove locks for all tables with the same name:
later they will all be closed anyway in
alter_partition_lock_handling().
*/
for (table= thd->open_tables; table ; table= table->next)
{
if (!strcmp(table->s->table_name.str, share->table_name.str) &&
!strcmp(table->s->db.str, share->db.str))
{
mysql_lock_remove(thd, thd->lock, table);
table->file->close();
table->db_stat= 0; // Mark file closed
/*
Ensure that we won't end up with a crippled table instance
in the table cache if an error occurs before we reach
alter_partition_lock_handling() and the table is closed
by close_thread_tables() instead.
*/
table->s->version= 0;
}
}
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(0);
}
@ -6429,6 +6427,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
DBUG_ENTER("fast_alter_partition_table");
lpt->thd= thd;
lpt->table_list= table_list;
lpt->part_info= part_info;
lpt->alter_info= alter_info;
lpt->create_info= create_info;
@ -6745,7 +6744,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
user
*/
DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted,
table, table_list, FALSE, NULL,
table_list, FALSE, NULL,
written_bin_log));
err:
close_thread_tables(thd);

View file

@ -224,7 +224,8 @@ bool servers_reload(THD *thd)
bool return_val= TRUE;
DBUG_ENTER("servers_reload");
unlock_locked_tables(thd); // Can't have locked tables here
/* Can't have locked tables here */
thd->locked_tables_list.unlock_locked_tables(thd);
DBUG_PRINT("info", ("locking servers_cache"));
rw_wrlock(&THR_LOCK_servers);

View file

@ -1966,7 +1966,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
case -1:
DBUG_ASSERT(thd->in_sub_stmt);
error= 1;
goto err_with_placeholders;
goto err;
default:
// temporary table not found
error= 0;
@ -2003,18 +2003,19 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
{
if (thd->locked_tables_mode)
{
if (close_cached_table(thd, table->table))
if (wait_while_table_is_used(thd, table->table, HA_EXTRA_FORCE_REOPEN))
{
error= -1;
goto err_with_placeholders;
goto err;
}
close_all_tables_for_name(thd, table->table->s, TRUE);
table->table= 0;
}
if (thd->killed)
{
error= -1;
goto err_with_placeholders;
goto err;
}
alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
/* remove .frm file and engine files */
@ -2178,7 +2179,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
*/
}
}
err_with_placeholders:
err:
if (!drop_temporary)
{
/*
@ -2195,7 +2196,7 @@ err_with_placeholders:
if (thd->locked_tables_mode &&
thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0)
{
unlock_locked_tables(thd);
thd->locked_tables_list.unlock_locked_tables(thd);
goto end;
}
for (table= tables; table; table= table->next_local)
@ -4315,6 +4316,7 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
HA_CHECK_OPT *check_opt)
{
MDL_LOCK_DATA *mdl_lock_data= 0;
enum enum_open_table_action ot_action_unused;
DBUG_ENTER("prepare_for_restore");
if (table->table) // do not overwrite existing tables on restore
@ -4360,16 +4362,16 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
"Failed generating table from .frm file"));
}
table->mdl_lock_data= mdl_lock_data;
}
/*
Now we should be able to open the partially restored table
to finish the restore in the handler later on
*/
pthread_mutex_lock(&LOCK_open);
if (reopen_name_locked_table(thd, table))
if (open_table(thd, table, thd->mem_root,
&ot_action_unused, MYSQL_OPEN_REOPEN))
{
pthread_mutex_unlock(&LOCK_open);
if (mdl_lock_data)
mdl_release_lock(&thd->mdl_context, mdl_lock_data);
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
@ -4377,7 +4379,6 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
}
/* A MERGE table must not come here. */
DBUG_ASSERT(!table->table || !table->table->child_l);
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(0);
}
@ -4392,7 +4393,10 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
const char **ext;
MY_STAT stat_info;
MDL_LOCK_DATA *mdl_lock_data;
enum enum_open_table_action ot_action_unused;
DBUG_ENTER("prepare_for_repair");
uint reopen_for_repair_flags= (MYSQL_LOCK_IGNORE_FLUSH |
MYSQL_OPEN_HAS_MDL_LOCK);
if (!(check_opt->sql_flags & TT_USEFRM))
DBUG_RETURN(0);
@ -4428,8 +4432,9 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(0); // Out of memory
}
table= &tmp_table;
pthread_mutex_unlock(&LOCK_open);
table= &tmp_table;
table_list->mdl_lock_data= mdl_lock_data;
}
else
{
@ -4490,8 +4495,9 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
Table was successfully open in mysql_admin_table(). Now we need
to close it, but leave it protected by exclusive metadata lock.
*/
if (close_cached_table(thd, table))
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto end;
close_all_tables_for_name(thd, table_list->table->s, FALSE);
table_list->table= 0;
}
/*
@ -4519,21 +4525,23 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
goto end;
}
if (thd->locked_tables_list.reopen_tables(thd))
goto end;
/*
Now we should be able to open the partially repaired table
to finish the repair in the handler later on.
*/
pthread_mutex_lock(&LOCK_open);
if (reopen_name_locked_table(thd, table_list))
if (open_table(thd, table_list, thd->mem_root,
&ot_action_unused, reopen_for_repair_flags))
{
pthread_mutex_unlock(&LOCK_open);
error= send_check_errmsg(thd, table_list, "repair",
"Failed to open partially repaired table");
goto end;
}
pthread_mutex_unlock(&LOCK_open);
end:
thd->locked_tables_list.unlink_all_closed_tables();
if (table == &tmp_table)
{
pthread_mutex_lock(&LOCK_open);
@ -5334,6 +5342,11 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
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_lock_data= target_lock_data;
}
DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););
@ -5463,20 +5476,16 @@ 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;
/*
Here we open the destination table, on which we already have
exclusive metada lock. This is needed for store_create_info()
to work. The table will be closed by unlink_open_table() at
the end of this function.
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.
*/
pthread_mutex_lock(&LOCK_open);
if (reopen_name_locked_table(thd, table))
{
pthread_mutex_unlock(&LOCK_open);
if (open_table(thd, table, thd->mem_root, &ot_action_unused,
MYSQL_OPEN_REOPEN))
goto err;
}
pthread_mutex_unlock(&LOCK_open);
int result __attribute__((unused))=
store_create_info(thd, table, &query,
@ -5485,8 +5494,14 @@ binlog:
DBUG_ASSERT(result == 0); // store_create_info() always return 0
write_bin_log(thd, TRUE, query.ptr(), query.length());
DBUG_ASSERT(thd->open_tables == table->table);
pthread_mutex_lock(&LOCK_open);
unlink_open_table(thd, table->table, FALSE);
/*
When opening the table, we ignored the locked tables
(MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table without
risking to close some locked table.
*/
close_thread_table(thd, &thd->open_tables);
pthread_mutex_unlock(&LOCK_open);
}
else // Case 1
@ -6789,13 +6804,14 @@ view_err:
/*
Then do a 'simple' rename of the table. First we need to close all
instances of 'source' table.
Note that if close_cached_table() returns error here (i.e. if
Note that if wait_while_table_is_used() returns error here (i.e. if
this thread was killed) then it must be that previous step of
simple rename did nothing and therefore we can safely reture
simple rename did nothing and therefore we can safely return
without additional clean-up.
*/
if (close_cached_table(thd, table))
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto err;
close_all_tables_for_name(thd, table->s, TRUE);
/*
Then, we want check once again that target table does not exist.
Actually the order of these two steps does not matter since
@ -7384,11 +7400,12 @@ view_err:
if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME))
goto err_new_table_cleanup;
pthread_mutex_lock(&LOCK_open);
close_data_files_and_leave_as_placeholders(thd, db, table_name);
close_all_tables_for_name(thd, table->s,
new_name != table_name || new_db != db);
error=0;
table_list->table= table= 0; /* Safety */
save_old_db_type= old_db_type;
/*
@ -7410,6 +7427,7 @@ view_err:
/* This type cannot happen in regular ALTER. */
new_db_type= old_db_type= NULL;
}
pthread_mutex_lock(&LOCK_open);
if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
FN_TO_IS_TMP))
{
@ -7433,10 +7451,15 @@ view_err:
FN_FROM_IS_TMP);
}
if (! error)
(void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP);
pthread_mutex_unlock(&LOCK_open);
if (error)
{
/* This shouldn't happen. But let us play it safe. */
goto err_with_placeholders;
goto err_with_mdl;
}
if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
@ -7446,6 +7469,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;
TABLE *t_table;
if (new_name != table_name || new_db != db)
{
@ -7454,51 +7478,39 @@ view_err:
table_list->table_name_length= strlen(new_name);
table_list->db= new_db;
table_list->db_length= strlen(new_db);
if (reopen_name_locked_table(thd, table_list))
goto err_with_placeholders;
t_table= table_list->table;
table_list->mdl_lock_data= target_lock_data;
}
else
{
if (reopen_table(table))
goto err_with_placeholders;
t_table= table;
/*
Under LOCK TABLES, we have a different mdl_lock_data
points to a different instance than the one set initially
to request the lock.
*/
table_list->mdl_lock_data= mdl_lock_data;
}
/* Tell the handler that a new frm file is in place. */
if (t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG,
create_info))
goto err_with_placeholders;
if (thd->locked_tables_mode)
if (open_table(thd, table_list, thd->mem_root,
&ot_action_unused, MYSQL_OPEN_REOPEN))
{
if (new_name == table_name && new_db == db)
{
/*
We are going to reopen table down on the road, so we have to restore
state of the TABLE object which we used for obtaining of handler
object to make it suitable for reopening.
*/
DBUG_ASSERT(t_table == table);
close_handle_and_leave_table_as_placeholder(table);
}
else
{
/* Unlink the new name from the list of locked tables. */
unlink_open_table(thd, t_table, FALSE);
}
goto err_with_mdl;
}
}
t_table= table_list->table;
(void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP);
/* Tell the handler that a new frm file is in place. */
error= t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG,
create_info);
DBUG_ASSERT(thd->open_tables == t_table);
pthread_mutex_lock(&LOCK_open);
close_thread_table(thd, &thd->open_tables);
pthread_mutex_unlock(&LOCK_open);
table_list->table= 0;
if (thd->locked_tables_mode && new_name == table_name && new_db == db)
{
thd->in_lock_tables= 1;
error= reopen_tables(thd, 1);
thd->in_lock_tables= 0;
if (error)
goto err_with_placeholders;
goto err_with_mdl;
}
pthread_mutex_unlock(&LOCK_open);
if (thd->locked_tables_list.reopen_tables(thd))
goto err_with_mdl;
thd_proc_info(thd, "end");
@ -7541,9 +7553,6 @@ view_err:
{
if ((new_name != table_name || new_db != db))
{
pthread_mutex_lock(&LOCK_open);
unlink_open_table(thd, table, FALSE);
pthread_mutex_unlock(&LOCK_open);
mdl_release_lock(&thd->mdl_context, target_lock_data);
mdl_release_all_locks_for_name(&thd->mdl_context, mdl_lock_data);
}
@ -7607,14 +7616,14 @@ err:
mdl_release_lock(&thd->mdl_context, target_lock_data);
DBUG_RETURN(TRUE);
err_with_placeholders:
err_with_mdl:
/*
An error happened while we were holding exclusive name metadata lock
on table being altered. To be safe under LOCK TABLES we should remove
placeholders from the list of open tables and relese metadata lock.
on table being altered. To be safe under LOCK TABLES we should
remove all references to the altered table from the list of locked
tables and release the exclusive metadata lock.
*/
unlink_open_table(thd, table, FALSE);
pthread_mutex_unlock(&LOCK_open);
thd->locked_tables_list.unlink_all_closed_tables();
if (target_lock_data)
mdl_release_lock(&thd->mdl_context, target_lock_data);
mdl_release_all_locks_for_name(&thd->mdl_context, mdl_lock_data);

View file

@ -328,6 +328,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
bool result= TRUE;
String stmt_query;
bool need_start_waiting= FALSE;
bool lock_upgrade_done= FALSE;
DBUG_ENTER("mysql_create_or_drop_trigger");
@ -450,72 +451,54 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
if (!(tables->table= find_write_locked_table(thd->open_tables, tables->db,
tables->table_name)))
goto end;
/*
Ensure that table is opened only by this thread and that no other
statement will open this table.
*/
if (wait_while_table_is_used(thd, tables->table, HA_EXTRA_FORCE_REOPEN))
goto end;
pthread_mutex_lock(&LOCK_open);
/* Later on we will need it to downgrade the lock */
tables->mdl_lock_data= tables->table->mdl_lock_data;
}
else
{
/*
Obtain exlusive meta-data lock on the table and remove TABLE
instances from cache.
*/
if (lock_table_names(thd, tables))
tables->table= open_n_lock_single_table(thd, tables,
TL_WRITE_ALLOW_READ,
MYSQL_OPEN_TAKE_UPGRADABLE_MDL);
if (! tables->table)
goto end;
pthread_mutex_lock(&LOCK_open);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, tables->db, tables->table_name);
if (reopen_name_locked_table(thd, tables))
goto end_unlock;
tables->table->use_all_columns();
}
table= tables->table;
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto end;
lock_upgrade_done= TRUE;
if (!table->triggers)
{
if (!create)
{
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
goto end_unlock;
goto end;
}
if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table)))
goto end_unlock;
goto end;
}
pthread_mutex_lock(&LOCK_open);
result= (create ?
table->triggers->create_trigger(thd, tables, &stmt_query):
table->triggers->drop_trigger(thd, tables, &stmt_query));
/* Under LOCK TABLES we must reopen the table to activate the trigger. */
if (!result && thd->locked_tables_mode)
{
/* Make table suitable for reopening */
close_data_files_and_leave_as_placeholders(thd, tables->db,
tables->table_name);
thd->in_lock_tables= 1;
if (reopen_tables(thd, 1))
{
/* To be safe remove this table from the set of LOCKED TABLES */
unlink_open_table(thd, tables->table, FALSE);
/*
Ignore reopen_tables errors for now. It's better not leave master/slave
in a inconsistent state.
*/
thd->clear_error();
}
thd->in_lock_tables= 0;
}
end_unlock:
pthread_mutex_unlock(&LOCK_open);
if (result)
goto end;
close_all_tables_for_name(thd, table->s, FALSE);
/*
Reopen the table if we were under LOCK TABLES.
Ignore the return value for now. It's better to
keep master/slave in consistent state.
*/
thd->locked_tables_list.reopen_tables(thd);
end:
if (!result)
{
@ -525,11 +508,11 @@ end:
/*
If we are under LOCK TABLES we should restore original state of meta-data
locks. Otherwise call to close_thread_tables() will take care about both
TABLE instance created by reopen_name_locked_table() and metadata lock.
TABLE instance created by open_n_lock_single_table() and metadata lock.
*/
if (thd->locked_tables_mode && tables && tables->table)
if (thd->locked_tables_mode && tables && lock_upgrade_done)
mdl_downgrade_exclusive_lock(&thd->mdl_context,
tables->table->mdl_lock_data);
tables->mdl_lock_data);
if (need_start_waiting)
start_waiting_global_read_lock(thd);

View file

@ -632,7 +632,6 @@ private:
TABLE *share_next, **share_prev;
friend struct TABLE_share;
friend bool reopen_table(TABLE *table);
public:
@ -679,6 +678,8 @@ public:
/* Table's triggers, 0 if there are no of them */
Table_triggers_list *triggers;
TABLE_LIST *pos_in_table_list;/* Element referring to this table */
/* Position in thd->locked_table_list under LOCK TABLES */
TABLE_LIST *pos_in_locked_tables;
ORDER *group;
const char *alias; /* alias or table name */
uchar *null_flags;