diff options
Diffstat (limited to 'storage/xtradb/handler/handler0alter.cc')
-rw-r--r-- | storage/xtradb/handler/handler0alter.cc | 192 |
1 files changed, 157 insertions, 35 deletions
diff --git a/storage/xtradb/handler/handler0alter.cc b/storage/xtradb/handler/handler0alter.cc index 076c4b6c3b6..ea02463010c 100644 --- a/storage/xtradb/handler/handler0alter.cc +++ b/storage/xtradb/handler/handler0alter.cc @@ -167,10 +167,13 @@ my_error_innodb( /* TODO: report the row, as we do for DB_DUPLICATE_KEY */ my_error(ER_INVALID_USE_OF_NULL, MYF(0)); break; + case DB_TABLESPACE_EXISTS: + my_error(ER_TABLESPACE_EXISTS, MYF(0), table); + break; + #ifdef UNIV_DEBUG case DB_SUCCESS: case DB_DUPLICATE_KEY: - case DB_TABLESPACE_EXISTS: case DB_ONLINE_LOG_TOO_BIG: /* These codes should not be passed here. */ ut_error; @@ -202,12 +205,14 @@ innobase_fulltext_exist( /*******************************************************************//** Determine if ALTER TABLE needs to rebuild the table. @param ha_alter_info the DDL operation +@param altered_table MySQL original table @return whether it is necessary to rebuild the table */ static __attribute__((nonnull, warn_unused_result)) bool innobase_need_rebuild( /*==================*/ - const Alter_inplace_info* ha_alter_info) + const Alter_inplace_info* ha_alter_info, + const TABLE* altered_table) { if (ha_alter_info->handler_flags == Alter_inplace_info::CHANGE_CREATE_OPTION @@ -219,6 +224,34 @@ innobase_need_rebuild( return(false); } + /* If alter table changes column name and adds a new + index, we need to check is this new index created + to new column name. This is because column name + changes are done normally after creating indexes. */ + if ((ha_alter_info->handler_flags + & Alter_inplace_info::ALTER_COLUMN_NAME) && + ((ha_alter_info->handler_flags + & Alter_inplace_info::ADD_INDEX) || + (ha_alter_info->handler_flags + & Alter_inplace_info::ADD_FOREIGN_KEY))) { + for (ulint i = 0; i < ha_alter_info->key_count; i++) { + const KEY* key = &ha_alter_info->key_info_buffer[ + ha_alter_info->index_add_buffer[i]]; + + for (ulint j = 0; j < key->user_defined_key_parts; j++) { + const KEY_PART_INFO* key_part = &(key->key_part[j]); + const Field* field = altered_table->field[key_part->fieldnr]; + + /* Field used on added index is renamed on + this same alter table. We need table + rebuild. */ + if (field->flags & FIELD_IS_RENAMED) { + return (true); + } + } + } + } + return(!!(ha_alter_info->handler_flags & INNOBASE_ALTER_REBUILD)); } @@ -557,7 +590,7 @@ ha_innobase::check_if_supported_inplace_alter( operation is possible. */ } else if (((ha_alter_info->handler_flags & Alter_inplace_info::ADD_PK_INDEX) - || innobase_need_rebuild(ha_alter_info)) + || innobase_need_rebuild(ha_alter_info, table)) && (innobase_fulltext_exist(altered_table))) { /* Refuse to rebuild the table online, if fulltext indexes are to survive the rebuild. */ @@ -1558,7 +1591,8 @@ innobase_create_index_def( index_def_t* index, /*!< out: index definition */ mem_heap_t* heap, /*!< in: heap where memory is allocated */ - const Field** fields) /*!z in: MySQL table fields */ + const Field** fields) /*!< in: MySQL table fields + */ { const KEY* key = &keys[key_number]; ulint i; @@ -1854,9 +1888,11 @@ innobase_create_key_defs( bool& add_fts_doc_id, /*!< in: whether we need to add new DOC ID column for FTS index */ - bool& add_fts_doc_idx) + bool& add_fts_doc_idx, /*!< in: whether we need to add new DOC ID index for FTS index */ + const TABLE* table) + /*!< in: MySQL table that is being altered */ { index_def_t* indexdef; index_def_t* indexdefs; @@ -1906,7 +1942,8 @@ innobase_create_key_defs( } const bool rebuild = new_primary || add_fts_doc_id - || innobase_need_rebuild(ha_alter_info); + || innobase_need_rebuild(ha_alter_info, table); + /* Reserve one more space if new_primary is true, and we might need to add the FTS_DOC_ID_INDEX */ indexdef = indexdefs = static_cast<index_def_t*>( @@ -2744,7 +2781,8 @@ prepare_inplace_alter_table_dict( ctx->heap, ha_alter_info, altered_table, ctx->num_to_add_index, num_fts_index, row_table_got_default_clust_index(ctx->new_table), - fts_doc_id_col, add_fts_doc_id, add_fts_doc_id_idx); + fts_doc_id_col, add_fts_doc_id, add_fts_doc_id_idx, + old_table); new_clustered = DICT_CLUSTERED & index_defs[0].ind_type; @@ -2757,7 +2795,7 @@ prepare_inplace_alter_table_dict( /* This is not an online operation (LOCK=NONE). */ } else if (ctx->add_autoinc == ULINT_UNDEFINED && num_fts_index == 0 - && (!innobase_need_rebuild(ha_alter_info) + && (!innobase_need_rebuild(ha_alter_info, old_table) || !innobase_fulltext_exist(altered_table))) { /* InnoDB can perform an online operation (LOCK=NONE). */ } else { @@ -2774,7 +2812,7 @@ prepare_inplace_alter_table_dict( is just copied from old table and stored in indexdefs[0] */ DBUG_ASSERT(!add_fts_doc_id || new_clustered); DBUG_ASSERT(!!new_clustered == - (innobase_need_rebuild(ha_alter_info) + (innobase_need_rebuild(ha_alter_info, old_table) || add_fts_doc_id)); /* Allocate memory for dictionary index definitions */ @@ -3034,7 +3072,7 @@ prepare_inplace_alter_table_dict( add_cols, ctx->heap); ctx->add_cols = add_cols; } else { - DBUG_ASSERT(!innobase_need_rebuild(ha_alter_info)); + DBUG_ASSERT(!innobase_need_rebuild(ha_alter_info, old_table)); if (!ctx->new_table->fts && innobase_fulltext_exist(altered_table)) { @@ -3055,7 +3093,7 @@ prepare_inplace_alter_table_dict( ctx->add_index[a] = row_merge_create_index( ctx->trx, ctx->new_table, - &index_defs[a]); + &index_defs[a], ctx->col_names); add_key_nums[a] = index_defs[a].key_number; @@ -3925,7 +3963,7 @@ err_exit: if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA) || (ha_alter_info->handler_flags == Alter_inplace_info::CHANGE_CREATE_OPTION - && !innobase_need_rebuild(ha_alter_info))) { + && !innobase_need_rebuild(ha_alter_info, table))) { if (heap) { ha_alter_info->handler_ctx @@ -4099,7 +4137,7 @@ ok_exit: if (ha_alter_info->handler_flags == Alter_inplace_info::CHANGE_CREATE_OPTION - && !innobase_need_rebuild(ha_alter_info)) { + && !innobase_need_rebuild(ha_alter_info, table)) { goto ok_exit; } @@ -4811,9 +4849,11 @@ commit_get_autoinc( Field* autoinc_field = old_table->found_next_number_field; + KEY* autoinc_key = + old_table->key_info + old_table->s->next_number_index; - dict_index_t* index = dict_table_get_index_on_first_col( - ctx->old_table, autoinc_field->field_index); + dict_index_t* index = dict_table_get_index_on_name( + ctx->old_table, autoinc_key->name); max_autoinc = ha_alter_info->create_info->auto_increment_value; @@ -5221,6 +5261,61 @@ commit_cache_rebuild( DBUG_VOID_RETURN; } +/** Store the column number of the columns in a list belonging +to indexes which are not being dropped. +@param[in] ctx In-place ALTER TABLE context +@param[out] drop_col_list list which will be set, containing columns + which is part of index being dropped */ +static +void +get_col_list_to_be_dropped( + ha_innobase_inplace_ctx* ctx, + std::set<ulint>& drop_col_list) +{ + for (ulint index_count = 0; index_count < ctx->num_to_drop_index; + index_count++) { + dict_index_t* index = ctx->drop_index[index_count]; + + for (ulint col = 0; col < index->n_user_defined_cols; col++) { + ulint col_no = dict_index_get_nth_col_no(index, col); + drop_col_list.insert(col_no); + } + } +} + +/** For each column, which is part of an index which is not going to be +dropped, it checks if the column number of the column is same as col_no +argument passed. +@param[in] table table object +@param[in] col_no column number of the column which is to be checked +@retval true column exists +@retval false column does not exist. */ +static +bool +check_col_exists_in_indexes( + const dict_table_t* table, + ulint col_no) +{ + for (dict_index_t* index = dict_table_get_first_index(table); index; + index = dict_table_get_next_index(index)) { + + if (index->to_be_dropped) { + continue; + } + + for (ulint col = 0; col < index->n_user_defined_cols; col++) { + + ulint index_col_no = dict_index_get_nth_col_no( + index, col); + if (col_no == index_col_no) { + return(true); + } + } + } + + return(false); +} + /** Commit the changes made during prepare_inplace_alter_table() and inplace_alter_table() inside the data dictionary tables, when not rebuilding the table. @@ -5356,6 +5451,20 @@ commit_cache_norebuild( DBUG_ASSERT(!ctx->need_rebuild()); + std::set<ulint> drop_list; + std::set<ulint>::const_iterator col_it; + + /* Check if the column, part of an index to be dropped is part of any + other index which is not being dropped. If it so, then set the ord_part + of the column to 0. */ + get_col_list_to_be_dropped(ctx, drop_list); + + for(col_it = drop_list.begin(); col_it != drop_list.end(); ++col_it) { + if (!check_col_exists_in_indexes(ctx->new_table, *col_it)) { + ctx->new_table->cols[*col_it].ord_part = 0; + } + } + for (ulint i = 0; i < ctx->num_to_add_index; i++) { dict_index_t* index = ctx->add_index[i]; DBUG_ASSERT(dict_index_get_online_status(index) @@ -5556,6 +5665,7 @@ ha_innobase::commit_inplace_alter_table( Alter_inplace_info* ha_alter_info, bool commit) { + dberr_t error; ha_innobase_inplace_ctx* ctx0 = static_cast<ha_innobase_inplace_ctx*> (ha_alter_info->handler_ctx); @@ -5637,7 +5747,7 @@ ha_innobase::commit_inplace_alter_table( transactions collected during crash recovery could be holding InnoDB locks only, not MySQL locks. */ - dberr_t error = row_merge_lock_table( + error = row_merge_lock_table( prebuilt->trx, ctx->old_table, LOCK_X); if (error != DB_SUCCESS) { @@ -5772,14 +5882,20 @@ ha_innobase::commit_inplace_alter_table( = static_cast<ha_innobase_inplace_ctx*>(*pctx); DBUG_ASSERT(ctx->need_rebuild()); - /* Generate the redo log for the file - operations that will be performed in - commit_cache_rebuild(). */ - fil_mtr_rename_log(ctx->old_table->space, - ctx->old_table->name, - ctx->new_table->space, - ctx->new_table->name, - ctx->tmp_name, &mtr); + /* Check for any possible problems for any + file operations that will be performed in + commit_cache_rebuild(), and if none, generate + the redo log for these operations. */ + error = fil_mtr_rename_log(ctx->old_table, + ctx->new_table, + ctx->tmp_name, &mtr); + if (error != DB_SUCCESS) { + /* Out of memory or a problem will occur + when renaming files. */ + fail = true; + my_error_innodb(error, ctx->old_table->name, + ctx->old_table->flags); + } DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++); } @@ -5792,18 +5908,25 @@ ha_innobase::commit_inplace_alter_table( DBUG_EXECUTE_IF("innodb_alter_commit_crash_before_commit", log_buffer_flush_to_disk(); DBUG_SUICIDE();); - ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE)); ut_ad(!trx->fts_trx); - ut_ad(trx->insert_undo || trx->update_undo); - /* The following call commits the - mini-transaction, making the data dictionary - transaction committed at mtr.end_lsn. The - transaction becomes 'durable' by the time when - log_buffer_flush_to_disk() returns. In the - logical sense the commit in the file-based - data structures happens here. */ - trx_commit_low(trx, &mtr); + if (fail) { + mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); + mtr_commit(&mtr); + trx_rollback_for_mysql(trx); + } else { + /* The following call commits the + mini-transaction, making the data dictionary + transaction committed at mtr.end_lsn. The + transaction becomes 'durable' by the time when + log_buffer_flush_to_disk() returns. In the + logical sense the commit in the file-based + data structures happens here. */ + ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE)); + ut_ad(trx->insert_undo || trx->update_undo); + + trx_commit_low(trx, &mtr); + } /* If server crashes here, the dictionary in InnoDB and MySQL will differ. The .ibd files @@ -5825,7 +5948,6 @@ ha_innobase::commit_inplace_alter_table( update the in-memory structures, close some handles, release temporary files, and (unless we rolled back) update persistent statistics. */ - dberr_t error = DB_SUCCESS; for (inplace_alter_handler_ctx** pctx = ctx_array; *pctx; pctx++) { |