summaryrefslogtreecommitdiff
path: root/sql/sql_table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r--sql/sql_table.cc298
1 files changed, 174 insertions, 124 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 1c6ca4a48d9..772496a10d5 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -23,9 +23,7 @@
#include "sql_parse.h" // test_if_data_home_dir
#include "sql_cache.h" // query_cache_*
#include "sql_base.h" // open_table_uncached, lock_table_names
-#include "lock.h" // wait_if_global_read_lock
- // start_waiting_global_read_lock,
- // mysql_unlock_tables
+#include "lock.h" // mysql_unlock_tables
#include "strfunc.h" // find_type2, find_set
#include "sql_view.h" // view_checksum
#include "sql_truncate.h" // regenerate_locked_table
@@ -1851,64 +1849,117 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
{
bool error;
Drop_table_error_handler err_handler;
+ TABLE_LIST *table;
DBUG_ENTER("mysql_rm_table");
- /* mark for close and remove all cached entries */
+ /* Disable drop of enabled log tables, must be done before name locking */
+ for (table= tables; table; table= table->next_local)
+ {
+ if (check_if_log_table(table->db_length, table->db,
+ table->table_name_length, table->table_name, true))
+ {
+ my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
+ DBUG_RETURN(true);
+ }
+ }
+
+ mysql_ha_rm_tables(thd, tables);
if (!drop_temporary)
{
- if (!thd->locked_tables_mode &&
- thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
- DBUG_RETURN(TRUE);
+ if (!thd->locked_tables_mode)
+ {
+ if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
+ MYSQL_OPEN_SKIP_TEMPORARY))
+ DBUG_RETURN(true);
+ for (table= tables; table; table= table->next_local)
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name,
+ false);
+ }
+ else
+ {
+ for (table= tables; table; table= table->next_local)
+ if (table->open_type != OT_BASE_ONLY &&
+ find_temporary_table(thd, table))
+ {
+ /*
+ A temporary table.
+
+ Don't try to find a corresponding MDL lock or assign it
+ to table->mdl_request.ticket. There can't be metadata
+ locks for temporary tables: they are local to the session.
+
+ Later in this function we release the MDL lock only if
+ table->mdl_requeset.ticket is not NULL. Thus here we
+ ensure that we won't release the metadata lock on the base
+ table locked with LOCK TABLES as a side effect of temporary
+ table drop.
+ */
+ DBUG_ASSERT(table->mdl_request.ticket == NULL);
+ }
+ else
+ {
+ /*
+ Not a temporary table.
+
+ Since 'tables' list can't contain duplicates (this is ensured
+ by parser) it is safe to cache pointer to the TABLE instances
+ in its elements.
+ */
+ table->table= find_table_for_mdl_upgrade(thd->open_tables, table->db,
+ table->table_name, false);
+ if (!table->table)
+ DBUG_RETURN(true);
+ table->mdl_request.ticket= table->table->mdl_ticket;
+ }
+ }
}
+ /* mark for close and remove all cached entries */
thd->push_internal_handler(&err_handler);
- error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
+ error= mysql_rm_table_no_locks(thd, tables, if_exists, drop_temporary,
+ false, false);
thd->pop_internal_handler();
- if (thd->global_read_lock.has_protection())
- thd->global_read_lock.start_waiting_global_read_lock(thd);
-
if (error)
DBUG_RETURN(TRUE);
my_ok(thd);
DBUG_RETURN(FALSE);
}
-/*
- Execute the drop of a normal or temporary table
-
- SYNOPSIS
- mysql_rm_table_part2()
- thd Thread handler
- tables Tables to drop
- if_exists If set, don't give an error if table doesn't exists.
- In this case we give an warning of level 'NOTE'
- drop_temporary Only drop temporary tables
- drop_view Allow to delete VIEW .frm
- dont_log_query Don't write query to log files. This will also not
- generate warnings if the handler files doesn't exists
-
- TODO:
- When logging to the binary log, we should log
- tmp_tables and transactional tables as separate statements if we
- are in a transaction; This is needed to get these tables into the
- cached binary log that is only written on COMMIT.
-
- The current code only writes DROP statements that only uses temporary
- tables to the cache binary log. This should be ok on most cases, but
- not all.
- RETURN
- 0 ok
- 1 Error
- -1 Thread was killed
+/**
+ Execute the drop of a normal or temporary table.
+
+ @param thd Thread handler
+ @param tables Tables to drop
+ @param if_exists If set, don't give an error if table doesn't exists.
+ In this case we give an warning of level 'NOTE'
+ @param drop_temporary Only drop temporary tables
+ @param drop_view Allow to delete VIEW .frm
+ @param dont_log_query Don't write query to log files. This will also not
+ generate warnings if the handler files doesn't exists
+
+ @retval 0 ok
+ @retval 1 Error
+ @retval -1 Thread was killed
+
+ @note This function assumes that metadata locks have already been taken.
+ It is also assumed that the tables have been removed from TDC.
+
+ @todo When logging to the binary log, we should log
+ tmp_tables and transactional tables as separate statements if we
+ are in a transaction; This is needed to get these tables into the
+ cached binary log that is only written on COMMIT.
+ The current code only writes DROP statements that only uses temporary
+ tables to the cache binary log. This should be ok on most cases, but
+ not all.
*/
-int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
- bool drop_temporary, bool drop_view,
- bool dont_log_query)
+int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
+ bool drop_temporary, bool drop_view,
+ bool dont_log_query)
{
TABLE_LIST *table;
char path[FN_REFLEN + 1], *alias= NULL;
@@ -1922,7 +1973,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
bool non_tmp_table_deleted= 0;
String built_query;
String built_trans_tmp_query, built_non_trans_tmp_query;
- DBUG_ENTER("mysql_rm_table_part2");
+ DBUG_ENTER("mysql_rm_table_no_locks");
/*
Prepares the drop statements that will be written into the binary
@@ -1976,71 +2027,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
}
}
- mysql_ha_rm_tables(thd, tables);
-
- /* Disable drop of enabled log tables, must be done before name locking */
- for (table= tables; table; table= table->next_local)
- {
- if (check_if_log_table(table->db_length, table->db,
- table->table_name_length, table->table_name, 1))
- {
- my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
- DBUG_RETURN(1);
- }
- }
-
- if (!drop_temporary)
- {
- if (!thd->locked_tables_mode)
- {
- if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
- MYSQL_OPEN_SKIP_TEMPORARY))
- DBUG_RETURN(1);
- for (table= tables; table; table= table->next_local)
- {
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name,
- FALSE);
- }
- }
- else
- {
- for (table= tables; table; table= table->next_local)
- if (table->open_type != OT_BASE_ONLY &&
- find_temporary_table(thd, table))
- {
- /*
- A temporary table.
-
- Don't try to find a corresponding MDL lock or assign it
- to table->mdl_request.ticket. There can't be metadata
- locks for temporary tables: they are local to the session.
-
- Later in this function we release the MDL lock only if
- table->mdl_requeset.ticket is not NULL. Thus here we
- ensure that we won't release the metadata lock on the base
- table locked with LOCK TABLES as a side effect of temporary
- table drop.
- */
- DBUG_ASSERT(table->mdl_request.ticket == NULL);
- }
- else
- {
- /*
- Not a temporary table.
-
- Since 'tables' list can't contain duplicates (this is ensured
- by parser) it is safe to cache pointer to the TABLE instances
- in its elements.
- */
- table->table= find_table_for_mdl_upgrade(thd->open_tables, table->db,
- table->table_name, FALSE);
- if (!table->table)
- DBUG_RETURN(1);
- table->mdl_request.ticket= table->table->mdl_ticket;
- }
- }
- }
-
for (table= tables; table; table= table->next_local)
{
bool is_trans;
@@ -2053,6 +2039,16 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
table->table ? (long) table->table->s : (long) -1));
/*
+ If we are in locked tables mode and are dropping a temporary table,
+ the ticket should be NULL to ensure that we don't release a lock
+ on a base table later.
+ */
+ DBUG_ASSERT(!(thd->locked_tables_mode &&
+ table->open_type != OT_BASE_ONLY &&
+ find_temporary_table(thd, table) &&
+ table->mdl_request.ticket != NULL));
+
+ /*
drop_temporary_table may return one of the following error codes:
. 0 - a temporary table was successfully dropped.
. 1 - a temporary table was not found.
@@ -2127,6 +2123,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
table->table= 0;
}
+ /* Check that we have an exclusive lock on the table to be dropped. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db,
+ table->table_name,
+ MDL_EXCLUSIVE));
if (thd->killed)
{
error= -1;
@@ -2169,8 +2169,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
built_query.append("`,");
}
}
- DEBUG_SYNC(thd, "rm_table_part2_before_delete_table");
- DBUG_EXECUTE_IF("sleep_before_part2_delete_table",
+ DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table");
+ DBUG_EXECUTE_IF("sleep_before_no_locks_delete_table",
my_sleep(100000););
error= 0;
if (drop_temporary ||
@@ -2257,7 +2257,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
ER(ER_BAD_TABLE_ERROR), MYF(0),
table->table_name););
}
- DEBUG_SYNC(thd, "rm_table_part2_before_binlog");
+ DEBUG_SYNC(thd, "rm_table_no_locks_before_binlog");
thd->thread_specific_used|= (trans_tmp_table_deleted ||
non_trans_tmp_table_deleted);
error= 0;
@@ -3815,6 +3815,46 @@ void sp_prepare_create_field(THD *thd, Create_field *sql_field)
(void) prepare_blob_field(thd, sql_field);
}
+
+/**
+ Auxiliary function which allows to check if freshly created .FRM
+ file for table can be opened.
+
+ @retval FALSE - Success.
+ @retval TRUE - Failure.
+*/
+
+static bool check_if_created_table_can_be_opened(THD *thd,
+ const char *path,
+ const char *db,
+ const char *table_name,
+ HA_CREATE_INFO *create_info,
+ handler *file)
+{
+ TABLE table;
+ TABLE_SHARE share;
+ bool result;
+
+ /*
+ It is impossible to open definition of partitioned table without .par file.
+ */
+ if (file->ha_create_handler_files(path, NULL, CHF_CREATE_FLAG, create_info))
+ return TRUE;
+
+ init_tmp_table_share(thd, &share, db, 0, table_name, path);
+
+ result= (open_table_def(thd, &share, 0) ||
+ open_table_from_share(thd, &share, "", 0, (uint) READ_ALL,
+ 0, &table, TRUE));
+ if (! result)
+ (void) closefrm(&table, 0);
+
+ free_table_share(&share);
+ (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info);
+ return result;
+}
+
+
/*
Create a table
@@ -4241,6 +4281,29 @@ bool mysql_create_table_no_lock(THD *thd,
thd->thread_specific_used= TRUE;
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ else if (part_info && create_info->frm_only)
+ {
+ /*
+ For partitioned tables we can't find some problems with table
+ until table is opened. Therefore in order to disallow creation
+ of corrupted tables we have to try to open table as the part
+ of its creation process.
+ In cases when both .FRM and SE part of table are created table
+ is implicitly open in ha_create_table() call.
+ In cases when we create .FRM without SE part we have to open
+ table explicitly.
+ */
+ if (check_if_created_table_can_be_opened(thd, path, db, table_name,
+ create_info, file))
+ {
+ char frm_name[FN_REFLEN];
+ strxmov(frm_name, path, reg_ext, NullS);
+ (void) mysql_file_delete(key_file_frm, frm_name, MYF(0));
+ goto err;
+ }
+ }
+#endif
error= FALSE;
err:
@@ -5592,7 +5655,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
TABLE *table, *new_table= 0;
MDL_ticket *mdl_ticket;
MDL_request target_mdl_request;
- bool has_target_mdl_lock= FALSE;
int error= 0;
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
@@ -5754,7 +5816,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
else
{
target_mdl_request.init(MDL_key::TABLE, new_db, new_name,
- MDL_EXCLUSIVE);
+ MDL_EXCLUSIVE, MDL_TRANSACTION);
/*
Global intention exclusive lock must have been already acquired when
table to be altered was open, so there is no need to do it here.
@@ -5772,7 +5834,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
DBUG_RETURN(TRUE);
}
DEBUG_SYNC(thd, "locked_table_name");
- has_target_mdl_lock= TRUE;
/*
Table maybe does not exist, but we got an exclusive lock
on the name, now we can safely try to find out for sure.
@@ -5959,10 +6020,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
along with the implicit commit.
*/
if (new_name != table_name || new_db != db)
- {
- thd->mdl_context.release_lock(target_mdl_request.ticket);
thd->mdl_context.release_all_locks_for_name(mdl_ticket);
- }
else
mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
}
@@ -6667,10 +6725,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)
{
if ((new_name != table_name || new_db != db))
- {
- thd->mdl_context.release_lock(target_mdl_request.ticket);
thd->mdl_context.release_all_locks_for_name(mdl_ticket);
- }
else
mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
}
@@ -6731,8 +6786,6 @@ err:
alter_info->datetime_field->field_name);
thd->abort_on_warning= save_abort_on_warning;
}
- if (has_target_mdl_lock)
- thd->mdl_context.release_lock(target_mdl_request.ticket);
DBUG_RETURN(TRUE);
@@ -6744,9 +6797,6 @@ err_with_mdl:
tables and release the exclusive metadata lock.
*/
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
- if (has_target_mdl_lock)
- thd->mdl_context.release_lock(target_mdl_request.ticket);
-
thd->mdl_context.release_all_locks_for_name(mdl_ticket);
DBUG_RETURN(TRUE);
}