3171
@param session Thread
3172
@param table The original table.
3173
@param alter_info Alter options, fields and keys for the new
3175
@param create_info Create options for the new table.
3176
@param order_num Number of order list elements.
3177
@param[out] ha_alter_flags Flags that indicate what will be changed
3178
@param[out] ha_alter_info Data structures needed for on-line alter
3179
@param[out] table_changes Information about particular change
3181
First argument 'table' contains information of the original
3182
table, which includes all corresponding parts that the new
3183
table has in arguments create_list, key_list and create_info.
3185
By comparing the changes between the original and new table
3186
we can determine how much it has changed after ALTER Table
3187
and whether we need to make a copy of the table, or just change
3190
Mark any changes detected in the ha_alter_flags.
3192
If there are no data changes, but index changes, 'index_drop_buffer'
3193
and/or 'index_add_buffer' are populated with offsets into
3194
table->key_info or key_info_buffer respectively for the indexes
3195
that need to be dropped and/or (re-)created.
3198
@retval false success
3203
compare_tables(Session *session,
3205
Alter_info *alter_info,
3206
HA_CREATE_INFO *create_info,
3208
HA_ALTER_FLAGS *alter_flags,
3209
HA_ALTER_INFO *ha_alter_info,
3210
uint32_t *table_changes)
3212
Field **f_ptr, *field;
3213
uint32_t table_changes_local= 0;
3214
List_iterator_fast<Create_field> new_field_it(alter_info->create_list);
3215
Create_field *new_field;
3216
KEY_PART_INFO *key_part;
3221
Create a copy of alter_info.
3222
To compare the new and old table definitions, we need to "prepare"
3223
the new definition - transform it from parser output to a format
3224
that describes the final table layout (all column defaults are
3225
initialized, duplicate columns are removed). This is done by
3226
mysql_prepare_create_table. Unfortunately,
3227
mysql_prepare_create_table performs its transformations
3228
"in-place", that is, modifies the argument. Since we would
3229
like to keep compare_tables() idempotent (not altering any
3230
of the arguments) we create a copy of alter_info here and
3231
pass it to mysql_prepare_create_table, then use the result
3232
to evaluate possibility of fast ALTER Table, and then
3235
Alter_info tmp_alter_info(*alter_info, session->mem_root);
3236
session= table->in_use;
3237
uint32_t db_options= 0; /* not used */
3238
/* Create the prepared information. */
3239
if (mysql_prepare_create_table(session, create_info,
3241
(table->s->tmp_table != NO_TMP_TABLE),
3244
&ha_alter_info->key_info_buffer,
3245
&ha_alter_info->key_count,
3246
/* select_field_count */ 0))
3248
/* Allocate result buffers. */
3249
if (! (ha_alter_info->index_drop_buffer=
3250
(uint*) session->alloc(sizeof(uint32_t) * table->s->keys)) ||
3251
! (ha_alter_info->index_add_buffer=
3252
(uint*) session->alloc(sizeof(uint32_t) *
3253
tmp_alter_info.key_list.elements)))
3257
First we setup ha_alter_flags based on what was detected
3260
setup_ha_alter_flags(alter_info, alter_flags);
3264
Some very basic checks. If number of fields changes, or the
3265
handler, we need to run full ALTER Table. In the future
3266
new fields can be added and old dropped without copy, but
3269
Test also that engine was not given during ALTER Table, or
3270
we are force to run regular alter table (copy).
3271
E.g. ALTER Table tbl_name ENGINE=MyISAM.
3273
For the following ones we also want to run regular alter table:
3274
ALTER Table tbl_name order_st BY ..
3275
ALTER Table tbl_name CONVERT TO CHARACTER SET ..
3277
At the moment we can't handle altering temporary tables without a copy.
3278
We also test if OPTIMIZE Table was given and was mapped to alter table.
3279
In that case we always do full copy.
3281
There was a bug prior to mysql-4.0.25. Number of null fields was
3282
calculated incorrectly. As a result frm and data files gets out of
3283
sync after fast alter table. There is no way to determine by which
3284
mysql version (in 4.0 and 4.1 branches) table was created, thus we
3285
disable fast alter table for all tables created by mysql versions
3286
prior to 5.0 branch.
3289
if (table->s->fields != alter_info->create_list.elements ||
3290
table->s->db_type() != create_info->db_type ||
3291
table->s->tmp_table ||
3292
create_info->used_fields & HA_CREATE_USED_ENGINE ||
3293
create_info->used_fields & HA_CREATE_USED_CHARSET ||
3294
create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET ||
3295
create_info->used_fields & HA_CREATE_USED_ROW_FORMAT ||
3296
(alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
3298
!table->s->mysql_version)
3300
*table_changes= IS_EQUAL_NO;
3302
Check what has changed and set alter_flags
3304
if (table->s->fields < alter_info->create_list.elements)
3305
alter_flags->set(HA_ADD_COLUMN);
3306
else if (table->s->fields > alter_info->create_list.elements)
3307
alter_flags->set(HA_DROP_COLUMN);
3308
if (create_info->db_type != table->s->db_type() ||
3309
create_info->used_fields & HA_CREATE_USED_ENGINE)
3310
alter_flags->set(HA_ALTER_STORAGE_ENGINE);
3311
if (create_info->used_fields & HA_CREATE_USED_CHARSET)
3312
alter_flags->set(HA_CHANGE_CHARACTER_SET);
3313
if (create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET)
3314
alter_flags->set(HA_SET_DEFAULT_CHARACTER_SET);
3315
if (alter_info->flags & ALTER_RECREATE)
3316
alter_flags->set(HA_RECREATE);
3317
/* TODO check for ADD/DROP FOREIGN KEY */
3318
if (alter_info->flags & ALTER_FOREIGN_KEY)
3319
alter_flags->set(HA_ALTER_FOREIGN_KEY);
3322
Go through fields and check if the original ones are compatible
3325
for (f_ptr= table->field, new_field= new_field_it++;
3326
(new_field && (field= *f_ptr));
3327
f_ptr++, new_field= new_field_it++)
3329
/* Make sure we have at least the default charset in use. */
3330
if (!new_field->charset)
3331
new_field->charset= create_info->default_table_charset;
3333
/* Don't pack rows in old tables if the user has requested this. */
3334
if (create_info->row_type == ROW_TYPE_DYNAMIC ||
3335
(new_field->flags & BLOB_FLAG) ||
3336
(new_field->sql_type == DRIZZLE_TYPE_VARCHAR && create_info->row_type != ROW_TYPE_FIXED))
3337
create_info->table_options|= HA_OPTION_PACK_RECORD;
3339
/* Check how fields have been modified */
3340
if (alter_info->flags & ALTER_CHANGE_COLUMN)
3342
/* Evaluate changes bitmap and send to check_if_incompatible_data() */
3343
if (!(table_changes_local= field->is_equal(new_field)))
3344
alter_flags->set(HA_ALTER_COLUMN_TYPE);
3346
/* Check if field was renamed */
3347
field->flags&= ~FIELD_IS_RENAMED;
3348
if (my_strcasecmp(system_charset_info,
3350
new_field->field_name))
3352
field->flags|= FIELD_IS_RENAMED;
3353
alter_flags->set(HA_ALTER_COLUMN_NAME);
3356
*table_changes&= table_changes_local;
3357
if (table_changes_local == IS_EQUAL_PACK_LENGTH)
3358
alter_flags->set(HA_ALTER_COLUMN_TYPE);
3360
/* Check that NULL behavior is same for old and new fields */
3361
if ((new_field->flags & NOT_NULL_FLAG) !=
3362
(uint32_t) (field->flags & NOT_NULL_FLAG))
3364
*table_changes= IS_EQUAL_NO;
3365
alter_flags->set(HA_ALTER_COLUMN_NULLABLE);
3369
/* Clear indexed marker */
3370
field->flags&= ~FIELD_IN_ADD_INDEX;
3374
Go through keys and check if the original ones are compatible
3378
KEY *table_key_end= table->key_info + table->s->keys;
3381
ha_alter_info->key_info_buffer + ha_alter_info->key_count;
3384
Step through all keys of the old table and search matching new keys.
3386
ha_alter_info->index_drop_count= 0;
3387
ha_alter_info->index_add_count= 0;
3388
for (table_key= table->key_info; table_key < table_key_end; table_key++)
3390
KEY_PART_INFO *table_part;
3391
KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
3392
KEY_PART_INFO *new_part;
3394
/* Search a new key with the same name. */
3395
for (new_key= ha_alter_info->key_info_buffer;
3396
new_key < new_key_end;
3399
if (! strcmp(table_key->name, new_key->name))
3402
if (new_key >= new_key_end)
3404
/* Key not found. Add the offset of the key to the drop buffer. */
3405
ha_alter_info->index_drop_buffer
3406
[ha_alter_info->index_drop_count++]=
3407
table_key - table->key_info;
3408
if (table_key->flags & HA_NOSAME)
3410
/* Unique key. Check for "PRIMARY". */
3411
if (is_primary_key(table_key))
3412
alter_flags->set(HA_DROP_PK_INDEX);
3414
alter_flags->set(HA_DROP_UNIQUE_INDEX);
3417
alter_flags->set(HA_DROP_INDEX);
3418
*table_changes= IS_EQUAL_NO;
3422
/* Check that the key types are compatible between old and new tables. */
3423
if ((table_key->algorithm != new_key->algorithm) ||
3424
((table_key->flags & HA_KEYFLAG_MASK) !=
3425
(new_key->flags & HA_KEYFLAG_MASK)) ||
3426
(table_key->key_parts != new_key->key_parts))
3428
if (table_key->flags & HA_NOSAME)
3430
// Unique key. Check for "PRIMARY".
3431
if (is_primary_key(table_key))
3432
alter_flags->set(HA_ALTER_PK_INDEX);
3434
alter_flags->set(HA_ALTER_UNIQUE_INDEX);
3437
alter_flags->set(HA_ALTER_INDEX);
3442
Check that the key parts remain compatible between the old and
3445
for (table_part= table_key->key_part, new_part= new_key->key_part;
3446
table_part < table_part_end;
3447
table_part++, new_part++)
3450
Key definition has changed if we are using a different field or
3451
if the used key part length is different. We know that the fields
3452
did not change. Comparing field numbers is sufficient.
3454
if ((table_part->length != new_part->length) ||
3455
(table_part->fieldnr - 1 != new_part->fieldnr))
3457
if (table_key->flags & HA_NOSAME)
3459
/* Unique key. Check for "PRIMARY" */
3460
if (is_primary_key(table_key))
3461
alter_flags->set(HA_ALTER_PK_INDEX);
3463
alter_flags->set(HA_ALTER_UNIQUE_INDEX);
3466
alter_flags->set(HA_ALTER_INDEX);
3473
/* Key modified. Add the offset of the key to both buffers. */
3474
ha_alter_info->index_drop_buffer
3475
[ha_alter_info->index_drop_count++]=
3476
table_key - table->key_info;
3477
ha_alter_info->index_add_buffer
3478
[ha_alter_info->index_add_count++]=
3479
new_key - ha_alter_info->key_info_buffer;
3480
key_part= new_key->key_part;
3481
end= key_part + new_key->key_parts;
3482
for(; key_part != end; key_part++)
3484
/* Mark field to be part of new key */
3485
if ((field= table->field[key_part->fieldnr]))
3486
field->flags|= FIELD_IN_ADD_INDEX;
3488
*table_changes= IS_EQUAL_NO;
3490
/*end of for (; table_key < table_key_end;) */
3493
Step through all keys of the new table and find matching old keys.
3495
for (new_key= ha_alter_info->key_info_buffer;
3496
new_key < new_key_end;
3499
/* Search an old key with the same name. */
3500
for (table_key= table->key_info; table_key < table_key_end; table_key++)
3502
if (! strcmp(table_key->name, new_key->name))
3505
if (table_key >= table_key_end)
3507
/* Key not found. Add the offset of the key to the add buffer. */
3508
ha_alter_info->index_add_buffer
3509
[ha_alter_info->index_add_count++]=
3510
new_key - ha_alter_info->key_info_buffer;
3511
key_part= new_key->key_part;
3512
end= key_part + new_key->key_parts;
3513
for(; key_part != end; key_part++)
3515
/* Mark field to be part of new key */
3516
if ((field= table->field[key_part->fieldnr]))
3517
field->flags|= FIELD_IN_ADD_INDEX;
3519
if (new_key->flags & HA_NOSAME)
3521
/* Unique key. Check for "PRIMARY" */
3522
if (is_primary_key(new_key))
3523
alter_flags->set(HA_ADD_PK_INDEX);
3525
alter_flags->set(HA_ADD_UNIQUE_INDEX);
3528
alter_flags->set(HA_ADD_INDEX);
3529
*table_changes= IS_EQUAL_NO;
3538
3171
Manages enabling/disabling of indexes for ALTER Table
3651
Create a temporary table that reflects what an alter table operation
3655
create_altered_table()
3656
session Thread handle
3657
table The original table
3658
create_info Information from the parsing phase about new
3660
alter_info Lists of fields, keys to be changed, added
3662
db_change Specifies if the table is moved to another database
3664
A temporary table with all changes
3667
The temporary table is created without storing it in any storage engine
3668
and is opened only to get the table struct and frm file reference.
3670
Table *create_altered_table(Session *session,
3673
HA_CREATE_INFO *create_info,
3674
Alter_info *alter_info,
3678
HA_CREATE_INFO altered_create_info(*create_info);
3679
Table *altered_table;
3681
char path[FN_REFLEN];
3683
snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%"PRIx64,
3684
TMP_FILE_PREFIX, (unsigned long)current_pid, session->thread_id);
3685
/* Safety fix for InnoDB */
3686
if (lower_case_table_names)
3687
my_casedn_str(files_charset_info, tmp_name);
3688
altered_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE;
3690
if ((error= create_temporary_table(session, table, new_db, tmp_name,
3691
&altered_create_info,
3692
alter_info, db_change)))
3697
build_table_filename(path, sizeof(path), new_db, tmp_name, "",
3699
altered_table= open_temporary_table(session, path, new_db, tmp_name, 1,
3701
return(altered_table);
3706
Perform a fast or on-line alter table
3709
mysql_fast_or_online_alter_table()
3710
session Thread handle
3711
table The original table
3712
altered_table A temporary table showing how we will change table
3713
create_info Information from the parsing phase about new
3715
alter_info Storage place for data used during different phases
3716
ha_alter_flags Bitmask that shows what will be changed
3717
keys_onoff Specifies if keys are to be enabled/disabled
3720
>0 An error occured during the on-line alter table operation
3721
-1 Error when re-opening table
3723
If mysql_alter_table does not need to copy the table, it is
3724
either a fast alter table where the storage engine does not
3725
need to know about the change, only the frm will change,
3726
or the storage engine supports performing the alter table
3727
operation directly, on-line without mysql having to copy
3730
int mysql_fast_or_online_alter_table(Session *session,
3732
Table *altered_table,
3733
HA_CREATE_INFO *create_info,
3734
HA_ALTER_INFO *alter_info,
3735
HA_ALTER_FLAGS *ha_alter_flags,
3736
enum enum_enable_or_disable keys_onoff)
3739
bool online= (table->file->ha_table_flags() & HA_ONLINE_ALTER)?true:false;
3745
Tell the handler to prepare for the online alter
3747
if ((error= table->file->alter_table_phase1(session,
3757
Tell the storage engine to perform the online alter table
3759
if check_if_supported_alter() returned HA_ALTER_SUPPORTED_WAIT_LOCK
3760
we need to wrap the next call with a DDL lock.
3762
if ((error= table->file->alter_table_phase2(session,
3772
The final .frm file is already created as a temporary file
3773
and will be renamed to the original table name.
3775
pthread_mutex_lock(&LOCK_open);
3776
wait_while_table_is_used(session, table, HA_EXTRA_FORCE_REOPEN);
3777
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
3779
close_data_files_and_morph_locks(session,
3780
table->pos_in_table_list->db,
3781
table->pos_in_table_list->table_name);
3782
if (mysql_rename_table(NULL,
3783
altered_table->s->db.str,
3784
altered_table->s->table_name.str,
3786
table->s->table_name.str, FN_FROM_IS_TMP))
3789
pthread_mutex_unlock(&LOCK_open);
3792
broadcast_refresh();
3793
pthread_mutex_unlock(&LOCK_open);
3796
The ALTER Table is always in its own transaction.
3797
Commit must not be called while LOCK_open is locked. It could call
3798
wait_if_global_read_lock(), which could create a deadlock if called
3801
error= ha_autocommit_or_rollback(session, 0);
3803
if (ha_commit(session))
3809
pthread_mutex_lock(&LOCK_open);
3810
if (reopen_table(table))
3815
pthread_mutex_unlock(&LOCK_open);
3819
Tell the handler that the changed frm is on disk and table
3822
if ((error= t_table->file->alter_table_phase3(session, t_table)))
3828
We are going to reopen table down on the road, so we have to restore
3829
state of the Table object which we used for obtaining of handler
3830
object to make it suitable for reopening.
3832
assert(t_table == table);
3833
table->open_placeholder= 1;
3834
pthread_mutex_lock(&LOCK_open);
3835
close_handle_and_leave_table_as_lock(table);
3836
pthread_mutex_unlock(&LOCK_open);
3847
3285
Prepare column and key definitions for CREATE TABLE in ALTER Table.
4572
4010
set_table_default_charset(session, create_info, db);
4574
if (session->variables.old_alter_table
4575
|| (table->s->db_type() != create_info->db_type)
4578
if (alter_info->build_method == HA_BUILD_ONLINE)
4580
my_error(ER_NOT_SUPPORTED_YET, MYF(0), session->query);
4583
alter_info->build_method= HA_BUILD_OFFLINE;
4586
if (alter_info->build_method != HA_BUILD_OFFLINE)
4588
Table *altered_table= 0;
4589
HA_ALTER_INFO ha_alter_info;
4590
HA_ALTER_FLAGS ha_alter_flags;
4591
uint32_t table_changes= IS_EQUAL_YES;
4592
bool need_copy_table= true;
4593
/* Check how much the tables differ. */
4594
if (compare_tables(session, table, alter_info,
4595
create_info, order_num,
4604
Check if storage engine supports altering the table
4610
If table is not renamed, changed database and
4611
some change was detected then check if engine
4612
can do the change on-line
4614
if (new_name == table_name && new_db == db &&
4615
ha_alter_flags.any())
4617
Alter_info tmp_alter_info(*alter_info, session->mem_root);
4621
check if table can be altered on-line
4623
if (!(altered_table= create_altered_table(session,
4628
!strcmp(db, new_db))))
4631
switch (table->file->check_if_supported_alter(altered_table,
4635
case HA_ALTER_SUPPORTED_WAIT_LOCK:
4636
case HA_ALTER_SUPPORTED_NO_LOCK:
4638
@todo: Currently we always acquire an exclusive name
4639
lock on the table metadata when performing fast or online
4640
ALTER Table. In future we may consider this unnecessary,
4641
and narrow the scope of the exclusive name lock to only
4642
cover manipulation with .frms. Storage engine API
4643
call check_if_supported_alter has provision for this
4646
need_copy_table= false;
4648
case HA_ALTER_NOT_SUPPORTED:
4649
if (alter_info->build_method == HA_BUILD_ONLINE)
4651
my_error(ER_NOT_SUPPORTED_YET, MYF(0), session->query);
4652
close_temporary_table(session, altered_table, 1, 1);
4655
need_copy_table= true;
4657
case HA_ALTER_ERROR:
4659
close_temporary_table(session, altered_table, 1, 1);
4664
/* TODO need to check if changes can be handled as fast ALTER Table */
4666
need_copy_table= true;
4668
if (!need_copy_table)
4670
error= mysql_fast_or_online_alter_table(session,
4676
alter_info->keys_onoff);
4679
mysql_unlock_tables(session, session->lock);
4682
close_temporary_table(session, altered_table, 1, 1);
4688
goto err_with_placeholders;
4695
pthread_mutex_lock(&LOCK_open);
4701
close_temporary_table(session, altered_table, 1, 1);
4012
alter_info->build_method= HA_BUILD_OFFLINE;
4704
4014
snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%"PRIx64, TMP_FILE_PREFIX,
4705
4015
(unsigned long)current_pid, session->thread_id);