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.cc614
1 files changed, 438 insertions, 176 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index f11a4e683f1..d9d36c260fd 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -19,10 +19,12 @@
#include <hash.h>
#include <myisam.h>
#include <my_dir.h>
+#include "create_options.h"
#include "sp_head.h"
#include "sql_trigger.h"
#include "sql_show.h"
#include "debug_sync.h"
+#include "sql_handler.h"
#ifdef __WIN__
#include <io.h>
@@ -41,9 +43,11 @@ static int copy_data_between_tables(TABLE *,TABLE *, List<Create_field> &, bool,
static bool prepare_blob_field(THD *thd, Create_field *sql_field);
static bool check_engine(THD *, const char *, HA_CREATE_INFO *);
static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *,
- bool, uint *, handler *, KEY **, uint *, int);
+ bool, uint *, handler *, KEY **, uint *,
+ int);
static bool mysql_prepare_alter_table(THD *, TABLE *, HA_CREATE_INFO *,
Alter_info *);
+static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list);
#ifndef DBUG_OFF
@@ -2002,9 +2006,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
{
TABLE *locked_table;
abort_locked_tables(thd, db, table->table_name);
+ table->deleting= TRUE;
remove_table_from_cache(thd, db, table->table_name,
RTFC_WAIT_OTHER_THREAD_FLAG |
- RTFC_CHECK_KILLED_FLAG);
+ RTFC_CHECK_KILLED_FLAG, FALSE);
/*
If the table was used in lock tables, remember it so that
unlock_table_names can free it
@@ -2031,7 +2036,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
(!drop_view &&
mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
{
- // Table was not found on disk and table can't be created from engine
+ /* Table was not found on disk and table can't be created from engine */
if (if_exists)
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
@@ -2107,7 +2112,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
{
if (!foreign_key_error)
my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
- wrong_tables.c_ptr());
+ wrong_tables.c_ptr_safe());
else
my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
error= 1;
@@ -2244,10 +2249,10 @@ static int sort_keys(KEY *a, KEY *b)
{
if (!(b_flags & HA_NOSAME))
return -1;
- if ((a_flags ^ b_flags) & (HA_NULL_PART_KEY | HA_END_SPACE_KEY))
+ if ((a_flags ^ b_flags) & HA_NULL_PART_KEY)
{
/* Sort NOT NULL keys before other keys */
- return (a_flags & (HA_NULL_PART_KEY | HA_END_SPACE_KEY)) ? 1 : -1;
+ return (a_flags & HA_NULL_PART_KEY) ? 1 : -1;
}
if (a->name == primary_key_name)
return -1;
@@ -2528,7 +2533,12 @@ int prepare_create_field(Create_field *sql_field,
(sql_field->decimals << FIELDFLAG_DEC_SHIFT));
break;
}
- if (!(sql_field->flags & NOT_NULL_FLAG))
+ if (sql_field->flags & NOT_NULL_FLAG)
+ DBUG_PRINT("info", ("1"));
+ if (sql_field->vcol_info)
+ DBUG_PRINT("info", ("2"));
+ if (!(sql_field->flags & NOT_NULL_FLAG) ||
+ (sql_field->vcol_info)) /* Make virtual columns allow NULL values */
sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL;
if (sql_field->flags & NO_DEFAULT_VALUE_FLAG)
sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT;
@@ -2842,6 +2852,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
null_fields--;
sql_field->flags= dup_field->flags;
sql_field->interval= dup_field->interval;
+ sql_field->vcol_info= dup_field->vcol_info;
+ sql_field->stored_in_db= dup_field->stored_in_db;
it2.remove(); // Remove first (create) definition
select_field_pos--;
break;
@@ -2851,7 +2863,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
/* Don't pack rows in old tables if the user has requested this */
if ((sql_field->flags & BLOB_FLAG) ||
(sql_field->sql_type == MYSQL_TYPE_VARCHAR &&
- create_info->row_type != ROW_TYPE_FIXED))
+ create_info->row_type != ROW_TYPE_FIXED))
(*db_options)|= HA_OPTION_PACK_RECORD;
it2.rewind();
}
@@ -2874,7 +2886,28 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
sql_field->offset= record_offset;
if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
auto_increment++;
- record_offset+= sql_field->pack_length;
+ if (parse_option_list(thd, &sql_field->option_struct,
+ sql_field->option_list,
+ create_info->db_type->field_options, FALSE,
+ thd->mem_root))
+ DBUG_RETURN(TRUE);
+ /*
+ For now skip fields that are not physically stored in the database
+ (virtual fields) and update their offset later
+ (see the next loop).
+ */
+ if (sql_field->stored_in_db)
+ record_offset+= sql_field->pack_length;
+ }
+ /* Update virtual fields' offset*/
+ it.rewind();
+ while ((sql_field=it++))
+ {
+ if (!sql_field->stored_in_db)
+ {
+ sql_field->offset= record_offset;
+ record_offset+= sql_field->pack_length;
+ }
}
if (timestamps_with_niladic > 1)
{
@@ -2924,6 +2957,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (key->type == Key::FOREIGN_KEY)
{
fk_key_count++;
+ if (((Foreign_key *)key)->validate(alter_info->create_list))
+ DBUG_RETURN(TRUE);
Foreign_key *fk_key= (Foreign_key*) key;
if (fk_key->ref_columns.elements &&
fk_key->ref_columns.elements != fk_key->columns.elements)
@@ -3054,6 +3089,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_info->key_part=key_part_info;
key_info->usable_key_parts= key_number;
key_info->algorithm= key->key_create_info.algorithm;
+ key_info->option_list= key->option_list;
+ if (parse_option_list(thd, &key_info->option_struct,
+ key_info->option_list,
+ create_info->db_type->index_options, FALSE,
+ thd->mem_root))
+ DBUG_RETURN(TRUE);
if (key->type == Key::FULLTEXT)
{
@@ -3210,6 +3251,17 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
}
#endif
+ if (!sql_field->stored_in_db)
+ {
+ /* Key fields must always be physically stored. */
+ my_error(ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (key->type == Key::PRIMARY && sql_field->vcol_info)
+ {
+ my_error(ER_PRIMARY_KEY_BASED_ON_VIRTUAL_COLUMN, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
if (!(sql_field->flags & NOT_NULL_FLAG))
{
if (key->type == Key::PRIMARY)
@@ -3287,10 +3339,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
length=column->length;
}
- else if (length == 0)
+ else if (length == 0 && (sql_field->flags & NOT_NULL_FLAG))
{
my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(TRUE);
}
if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
{
@@ -3421,6 +3473,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
}
+ if (parse_option_list(thd, &create_info->option_struct,
+ create_info->option_list,
+ file->partition_ht()->table_options, FALSE,
+ thd->mem_root))
+ DBUG_RETURN(TRUE);
+
DBUG_RETURN(FALSE);
}
@@ -3599,6 +3657,68 @@ static inline int write_create_table_bin_log(THD *thd,
}
+/**
+ Check that there is no frm file for given table
+
+ @param old_path path to the old frm file
+ @param path path to the frm file in new encoding
+ @param db database name
+ @param table_name table name
+ @param alias table name for error message (for new encoding)
+ @param issue_error should we issue error messages
+
+ @retval FALSE there is no frm file
+ @retval TRUE there is frm file
+*/
+
+bool check_table_file_presence(char *old_path,
+ char *path,
+ const char *db,
+ const char *table_name,
+ const char *alias,
+ bool issue_error)
+{
+ if (!access(path,F_OK))
+ {
+ if (issue_error)
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),alias);
+ return TRUE;
+ }
+ {
+ /*
+ Check if file of the table in 5.0 file name encoding exists.
+
+ Except case when it is the same table.
+ */
+ char tbl50[FN_REFLEN];
+#ifdef _WIN32
+ if (check_if_legal_tablename(table_name) != 0)
+ {
+ /*
+ Check for reserved device names for which access() returns 0
+ (CON, AUX etc).
+ */
+ return FALSE;
+ }
+#endif
+ strxmov(tbl50, mysql_data_home, "/", db, "/", table_name, NullS);
+ fn_format(tbl50, tbl50, "", reg_ext, MY_UNPACK_FILENAME);
+ if (!access(tbl50, F_OK) &&
+ (old_path == NULL ||
+ strcmp(old_path, tbl50) != 0))
+ {
+ if (issue_error)
+ {
+ strxmov(tbl50, MYSQL50_TABLE_NAME_PREFIX, table_name, NullS);
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), tbl50);
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
/*
Create a table
@@ -3661,8 +3781,9 @@ bool mysql_create_table_no_lock(THD *thd,
if (check_engine(thd, table_name, create_info))
DBUG_RETURN(TRUE);
db_options= create_info->table_options;
- if (create_info->row_type == ROW_TYPE_DYNAMIC)
- db_options|=HA_OPTION_PACK_RECORD;
+ if (create_info->row_type != ROW_TYPE_FIXED &&
+ create_info->row_type != ROW_TYPE_DEFAULT)
+ db_options|= HA_OPTION_PACK_RECORD;
alias= table_case_name(create_info, table_name);
if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
create_info->db_type)))
@@ -3868,14 +3989,27 @@ bool mysql_create_table_no_lock(THD *thd,
goto err;
}
+ /* Give warnings for not supported table options */
+#if defined(WITH_ARIA_STORAGE_ENGINE)
+ extern handlerton *maria_hton;
+ if (file->ht != maria_hton)
+#endif
+ if (create_info->transactional)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_ILLEGAL_HA_CREATE_OPTION,
+ ER(ER_ILLEGAL_HA_CREATE_OPTION),
+ file->engine_name()->str,
+ "TRANSACTIONAL=1");
+
VOID(pthread_mutex_lock(&LOCK_open));
if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
- if (!access(path,F_OK))
+ if (check_table_file_presence(NULL, path, db, table_name, table_name,
+ !(create_info->options &
+ HA_LEX_CREATE_IF_NOT_EXISTS)))
{
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;
}
/*
@@ -3920,7 +4054,6 @@ bool mysql_create_table_no_lock(THD *thd,
goto warn;
my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
goto unlock_and_end;
- break;
default:
DBUG_PRINT("info", ("error: %u from storage engine", retcode));
my_error(retcode, MYF(0),table_name);
@@ -4154,7 +4287,7 @@ mysql_rename_table(handlerton *base, const char *old_db,
char from[FN_REFLEN + 1], to[FN_REFLEN + 1],
lc_from[FN_REFLEN + 1], lc_to[FN_REFLEN + 1];
char *from_base= from, *to_base= to;
- char tmp_name[NAME_LEN+1];
+ char tmp_name[SAFE_NAME_LEN+1];
handler *file;
int error=0;
DBUG_ENTER("mysql_rename_table");
@@ -4228,7 +4361,7 @@ mysql_rename_table(handlerton *base, const char *old_db,
Win32 clients must also have a WRITE LOCK on the table !
*/
-void wait_while_table_is_used(THD *thd, TABLE *table,
+void wait_while_table_is_used(THD *thd,TABLE *table,
enum ha_extra_function function)
{
DBUG_ENTER("wait_while_table_is_used");
@@ -4237,15 +4370,17 @@ void wait_while_table_is_used(THD *thd, TABLE *table,
table->db_stat, table->s->version));
safe_mutex_assert_owner(&LOCK_open);
-
- VOID(table->file->extra(function));
+
/* Mark all tables that are in use as 'old' */
mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */
/* Wait until all there are no other threads that has this table open */
remove_table_from_cache(thd, table->s->db.str,
table->s->table_name.str,
- RTFC_WAIT_OTHER_THREAD_FLAG);
+ RTFC_WAIT_OTHER_THREAD_FLAG, FALSE);
+ /* extra() call must come only after all instances above are closed */
+ if (function != HA_EXTRA_NOT_USED)
+ VOID(table->file->extra(function));
DBUG_VOID_RETURN;
}
@@ -4544,6 +4679,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
Protocol *protocol= thd->protocol;
LEX *lex= thd->lex;
int result_code;
+ bool need_repair_or_alter= 0;
DBUG_ENTER("mysql_admin_table");
if (end_active_trans(thd))
@@ -4564,7 +4700,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
for (table= tables; table; table= table->next_local)
{
- char table_name[NAME_LEN*2+2];
+ char table_name[SAFE_NAME_LEN*2+2];
char* db = table->db;
bool fatal_error=0;
@@ -4744,7 +4880,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
remove_table_from_cache(thd, table->table->s->db.str,
table->table->s->table_name.str,
RTFC_WAIT_OTHER_THREAD_FLAG |
- RTFC_CHECK_KILLED_FLAG);
+ RTFC_CHECK_KILLED_FLAG, FALSE);
thd->exit_cond(old_message);
DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd););
if (thd->killed)
@@ -4772,32 +4908,38 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
if (operator_func == &handler::ha_repair &&
!(check_opt->sql_flags & TT_USEFRM))
{
- if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
- (table->table->file->ha_check_for_upgrade(check_opt) ==
- HA_ADMIN_NEEDS_ALTER))
+ handler *file= table->table->file;
+ int check_old_types= file->check_old_types();
+ int check_for_upgrade= file->ha_check_for_upgrade(check_opt);
+
+ if (check_old_types == HA_ADMIN_NEEDS_ALTER ||
+ check_for_upgrade == HA_ADMIN_NEEDS_ALTER)
{
- DBUG_PRINT("admin", ("recreating table"));
- ha_autocommit_or_rollback(thd, 1);
- close_thread_tables(thd);
- tmp_disable_binlog(thd); // binlogging is done by caller if wanted
- result_code= mysql_recreate_table(thd, table);
- reenable_binlog(thd);
- /*
- mysql_recreate_table() can push OK or ERROR.
- Clear 'OK' status. If there is an error, keep it:
- we will store the error message in a result set row
- and then clear.
- */
- if (thd->main_da.is_ok())
- thd->main_da.reset_diagnostics_area();
+ /* We use extra_open_options to be able to open crashed tables */
+ thd->open_options|= extra_open_options;
+ result_code= admin_recreate_table(thd, table);
+ thd->open_options= ~extra_open_options;
goto send_result;
}
+ if (check_old_types || check_for_upgrade)
+ {
+ /* If repair is not implemented for the engine, run ALTER TABLE */
+ need_repair_or_alter= 1;
+ }
}
DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
result_code = (table->table->file->*operator_func)(thd, check_opt);
DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
+ if (result_code == HA_ADMIN_NOT_IMPLEMENTED && need_repair_or_alter)
+ {
+ /*
+ repair was not implemented and we need to upgrade the table
+ to a new version so we recreate the table with ALTER TABLE
+ */
+ result_code= admin_recreate_table(thd, table);
+ }
send_result:
lex->cleanup_after_one_table_open();
@@ -4897,23 +5039,13 @@ send_result_message:
system_charset_info);
if (protocol->write())
goto err;
- ha_autocommit_or_rollback(thd, 0);
- close_thread_tables(thd);
DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
TABLE_LIST *save_next_local= table->next_local,
*save_next_global= table->next_global;
table->next_local= table->next_global= 0;
- tmp_disable_binlog(thd); // binlogging is done by caller if wanted
- result_code= mysql_recreate_table(thd, table);
- reenable_binlog(thd);
- /*
- mysql_recreate_table() can push OK or ERROR.
- Clear 'OK' status. If there is an error, keep it:
- we will store the error message in a result set row
- and then clear.
- */
- if (thd->main_da.is_ok())
- thd->main_da.reset_diagnostics_area();
+
+ result_code= admin_recreate_table(thd, table);
+
ha_autocommit_or_rollback(thd, 0);
close_thread_tables(thd);
if (!result_code) // recreation went ok
@@ -5002,7 +5134,8 @@ send_result_message:
{
pthread_mutex_lock(&LOCK_open);
remove_table_from_cache(thd, table->table->s->db.str,
- table->table->s->table_name.str, RTFC_NO_FLAG);
+ table->table->s->table_name.str,
+ RTFC_NO_FLAG, FALSE);
pthread_mutex_unlock(&LOCK_open);
}
/* May be something modified consequently we have to invalidate cache */
@@ -5669,8 +5802,8 @@ is_index_maintenance_unique (TABLE *table, Alter_info *alter_info)
that need to be dropped and/or (re-)created.
RETURN VALUES
- TRUE error
- FALSE success
+ TRUE The tables are not compatible; We have to do a full alter table
+ FALSE The tables are compatible; We only have to modify the .frm
*/
static
@@ -5693,6 +5826,7 @@ compare_tables(TABLE *table,
KEY_PART_INFO *key_part;
KEY_PART_INFO *end;
THD *thd= table->in_use;
+ uint i;
/*
Remember if the new definition has new VARCHAR column;
create_info->varchar will be reset in mysql_prepare_create_table.
@@ -5719,6 +5853,9 @@ compare_tables(TABLE *table,
Alter_info tmp_alter_info(*alter_info, thd->mem_root);
uint db_options= 0; /* not used */
+ /* Set default value for return value (to ensure it's always set) */
+ *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+
/* Create the prepared information. */
if (mysql_prepare_create_table(thd, create_info,
&tmp_alter_info,
@@ -5767,6 +5904,8 @@ compare_tables(TABLE *table,
create_info->used_fields & HA_CREATE_USED_CHARSET ||
create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET ||
(table->s->row_type != create_info->row_type) ||
+ create_info->used_fields & HA_CREATE_USED_PAGE_CHECKSUM ||
+ create_info->used_fields & HA_CREATE_USED_TRANSACTIONAL ||
create_info->used_fields & HA_CREATE_USED_PACK_KEYS ||
create_info->used_fields & HA_CREATE_USED_MAX_ROWS ||
(alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
@@ -5774,10 +5913,16 @@ compare_tables(TABLE *table,
!table->s->mysql_version ||
(table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
{
- *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ DBUG_PRINT("info", ("Basic checks -> ALTER_TABLE_DATA_CHANGED"));
DBUG_RETURN(0);
}
+ if ((create_info->fields_option_struct= (ha_field_option_struct**)
+ thd->calloc(sizeof(void*) * table->s->fields)) == NULL ||
+ (create_info->indexes_option_struct= (ha_index_option_struct**)
+ thd->calloc(sizeof(void*) * table->s->keys)) == NULL)
+ DBUG_RETURN(1);
+
/*
Use transformed info to evaluate possibility of fast ALTER TABLE
but use the preserved field to persist modifications.
@@ -5789,12 +5934,18 @@ compare_tables(TABLE *table,
Go through fields and check if the original ones are compatible
with new table.
*/
- for (f_ptr= table->field, new_field= new_field_it++,
+ for (i= 0, f_ptr= table->field, new_field= new_field_it++,
tmp_new_field= tmp_new_field_it++;
(field= *f_ptr);
- f_ptr++, new_field= new_field_it++,
+ i++, f_ptr++, new_field= new_field_it++,
tmp_new_field= tmp_new_field_it++)
{
+ DBUG_ASSERT(i < table->s->fields);
+ create_info->fields_option_struct[i]= tmp_new_field->option_struct;
+
+ /* reset common markers of how field changed */
+ field->flags&= ~(FIELD_IS_RENAMED | FIELD_IN_ADD_INDEX);
+
/* Make sure we have at least the default charset in use. */
if (!new_field->charset)
new_field->charset= create_info->default_table_charset;
@@ -5803,19 +5954,32 @@ compare_tables(TABLE *table,
if ((tmp_new_field->flags & NOT_NULL_FLAG) !=
(uint) (field->flags & NOT_NULL_FLAG))
{
+ DBUG_PRINT("info", ("NULL behaviour difference in field '%s' -> "
+ "ALTER_TABLE_DATA_CHANGED", new_field->field_name));
+ DBUG_RETURN(0);
+ }
+
+ /*
+ Check if the altered column is computed and either
+ is stored or is used in the partitioning expression.
+ TODO: Mark such a column with an alter flag only if
+ the defining expression has changed.
+ */
+ if (field->vcol_info &&
+ (field->stored_in_db || field->vcol_info->is_in_partitioning_expr()))
+ {
*need_copy_table= ALTER_TABLE_DATA_CHANGED;
DBUG_RETURN(0);
}
/* Don't pack rows in old tables if the user has requested this. */
- if (create_info->row_type == ROW_TYPE_DYNAMIC ||
- (tmp_new_field->flags & BLOB_FLAG) ||
- (tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR &&
- create_info->row_type != ROW_TYPE_FIXED))
- create_info->table_options|= HA_OPTION_PACK_RECORD;
+ if (create_info->row_type == ROW_TYPE_DYNAMIC ||
+ (tmp_new_field->flags & BLOB_FLAG) ||
+ (tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR &&
+ create_info->row_type != ROW_TYPE_FIXED))
+ create_info->table_options|= HA_OPTION_PACK_RECORD;
/* Check if field was renamed */
- field->flags&= ~FIELD_IS_RENAMED;
if (my_strcasecmp(system_charset_info,
field->field_name,
tmp_new_field->field_name))
@@ -5824,11 +5988,10 @@ compare_tables(TABLE *table,
/* Evaluate changes bitmap and send to check_if_incompatible_data() */
if (!(tmp= field->is_equal(tmp_new_field)))
{
- *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ DBUG_PRINT("info", ("!field_is_equal('%s') -> ALTER_TABLE_DATA_CHANGED",
+ new_field->field_name));
DBUG_RETURN(0);
}
- // Clear indexed marker
- field->flags&= ~FIELD_IN_ADD_INDEX;
changes|= tmp;
}
@@ -5933,7 +6096,9 @@ compare_tables(TABLE *table,
for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
{
/* Search an old key with the same name. */
- for (table_key= table->key_info; table_key < table_key_end; table_key++)
+ for (i= 0, table_key= table->key_info;
+ table_key < table_key_end;
+ i++, table_key++)
{
if (! strcmp(table_key->name, new_key->name))
break;
@@ -5952,21 +6117,31 @@ compare_tables(TABLE *table,
}
DBUG_PRINT("info", ("index added: '%s'", new_key->name));
}
+ else
+ {
+ DBUG_ASSERT(i < table->s->keys);
+ create_info->indexes_option_struct[i]= new_key->option_struct;
+ }
}
/* Check if changes are compatible with current handler without a copy */
if (table->file->check_if_incompatible_data(create_info, changes))
{
- *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ DBUG_PRINT("info", ("check_if_incompatible_data() -> "
+ "ALTER_TABLE_DATA_CHANGED"));
DBUG_RETURN(0);
}
if (*index_drop_count || *index_add_count)
{
+ DBUG_PRINT("info", ("Index dropped=%u added=%u -> "
+ "ALTER_TABLE_INDEX_CHANGED",
+ *index_drop_count, *index_add_count));
*need_copy_table= ALTER_TABLE_INDEX_CHANGED;
DBUG_RETURN(0);
}
+ DBUG_PRINT("info", (" -> ALTER_TABLE_METADATA_ONLY"));
*need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
DBUG_RETURN(0);
}
@@ -6057,6 +6232,7 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
Sets create_info->varchar if the table has a VARCHAR column.
Prepares alter_info->create_list and alter_info->key_list with
columns and keys of the new table.
+
@retval TRUE error, out of memory or a semantical error in ALTER
TABLE instructions
@retval FALSE success
@@ -6083,7 +6259,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
uint used_fields= create_info->used_fields;
KEY *key_info=table->key_info;
bool rc= TRUE;
-
+ Create_field *def;
+ Field **f_ptr,*field;
DBUG_ENTER("mysql_prepare_alter_table");
create_info->varchar= FALSE;
@@ -6104,6 +6281,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
create_info->key_block_size= table->s->key_block_size;
+ if (!(used_fields & HA_CREATE_USED_TRANSACTIONAL))
+ create_info->transactional= table->s->transactional;
if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
{
@@ -6117,18 +6296,18 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
create_info->tablespace= tablespace;
}
restore_record(table, s->default_values); // Empty record for DEFAULT
- Create_field *def;
+ create_info->option_list= merge_engine_table_options(table->s->option_list,
+ create_info->option_list, thd->mem_root);
/*
First collect all fields from table which isn't in drop_list
*/
- Field **f_ptr,*field;
for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
{
- if (field->type() == MYSQL_TYPE_STRING)
+ Alter_drop *drop;
+ if (field->type() == MYSQL_TYPE_VARCHAR)
create_info->varchar= TRUE;
/* Check if field should be dropped */
- Alter_drop *drop;
drop_it.rewind();
while ((drop=drop_it++))
{
@@ -6161,6 +6340,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (def)
{ // Field is changed
def->field=field;
+ if (field->stored_in_db != def->stored_in_db)
+ {
+ my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN, MYF(0));
+ goto err;
+ }
if (!def->after)
{
new_create_list.push_back(def);
@@ -6202,7 +6386,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
{
if (def->change && ! def->field)
{
- my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table->s->table_name.str);
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change,
+ table->s->table_name.str);
goto err;
}
/*
@@ -6237,7 +6422,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
if (!find)
{
- my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str);
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after,
+ table->s->table_name.str);
goto err;
}
find_it.after(def); // Put element after this
@@ -6287,6 +6473,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
continue; // Wrong field (from UNIREG)
const char *key_part_name=key_part->field->field_name;
Create_field *cfield;
+ uint key_part_length;
+
field_it.rewind();
while ((cfield=field_it++))
{
@@ -6302,7 +6490,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
if (!cfield)
continue; // Field is removed
- uint key_part_length=key_part->length;
+ key_part_length= key_part->length;
if (cfield->field) // Not new field
{
/*
@@ -6361,7 +6549,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
key= new Key(key_type, key_name,
&key_create_info,
test(key_info->flags & HA_GENERATED_KEY),
- key_parts);
+ key_parts, key_info->option_list);
new_key_list.push_back(key);
}
}
@@ -6369,6 +6557,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
Key *key;
while ((key=key_it++)) // Add new keys
{
+ if (key->type == Key::FOREIGN_KEY &&
+ ((Foreign_key *)key)->validate(new_create_list))
+ goto err;
if (key->type != Key::FOREIGN_KEY)
new_key_list.push_back(key);
if (key->name &&
@@ -6440,6 +6631,7 @@ err:
order_num How many ORDER BY fields has been specified.
order List of fields to ORDER BY.
ignore Whether we have ALTER IGNORE TABLE
+ require_online Give an error if we can't do operation online
DESCRIPTION
This is a veery long function and is everything but the kitchen sink :)
@@ -6470,11 +6662,13 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
HA_CREATE_INFO *create_info,
TABLE_LIST *table_list,
Alter_info *alter_info,
- uint order_num, ORDER *order, bool ignore)
+ uint order_num, ORDER *order, bool ignore,
+ bool require_online)
{
TABLE *table, *new_table= 0, *name_lock= 0;
int error= 0;
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
+ char old_name_buff[FN_REFLEN + 1];
char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
char index_file[FN_REFLEN], data_file[FN_REFLEN];
char path[FN_REFLEN + 1];
@@ -6496,6 +6690,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
uint *index_add_buffer= NULL;
uint candidate_key_count= 0;
bool no_pk;
+ ulong explicit_used_fields= 0;
DBUG_ENTER("mysql_alter_table");
/*
@@ -6703,10 +6898,12 @@ view_err:
build_table_filename(new_name_buff, sizeof(new_name_buff) - 1,
new_db, new_name_buff, reg_ext, 0);
- if (!access(new_name_buff, F_OK))
+ build_table_filename(old_name_buff, sizeof(old_name_buff) - 1,
+ db, table_name, reg_ext, 0);
+ if (check_table_file_presence(old_name_buff, new_name_buff, new_db,
+ new_name, new_alias, TRUE))
{
/* Table will be closed in do_command() */
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
goto err;
}
}
@@ -6752,19 +6949,21 @@ view_err:
}
/*
- If this is an ALTER TABLE and no explicit row type specified reuse
- the table's row type.
- Note : this is the same as if the row type was specified explicitly.
+ If this is an ALTER TABLE and no explicit row type specified reuse
+ the table's row type.
+ Note: this is the same as if the row type was specified explicitly and
+ we must thus set HA_CREATE_USED_ROW_FORMAT!
*/
if (create_info->row_type == ROW_TYPE_NOT_USED)
{
/* ALTER TABLE without explicit row type */
create_info->row_type= table->s->row_type;
- }
- else
- {
- /* ALTER TABLE with specific row type */
- create_info->used_fields |= HA_CREATE_USED_ROW_FORMAT;
+ /*
+ We have to mark the row type as used, as otherwise the engine may
+ change the row format in update_create_info().
+ */
+ create_info->used_fields|= HA_CREATE_USED_ROW_FORMAT;
+ explicit_used_fields|= HA_CREATE_USED_ROW_FORMAT;
}
DBUG_PRINT("info", ("old type: %s new type: %s",
@@ -6796,7 +6995,9 @@ view_err:
from concurrent DDL statements.
*/
VOID(pthread_mutex_lock(&LOCK_open));
- wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
+ wait_while_table_is_used(thd, table,
+ thd->locked_tables ? HA_EXTRA_NOT_USED :
+ HA_EXTRA_FORCE_REOPEN);
VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
@@ -6804,7 +7005,9 @@ view_err:
break;
case DISABLE:
VOID(pthread_mutex_lock(&LOCK_open));
- wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
+ wait_while_table_is_used(thd, table,
+ thd->locked_tables ? HA_EXTRA_NOT_USED :
+ HA_EXTRA_FORCE_REOPEN);
VOID(pthread_mutex_unlock(&LOCK_open));
error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
/* COND_refresh will be signaled in close_thread_tables() */
@@ -6819,7 +7022,7 @@ view_err:
error= 0;
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
- table->alias);
+ table->alias.c_ptr());
}
/*
@@ -6883,7 +7086,7 @@ view_err:
error= 0;
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
- table->alias);
+ table->alias.c_ptr());
}
if (!error)
@@ -6931,6 +7134,9 @@ view_err:
if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
goto err;
+ /* Remove markers set for update_create_info */
+ create_info->used_fields&= ~explicit_used_fields;
+
if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
need_copy_table= alter_info->change_level;
@@ -7085,6 +7291,16 @@ view_err:
/* Non-primary unique key. */
needed_online_flags|= HA_ONLINE_ADD_UNIQUE_INDEX;
needed_fast_flags|= HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES;
+ if (ignore)
+ {
+ /*
+ If ignore is used, we have to remove all duplicate rows,
+ which require a full table copy.
+ */
+ need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ pk_changed= 2; // Don't change need_copy_table
+ break;
+ }
}
}
else
@@ -7250,10 +7466,23 @@ view_err:
*/
}
+ /* Check if we can do the ALTER TABLE as online */
+ if (require_online)
+ {
+ if (index_add_count || index_drop_count ||
+ (new_table &&
+ !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)))
+ {
+ my_error(ER_CANT_DO_ONLINE, MYF(0), "ALTER");
+ goto close_table_and_return_error;
+ }
+ }
+
/* Copy the data if necessary. */
thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields
thd->cuted_fields=0L;
copied=deleted=0;
+
/*
We do not copy data for MERGE tables. Only the children have data.
MERGE tables have HA_NO_COPY_ON_ALTER set.
@@ -7273,7 +7502,9 @@ view_err:
else
{
VOID(pthread_mutex_lock(&LOCK_open));
- wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
+ wait_while_table_is_used(thd, table,
+ thd->locked_tables ? HA_EXTRA_NOT_USED :
+ HA_EXTRA_FORCE_REOPEN);
VOID(pthread_mutex_unlock(&LOCK_open));
thd_proc_info(thd, "manage keys");
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
@@ -7283,6 +7514,8 @@ view_err:
error= 1;
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+ if (error)
+ goto close_table_and_return_error;
/* If we did not need to copy, we might still need to add/drop indexes. */
if (! new_table)
@@ -7317,14 +7550,18 @@ view_err:
/* Add the indexes. */
if ((error= table->file->add_index(table, key_info, index_add_count)))
{
- /*
- Exchange the key_info for the error message. If we exchange
- key number by key name in the message later, we need correct info.
- */
- KEY *save_key_info= table->key_info;
- table->key_info= key_info;
- table->file->print_error(error, MYF(0));
- table->key_info= save_key_info;
+ /* Only report error if handler has not already reported an error */
+ if (!thd->main_da.is_error())
+ {
+ /*
+ Exchange the key_info for the error message. If we exchange
+ key number by key name in the message later, we need correct info.
+ */
+ KEY *save_key_info= table->key_info;
+ table->key_info= key_info;
+ table->file->print_error(error, MYF(0));
+ table->key_info= save_key_info;
+ }
goto err1;
}
}
@@ -7372,11 +7609,11 @@ view_err:
}
/*end of if (! new_table) for add/drop index*/
+ DBUG_ASSERT(error == 0);
+
if (table->s->tmp_table != NO_TMP_TABLE)
{
/* We changed a temporary table */
- if (error)
- goto err1;
/* Close lock if this is a transactional table */
if (thd->lock)
{
@@ -7411,12 +7648,6 @@ view_err:
}
DEBUG_SYNC(thd, "alter_table_before_rename_result_table");
VOID(pthread_mutex_lock(&LOCK_open));
- if (error)
- {
- VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
- VOID(pthread_mutex_unlock(&LOCK_open));
- goto err;
- }
/*
Data is copied. Now we:
@@ -7474,11 +7705,11 @@ view_err:
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, table_name,
+ new_db, new_alias)))
{
/* Try to get everything back. */
error=1;
@@ -7564,27 +7795,6 @@ view_err:
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
DBUG_RETURN(TRUE);
- if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
- {
- /*
- For the alter table to be properly flushed to the logs, we
- have to open the new table. If not, we get a problem on server
- shutdown. But we do not need to attach MERGE children.
- */
- char path[FN_REFLEN];
- TABLE *t_table;
- build_table_filename(path + 1, sizeof(path) - 1, new_db, table_name, "", 0);
- t_table= open_temporary_table(thd, path, new_db, tmp_name, 0);
- if (t_table)
- {
- intern_close_table(t_table);
- my_free(t_table, MYF(0));
- }
- else
- sql_print_warning("Could not open table %s.%s after rename\n",
- new_db,table_name);
- ha_flush_logs(old_db_type);
- }
table_list->table=0; // For query cache
query_cache_invalidate3(thd, table_list, 0);
@@ -7610,6 +7820,16 @@ end_temporary:
thd->some_tables_deleted=0;
DBUG_RETURN(FALSE);
+close_table_and_return_error:
+ if (new_table && table->s->tmp_table == NO_TMP_TABLE)
+ {
+ /* This is not a temporary table, so close it the normal way */
+ new_table->s->deleting= TRUE;
+ intern_close_table(new_table);
+ my_free(new_table,MYF(0));
+ new_table= 0; // This forces call to quick_rm_table() below
+ }
+
err1:
if (new_table)
{
@@ -7675,7 +7895,9 @@ err_with_placeholders:
VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(TRUE);
}
-/* mysql_alter_table */
+
+
+/* Copy all rows from one table to another */
static int
copy_data_between_tables(TABLE *from,TABLE *to,
@@ -7687,9 +7909,9 @@ copy_data_between_tables(TABLE *from,TABLE *to,
enum enum_enable_or_disable keys_onoff,
bool error_if_not_empty)
{
- int error;
- Copy_field *copy,*copy_end;
- ulong found_count,delete_count;
+ int error= 1, errpos= 0;
+ Copy_field *copy= NULL, *copy_end;
+ ha_rows found_count= 0, delete_count= 0;
THD *thd= current_thd;
uint length= 0;
SORT_FIELD *sortorder;
@@ -7699,8 +7921,10 @@ copy_data_between_tables(TABLE *from,TABLE *to,
List<Item> all_fields;
ha_rows examined_rows;
bool auto_increment_field_copied= 0;
- ulong save_sql_mode;
+ ulong save_sql_mode= thd->variables.sql_mode;
ulonglong prev_insert_id;
+ List_iterator<Create_field> it(create);
+ Create_field *def;
DBUG_ENTER("copy_data_between_tables");
/*
@@ -7709,15 +7933,16 @@ copy_data_between_tables(TABLE *from,TABLE *to,
This needs to be done before external_lock
*/
- error= ha_enable_transaction(thd, FALSE);
- if (error)
- DBUG_RETURN(-1);
-
+ if (ha_enable_transaction(thd, FALSE))
+ goto err;
+ errpos=1;
+
if (!(copy= new Copy_field[to->s->fields]))
- DBUG_RETURN(-1); /* purecov: inspected */
+ goto err; /* purecov: inspected */
if (to->file->ha_external_lock(thd, F_WRLCK))
- DBUG_RETURN(-1);
+ goto err;
+ errpos= 2;
/* We need external lock before we can disable/enable keys */
alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff);
@@ -7729,11 +7954,8 @@ copy_data_between_tables(TABLE *from,TABLE *to,
from->file->info(HA_STATUS_VARIABLE);
to->file->ha_start_bulk_insert(from->file->stats.records);
+ errpos= 3;
- save_sql_mode= thd->variables.sql_mode;
-
- List_iterator<Create_field> it(create);
- Create_field *def;
copy_end=copy;
for (Field **ptr=to->field ; *ptr ; ptr++)
{
@@ -7757,11 +7979,10 @@ copy_data_between_tables(TABLE *from,TABLE *to,
}
- found_count=delete_count=0;
-
if (order)
{
- if (to->s->primary_key != MAX_KEY && to->file->primary_key_is_clustered())
+ if (to->s->primary_key != MAX_KEY &&
+ to->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
my_snprintf(warn_buff, sizeof(warn_buff),
@@ -7778,7 +7999,6 @@ copy_data_between_tables(TABLE *from,TABLE *to,
tables.table= from;
tables.alias= tables.table_name= from->s->table_name.str;
tables.db= from->s->db.str;
- error= 1;
if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
setup_order(thd, thd->lex->select_lex.ref_pointer_array,
@@ -7794,7 +8014,10 @@ copy_data_between_tables(TABLE *from,TABLE *to,
/* Tell handler that we have values for all columns in the to table */
to->use_all_columns();
- init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE);
+ to->mark_virtual_columns_for_write(TRUE);
+ if (init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE))
+ goto err;
+ errpos= 4;
if (ignore)
to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
thd->row_count= 0;
@@ -7807,6 +8030,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
error= 1;
break;
}
+ update_virtual_fields(thd, from);
thd->row_count++;
/* Return error if source table isn't empty. */
if (error_if_not_empty)
@@ -7827,6 +8051,12 @@ copy_data_between_tables(TABLE *from,TABLE *to,
copy_ptr->do_copy(copy_ptr);
}
prev_insert_id= to->file->next_insert_id;
+ update_virtual_fields(thd, to, TRUE);
+ if (thd->is_error())
+ {
+ error= 1;
+ break;
+ }
error=to->file->ha_write_row(to->record[0]);
to->auto_increment_field_not_null= FALSE;
if (error)
@@ -7858,22 +8088,24 @@ copy_data_between_tables(TABLE *from,TABLE *to,
else
found_count++;
}
- end_read_record(&info);
+
+err:
+ if (errpos >= 4)
+ end_read_record(&info);
free_io_cache(from);
- delete [] copy; // This is never 0
+ delete [] copy;
- if (to->file->ha_end_bulk_insert() && error <= 0)
+ if (error > 0)
+ to->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
+ if (errpos >= 3 && to->file->ha_end_bulk_insert() && error <= 0)
{
to->file->print_error(my_errno,MYF(0));
- error=1;
+ error= 1;
}
to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
- if (ha_enable_transaction(thd, TRUE))
- {
+ if (errpos >= 1 && ha_enable_transaction(thd, TRUE))
error= 1;
- goto err;
- }
/*
Ensure that the new table is saved properly to disk so that we
@@ -7884,19 +8116,43 @@ copy_data_between_tables(TABLE *from,TABLE *to,
if (end_active_trans(thd))
error=1;
- err:
thd->variables.sql_mode= save_sql_mode;
thd->abort_on_warning= 0;
- free_io_cache(from);
*copied= found_count;
*deleted=delete_count;
to->file->ha_release_auto_increment();
- if (to->file->ha_external_lock(thd,F_UNLCK))
+ if (errpos >= 2 && to->file->ha_external_lock(thd,F_UNLCK))
error=1;
+ if (error < 0 && to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME))
+ error= 1;
DBUG_RETURN(error > 0 ? -1 : 0);
}
+/* Prepare, run and cleanup for mysql_recreate_table() */
+
+static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list)
+{
+ bool result_code;
+ DBUG_ENTER("admin_recreate_table");
+
+ ha_autocommit_or_rollback(thd, 1);
+ close_thread_tables(thd);
+ tmp_disable_binlog(thd); // binlogging is done by caller if wanted
+ result_code= mysql_recreate_table(thd, table_list);
+ reenable_binlog(thd);
+ /*
+ mysql_recreate_table() can push OK or ERROR.
+ Clear 'OK' status. If there is an error, keep it:
+ we will store the error message in a result set row
+ and then clear.
+ */
+ if (thd->main_da.is_ok())
+ thd->main_da.reset_diagnostics_area();
+ DBUG_RETURN(result_code);
+}
+
+
/*
Recreates tables by calling mysql_alter_table().
@@ -7928,7 +8184,7 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
table_list, &alter_info, 0,
- (ORDER *) 0, 0));
+ (ORDER *) 0, 0, 0));
}
@@ -7953,7 +8209,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
/* Open one table after the other to keep lock time as short as possible. */
for (table= tables; table; table= table->next_local)
{
- char table_name[NAME_LEN*2+2];
+ char table_name[SAFE_NAME_LEN*2+2];
TABLE *t;
strxmov(table_name, table->db ,".", table->table_name, NullS);
@@ -7972,11 +8228,14 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
}
else
{
- if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
- !(check_opt->flags & T_EXTEND))
+ /* Call ->checksum() if the table checksum matches 'old_mode' settings */
+ if (!(check_opt->flags & T_EXTEND) &&
+ (((t->file->ha_table_flags() & HA_HAS_OLD_CHECKSUM) &&
+ thd->variables.old_mode) ||
+ ((t->file->ha_table_flags() & HA_HAS_NEW_CHECKSUM) &&
+ !thd->variables.old_mode)))
protocol->store((ulonglong)t->file->checksum());
- else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
- (check_opt->flags & T_QUICK))
+ else if (check_opt->flags & T_QUICK)
protocol->store_null();
else
{
@@ -8003,7 +8262,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
goto err;
}
ha_checksum row_crc= 0;
- int error= t->file->rnd_next(t->record[0]);
+ int error= t->file->ha_rnd_next(t->record[0]);
if (unlikely(error))
{
if (error == HA_ERR_RECORD_DELETED)
@@ -8024,6 +8283,9 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
{
Field *f= t->field[i];
+ if (! thd->variables.old_mode &&
+ f->is_real_null(0))
+ continue;
/*
BLOB and VARCHAR have pointers in their field, we must convert
to string; GEOMETRY is implemented on top of BLOB.
@@ -8096,7 +8358,7 @@ static bool check_engine(THD *thd, const char *table_name,
if (create_info->used_fields & HA_CREATE_USED_ENGINE)
{
my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
- ha_resolve_storage_engine_name(*new_engine), "TEMPORARY");
+ hton_name(*new_engine)->str, "TEMPORARY");
*new_engine= 0;
return TRUE;
}