* Konstantin Osipov <kostja@stripped> [09/07/17 02:00]:
> I wrote a comment about that. Please see the patch at the end of
> the reply. Note that my patch is a sketch, please feel free to
> improve on it.
Correction: the last version of the patch. For some reason I sent
you an older version at first.
=== modified file 'sql/sql_delete.cc'
--- sql/sql_delete.cc 2009-06-19 09:28:44 +0000
+++ sql/sql_delete.cc 2009-07-16 19:34:47 +0000
@@ -1084,6 +1084,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST
bool error;
uint path_length;
MDL_request *mdl_request= NULL;
+ /*
+ Is set if we're under LOCK TABLES, and used
+ to downgrade the exclusive lock after the
+ table was truncated.
+ */
+ MDL_ticket *mdl_ticket= NULL;
Ha_global_schema_lock_guard global_schema_lock_guard(thd);
DBUG_ENTER("mysql_truncate");
@@ -1129,6 +1135,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST
if (!dont_send_ok)
{
enum legacy_db_type table_type;
+ /*
+ FIXME: Code of TRUNCATE breaks the meta-data
+ locking protocol since it tries to find out the table storage
+ engine and therefore accesses table in some way without holding
+ any kind of meta-data lock.
+ */
mysql_frm_type(thd, path, &table_type);
if (table_type == DB_TYPE_UNKNOWN)
{
@@ -1143,24 +1155,47 @@ bool mysql_truncate(THD *thd, TABLE_LIST
if (table_type == DB_TYPE_NDBCLUSTER)
global_schema_lock_guard.lock();
- /*
- FIXME: Actually code of TRUNCATE breaks meta-data locking protocol since
- tries to get table enging and therefore accesses table in some way
- without holding any kind of meta-data lock.
- */
- mdl_request= MDL_request::create(0, table_list->db,
- table_list->table_name, thd->mem_root);
- mdl_request->set_type(MDL_EXCLUSIVE);
- thd->mdl_context.add_request(mdl_request);
- if (thd->mdl_context.acquire_exclusive_locks())
+
+ mysql_ha_rm_tables(thd, table_list);
+
+ if (thd->locked_tables_mode)
{
- thd->mdl_context.remove_request(mdl_request);
- DBUG_RETURN(TRUE);
+ if (!(table= find_write_locked_table(thd->open_tables, table_list->db,
+ table_list->table_name)))
+ DBUG_RETURN(TRUE);
+ mdl_ticket= table->mdl_ticket;
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
+ goto end;
+ close_all_tables_for_name(thd, table->s, FALSE);
+ }
+ else
+ {
+ /*
+ Even though we could use the previous execution branch
+ here just as well, we must not try to open the table:
+ MySQL manual documents that TRUNCATE can be used to
+ repair a damaged table, i.e. a table that can not be
+ fully "opened". In particular MySQL manual says:
+
+ As long as the table format file tbl_name.frm is valid,
+ the table can be re-created as an empty table with TRUNCATE
+ TABLE, even if the data or index files have become corrupted.
+ */
+
+ mdl_request= MDL_request::create(0, table_list->db,
+ table_list->table_name, thd->mem_root);
+ mdl_request->set_type(MDL_EXCLUSIVE);
+ thd->mdl_context.add_request(mdl_request);
+ if (thd->mdl_context.acquire_exclusive_locks())
+ {
+ thd->mdl_context.remove_request(mdl_request);
+ DBUG_RETURN(TRUE);
+ }
+ pthread_mutex_lock(&LOCK_open);
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db,
+ table_list->table_name);
+ pthread_mutex_unlock(&LOCK_open);
}
- pthread_mutex_lock(&LOCK_open);
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db,
- table_list->table_name);
- pthread_mutex_unlock(&LOCK_open);
}
/*
@@ -1178,6 +1213,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST
end:
if (!dont_send_ok)
{
+ if (thd->locked_tables_mode &&
thd->locked_tables_list.reopen_tables(thd))
+ thd->locked_tables_list.unlink_all_closed_tables();
+ /*
+ Even if we failed to reopen some tables,
+ the operation itself succeeded, write the binlog.
+ */
if (!error)
{
/*
@@ -1192,14 +1233,8 @@ end:
thd->mdl_context.release_lock(mdl_request->ticket);
thd->mdl_context.remove_request(mdl_request);
}
- }
- else if (error)
- {
- if (mdl_request)
- {
- thd->mdl_context.release_lock(mdl_request->ticket);
- thd->mdl_context.remove_request(mdl_request);
- }
+ if (mdl_ticket)
+ mdl_ticket->downgrade_exclusive_lock();
}
DBUG_RETURN(error);
=== modified file 'sql/sql_parse.cc'
--- sql/sql_parse.cc 2009-07-10 12:31:32 +0000
+++ sql/sql_parse.cc 2009-07-16 17:13:55 +0000
@@ -3316,7 +3316,7 @@ end_with_restore_list:
Don't allow this within a transaction because we want to use
re-generate table
*/
- if (thd->locked_tables_mode || thd->active_transaction())
+ if (thd->active_transaction())
{
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
--