diff options
Diffstat (limited to 'storage/innobase/handler/handler0alter_innopart.cc')
-rw-r--r-- | storage/innobase/handler/handler0alter_innopart.cc | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/storage/innobase/handler/handler0alter_innopart.cc b/storage/innobase/handler/handler0alter_innopart.cc new file mode 100644 index 00000000000..0f2d5c7e576 --- /dev/null +++ b/storage/innobase/handler/handler0alter_innopart.cc @@ -0,0 +1,307 @@ +/* JAN: TODO: MySQL 5.7 InnoDB partitioning. */ + +/** Prepare inplace alter table. +Allows InnoDB to update internal structures with concurrent +writes blocked (provided that check_if_supported_inplace_alter() +did not return HA_ALTER_INPLACE_NO_LOCK). +This will be invoked before inplace_alter_table(). +@param[in] altered_table TABLE object for new version of table. +@param[in] ha_alter_info Structure describing changes to be done +by ALTER TABLE and holding data used during in-place alter. +@retval true Failure. +@retval false Success. */ +bool +ha_innopart::prepare_inplace_alter_table( + TABLE* altered_table, + Alter_inplace_info* ha_alter_info) +{ + THD* thd; + ha_innopart_inplace_ctx* ctx_parts; + bool res = true; + DBUG_ENTER("ha_innopart::prepare_inplace_alter_table"); + DBUG_ASSERT(ha_alter_info->handler_ctx == NULL); + + thd = ha_thd(); + + /* Clean up all ins/upd nodes. */ + clear_ins_upd_nodes(); + /* Based on Sql_alloc class, return NULL for new on failure. */ + ctx_parts = new ha_innopart_inplace_ctx(thd, m_tot_parts); + if (!ctx_parts) { + DBUG_RETURN(HA_ALTER_ERROR); + } + + uint ctx_array_size = sizeof(inplace_alter_handler_ctx*) + * (m_tot_parts + 1); + ctx_parts->ctx_array = + static_cast<inplace_alter_handler_ctx**>( + ut_malloc(ctx_array_size, + mem_key_partitioning)); + if (!ctx_parts->ctx_array) { + DBUG_RETURN(HA_ALTER_ERROR); + } + + /* Set all to NULL, including the terminating one. */ + memset(ctx_parts->ctx_array, 0, ctx_array_size); + + ctx_parts->prebuilt_array = static_cast<row_prebuilt_t**>( + ut_malloc(sizeof(row_prebuilt_t*) + * m_tot_parts, + mem_key_partitioning)); + if (!ctx_parts->prebuilt_array) { + DBUG_RETURN(HA_ALTER_ERROR); + } + /* For the first partition use the current prebuilt. */ + ctx_parts->prebuilt_array[0] = m_prebuilt; + /* Create new prebuilt for the rest of the partitions. + It is needed for the current implementation of + ha_innobase::commit_inplace_alter_table(). */ + for (uint i = 1; i < m_tot_parts; i++) { + row_prebuilt_t* tmp_prebuilt; + tmp_prebuilt = row_create_prebuilt( + m_part_share->get_table_part(i), + table_share->reclength); + /* Use same trx as original prebuilt. */ + tmp_prebuilt->trx = m_prebuilt->trx; + ctx_parts->prebuilt_array[i] = tmp_prebuilt; + } + + for (uint i = 0; i < m_tot_parts; i++) { + m_prebuilt = ctx_parts->prebuilt_array[i]; + m_prebuilt_ptr = ctx_parts->prebuilt_array + i; + ha_alter_info->handler_ctx = ctx_parts->ctx_array[i]; + set_partition(i); + res = ha_innobase::prepare_inplace_alter_table(altered_table, + ha_alter_info); + update_partition(i); + ctx_parts->ctx_array[i] = ha_alter_info->handler_ctx; + if (res) { + break; + } + } + m_prebuilt = ctx_parts->prebuilt_array[0]; + m_prebuilt_ptr = &m_prebuilt; + ha_alter_info->handler_ctx = ctx_parts; + ha_alter_info->group_commit_ctx = ctx_parts->ctx_array; + DBUG_RETURN(res); +} + +/** Inplace alter table. +Alter the table structure in-place with operations +specified using Alter_inplace_info. +The level of concurrency allowed during this operation depends +on the return value from check_if_supported_inplace_alter(). +@param[in] altered_table TABLE object for new version of table. +@param[in] ha_alter_info Structure describing changes to be done +by ALTER TABLE and holding data used during in-place alter. +@retval true Failure. +@retval false Success. */ +bool +ha_innopart::inplace_alter_table( + TABLE* altered_table, + Alter_inplace_info* ha_alter_info) +{ + bool res = true; + ha_innopart_inplace_ctx* ctx_parts; + + ctx_parts = static_cast<ha_innopart_inplace_ctx*>( + ha_alter_info->handler_ctx); + for (uint i = 0; i < m_tot_parts; i++) { + m_prebuilt = ctx_parts->prebuilt_array[i]; + ha_alter_info->handler_ctx = ctx_parts->ctx_array[i]; + set_partition(i); + res = ha_innobase::inplace_alter_table(altered_table, + ha_alter_info); + ut_ad(ctx_parts->ctx_array[i] == ha_alter_info->handler_ctx); + ctx_parts->ctx_array[i] = ha_alter_info->handler_ctx; + if (res) { + break; + } + } + m_prebuilt = ctx_parts->prebuilt_array[0]; + ha_alter_info->handler_ctx = ctx_parts; + return(res); +} + +/** Commit or rollback inplace alter table. +Commit or rollback the changes made during +prepare_inplace_alter_table() and inplace_alter_table() inside +the storage engine. Note that the allowed level of concurrency +during this operation will be the same as for +inplace_alter_table() and thus might be higher than during +prepare_inplace_alter_table(). (E.g concurrent writes were +blocked during prepare, but might not be during commit). +@param[in] altered_table TABLE object for new version of table. +@param[in] ha_alter_info Structure describing changes to be done +by ALTER TABLE and holding data used during in-place alter. +@param[in] commit true => Commit, false => Rollback. +@retval true Failure. +@retval false Success. */ +bool +ha_innopart::commit_inplace_alter_table( + TABLE* altered_table, + Alter_inplace_info* ha_alter_info, + bool commit) +{ + bool res = false; + ha_innopart_inplace_ctx* ctx_parts; + + ctx_parts = static_cast<ha_innopart_inplace_ctx*>( + ha_alter_info->handler_ctx); + ut_ad(ctx_parts); + ut_ad(ctx_parts->prebuilt_array); + ut_ad(ctx_parts->prebuilt_array[0] == m_prebuilt); + if (commit) { + /* Commit is done through first partition (group commit). */ + ut_ad(ha_alter_info->group_commit_ctx == ctx_parts->ctx_array); + ha_alter_info->handler_ctx = ctx_parts->ctx_array[0]; + set_partition(0); + res = ha_innobase::commit_inplace_alter_table(altered_table, + ha_alter_info, + commit); + ut_ad(res || !ha_alter_info->group_commit_ctx); + goto end; + } + /* Rollback is done for each partition. */ + for (uint i = 0; i < m_tot_parts; i++) { + m_prebuilt = ctx_parts->prebuilt_array[i]; + ha_alter_info->handler_ctx = ctx_parts->ctx_array[i]; + set_partition(i); + if (ha_innobase::commit_inplace_alter_table(altered_table, + ha_alter_info, commit)) { + res = true; + } + ut_ad(ctx_parts->ctx_array[i] == ha_alter_info->handler_ctx); + ctx_parts->ctx_array[i] = ha_alter_info->handler_ctx; + } +end: + /* Move the ownership of the new tables back to + the m_part_share. */ + ha_innobase_inplace_ctx* ctx; + for (uint i = 0; i < m_tot_parts; i++) { + /* TODO: Fix to only use one prebuilt (i.e. make inplace + alter partition aware instead of using multiple prebuilt + copies... */ + ctx = static_cast<ha_innobase_inplace_ctx*>( + ctx_parts->ctx_array[i]); + if (ctx) { + m_part_share->set_table_part(i, ctx->prebuilt->table); + ctx->prebuilt->table = NULL; + ctx_parts->prebuilt_array[i] = ctx->prebuilt; + } + } + /* The above juggling of prebuilt must be reset here. */ + m_prebuilt = ctx_parts->prebuilt_array[0]; + m_prebuilt->table = m_part_share->get_table_part(0); + ha_alter_info->handler_ctx = ctx_parts; + return(res); +} + +/** Notify the storage engine that the table structure (.frm) has +been updated. + +ha_partition allows inplace operations that also upgrades the engine +if it supports partitioning natively. So if this is the case then +we will remove the .par file since it is not used with ha_innopart +(we use the internal data dictionary instead). */ +void +ha_innopart::notify_table_changed() +{ + char tmp_par_path[FN_REFLEN + 1]; + strxnmov(tmp_par_path, FN_REFLEN, table->s->normalized_path.str, + ".par", NullS); + + if (my_access(tmp_par_path, W_OK) == 0) + { + my_delete(tmp_par_path, MYF(0)); + } +} + +/** Check if supported inplace alter table. +@param[in] altered_table Altered MySQL table. +@param[in] ha_alter_info Information about inplace operations to do. +@return Lock level, not supported or error */ +enum_alter_inplace_result +ha_innopart::check_if_supported_inplace_alter( + TABLE* altered_table, + Alter_inplace_info* ha_alter_info) +{ + DBUG_ENTER("ha_innopart::check_if_supported_inplace_alter"); + DBUG_ASSERT(ha_alter_info->handler_ctx == NULL); + + /* Not supporting these for partitioned tables yet! */ + + /* FK not yet supported. */ + if (ha_alter_info->handler_flags + & (Alter_inplace_info::ADD_FOREIGN_KEY + | Alter_inplace_info::DROP_FOREIGN_KEY)) { + + ha_alter_info->unsupported_reason = innobase_get_err_msg( + ER_FOREIGN_KEY_ON_PARTITIONED); + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + } + /* FTS not yet supported either. */ + if ((ha_alter_info->handler_flags + & Alter_inplace_info::ADD_INDEX)) { + + for (uint i = 0; i < ha_alter_info->index_add_count; i++) { + const KEY* key = + &ha_alter_info->key_info_buffer[ + ha_alter_info->index_add_buffer[i]]; + if (key->flags & HA_FULLTEXT) { + DBUG_ASSERT(!(key->flags & HA_KEYFLAG_MASK + & ~(HA_FULLTEXT + | HA_PACK_KEY + | HA_GENERATED_KEY + | HA_BINARY_PACK_KEY))); + ha_alter_info->unsupported_reason = + innobase_get_err_msg( + ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING); + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + } + } + } + /* We cannot allow INPLACE to change order of KEY partitioning fields! */ + if ((ha_alter_info->handler_flags + & Alter_inplace_info::ALTER_STORED_COLUMN_ORDER) + && !m_part_info->same_key_column_order( + &ha_alter_info->alter_info->create_list)) { + + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + } + + /* Cannot allow INPLACE for drop and create PRIMARY KEY if partition is + on Primary Key - PARTITION BY KEY() */ + if ((ha_alter_info->handler_flags + & (Alter_inplace_info::ADD_PK_INDEX + | Alter_inplace_info::DROP_PK_INDEX))) { + + /* Check partition by key(). */ + if ((m_part_info->part_type == HASH_PARTITION) + && m_part_info->list_of_part_fields + && m_part_info->part_field_list.is_empty()) { + + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + } + + /* Check sub-partition by key(). */ + if ((m_part_info->subpart_type == HASH_PARTITION) + && m_part_info->list_of_subpart_fields + && m_part_info->subpart_field_list.is_empty()) { + + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + } + } + + /* Check for PK and UNIQUE should already be done when creating the + new table metadata. + (fix_partition_info/check_primary_key+check_unique_key) */ + + set_partition(0); + enum_alter_inplace_result res = + ha_innobase::check_if_supported_inplace_alter(altered_table, + ha_alter_info); + + DBEUG_RETURN(res); +} + |