1
0
mirror of https://github.com/MariaDB/server.git synced 2026-01-06 05:22:24 +03:00

A fix for Bug#41158 "DROP TABLE holds LOCK_open during unlink()".

Remove acquisition of LOCK_open around file system operations,
since such operations are now protected by metadata locks.
Rework table discovery algorithm to not require LOCK_open.

No new tests added since all MDL locking operations are covered
in lock.test and mdl_sync.test, and as long as these tests
pass despite the increased concurrency, consistency must be
unaffected.

mysql-test/t/disabled.def:
  Disable NDB tests due to Bug#55799.
sql/datadict.cc:
  No longer necessary to protect ha_create_table() with
  LOCK_open. Serial execution is now ensured by metadata
  locks.
sql/ha_ndbcluster.cc:
  Do not manipulate with LOCK_open in cluster code.
sql/ha_ndbcluster_binlog.cc:
  Do not manipulate with LOCK_open in cluster code.
sql/ha_ndbcluster_binlog.h:
  Update function signature.
sql/handler.cc:
  Implement ha_check_if_table_exists().
  @todo: some engines provide ha_table_exists_in_engine()
  handlerton call, for those we perhaps shouldn't
  call ha_discover(), to be more efficient.
  Since currently it's only NDB, postpone till
  integration with NDB.
sql/handler.h:
  Declare ha_check_if_table_exists() function.
sql/mdl.cc:
  Remove an obsolete comment.
sql/sql_base.cc:
  Update to a new signature of close_cached_tables():
  from now on we always call it without LOCK_open.
  Update comments.
  Remove get_table_share_with_create(), we should
  not attempt to create a table under LOCK_open.
  Introduce get_table_share_with_discover() instead,
  which would request a back off action if the table
  exists in engine.
  Remove acquisition of LOCK_open for 
  data dictionary operations, such as check_if_table_exists().
  Do not use get_table_share_with_create/discover for views,
  where it's not needed.
  Make tdc_remove_table() optionally acquire LOCK_open
  to simplify usage of this function.
  Use the right mutex in the partitioning code when
  manipulating with thd->open_tables.
sql/sql_base.h:
  Update signatures of changes functions.
sql/sql_insert.cc:
  Do not wrap quick_rm_table() with LOCK_open acquisition, 
  this is unnecessary.
sql/sql_parse.cc:
  Update to the new calling convention of tdc_remove_table().
  Update to the new signature of close_cached_tables().
  Update comments.
sql/sql_rename.cc:
  Update to the new calling convention of tdc_remove_table().
  Remove acquisition of LOCK_open around filesystem
  operations.
sql/sql_show.cc:
  Remove get_trigger_table_impl().
  Do not acquire LOCK_open for a dirty read of the trigger
  file.
sql/sql_table.cc:
  Do not acquire LOCK_open for filesystem operations.
sql/sql_trigger.cc:
  Do not require LOCK_open for trigger file I/O.
sql/sql_truncate.cc:
  Update to the new signature of tdc_remove_table().
sql/sql_view.cc:
  Do not require LOCK_open for view I/O.
  Use tdc_remove_table() to expel view share.
  Update comments.
sql/sys_vars.cc:
  Update to the new signature of close_cached_tables().
This commit is contained in:
Konstantin Osipov
2010-08-09 22:33:47 +04:00
parent b1207bf1b8
commit 523066987d
19 changed files with 219 additions and 320 deletions

View File

@@ -1723,7 +1723,6 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
completing this we write a new phase to the log entry that will
deactivate it.
*/
mysql_mutex_lock(&LOCK_open);
if (mysql_file_delete(key_file_frm, frm_name, MYF(MY_WME)) ||
#ifdef WITH_PARTITION_STORAGE_ENGINE
lpt->table->file->ha_create_handler_files(path, shadow_path,
@@ -1779,7 +1778,6 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
#endif
err:
mysql_mutex_unlock(&LOCK_open);
#ifdef WITH_PARTITION_STORAGE_ENGINE
deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
part_info->frm_log_entry= NULL;
@@ -1955,10 +1953,11 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
MYSQL_OPEN_SKIP_TEMPORARY))
DBUG_RETURN(1);
mysql_mutex_lock(&LOCK_open);
for (table= tables; table; table= table->next_local)
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
mysql_mutex_unlock(&LOCK_open);
{
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name,
FALSE);
}
}
else
{
@@ -2104,14 +2103,9 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
table->internal_tmp_table ?
FN_IS_TMP : 0);
}
/*
TODO: Investigate what should be done to remove this lock completely.
Is exclusive meta-data lock enough ?
*/
DEBUG_SYNC(thd, "rm_table_part2_before_delete_table");
DBUG_EXECUTE_IF("sleep_before_part2_delete_table",
my_sleep(100000););
mysql_mutex_lock(&LOCK_open);
if (drop_temporary ||
((access(path, F_OK) &&
ha_create_table_from_engine(thd, db, alias)) ||
@@ -2131,8 +2125,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
char *end;
/*
Cannot use the db_type from the table, since that might have changed
while waiting for the exclusive name lock. We are under LOCK_open,
so reading from the frm-file is safe.
while waiting for the exclusive name lock.
*/
if (frm_db_type == DB_TYPE_UNKNOWN)
{
@@ -2173,7 +2166,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
error|= new_error;
}
}
mysql_mutex_unlock(&LOCK_open);
if (error)
{
if (wrong_tables.length())
@@ -4053,7 +4045,6 @@ bool mysql_create_table_no_lock(THD *thd,
goto err;
}
mysql_mutex_lock(&LOCK_open);
if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
if (!access(path,F_OK))
@@ -4061,7 +4052,7 @@ bool mysql_create_table_no_lock(THD *thd,
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
goto warn;
my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
goto unlock_and_end;
goto err;
}
/*
We don't assert here, but check the result, because the table could be
@@ -4071,11 +4062,14 @@ bool mysql_create_table_no_lock(THD *thd,
Then she could create the table. This case is pretty obscure and
therefore we don't introduce a new error message only for it.
*/
mysql_mutex_lock(&LOCK_open);
if (get_cached_table_share(db, table_name))
{
mysql_mutex_unlock(&LOCK_open);
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
goto unlock_and_end;
goto err;
}
mysql_mutex_unlock(&LOCK_open);
}
/*
@@ -4083,7 +4077,7 @@ bool mysql_create_table_no_lock(THD *thd,
exist in any storage engine. In such a case it should
be discovered and the error ER_TABLE_EXISTS_ERROR be returned
unless user specified CREATE TABLE IF EXISTS
The LOCK_open mutex has been locked to make sure no
An exclusive metadata lock ensures that no
one else is attempting to discover the table. Since
it's not on disk as a frm file, no one could be using it!
*/
@@ -4104,12 +4098,12 @@ bool mysql_create_table_no_lock(THD *thd,
if (create_if_not_exists)
goto warn;
my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
goto unlock_and_end;
goto err;
break;
default:
DBUG_PRINT("info", ("error: %u from storage engine", retcode));
my_error(retcode, MYF(0),table_name);
goto unlock_and_end;
goto err;
}
}
@@ -4142,7 +4136,7 @@ bool mysql_create_table_no_lock(THD *thd,
if (test_if_data_home_dir(dirpath))
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "DATA DIRECTORY");
goto unlock_and_end;
goto err;
}
}
if (create_info->index_file_name)
@@ -4151,7 +4145,7 @@ bool mysql_create_table_no_lock(THD *thd,
if (test_if_data_home_dir(dirpath))
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "INDEX DIRECTORY");
goto unlock_and_end;
goto err;
}
}
}
@@ -4159,7 +4153,7 @@ bool mysql_create_table_no_lock(THD *thd,
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (check_partition_dirs(thd->lex->part_info))
{
goto unlock_and_end;
goto err;
}
#endif /* WITH_PARTITION_STORAGE_ENGINE */
@@ -4182,7 +4176,7 @@ bool mysql_create_table_no_lock(THD *thd,
if (rea_create_table(thd, path, db, table_name,
create_info, alter_info->create_list,
key_count, key_info_buffer, file))
goto unlock_and_end;
goto err;
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{
@@ -4190,15 +4184,12 @@ bool mysql_create_table_no_lock(THD *thd,
if (!(open_temporary_table(thd, path, db, table_name, 1)))
{
(void) rm_temporary_table(create_info->db_type, path);
goto unlock_and_end;
goto err;
}
thd->thread_specific_used= TRUE;
}
error= FALSE;
unlock_and_end:
mysql_mutex_unlock(&LOCK_open);
err:
thd_proc_info(thd, "After create");
delete file;
@@ -4210,7 +4201,7 @@ warn:
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
alias);
create_info->table_existed= 1; // Mark that table existed
goto unlock_and_end;
goto err;
}
@@ -4459,20 +4450,19 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
mysql_mutex_lock(&LOCK_open);
if (!(share= (get_table_share(thd, table_list, key, key_length, 0,
&error, hash_value))))
{
mysql_mutex_unlock(&LOCK_open);
share= get_table_share(thd, table_list, key, key_length, 0,
&error, hash_value);
mysql_mutex_unlock(&LOCK_open);
if (share == NULL)
DBUG_RETURN(0); // Can't open frm file
}
if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
{
mysql_mutex_lock(&LOCK_open);
release_table_share(share);
mysql_mutex_unlock(&LOCK_open);
DBUG_RETURN(0); // Out of memory
}
mysql_mutex_unlock(&LOCK_open);
table= &tmp_table;
}
@@ -5136,10 +5126,8 @@ send_result_message:
}
else if (open_for_modify || fatal_error)
{
mysql_mutex_lock(&LOCK_open);
tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
table->db, table->table_name);
mysql_mutex_unlock(&LOCK_open);
table->db, table->table_name, FALSE);
/*
May be something modified. Consequently, we have to
invalidate the query cache.
@@ -6775,7 +6763,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
else
{
*fn_ext(new_name)=0;
mysql_mutex_lock(&LOCK_open);
if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
error= -1;
else if (Table_triggers_list::change_table_name(thd, db, table_name,
@@ -6785,7 +6772,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
table_name, 0);
error= -1;
}
mysql_mutex_unlock(&LOCK_open);
}
}
@@ -7404,7 +7390,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
/* This type cannot happen in regular ALTER. */
new_db_type= old_db_type= NULL;
}
mysql_mutex_lock(&LOCK_open);
if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
FN_TO_IS_TMP))
{
@@ -7431,8 +7416,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (! error)
(void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP);
mysql_mutex_unlock(&LOCK_open);
if (error)
{
/* This shouldn't happen. But let us play it safe. */