Commit f1e25513 authored by unknown's avatar unknown

Faster alter table code for 5.1.


mysql-test/r/alter_table.result:
  Added some additional tests for new alter table code.
mysql-test/t/alter_table.test:
  Added some additional tests for new alter table code.
sql/field.cc:
  Functions to check whether new field is equal with old field.
  Classes for different types.
sql/field.h:
  Functions to check whether new field is equal with old field.
  Classes for different types.
sql/ha_berkeley.cc:
  check_if_incompatible_data() for BDB.
sql/ha_berkeley.h:
  check_if_incompatible_data() for BDB.
sql/ha_heap.cc:
  check_if_incompatible_data() for HEAP.
sql/ha_heap.h:
  check_if_incompatible_data() for HEAP.
sql/ha_innodb.cc:
  check_if_incompatible_data() for InnoDB.
sql/ha_innodb.h:
  check_if_incompatible_data() for InnoBD.
sql/ha_myisam.cc:
  check_if_incompatible_data() for MyISAM.
sql/ha_myisam.h:
  check_if_incompatible_data() for MyISAM.
sql/ha_myisammrg.cc:
  check_if_incompatible_data() for Merge tables.
sql/ha_myisammrg.h:
  check_if_incompatible_data() for Merge tables.
sql/ha_ndbcluster.cc:
  check_if_incompatible_data() for NDB.
sql/ha_ndbcluster.h:
  check_if_incompatible_data() for NDB.
sql/handler.h:
  Defines for COMPATIBLE_DATA (yes and no) and the default function
  for check_if_incompatible_data().
sql/mysql_priv.h:
  Defines for IS_EQUAL_*
sql/mysqld.cc:
  Added option --old-alter-table to disable new alter table code.
sql/set_var.cc:
  Added option --old-alter-table to disable new alter table code.
sql/set_var.h:
  Added option --old-alter-table to disable new alter table code.
sql/sql_class.h:
  Added option --old-alter-table to disable new alter table code.
sql/sql_lex.h:
  Added a flag for forcing recreation of a table
  (needed for optimize table mapped to alter table)
sql/sql_table.cc:
  Made a function of setting table default charset, used
  now in two places.
  
  Added defines for ALTER_TABLE_* possible changes.
  Currently just overall data and index.
  
  Added function compare_tables, which checks fields
  compatibility in old and new tables.
BitKeeper/etc/config:
  Disabled logging
parent 501f7307
......@@ -24,7 +24,7 @@ description: MySQL - fast and reliable SQL database
# repository is commercial it can be an internal email address or "none"
# to disable logging.
#
logging: logging@openlogging.org
logging: none
#
# If this field is set, all checkins will appear to be made by this user,
# in effect making this a single user package. Single user packages are
......@@ -73,3 +73,8 @@ hours:
[nick:]checkout:get
checkout:edit
eoln:unix
license: BKL5433d4e6925a06a150001200fff9b
licsign1: YgAAAo0AAAADgAAAAEYUtZil1XCmH6z+LTlQMDJ+1ZeBLIgtHo1azUxQ8/8G1JuW
licsign2: fxW3y9raSlpYVAleJSaBDKYiVtEuSdaUN2ILLo6Wc8TJmLl0aprUy7Lh/m/Sq/YC
licsign3: 0H7qah3bdItuw7NGNSLfBzigbKOF6kPbU84VlAUhOqLR2e5Zf32SBZhtCYGA
......@@ -537,3 +537,57 @@ create table t1 ( a timestamp );
alter table t1 add unique ( a(1) );
ERROR HY000: Incorrect sub part key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique sub keys
drop table t1;
create table t1 (v varchar(32));
insert into t1 values ('def'),('abc'),('hij'),('3r4f');
select * from t1;
v
def
abc
hij
3r4f
alter table t1 change v v2 varchar(32);
select * from t1;
v2
def
abc
hij
3r4f
alter table t1 change v2 v varchar(64);
select * from t1;
v
def
abc
hij
3r4f
update t1 set v = 'lmn' where v = 'hij';
select * from t1;
v
def
abc
lmn
3r4f
alter table t1 add i int auto_increment not null primary key first;
select * from t1;
i v
1 def
2 abc
3 lmn
4 3r4f
update t1 set i=5 where i=3;
select * from t1;
i v
1 def
2 abc
5 lmn
4 3r4f
alter table t1 change i i bigint;
select * from t1;
i v
1 def
2 abc
5 lmn
4 3r4f
alter table t1 add unique key (i, v);
select * from t1 where i between 2 and 4 and v in ('def','3r4f','lmn');
i v
4 3r4f
......@@ -360,3 +360,30 @@ create table t1 ( a timestamp );
--error 1089
alter table t1 add unique ( a(1) );
drop table t1;
#
# Some additional tests for new, faster alter table.
# Note that most of the whole alter table code is being
# tested all around the test suite already.
#
create table t1 (v varchar(32));
insert into t1 values ('def'),('abc'),('hij'),('3r4f');
select * from t1;
# Fast alter, no copy performed
alter table t1 change v v2 varchar(32);
select * from t1;
# Fast alter, no copy performed
alter table t1 change v2 v varchar(64);
select * from t1;
update t1 set v = 'lmn' where v = 'hij';
select * from t1;
# Regular alter table
alter table t1 add i int auto_increment not null primary key first;
select * from t1;
update t1 set i=5 where i=3;
select * from t1;
alter table t1 change i i bigint;
select * from t1;
alter table t1 add unique key (i, v);
select * from t1 where i between 2 and 4 and v in ('def','3r4f','lmn');
......@@ -65,6 +65,7 @@ inline int field_type2index (enum_field_types field_type)
((int)FIELDTYPE_TEAR_FROM) + (field_type - FIELDTYPE_TEAR_TO) - 1);
}
static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
{
/* MYSQL_TYPE_DECIMAL -> */
......@@ -5920,6 +5921,26 @@ int Field_str::store(double nr)
}
uint Field::is_equal(create_field *new_field)
{
return (new_field->sql_type == type());
}
uint Field_str::is_equal(create_field *new_field)
{
if (((new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) &&
!(flags & (BINCMP_FLAG | BINARY_FLAG))) ||
(!(new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) &&
(flags & (BINCMP_FLAG | BINARY_FLAG))))
return 0; /* One of the fields is binary and the other one isn't */
return ((new_field->sql_type == type()) &&
new_field->charset == field_charset &&
new_field->length == max_length());
}
int Field_string::store(longlong nr)
{
char buff[64];
......@@ -6676,6 +6697,22 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root,
}
uint Field_varstring::is_equal(create_field *new_field)
{
if (new_field->sql_type == type() &&
new_field->charset == field_charset)
{
if (new_field->length == max_length())
return IS_EQUAL_YES;
if (new_field->length > max_length() &&
((new_field->length <= 255 && max_length() <= 255) ||
(new_field->length > 255 && max_length() > 255)))
return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length
}
return IS_EQUAL_NO;
}
/****************************************************************************
** blob type
** A blob is saved as a length and a pointer. The length is stored in the
......@@ -7777,6 +7814,17 @@ bool Field_num::eq_def(Field *field)
}
uint Field_num::is_equal(create_field *new_field)
{
return ((new_field->sql_type == type()) &&
((new_field->flags & UNSIGNED_FLAG) == (uint) (flags &
UNSIGNED_FLAG)) &&
((new_field->flags & AUTO_INCREMENT_FLAG) ==
(uint) (flags & AUTO_INCREMENT_FLAG)) &&
(new_field->length >= max_length()));
}
/*
Bit field.
......
......@@ -28,6 +28,7 @@
class Send_field;
class Protocol;
class create_field;
struct st_cache_field;
void field_conv(Field *to,Field *from);
......@@ -305,6 +306,8 @@ class Field
int warn_if_overflow(int op_result);
/* maximum possible display length */
virtual uint32 max_length()= 0;
virtual uint is_equal(create_field *new_field);
/* convert decimal to longlong with overflow check */
longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag,
int *err);
......@@ -345,6 +348,7 @@ class Field_num :public Field {
bool eq_def(Field *field);
int store_decimal(const my_decimal *);
my_decimal *val_decimal(my_decimal *);
uint is_equal(create_field *new_field);
};
......@@ -369,6 +373,7 @@ class Field_str :public Field {
uint32 max_length() { return field_length; }
friend class create_field;
my_decimal *val_decimal(my_decimal *);
uint is_equal(create_field *new_field);
};
......@@ -1082,6 +1087,7 @@ class Field_varstring :public Field_longstr {
Field *new_key_field(MEM_ROOT *root, struct st_table *new_table,
char *new_ptr, uchar *new_null_ptr,
uint new_null_bit);
uint is_equal(create_field *new_field);
};
......
......@@ -2638,4 +2638,14 @@ int ha_berkeley::cmp_ref(const byte *ref1, const byte *ref2)
return 0;
}
bool ha_berkeley::check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes)
{
if (table_changes < IS_EQUAL_YES)
return COMPATIBLE_DATA_NO;
return COMPATIBLE_DATA_YES;
}
#endif /* HAVE_BERKELEY_DB */
......@@ -157,6 +157,7 @@ class ha_berkeley: public handler
uint8 table_cache_type() { return HA_CACHE_TBL_TRANSACT; }
bool primary_key_is_clustered() { return true; }
int cmp_ref(const byte *ref1, const byte *ref2);
bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
};
extern bool berkeley_shared_data;
......
......@@ -582,3 +582,15 @@ ulonglong ha_heap::get_auto_increment()
ha_heap::info(HA_STATUS_AUTO);
return auto_increment_value;
}
bool ha_heap::check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes)
{
/* Check that auto_increment value was not changed */
if ((table_changes != IS_EQUAL_YES &&
info->used_fields & HA_CREATE_USED_AUTO) &&
info->auto_increment_value != 0)
return COMPATIBLE_DATA_NO;
return COMPATIBLE_DATA_YES;
}
......@@ -103,6 +103,7 @@ class ha_heap: public handler
HEAP_PTR ptr2=*(HEAP_PTR*)ref2;
return ptr1 < ptr2? -1 : (ptr1 > ptr2? 1 : 0);
}
bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
private:
void update_key_stats();
};
......@@ -7038,4 +7038,24 @@ innobase_rollback_by_xid(
}
}
bool ha_innobase::check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes)
{
if (table_changes != IS_EQUAL_YES)
return COMPATIBLE_DATA_NO;
/* Check that auto_increment value was not changed */
if ((info->used_fields & HA_CREATE_USED_AUTO) &&
info->auto_increment_value != 0)
return COMPATIBLE_DATA_NO;
/* Check that row format didn't change */
if ((info->used_fields & HA_CREATE_USED_AUTO) &&
get_row_type() != info->row_type)
return COMPATIBLE_DATA_NO;
return COMPATIBLE_DATA_YES;
}
#endif /* HAVE_INNOBASE_DB */
......@@ -214,6 +214,8 @@ class ha_innobase: public handler
static ulonglong get_mysql_bin_log_pos();
bool primary_key_is_clustered() { return true; }
int cmp_ref(const byte *ref1, const byte *ref2);
bool ha_innobase::check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes);
};
extern struct show_var_st innodb_status_variables[];
......
......@@ -1660,3 +1660,25 @@ uint ha_myisam::checksum() const
return (uint)file->s->state.checksum;
}
bool ha_myisam::check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes)
{
uint options= table->s->db_options_in_use;
if (info->auto_increment_value != auto_increment_value ||
info->raid_type != raid_type ||
info->raid_chunks != raid_chunks ||
info->raid_chunksize != raid_chunksize ||
info->data_file_name != data_file_name ||
info->index_file_name != index_file_name ||
table_changes == IS_EQUAL_NO)
return COMPATIBLE_DATA_NO;
if ((options & (HA_OPTION_PACK_RECORD | HA_OPTION_CHECKSUM |
HA_OPTION_DELAY_KEY_WRITE)) !=
(info->table_options & (HA_OPTION_PACK_RECORD | HA_OPTION_CHECKSUM |
HA_OPTION_DELAY_KEY_WRITE)))
return COMPATIBLE_DATA_NO;
return COMPATIBLE_DATA_YES;
}
......@@ -129,6 +129,7 @@ class ha_myisam: public handler
int backup(THD* thd, HA_CHECK_OPT* check_opt);
int assign_to_keycache(THD* thd, HA_CHECK_OPT* check_opt);
int preload_keys(THD* thd, HA_CHECK_OPT* check_opt);
bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
#ifdef HAVE_REPLICATION
int dump(THD* thd, int fd);
int net_read_dump(NET* net);
......
......@@ -488,3 +488,14 @@ void ha_myisammrg::append_create_info(String *packet)
}
packet->append(')');
}
bool ha_myisammrg::check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes)
{
/*
For myisammrg, we should always re-generate the mapping file as this
is trivial to do
*/
return COMPATIBLE_DATA_NO;
}
......@@ -82,4 +82,5 @@ class ha_myisammrg: public handler
void update_create_info(HA_CREATE_INFO *create_info);
void append_create_info(String *packet);
MYRG_INFO *myrg_info() { return file; }
bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
};
......@@ -7233,4 +7233,26 @@ ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack,
DBUG_RETURN(0);
}
bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes)
{
return COMPATIBLE_DATA_NO;
if (table_changes != IS_EQUAL_YES)
return COMPATIBLE_DATA_NO;
/* Check that auto_increment value was not changed */
if ((info->used_fields & HA_CREATE_USED_AUTO) &&
info->auto_increment_value != 0)
return COMPATIBLE_DATA_NO;
/* Check that row format didn't change */
if ((info->used_fields & HA_CREATE_USED_AUTO) &&
get_row_type() != info->row_type)
return COMPATIBLE_DATA_NO;
return COMPATIBLE_DATA_YES;
}
#endif /* HAVE_NDBCLUSTER_DB */
......@@ -534,6 +534,10 @@ static void set_tabname(const char *pathname, char *tabname);
uint key_length,
qc_engine_callback *engine_callback,
ulonglong *engine_data);
bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes);
private:
int alter_table_name(const char *to);
int drop_table();
......
......@@ -218,6 +218,9 @@ typedef ulonglong my_xid; // this line is the same as in log_event.h
#define MAXGTRIDSIZE 64
#define MAXBQUALSIZE 64
#define COMPATIBLE_DATA_YES 0
#define COMPATIBLE_DATA_NO 1
struct xid_t {
long formatID;
long gtrid_length;
......@@ -1005,6 +1008,9 @@ class handler :public Sql_alloc
Pops the top if condition stack, if stack is not empty
*/
virtual void cond_pop() { return; };
virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
uint table_changes)
{ return COMPATIBLE_DATA_NO; }
};
/* Some extern variables used with handlers */
......
......@@ -391,6 +391,13 @@ void debug_sync_point(const char* lock_name, uint lock_timeout);
#define STRING_BUFFER_USUAL_SIZE 80
/*
Some defines for exit codes for ::is_equal class functions.
*/
#define IS_EQUAL_NO 0
#define IS_EQUAL_YES 1
#define IS_EQUAL_PACK_LENGTH 2
enum enum_parsing_place
{
NO_MATTER,
......
......@@ -4317,6 +4317,7 @@ enum options_mysqld
OPT_ENABLE_SHARED_MEMORY,
OPT_SHARED_MEMORY_BASE_NAME,
OPT_OLD_PASSWORDS,
OPT_OLD_ALTER_TABLE,
OPT_EXPIRE_LOGS_DAYS,
OPT_GROUP_CONCAT_MAX_LEN,
OPT_DEFAULT_COLLATION,
......@@ -4836,6 +4837,11 @@ Disable with --skip-ndbcluster (will save memory).",
(gptr*) &opt_no_mix_types, (gptr*) &opt_no_mix_types, 0, GET_BOOL, NO_ARG,
0, 0, 0, 0, 0, 0},
#endif
{"old-alter-table", OPT_OLD_ALTER_TABLE,
"Use old, non-optimized alter table.",
(gptr*) &global_system_variables.old_alter_table,
(gptr*) &max_system_variables.old_alter_table, 0, GET_BOOL, NO_ARG,
0, 0, 0, 0, 0, 0},
{"old-passwords", OPT_OLD_PASSWORDS, "Use old password encryption method (needed for 4.0 and older clients).",
(gptr*) &global_system_variables.old_passwords,
(gptr*) &max_system_variables.old_passwords, 0, GET_BOOL, NO_ARG,
......@@ -6016,6 +6022,7 @@ static void mysql_init_variables(void)
global_system_variables.max_join_size= (ulonglong) HA_POS_ERROR;
max_system_variables.max_join_size= (ulonglong) HA_POS_ERROR;
global_system_variables.old_passwords= 0;
global_system_variables.old_alter_table= 0;
/* Variables that depends on compile options */
#ifndef DBUG_OFF
......
......@@ -284,6 +284,8 @@ sys_var_thd_ulong sys_net_retry_count("net_retry_count",
&SV::net_retry_count,
0, fix_net_retry_count);
sys_var_thd_bool sys_new_mode("new", &SV::new_mode);
sys_var_thd_bool sys_old_alter_table("old_alter_table",
&SV::old_alter_table);
sys_var_thd_bool sys_old_passwords("old_passwords", &SV::old_passwords);
sys_var_thd_ulong sys_optimizer_prune_level("optimizer_prune_level",
&SV::optimizer_prune_level);
......@@ -630,6 +632,7 @@ sys_var *sys_variables[]=
&sys_net_wait_timeout,
&sys_net_write_timeout,
&sys_new_mode,
&sys_old_alter_table,
&sys_old_passwords,
&sys_optimizer_prune_level,
&sys_optimizer_search_depth,
......@@ -903,6 +906,7 @@ struct show_var_st init_vars[]= {
{sys_net_retry_count.name, (char*) &sys_net_retry_count, SHOW_SYS},
{sys_net_write_timeout.name,(char*) &sys_net_write_timeout, SHOW_SYS},
{sys_new_mode.name, (char*) &sys_new_mode, SHOW_SYS},
{sys_old_alter_table.name, (char*) &sys_old_alter_table, SHOW_SYS},
{sys_old_passwords.name, (char*) &sys_old_passwords, SHOW_SYS},
{"open_files_limit", (char*) &open_files_limit, SHOW_LONG},
{sys_optimizer_prune_level.name, (char*) &sys_optimizer_prune_level,
......
......@@ -882,6 +882,7 @@ class NAMED_LIST :public ilink
/* updated in sql_acl.cc */
extern sys_var_thd_bool sys_old_alter_table;
extern sys_var_thd_bool sys_old_passwords;
extern LEX_STRING default_key_cache_base;
......
......@@ -564,6 +564,7 @@ struct system_variables
my_bool ndb_use_exact_count;
my_bool ndb_use_transactions;
#endif /* HAVE_NDBCLUSTER_DB */
my_bool old_alter_table;
my_bool old_passwords;
/* Only charset part of these variables is sensible */
......
......@@ -645,6 +645,7 @@ typedef class st_select_lex SELECT_LEX;
#define ALTER_KEYS_ONOFF 512
#define ALTER_CONVERT 1024
#define ALTER_FORCE 2048
#define ALTER_RECREATE 4096
typedef struct st_alter_info
{
......
......@@ -1348,6 +1348,34 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
}
/*
Set table default charset, if not set
SYNOPSIS
set_table_default_charset()
create_info Table create information
DESCRIPTION
If the table character set was not given explicitely,
let's fetch the database default character set and
apply it to the table.
*/
static void set_table_default_charset(THD *thd,
HA_CREATE_INFO *create_info, char *db)
{
if (!create_info->default_table_charset)
{
HA_CREATE_INFO db_info;
char path[FN_REFLEN];
/* Abuse build_table_path() to build the path to the db.opt file */
build_table_path(path, sizeof(path), db, MY_DB_OPT_FILE, "");
load_db_opt(thd, path, &db_info);
create_info->default_table_charset= db_info.default_table_charset;
}
}
/*
Extend long VARCHAR fields to blob & prepare field if it's a blob
......@@ -1532,20 +1560,7 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name,
}
#endif
/*
If the table character set was not given explicitely,
let's fetch the database default character set and
apply it to the table.
*/
if (!create_info->default_table_charset)
{
HA_CREATE_INFO db_info;
char path[FN_REFLEN];
/* Abuse build_table_path() to build the path to the db.opt file */
build_table_path(path, sizeof(path), db, MY_DB_OPT_FILE, "");
load_db_opt(thd, path, &db_info);
create_info->default_table_charset= db_info.default_table_charset;
}
set_table_default_charset(thd, create_info, (char*) db);
if (mysql_prepare_table(thd, create_info, &fields,
&keys, internal_tmp_table, &db_options, file,
......@@ -3029,6 +3044,166 @@ int mysql_drop_indexes(THD *thd, TABLE_LIST *table_list,
#endif /* NOT_USED */
#define ALTER_TABLE_DATA_CHANGED 1
#define ALTER_TABLE_INDEX_CHANGED 2
/*
SYNOPSIS
compare tables()
table original table
create_list fields in new table
key_list keys in new table
create_info create options in new table
DESCRIPTION
'table' (first argument) contains information of the original
table, which includes all corresponding parts that the new
table has in arguments create_list, key_list and create_info.
By comparing the changes between the original and new table
we can determine how much it has changed after ALTER TABLE
and whether we need to make a copy of the table, or just change
the .frm file.
RETURN VALUES
0 No copy needed
1 Data changes, copy needed
2 Index changes, copy needed
*/
uint compare_tables(TABLE *table, List<create_field> *create_list,
List<Key> *key_list, HA_CREATE_INFO *create_info,
ALTER_INFO *alter_info, uint order_num)
{
Field **f_ptr, *field;
uint changes= 0, tmp;
List_iterator_fast<create_field> new_field_it(*create_list);
create_field *new_field;
/*
Some very basic checks. If number of fields changes, or the
handler, we need to run full ALTER TABLE. In the future
new fields can be added and old dropped without copy, but
not yet.
Test also that engine was not given during ALTER TABLE, or
we are force to run regular alter table (copy).
E.g. ALTER TABLE tbl_name ENGINE=MyISAM.
For the following ones we also want to run regular alter table:
ALTER TABLE tbl_name ORDER BY ..
ALTER TABLE tbl_name CONVERT TO CHARACTER SET ..
At the moment we can't handle altering temporary tables without a copy.
We also test if OPTIMIZE TABLE was given and was mapped to alter table.
In that case we always do full copy.
*/
if (table->s->fields != create_list->elements ||
table->s->db_type != create_info->db_type ||
table->s->tmp_table ||
create_info->used_fields & HA_CREATE_USED_ENGINE ||
create_info->used_fields & HA_CREATE_USED_CHARSET ||
create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET ||
(alter_info->flags & ALTER_RECREATE) ||
order_num)
return ALTER_TABLE_DATA_CHANGED;
/*
Go through fields and check if the original ones are compatible
with new table.
*/
for (f_ptr= table->field, new_field= new_field_it++;
(field= *f_ptr); f_ptr++, new_field= new_field_it++)
{
/* Make sure we have at least the default charset in use. */
if (!new_field->charset)
new_field->charset= create_info->default_table_charset;
/* Check that NULL behavior is same for old and new fields */
if ((new_field->flags & NOT_NULL_FLAG) !=
(uint) (field->flags & NOT_NULL_FLAG))
return ALTER_TABLE_DATA_CHANGED;
/* Don't pack rows in old tables if the user has requested this. */
if (create_info->row_type == ROW_TYPE_DYNAMIC ||
(new_field->flags & BLOB_FLAG) ||
new_field->sql_type == MYSQL_TYPE_VARCHAR &&
create_info->row_type != ROW_TYPE_FIXED)
create_info->table_options|= HA_OPTION_PACK_RECORD;
/* Evaluate changes bitmap and send to check_if_incompatible_data() */
if (!(tmp= field->is_equal(new_field)))
return ALTER_TABLE_DATA_CHANGED;
changes|= tmp;
}
/* Check if changes are compatible with current handler without a copy */
if (table->file->check_if_incompatible_data(create_info, changes))
return ALTER_TABLE_DATA_CHANGED;
/*
Go through keys and check if the original ones are compatible
with new table.
*/
KEY *table_key_info= table->key_info;
List_iterator_fast<Key> key_it(*key_list);
Key *key= key_it++;
/* Check if the number of key elements has changed */
if (table->s->keys != key_list->elements)
return ALTER_TABLE_INDEX_CHANGED;
for (uint i= 0; i < table->s->keys; i++, table_key_info++, key= key_it++)
{
/*
Check that the key types are compatible between old and new tables.
*/
if (table_key_info->algorithm != key->algorithm ||
((key->type == Key::PRIMARY || key->type == Key::UNIQUE) &&
!(table_key_info->flags & HA_NOSAME)) ||
(!(key->type == Key::PRIMARY || key->type == Key::UNIQUE) &&
(table_key_info->flags & HA_NOSAME)) ||
((key->type == Key::SPATIAL) &&
!(table_key_info->flags & HA_SPATIAL)) ||
(!(key->type == Key::SPATIAL) &&
(table_key_info->flags & HA_SPATIAL)) ||
((key->type == Key::FULLTEXT) &&
!(table_key_info->flags & HA_FULLTEXT)) ||
(!(key->type == Key::FULLTEXT) &&
(table_key_info->flags & HA_FULLTEXT)))
return ALTER_TABLE_INDEX_CHANGED;
if (table_key_info->key_parts != key->columns.elements)
return ALTER_TABLE_INDEX_CHANGED;
/*
Check that the key parts remain compatible between the old and
new tables.
*/
KEY_PART_INFO *table_key_part= table_key_info->key_part;
List_iterator_fast<key_part_spec> key_part_it(key->columns);
key_part_spec *key_part= key_part_it++;
for (uint j= 0; j < table_key_info->key_parts; j++,
table_key_part++, key_part= key_part_it++)
{
/*
Key definition has changed if we are using a different field or
if the used key length is different
(If key_part->length == 0 it means we are using the whole field)
*/
if (strcmp(key_part->field_name, table_key_part->field->field_name) ||
(key_part->length && key_part->length != table_key_part->length) ||
(key_part->length == 0 && table_key_part->length !=
table_key_part->field->pack_length()))
return ALTER_TABLE_INDEX_CHANGED;
}
}
return 0; // Tables are compatible
}
/*
Alter table
*/
......@@ -3050,7 +3225,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
ulonglong next_insert_id;
uint db_create_options, used_fields;
enum db_type old_db_type,new_db_type;
bool need_copy_table;
uint need_copy_table= 0;
DBUG_ENTER("mysql_alter_table");
thd->proc_info="init";
......@@ -3282,8 +3457,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
def_it.remove();
}
}
else
{ // Use old field value
else // This field was not dropped and not changed, add it to the list
{ // for the new table.
create_list.push_back(def=new create_field(field,field));
alter_it.rewind(); // Change default if ALTER
Alter_column *alter;
......@@ -3495,17 +3670,22 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (table->s->tmp_table)
create_info->options|=HA_LEX_CREATE_TMP_TABLE;
set_table_default_charset(thd, create_info, db);
if (thd->variables.old_alter_table)
need_copy_table= 1;
else
need_copy_table= compare_tables(table, &create_list, &key_list,
create_info, alter_info, order_num);
/*
better have a negative test here, instead of positive, like
alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
so that ALTER TABLE won't break when somebody will add new flag
*/
need_copy_table= (alter_info->flags &
~(ALTER_CHANGE_COLUMN_DEFAULT|ALTER_OPTIONS) ||
(create_info->used_fields &
~(HA_CREATE_USED_COMMENT|HA_CREATE_USED_PASSWORD)) ||
table->s->tmp_table);
create_info->frm_only= !need_copy_table;
if (!need_copy_table)
create_info->frm_only= 1;
/*
Handling of symlinked tables:
......@@ -3811,7 +3991,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
err:
DBUG_RETURN(TRUE);
}
/* mysql_alter_table */
static int
copy_data_between_tables(TABLE *from,TABLE *to,
......@@ -4023,7 +4203,7 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
create_info.row_type=ROW_TYPE_NOT_USED;
create_info.default_table_charset=default_charset_info;
/* Force alter table to recreate table */
lex->alter_info.flags= ALTER_CHANGE_COLUMN;
lex->alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
table_list, lex->create_list,
lex->key_list, 0, (ORDER *) 0,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment