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.cc549
1 files changed, 338 insertions, 211 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 1a7162313f5..7d59598d606 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1,4 +1,4 @@
-/* Copyright 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,10 +22,8 @@
#include "sql_rename.h" // do_rename
#include "sql_parse.h" // test_if_data_home_dir
#include "sql_cache.h" // query_cache_*
-#include "sql_base.h" // open_temporary_table, lock_table_names
-#include "lock.h" // wait_if_global_read_lock
- // start_waiting_global_read_lock,
- // mysql_unlock_tables
+#include "sql_base.h" // open_table_uncached, lock_table_names
+#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
@@ -368,7 +366,11 @@ uint explain_filename(THD* thd,
Table name length.
*/
-uint filename_to_tablename(const char *from, char *to, uint to_length)
+uint filename_to_tablename(const char *from, char *to, uint to_length
+#ifndef DBUG_OFF
+ , bool stay_quiet
+#endif /* DBUG_OFF */
+ )
{
uint errors;
size_t res;
@@ -388,7 +390,13 @@ uint filename_to_tablename(const char *from, char *to, uint to_length)
{
res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) -
to);
- sql_print_error("Invalid (old?) table or database name '%s'", from);
+#ifndef DBUG_OFF
+ if (!stay_quiet) {
+#endif /* DBUG_OFF */
+ sql_print_error("Invalid (old?) table or database name '%s'", from);
+#ifndef DBUG_OFF
+ }
+#endif /* DBUG_OFF */
/*
TODO: add a stored procedure for fix table and database names,
and mention its name in error log.
@@ -1720,8 +1728,6 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
CHF_DELETE_FLAG, NULL) ||
deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
(sync_ddl_log(), FALSE) ||
-#endif
-#ifdef WITH_PARTITION_STORAGE_ENGINE
mysql_file_rename(key_file_frm,
shadow_frm_name, frm_name, MYF(MY_WME)) ||
lpt->table->file->ha_create_handler_files(path, shadow_path,
@@ -1848,64 +1854,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, 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;
@@ -1919,7 +1978,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
@@ -1973,71 +2032,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->db, table->table_name))
- {
- /*
- 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;
@@ -2050,6 +2044,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.
@@ -2124,6 +2128,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;
@@ -2166,8 +2174,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 ||
@@ -2254,7 +2262,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;
@@ -2605,7 +2613,8 @@ int prepare_create_field(Create_field *sql_field,
MAX_FIELD_CHARLENGTH)
{
my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH),
- MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH);
+ MYF(0), sql_field->field_name,
+ static_cast<ulong>(MAX_FIELD_CHARLENGTH));
DBUG_RETURN(1);
}
}
@@ -3648,12 +3657,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
(MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
{
my_error(ER_TOO_LONG_INDEX_COMMENT, MYF(0),
- key_info->name, (uint) INDEX_COMMENT_MAXLEN);
+ key_info->name, static_cast<ulong>(INDEX_COMMENT_MAXLEN));
DBUG_RETURN(-1);
}
char warn_buff[MYSQL_ERRMSG_SIZE];
my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_INDEX_COMMENT),
- key_info->name, (uint) INDEX_COMMENT_MAXLEN);
+ key_info->name, static_cast<ulong>(INDEX_COMMENT_MAXLEN));
/* do not push duplicate warnings */
if (!check_duplicate_warning(thd, warn_buff, strlen(warn_buff)))
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
@@ -3787,7 +3796,8 @@ static bool prepare_blob_field(THD *thd, Create_field *sql_field)
MODE_STRICT_ALL_TABLES)))
{
my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
- MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
+ static_cast<ulong>(MAX_FIELD_VARCHARLENGTH /
+ sql_field->charset->mbmaxlen));
DBUG_RETURN(1);
}
sql_field->sql_type= MYSQL_TYPE_BLOB;
@@ -3865,6 +3875,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
@@ -4282,9 +4332,14 @@ bool mysql_create_table_no_lock(THD *thd,
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{
- TABLE *table= NULL;
- /* Open table and put in temporary table list */
- if (!(table= open_temporary_table(thd, path, db, table_name, 1)))
+ /*
+ Open a table (skipping table cache) and add it into
+ THD::temporary_tables list.
+ */
+
+ TABLE *table= open_table_uncached(thd, path, db, table_name, TRUE);
+
+ if (!table)
{
(void) rm_temporary_table(create_info->db_type, path);
goto err;
@@ -4295,6 +4350,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:
@@ -4561,6 +4639,11 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
local_create_info.options|= create_info->options & HA_LEX_CREATE_TMP_TABLE;
/* Reset auto-increment counter for the new table. */
local_create_info.auto_increment_value= 0;
+ /*
+ Do not inherit values of DATA and INDEX DIRECTORY options from
+ the original table. This is documented behavior.
+ */
+ local_create_info.data_file_name= local_create_info.index_file_name= NULL;
if ((res= mysql_create_table_no_lock(thd, table->db, table->table_name,
&local_create_info, &local_alter_info,
@@ -4726,7 +4809,7 @@ err:
@details Checks if any index is being modified (present as both DROP INDEX
and ADD INDEX) in the current ALTER TABLE statement. Needed for disabling
- online ALTER TABLE.
+ in-place ALTER TABLE.
@param table The table being altered
@param alter_info The ALTER TABLE structure
@@ -4843,7 +4926,7 @@ mysql_compare_tables(TABLE *table,
like to keep mysql_compare_tables() idempotent (not altering any
of the arguments) we create a copy of alter_info here and
pass it to mysql_prepare_create_table, then use the result
- to evaluate possibility of fast ALTER TABLE, and then
+ to evaluate possibility of in-place ALTER TABLE, and then
destroy the copy.
*/
Alter_info tmp_alter_info(*alter_info, thd->mem_root);
@@ -4887,9 +4970,9 @@ mysql_compare_tables(TABLE *table,
There was a bug prior to mysql-4.0.25. Number of null fields was
calculated incorrectly. As a result frm and data files gets out of
- sync after fast alter table. There is no way to determine by which
+ sync after in-place alter table. There is no way to determine by which
mysql version (in 4.0 and 4.1 branches) table was created, thus we
- disable fast alter table for all tables created by mysql versions
+ disable in-place alter table for all tables created by mysql versions
prior to 5.0 branch.
See BUG#6236.
*/
@@ -4920,7 +5003,7 @@ mysql_compare_tables(TABLE *table,
DBUG_RETURN(1);
/*
- Use transformed info to evaluate possibility of fast ALTER TABLE
+ Use transformed info to evaluate possibility of in-place ALTER TABLE
but use the preserved field to persist modifications.
*/
new_field_it.init(alter_info->create_list);
@@ -5230,7 +5313,7 @@ blob_length_by_type(enum_field_types type)
semantic checks.
This function is invoked when we know that we're going to
- perform ALTER TABLE via a temporary table -- i.e. fast ALTER TABLE
+ perform ALTER TABLE via a temporary table -- i.e. in-place ALTER TABLE
is not possible, perhaps because the ALTER statement contains
instructions that require change in table data, not only in
table definition or indexes.
@@ -5308,17 +5391,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (!(used_fields & HA_CREATE_USED_TRANSACTIONAL))
create_info->transactional= table->s->transactional;
- if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
- {
- char *tablespace= static_cast<char *>(thd->alloc(FN_LEN + 1));
- /*
- Regular alter table of disk stored table (no tablespace/storage change)
- Copy tablespace name
- */
- if (tablespace &&
- (table->file->get_tablespace_name(thd, tablespace, FN_LEN)))
- create_info->tablespace= tablespace;
- }
restore_record(table, s->default_values); // Empty record for DEFAULT
create_info->option_list= merge_engine_table_options(table->s->option_list,
@@ -5706,7 +5778,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;
@@ -5717,7 +5788,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
handlerton *old_db_type, *new_db_type, *save_old_db_type;
enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
#ifdef WITH_PARTITION_STORAGE_ENGINE
- uint fast_alter_partition= 0;
+ TABLE *table_for_fast_alter_partition= NULL;
bool partition_changed= FALSE;
#endif
bool need_lock_for_indexes= TRUE;
@@ -5725,6 +5796,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
uint index_drop_count= 0;
uint *index_drop_buffer= NULL;
uint index_add_count= 0;
+ handler_add_index *add= NULL;
+ bool pending_inplace_add_index= false;
uint *index_add_buffer= NULL;
uint candidate_key_count= 0;
bool no_pk;
@@ -5872,7 +5945,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.
@@ -5890,7 +5963,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.
@@ -6037,7 +6109,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
*fn_ext(new_name)=0;
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,
+ else if (Table_triggers_list::change_table_name(thd, db,
+ alias, table_name,
new_db, new_alias))
{
(void) mysql_rename_table(old_db_type, new_db, new_alias, db,
@@ -6078,10 +6151,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);
}
@@ -6092,7 +6162,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
- &partition_changed, &fast_alter_partition))
+ &partition_changed,
+ db, table_name, path,
+ &table_for_fast_alter_partition))
goto err;
#endif
/*
@@ -6154,7 +6226,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
/*
- If there are index changes only, try to do them online. "Index
+ If there are index changes only, try to do them in-place. "Index
changes only" means also that the handler for the table does not
change. The table is open and locked. The handler can be accessed.
*/
@@ -6162,8 +6234,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
int pk_changed= 0;
ulong alter_flags= 0;
- ulong needed_online_flags= 0;
- ulong needed_fast_flags= 0;
+ ulong needed_inplace_with_read_flags= 0;
+ ulong needed_inplace_flags= 0;
KEY *key;
uint *idx_p;
uint *idx_end_p;
@@ -6187,8 +6259,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
DBUG_PRINT("info", ("Dropping primary key"));
/* Primary key. */
- needed_online_flags|= HA_ONLINE_DROP_PK_INDEX;
- needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_DROP_PK_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE;
pk_changed++;
candidate_key_count--;
}
@@ -6198,8 +6270,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
bool is_candidate_key= true;
/* Non-primary unique key. */
- needed_online_flags|= HA_ONLINE_DROP_UNIQUE_INDEX;
- needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|=
+ HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE;
/*
Check if all fields in key are declared
@@ -6218,12 +6291,13 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
else
{
/* Non-unique key. */
- needed_online_flags|= HA_ONLINE_DROP_INDEX;
- needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_DROP_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_DROP_INDEX_NO_READ_WRITE;
}
}
no_pk= ((table->s->primary_key == MAX_KEY) ||
- (needed_online_flags & HA_ONLINE_DROP_PK_INDEX));
+ (needed_inplace_with_read_flags &
+ HA_INPLACE_DROP_PK_INDEX_NO_WRITE));
/* Check added indexes. */
for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count;
idx_p < idx_end_p;
@@ -6261,57 +6335,59 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
DBUG_PRINT("info", ("Adding primary key"));
/* Primary key. */
- needed_online_flags|= HA_ONLINE_ADD_PK_INDEX;
- needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE;
pk_changed++;
no_pk= false;
}
else
{
/* Non-primary unique key. */
- needed_online_flags|= HA_ONLINE_ADD_UNIQUE_INDEX;
- needed_fast_flags|= HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE;
}
}
else
{
/* Non-unique key. */
- needed_online_flags|= HA_ONLINE_ADD_INDEX;
- needed_fast_flags|= HA_ONLINE_ADD_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_ADD_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_ADD_INDEX_NO_READ_WRITE;
}
}
- if ((candidate_key_count > 0) &&
- (needed_online_flags & HA_ONLINE_DROP_PK_INDEX))
+ if ((candidate_key_count > 0) &&
+ (needed_inplace_with_read_flags & HA_INPLACE_DROP_PK_INDEX_NO_WRITE))
{
/*
Dropped primary key when there is some other unique
not null key that should be converted to primary key
*/
- needed_online_flags|= HA_ONLINE_ADD_PK_INDEX;
- needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE;
pk_changed= 2;
}
- DBUG_PRINT("info", ("needed_online_flags: 0x%lx, needed_fast_flags: 0x%lx",
- needed_online_flags, needed_fast_flags));
+ DBUG_PRINT("info",
+ ("needed_inplace_with_read_flags: 0x%lx, needed_inplace_flags: 0x%lx",
+ needed_inplace_with_read_flags, needed_inplace_flags));
/*
- Online or fast add/drop index is possible only if
+ In-place add/drop index is possible only if
the primary key is not added and dropped in the same statement.
Otherwise we have to recreate the table.
need_copy_table is no-zero at this place.
*/
if ( pk_changed < 2 )
{
- if ((alter_flags & needed_online_flags) == needed_online_flags)
+ if ((alter_flags & needed_inplace_with_read_flags) ==
+ needed_inplace_with_read_flags)
{
- /* All required online flags are present. */
+ /* All required in-place flags to allow concurrent reads are present. */
need_copy_table= ALTER_TABLE_METADATA_ONLY;
need_lock_for_indexes= FALSE;
}
- else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
+ else if ((alter_flags & needed_inplace_flags) == needed_inplace_flags)
{
- /* All required fast flags are present. */
+ /* All required in-place flags are present. */
need_copy_table= ALTER_TABLE_METADATA_ONLY;
}
}
@@ -6328,12 +6404,12 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
create_info->frm_only= 1;
#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (fast_alter_partition)
+ if (table_for_fast_alter_partition)
{
DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
create_info, table_list,
db, table_name,
- fast_alter_partition));
+ table_for_fast_alter_partition));
}
#endif
@@ -6428,8 +6504,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
/* table is a normal table: Create temporary table in same directory */
build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "",
FN_IS_TMP);
- /* Open our intermediate table */
- new_table= open_temporary_table(thd, path, new_db, tmp_name, 1);
+ /* Open our intermediate table. */
+ new_table= open_table_uncached(thd, path, new_db, tmp_name, TRUE);
}
if (!new_table)
goto err_new_table_cleanup;
@@ -6465,10 +6541,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
else
{
+ /*
+ Ensure that we will upgrade the metadata lock if
+ handler::enable/disable_indexes() will be called.
+ */
+ if (alter_info->keys_onoff != LEAVE_AS_IS ||
+ table->file->indexes_are_disabled())
+ need_lock_for_indexes= true;
if (!table->s->tmp_table &&
wait_while_table_is_used(thd, table, extra_func))
goto err_new_table_cleanup;
thd_proc_info(thd, "manage keys");
+ DEBUG_SYNC(thd, "alter_table_manage_keys");
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
alter_info->keys_onoff);
error= trans_commit_stmt(thd);
@@ -6477,6 +6561,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+ if (error)
+ goto err_new_table_cleanup;
+
/* If we did not need to copy, we might still need to add/drop indexes. */
if (! new_table)
{
@@ -6508,7 +6595,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
key_part->field= table->field[key_part->fieldnr];
}
/* Add the indexes. */
- if ((error= table->file->add_index(table, key_info, index_add_count)))
+ if ((error= table->file->add_index(table, key_info, index_add_count,
+ &add)))
{
#ifdef MERGE_MONTY_ADDITION_THAT_BREAKS_5_5_TESTS
/* Only report error if handler has not already reported an error */
@@ -6526,11 +6614,27 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
goto err_new_table_cleanup;
}
+ pending_inplace_add_index= true;
}
/*end of if (index_add_count)*/
if (index_drop_count)
{
+ /* Currently we must finalize add index if we also drop indexes */
+ if (pending_inplace_add_index)
+ {
+ /* Committing index changes needs exclusive metadata lock. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE,
+ table_list->db,
+ table_list->table_name,
+ MDL_EXCLUSIVE));
+ if ((error= table->file->final_add_index(add, true)))
+ {
+ table->file->print_error(error, MYF(0));
+ goto err_new_table_cleanup;
+ }
+ pending_inplace_add_index= false;
+ }
/* The prepare_drop_index() method takes an array of key numbers. */
key_numbers= (uint*) thd->alloc(sizeof(uint) * index_drop_count);
keyno_p= key_numbers;
@@ -6571,11 +6675,14 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
/*end of if (! new_table) for add/drop index*/
- if (error)
- goto err_new_table_cleanup;
-
if (table->s->tmp_table != NO_TMP_TABLE)
{
+ /*
+ In-place operations are not supported for temporary tables, so
+ we don't have to call final_add_index() in this case. The assert
+ verifies that in-place add index has not been done.
+ */
+ DBUG_ASSERT(!pending_inplace_add_index);
/* Close lock if this is a transactional table */
if (thd->lock)
{
@@ -6642,8 +6749,30 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
my_casedn_str(files_charset_info, old_name);
if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME))
+ {
+ if (pending_inplace_add_index)
+ {
+ pending_inplace_add_index= false;
+ table->file->final_add_index(add, false);
+ }
+ // Mark this TABLE instance as stale to avoid out-of-sync index information.
+ table->m_needs_reopen= true;
goto err_new_table_cleanup;
-
+ }
+ if (pending_inplace_add_index)
+ {
+ pending_inplace_add_index= false;
+ DBUG_EXECUTE_IF("alter_table_rollback_new_index", {
+ table->file->final_add_index(add, false);
+ my_error(ER_UNKNOWN_ERROR, MYF(0));
+ goto err_new_table_cleanup;
+ });
+ if ((error= table->file->final_add_index(add, true)))
+ {
+ table->file->print_error(error, MYF(0));
+ goto err_new_table_cleanup;
+ }
+ }
close_all_tables_for_name(thd, table->s,
new_name != table_name || new_db != db);
@@ -6680,11 +6809,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db,
new_alias, FN_FROM_IS_TMP) ||
((new_name != table_name || new_db != db) && // we also do rename
- (need_copy_table != ALTER_TABLE_METADATA_ONLY ||
- mysql_rename_table(save_old_db_type, db, table_name, new_db,
- new_alias, NO_FRM_RENAME)) &&
- Table_triggers_list::change_table_name(thd, db, table_name,
- new_db, new_alias)))
+ (need_copy_table != ALTER_TABLE_METADATA_ONLY ||
+ mysql_rename_table(save_old_db_type, db, table_name, new_db,
+ new_alias, NO_FRM_RENAME)) &&
+ Table_triggers_list::change_table_name(thd, db, alias, table_name,
+ new_db, new_alias)))
{
/* Try to get everything back. */
error=1;
@@ -6711,15 +6840,15 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
NO need to tamper with MERGE tables. The real open is done later.
*/
Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
- TABLE *t_table;
+ TABLE_LIST temp_table_list;
+ TABLE_LIST *t_table_list;
if (new_name != table_name || new_db != db)
{
- table_list->alias= new_name;
- table_list->table_name= new_name;
- table_list->table_name_length= strlen(new_name);
- table_list->db= new_db;
- table_list->db_length= strlen(new_db);
- table_list->mdl_request.ticket= target_mdl_request.ticket;
+ temp_table_list.init_one_table(new_db, strlen(new_db),
+ new_name, strlen(new_name),
+ new_name, TL_READ_NO_INSERT);
+ temp_table_list.mdl_request.ticket= target_mdl_request.ticket;
+ t_table_list= &temp_table_list;
}
else
{
@@ -6729,20 +6858,21 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
to request the lock.
*/
table_list->mdl_request.ticket= mdl_ticket;
+ t_table_list= table_list;
}
- if (open_table(thd, table_list, thd->mem_root, &ot_ctx))
+ if (open_table(thd, t_table_list, thd->mem_root, &ot_ctx))
{
goto err_with_mdl;
}
- t_table= table_list->table;
/* 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);
+ error= t_table_list->table->file->ha_create_handler_files(path, NULL,
+ CHF_INDEX_FLAG,
+ create_info);
- DBUG_ASSERT(thd->open_tables == t_table);
+ DBUG_ASSERT(thd->open_tables == t_table_list->table);
close_thread_table(thd, &thd->open_tables);
- table_list->table= 0;
+ t_table_list->table= NULL;
if (error)
goto err_with_mdl;
@@ -6772,10 +6902,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);
}
@@ -6798,6 +6925,11 @@ err_new_table_cleanup:
create_info->frm_only ? FN_IS_TMP | FRM_ONLY : FN_IS_TMP);
err:
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ /* If prep_alter_part_table created an intermediate table, destroy it. */
+ if (table_for_fast_alter_partition)
+ close_temporary(table_for_fast_alter_partition, 1, 0);
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
/*
No default value was provided for a DATE/DATETIME field, the
current sql_mode doesn't allow the '0000-00-00' value and
@@ -6831,8 +6963,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);
@@ -6844,9 +6974,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);
}