Added ALTER ONLINE TABLE, which will give an error if the change can't be done 'instantly' (without a table copy)

mysql-test/r/alter_table_online.result:
  Test new feature
mysql-test/t/alter_table_online.test:
  Test new feature
sql/handler.cc:
  Added comment
sql/lex.h:
  Added ONLINE keyword
sql/mysql_priv.h:
  Added option to alter table to require online operation
sql/share/errmsg.txt:
  Added error message if ONLINE can't be done
sql/sql_lex.h:
  Added online option
sql/sql_parse.cc:
  Added online option to mysql_alter_table()
sql/sql_table.cc:
  Added test that gives error if table can't be done instantly when requsted to be online.
  Fixed wrong test if table includes a VARCHAR
  Fixed wrong (but unlikely) handling of error conditions in ALTER table
sql/sql_yacc.yy:
  Added ALTER ONLINE TABLE syntax
storage/maria/ha_maria.cc:
  Fixed bug where 'start_bulk_insert' used too small buffer if used with unknown number of rows
This commit is contained in:
Michael Widenius 2011-04-28 19:56:10 +03:00
parent 6da8ac5f71
commit 2721e912ba
11 changed files with 268 additions and 25 deletions

View File

@ -0,0 +1,74 @@
drop table if exists t1,t2,t3;
create table t1 (a int not null primary key, b int, c varchar(80), e enum('a','b'));
insert into t1 (a) values (1),(2),(3);
alter online table t1 modify b int default 5;
alter online table t1 change b new_name int;
alter online table t1 modify e enum('a','b','c');
alter online table t1 comment "new comment";
alter online table t1 rename to t2;
alter online table t2 rename to t1;
drop table t1;
create temporary table t1 (a int not null primary key, b int, c varchar(80), e enum('a','b'));
insert into t1 (a) values (1),(2),(3);
alter online table t1 modify b int default 5;
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 change b new_name int;
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 modify e enum('a','b','c');
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 comment "new comment";
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 rename to t2;
ERROR HY000: Can't execute the given 'ALTER' command as online
drop table t1;
create table t1 (a int not null primary key, b int, c varchar(80), e enum('a','b'));
insert into t1 (a) values (1),(2),(3);
alter online table t1 drop column b, add b int;
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 modify b bigint;
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 modify e enum('c','a','b');
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 modify c varchar(50);
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 modify c varchar(100);
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 add f int;
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 engine=memory;
ERROR HY000: Can't execute the given 'ALTER' command as online
alter table t1 engine=innodb;
alter table t1 add index (b);
alter online table t1 add index c (c);
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 drop index b;
ERROR HY000: Can't execute the given 'ALTER' command as online
drop table t1;
create temporary table t1 (a int not null primary key, b int, c varchar(80), e enum('a','b'));
insert into t1 (a) values (1),(2),(3);
alter online table t1 drop column b, add b int;
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 modify b bigint;
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 modify e enum('c','a','b');
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 modify c varchar(50);
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 modify c varchar(100);
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 add f int;
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 engine=memory;
ERROR HY000: Can't execute the given 'ALTER' command as online
alter table t1 engine=innodb;
alter table t1 add index (b);
alter online table t1 add index c (c);
ERROR HY000: Can't execute the given 'ALTER' command as online
alter online table t1 drop index b;
ERROR HY000: Can't execute the given 'ALTER' command as online
drop table t1;
create table t1 (a int not null primary key, b int, c varchar(80));
create table t2 (a int not null primary key, b int, c varchar(80));
create table t3 (a int not null primary key, b int, c varchar(80)) engine=merge UNION=(t1);
alter online table t3 union=(t1,t2);
drop table t1,t2,t3;

View File

@ -0,0 +1,108 @@
#
# Test of alter online table
#
--source include/have_innodb.inc
--disable_warnings
drop table if exists t1,t2,t3;
--enable_warnings
#
# Test of things that can be done online
#
create table t1 (a int not null primary key, b int, c varchar(80), e enum('a','b'));
insert into t1 (a) values (1),(2),(3);
alter online table t1 modify b int default 5;
alter online table t1 change b new_name int;
alter online table t1 modify e enum('a','b','c');
alter online table t1 comment "new comment";
alter online table t1 rename to t2;
alter online table t2 rename to t1;
drop table t1;
#
# temporary tables always require a copy
#
create temporary table t1 (a int not null primary key, b int, c varchar(80), e enum('a','b'));
insert into t1 (a) values (1),(2),(3);
--error ER_CANT_DO_ONLINE
alter online table t1 modify b int default 5;
--error ER_CANT_DO_ONLINE
alter online table t1 change b new_name int;
--error ER_CANT_DO_ONLINE
alter online table t1 modify e enum('a','b','c');
--error ER_CANT_DO_ONLINE
alter online table t1 comment "new comment";
--error ER_CANT_DO_ONLINE
alter online table t1 rename to t2;
drop table t1;
#
# Test of things that is not possible to do online
#
create table t1 (a int not null primary key, b int, c varchar(80), e enum('a','b'));
insert into t1 (a) values (1),(2),(3);
--error ER_CANT_DO_ONLINE
alter online table t1 drop column b, add b int;
--error ER_CANT_DO_ONLINE
alter online table t1 modify b bigint;
--error ER_CANT_DO_ONLINE
alter online table t1 modify e enum('c','a','b');
--error ER_CANT_DO_ONLINE
alter online table t1 modify c varchar(50);
--error ER_CANT_DO_ONLINE
alter online table t1 modify c varchar(100);
--error ER_CANT_DO_ONLINE
alter online table t1 add f int;
--error ER_CANT_DO_ONLINE
alter online table t1 engine=memory;
alter table t1 engine=innodb;
alter table t1 add index (b);
--error ER_CANT_DO_ONLINE
alter online table t1 add index c (c);
--error ER_CANT_DO_ONLINE
alter online table t1 drop index b;
drop table t1;
create temporary table t1 (a int not null primary key, b int, c varchar(80), e enum('a','b'));
insert into t1 (a) values (1),(2),(3);
--error ER_CANT_DO_ONLINE
alter online table t1 drop column b, add b int;
--error ER_CANT_DO_ONLINE
alter online table t1 modify b bigint;
--error ER_CANT_DO_ONLINE
alter online table t1 modify e enum('c','a','b');
--error ER_CANT_DO_ONLINE
alter online table t1 modify c varchar(50);
--error ER_CANT_DO_ONLINE
alter online table t1 modify c varchar(100);
--error ER_CANT_DO_ONLINE
alter online table t1 add f int;
--error ER_CANT_DO_ONLINE
alter online table t1 engine=memory;
alter table t1 engine=innodb;
alter table t1 add index (b);
--error ER_CANT_DO_ONLINE
alter online table t1 add index c (c);
--error ER_CANT_DO_ONLINE
alter online table t1 drop index b;
drop table t1;
#
# Test merge tables
#
create table t1 (a int not null primary key, b int, c varchar(80));
create table t2 (a int not null primary key, b int, c varchar(80));
create table t3 (a int not null primary key, b int, c varchar(80)) engine=merge UNION=(t1);
alter online table t3 union=(t1,t2);
drop table t1,t2,t3;

View File

@ -3532,6 +3532,9 @@ handler::ha_delete_table(const char *name)
Drop table in the engine: public interface.
@sa handler::drop_table()
The difference between this and delete_table() is that the table is open in
drop_table().
*/
void

View File

@ -378,6 +378,7 @@ static SYMBOL symbols[] = {
{ "ON", SYM(ON)},
{ "ONE", SYM(ONE_SYM)},
{ "ONE_SHOT", SYM(ONE_SHOT_SYM)},
{ "ONLINE", SYM(ONLINE_SYM)},
{ "OPEN", SYM(OPEN_SYM)},
{ "OPTIMIZE", SYM(OPTIMIZE)},
{ "OPTIONS", SYM(OPTIONS_SYM)},

View File

@ -1312,7 +1312,8 @@ 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);
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list);
bool mysql_create_like_table(THD *thd, TABLE_LIST *table,
TABLE_LIST *src_table,

View File

@ -6249,3 +6249,6 @@ ER_UNKNOWN_OPTION
eng "Unknown option '%-.64s'"
ER_BAD_OPTION_VALUE
eng "Incorrect value '%-.64s' for option '%-.64s'"
ER_CANT_DO_ONLINE
eng "Can't execute the given '%s' command as online"

View File

@ -1745,7 +1745,7 @@ typedef struct st_lex : public Query_tables_list
uint8 context_analysis_only;
bool safe_to_cache_query;
bool subqueries, ignore;
bool subqueries, ignore, online;
st_parsing_options parsing_options;
Alter_info alter_info;
/* Prepared statements SQL syntax:*/

View File

@ -2801,7 +2801,7 @@ end_with_restore_list:
res= mysql_alter_table(thd, first_table->db, first_table->table_name,
&create_info, first_table, &alter_info,
0, (ORDER*) 0, 0);
0, (ORDER*) 0, 0, 0);
break;
}
#ifdef HAVE_REPLICATION
@ -2918,7 +2918,7 @@ end_with_restore_list:
&alter_info,
select_lex->order_list.elements,
(ORDER *) select_lex->order_list.first,
lex->ignore);
lex->ignore, lex->online);
break;
}
case SQLCOM_RENAME_TABLE:

View File

@ -5741,8 +5741,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
@ -5882,6 +5882,9 @@ compare_tables(TABLE *table,
DBUG_ASSERT(i < table->s->fields);
create_info->fileds_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;
@ -5916,7 +5919,6 @@ compare_tables(TABLE *table,
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))
@ -5929,8 +5931,6 @@ compare_tables(TABLE *table,
new_field->field_name));
DBUG_RETURN(0);
}
// Clear indexed marker
field->flags&= ~FIELD_IN_ADD_INDEX;
changes|= tmp;
}
@ -6244,7 +6244,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
{
Alter_drop *drop;
if (field->type() == MYSQL_TYPE_STRING)
if (field->type() == MYSQL_TYPE_VARCHAR)
create_info->varchar= TRUE;
/* Check if field should be dropped */
drop_it.rewind();
@ -6570,6 +6570,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 :)
@ -6600,7 +6601,8 @@ 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;
@ -7400,10 +7402,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.
@ -7435,6 +7450,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)
@ -7528,11 +7545,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)
{
@ -7567,12 +7584,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:
@ -7745,6 +7756,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)
{
@ -8098,7 +8119,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));
}

View File

@ -1042,6 +1042,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ON /* SQL-2003-R */
%token ONE_SHOT_SYM
%token ONE_SYM
%token ONLINE_SYM
%token OPEN_SYM /* SQL-2003-R */
%token OPTIMIZE
%token OPTIONS_SYM
@ -5865,7 +5866,7 @@ string_list:
*/
alter:
ALTER opt_ignore TABLE_SYM table_ident
ALTER alter_options TABLE_SYM table_ident
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
@ -6432,6 +6433,25 @@ opt_ignore:
| IGNORE_SYM { Lex->ignore= 1;}
;
alter_options:
{ Lex->ignore= Lex->online= 0;} alter_options_part2
;
alter_options_part2:
/* empty */
| alter_option_list
;
alter_option_list:
alter_option_list alter_option
| alter_option
;
alter_option:
IGNORE_SYM { Lex->ignore= 1;}
| ONLINE_SYM { Lex->online= 1;}
opt_restrict:
/* empty */ { Lex->drop_mode= DROP_DEFAULT; }
| RESTRICT { Lex->drop_mode= DROP_RESTRICT; }
@ -12077,6 +12097,7 @@ keyword_sp:
| OLD_PASSWORD {}
| ONE_SHOT_SYM {}
| ONE_SYM {}
| ONLINE_SYM {}
| PACK_KEYS_SYM {}
| PAGE_SYM {}
| PARTIAL {}

View File

@ -1961,15 +1961,26 @@ void ha_maria::start_bulk_insert(ha_rows rows)
{
DBUG_ENTER("ha_maria::start_bulk_insert");
THD *thd= table->in_use;
ulong size= min(thd->variables.read_buff_size,
(ulong) (table->s->avg_row_length * rows));
MARIA_SHARE *share= file->s;
DBUG_PRINT("info", ("start_bulk_insert: rows %lu size %lu",
(ulong) rows, size));
DBUG_PRINT("info", ("start_bulk_insert: rows %lu", (ulong) rows));
/* don't enable row cache if too few rows */
if (!rows || (rows > MARIA_MIN_ROWS_TO_USE_WRITE_CACHE))
{
size_t size= thd->variables.read_buff_size;
if (rows)
{
if (file->state->records)
{
MARIA_INFO maria_info;
maria_status(file, &maria_info, HA_STATUS_NO_LOCK |HA_STATUS_VARIABLE);
set_if_smaller(size, maria_info.mean_reclength * rows);
}
else if (table->s->avg_row_length)
set_if_smaller(size, (size_t) (table->s->avg_row_length * rows));
}
maria_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size);
}
can_enable_indexes= (maria_is_all_keys_active(share->state.key_map,
share->base.keys));