summaryrefslogtreecommitdiff
path: root/storage/innobase/handler/handler0alter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/handler/handler0alter.cc')
-rw-r--r--storage/innobase/handler/handler0alter.cc400
1 files changed, 232 insertions, 168 deletions
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index ab2e837d5f0..35126431c73 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -836,6 +836,117 @@ inline void dict_table_t::rollback_instant(
}
}
+/* Report an InnoDB error to the client by invoking my_error(). */
+static ATTRIBUTE_COLD __attribute__((nonnull))
+void
+my_error_innodb(
+/*============*/
+ dberr_t error, /*!< in: InnoDB error code */
+ const char* table, /*!< in: table name */
+ ulint flags) /*!< in: table flags */
+{
+ switch (error) {
+ case DB_MISSING_HISTORY:
+ my_error(ER_TABLE_DEF_CHANGED, MYF(0));
+ break;
+ case DB_RECORD_NOT_FOUND:
+ my_error(ER_KEY_NOT_FOUND, MYF(0), table);
+ break;
+ case DB_DEADLOCK:
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ break;
+ case DB_LOCK_WAIT_TIMEOUT:
+ my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+ break;
+ case DB_INTERRUPTED:
+ my_error(ER_QUERY_INTERRUPTED, MYF(0));
+ break;
+ case DB_OUT_OF_MEMORY:
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ break;
+ case DB_OUT_OF_FILE_SPACE:
+ my_error(ER_RECORD_FILE_FULL, MYF(0), table);
+ break;
+ case DB_TEMP_FILE_WRITE_FAIL:
+ my_error(ER_TEMP_FILE_WRITE_FAILURE, MYF(0));
+ break;
+ case DB_TOO_BIG_INDEX_COL:
+ my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0),
+ (ulong) DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(flags));
+ break;
+ case DB_TOO_MANY_CONCURRENT_TRXS:
+ my_error(ER_TOO_MANY_CONCURRENT_TRXS, MYF(0));
+ break;
+ case DB_LOCK_TABLE_FULL:
+ my_error(ER_LOCK_TABLE_FULL, MYF(0));
+ break;
+ case DB_UNDO_RECORD_TOO_BIG:
+ my_error(ER_UNDO_RECORD_TOO_BIG, MYF(0));
+ break;
+ case DB_CORRUPTION:
+ my_error(ER_NOT_KEYFILE, MYF(0), table);
+ break;
+ case DB_TOO_BIG_RECORD: {
+ /* Note that in page0zip.ic page_zip_rec_needs_ext() rec_size
+ is limited to COMPRESSED_REC_MAX_DATA_SIZE (16K) or
+ REDUNDANT_REC_MAX_DATA_SIZE (16K-1). */
+ bool comp = !!(flags & DICT_TF_COMPACT);
+ ulint free_space = page_get_free_space_of_empty(comp) / 2;
+
+ if (free_space >= ulint(comp ? COMPRESSED_REC_MAX_DATA_SIZE :
+ REDUNDANT_REC_MAX_DATA_SIZE)) {
+ free_space = (comp ? COMPRESSED_REC_MAX_DATA_SIZE :
+ REDUNDANT_REC_MAX_DATA_SIZE) - 1;
+ }
+
+ my_error(ER_TOO_BIG_ROWSIZE, MYF(0), free_space);
+ break;
+ }
+ case DB_INVALID_NULL:
+ /* TODO: report the row, as we do for DB_DUPLICATE_KEY */
+ my_error(ER_INVALID_USE_OF_NULL, MYF(0));
+ break;
+ case DB_CANT_CREATE_GEOMETRY_OBJECT:
+ my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, 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_ONLINE_LOG_TOO_BIG:
+ /* These codes should not be passed here. */
+ ut_error;
+#endif /* UNIV_DEBUG */
+ default:
+ my_error(ER_GET_ERRNO, MYF(0), error, "InnoDB");
+ break;
+ }
+}
+
+/** Get the name of an erroneous key.
+@param[in] error_key_num InnoDB number of the erroneus key
+@param[in] ha_alter_info changes that were being performed
+@param[in] table InnoDB table
+@return the name of the erroneous key */
+static
+const char*
+get_error_key_name(
+ ulint error_key_num,
+ const Alter_inplace_info* ha_alter_info,
+ const dict_table_t* table)
+{
+ if (error_key_num == ULINT_UNDEFINED) {
+ return(FTS_DOC_ID_INDEX_NAME);
+ } else if (ha_alter_info->key_count == 0) {
+ return(dict_table_get_first_index(table)->name);
+ } else {
+ return(ha_alter_info->key_info_buffer[error_key_num].name.str);
+ }
+}
+
struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx
{
/** Dummy query graph */
@@ -1173,6 +1284,45 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx
return true;
return false;
}
+
+ /** Handle the apply log failure for online DDL operation.
+ @param ha_alter_info handler alter inplace info
+ @param altered_table MySQL table that is being altered
+ @param error error code
+ @retval false if error value is DB_SUCCESS or
+ TRUE in case of error */
+ bool log_failure(Alter_inplace_info *ha_alter_info,
+ TABLE *altered_table, dberr_t error)
+ {
+ ulint err_key= thr_get_trx(thr)->error_key_num;
+ switch (error) {
+ KEY *dup_key;
+ case DB_SUCCESS:
+ return false;
+ case DB_DUPLICATE_KEY:
+ if (err_key == ULINT_UNDEFINED)
+ /* This should be the hidden index on FTS_DOC_ID */
+ dup_key= nullptr;
+ else
+ {
+ DBUG_ASSERT(err_key < ha_alter_info->key_count);
+ dup_key= &ha_alter_info->key_info_buffer[err_key];
+ }
+ print_keydup_error(altered_table, dup_key, MYF(0));
+ break;
+ case DB_ONLINE_LOG_TOO_BIG:
+ my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
+ get_error_key_name(err_key, ha_alter_info, new_table));
+ break;
+ case DB_INDEX_CORRUPT:
+ my_error(ER_INDEX_CORRUPT, MYF(0),
+ get_error_key_name(err_key, ha_alter_info, new_table));
+ break;
+ default:
+ my_error_innodb(error, old_table->name.m_name, old_table->flags);
+ }
+ return true;
+ }
};
/********************************************************************//**
@@ -1180,96 +1330,6 @@ Get the upper limit of the MySQL integral and floating-point type.
@return maximum allowed value for the field */
ulonglong innobase_get_int_col_max_value(const Field *field);
-/* Report an InnoDB error to the client by invoking my_error(). */
-static ATTRIBUTE_COLD __attribute__((nonnull))
-void
-my_error_innodb(
-/*============*/
- dberr_t error, /*!< in: InnoDB error code */
- const char* table, /*!< in: table name */
- ulint flags) /*!< in: table flags */
-{
- switch (error) {
- case DB_MISSING_HISTORY:
- my_error(ER_TABLE_DEF_CHANGED, MYF(0));
- break;
- case DB_RECORD_NOT_FOUND:
- my_error(ER_KEY_NOT_FOUND, MYF(0), table);
- break;
- case DB_DEADLOCK:
- my_error(ER_LOCK_DEADLOCK, MYF(0));
- break;
- case DB_LOCK_WAIT_TIMEOUT:
- my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
- break;
- case DB_INTERRUPTED:
- my_error(ER_QUERY_INTERRUPTED, MYF(0));
- break;
- case DB_OUT_OF_MEMORY:
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- break;
- case DB_OUT_OF_FILE_SPACE:
- my_error(ER_RECORD_FILE_FULL, MYF(0), table);
- break;
- case DB_TEMP_FILE_WRITE_FAIL:
- my_error(ER_TEMP_FILE_WRITE_FAILURE, MYF(0));
- break;
- case DB_TOO_BIG_INDEX_COL:
- my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0),
- (ulong) DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(flags));
- break;
- case DB_TOO_MANY_CONCURRENT_TRXS:
- my_error(ER_TOO_MANY_CONCURRENT_TRXS, MYF(0));
- break;
- case DB_LOCK_TABLE_FULL:
- my_error(ER_LOCK_TABLE_FULL, MYF(0));
- break;
- case DB_UNDO_RECORD_TOO_BIG:
- my_error(ER_UNDO_RECORD_TOO_BIG, MYF(0));
- break;
- case DB_CORRUPTION:
- my_error(ER_NOT_KEYFILE, MYF(0), table);
- break;
- case DB_TOO_BIG_RECORD: {
- /* Note that in page0zip.ic page_zip_rec_needs_ext() rec_size
- is limited to COMPRESSED_REC_MAX_DATA_SIZE (16K) or
- REDUNDANT_REC_MAX_DATA_SIZE (16K-1). */
- bool comp = !!(flags & DICT_TF_COMPACT);
- ulint free_space = page_get_free_space_of_empty(comp) / 2;
-
- if (free_space >= ulint(comp ? COMPRESSED_REC_MAX_DATA_SIZE :
- REDUNDANT_REC_MAX_DATA_SIZE)) {
- free_space = (comp ? COMPRESSED_REC_MAX_DATA_SIZE :
- REDUNDANT_REC_MAX_DATA_SIZE) - 1;
- }
-
- my_error(ER_TOO_BIG_ROWSIZE, MYF(0), free_space);
- break;
- }
- case DB_INVALID_NULL:
- /* TODO: report the row, as we do for DB_DUPLICATE_KEY */
- my_error(ER_INVALID_USE_OF_NULL, MYF(0));
- break;
- case DB_CANT_CREATE_GEOMETRY_OBJECT:
- my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, 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_ONLINE_LOG_TOO_BIG:
- /* These codes should not be passed here. */
- ut_error;
-#endif /* UNIV_DEBUG */
- default:
- my_error(ER_GET_ERRNO, MYF(0), error, "InnoDB");
- break;
- }
-}
-
/** Determine if fulltext indexes exist in a given table.
@param table MySQL table
@return number of fulltext indexes */
@@ -2828,7 +2888,7 @@ no_match:
= key_part.field->pack_length();
/* Any index on virtual columns cannot be used
- for reference constaint */
+ for reference constraint */
if (!key_part.field->stored_in_db()) {
goto no_match;
}
@@ -7026,6 +7086,8 @@ error_handling_drop_uncached:
row_log_free(
index->online_log);
index->online_log = NULL;
+ ctx->old_table->indexes.start
+ ->online_log = nullptr;
ok = false;
});
@@ -7148,24 +7210,8 @@ error_handling:
ctx->trx->rollback();
- if (ctx->need_rebuild()) {
- /* Free the log for online table rebuild, if
- one was allocated. */
-
- dict_index_t* clust_index = dict_table_get_first_index(
- user_table);
-
- clust_index->lock.x_lock(SRW_LOCK_CALL);
-
- if (clust_index->online_log) {
- ut_ad(ctx->online);
- row_log_abort_sec(clust_index);
- clust_index->online_status
- = ONLINE_INDEX_COMPLETE;
- }
-
- clust_index->lock.x_unlock();
- }
+ ut_ad(!ctx->need_rebuild()
+ || !user_table->indexes.start->online_log);
ctx->prebuilt->trx->error_info = NULL;
ctx->trx->error_state = DB_SUCCESS;
@@ -8338,27 +8384,6 @@ alter_templ_needs_rebuild(
return(false);
}
-/** Get the name of an erroneous key.
-@param[in] error_key_num InnoDB number of the erroneus key
-@param[in] ha_alter_info changes that were being performed
-@param[in] table InnoDB table
-@return the name of the erroneous key */
-static
-const char*
-get_error_key_name(
- ulint error_key_num,
- const Alter_inplace_info* ha_alter_info,
- const dict_table_t* table)
-{
- if (error_key_num == ULINT_UNDEFINED) {
- return(FTS_DOC_ID_INDEX_NAME);
- } else if (ha_alter_info->key_count == 0) {
- return(dict_table_get_first_index(table)->name);
- } else {
- return(ha_alter_info->key_info_buffer[error_key_num].name.str);
- }
-}
-
/** Alter the table structure in-place with operations
specified using Alter_inplace_info.
The level of concurrency allowed during this operation depends
@@ -8723,6 +8748,7 @@ inline bool rollback_inplace_alter_table(Alter_inplace_info *ha_alter_info,
DBUG_ENTER("rollback_inplace_alter_table");
+ DEBUG_SYNC_C("innodb_rollback_inplace_alter_table");
if (!ctx)
/* If we have not started a transaction yet,
(almost) nothing has been or needs to be done. */
@@ -8786,7 +8812,10 @@ inline bool rollback_inplace_alter_table(Alter_inplace_info *ha_alter_info,
thd_lock_wait_timeout(ctx->trx->mysql_thd);
const uint save_timeout= innodb_lock_wait_timeout;
innodb_lock_wait_timeout= ~0U; /* infinite */
-
+ dict_index_t *old_clust_index= ctx->old_table->indexes.start;
+ old_clust_index->lock.x_lock(SRW_LOCK_CALL);
+ old_clust_index->online_log= nullptr;
+ old_clust_index->lock.x_unlock();
if (fts_exist)
{
const dict_index_t *fts_index= nullptr;
@@ -8870,7 +8899,6 @@ free_and_exit:
}
}
- DBUG_ASSERT(!prebuilt->table->indexes.start->online_log);
DBUG_ASSERT(prebuilt->table->indexes.start->online_status ==
ONLINE_INDEX_COMPLETE);
@@ -10753,7 +10781,6 @@ static bool alter_rebuild_apply_log(
dropped were not created in the copy of the table. Apply any
last bit of the rebuild log and then rename the tables. */
dict_table_t* user_table = ctx->old_table;
- dict_table_t* rebuilt_table = ctx->new_table;
DEBUG_SYNC_C("row_log_table_apply2_before");
@@ -10782,41 +10809,8 @@ static bool alter_rebuild_apply_log(
ctx->new_table->vc_templ = NULL;
}
- ulint err_key = thr_get_trx(ctx->thr)->error_key_num;
-
- switch (error) {
- KEY* dup_key;
- case DB_SUCCESS:
- break;
- case DB_DUPLICATE_KEY:
- if (err_key == ULINT_UNDEFINED) {
- /* This should be the hidden index on
- FTS_DOC_ID. */
- dup_key = NULL;
- } else {
- DBUG_ASSERT(err_key < ha_alter_info->key_count);
- dup_key = &ha_alter_info->key_info_buffer[err_key];
- }
-
- print_keydup_error(altered_table, dup_key, MYF(0));
- DBUG_RETURN(true);
- case DB_ONLINE_LOG_TOO_BIG:
- my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
- get_error_key_name(err_key, ha_alter_info,
- rebuilt_table));
- DBUG_RETURN(true);
- case DB_INDEX_CORRUPT:
- my_error(ER_INDEX_CORRUPT, MYF(0),
- get_error_key_name(err_key, ha_alter_info,
- rebuilt_table));
- DBUG_RETURN(true);
- default:
- my_error_innodb(error, ctx->old_table->name.m_name,
- user_table->flags);
- DBUG_RETURN(true);
- }
-
- DBUG_RETURN(false);
+ DBUG_RETURN(ctx->log_failure(
+ ha_alter_info, altered_table, error));
}
/** Commit or rollback the changes made during
@@ -11007,6 +11001,72 @@ lock_fail:
DBUG_RETURN(true);
}
}
+ } else {
+ dberr_t error= DB_SUCCESS;
+ for (inplace_alter_handler_ctx** pctx= ctx_array; *pctx;
+ pctx++) {
+ auto ctx= static_cast<ha_innobase_inplace_ctx*>(*pctx);
+
+ if (!ctx->online || !ctx->old_table->space
+ || !ctx->old_table->is_readable()) {
+ continue;
+ }
+
+ for (ulint i = 0; i < ctx->num_to_add_index; i++) {
+ dict_index_t *index= ctx->add_index[i];
+
+ ut_ad(!(index->type &
+ (DICT_FTS | DICT_SPATIAL)));
+
+ index->lock.x_lock(SRW_LOCK_CALL);
+ if (!index->online_log) {
+ /* online log would've cleared
+ when we detect the error in
+ other index */
+ index->lock.x_unlock();
+ continue;
+ }
+
+ if (index->is_corrupted()) {
+ /* Online index log has been
+ preserved to show the error
+ when it happened via
+ row_log_apply() by DML thread */
+ error= row_log_get_error(index);
+err_index:
+ ut_ad(error != DB_SUCCESS);
+ ctx->log_failure(
+ ha_alter_info,
+ altered_table, error);
+ row_log_free(index->online_log);
+ index->online_log= nullptr;
+ index->lock.x_unlock();
+
+ ctx->old_table->indexes.start
+ ->online_log= nullptr;
+ if (fts_exist) {
+ purge_sys.resume_FTS();
+ }
+ MONITOR_ATOMIC_INC(
+ MONITOR_BACKGROUND_DROP_INDEX);
+ DBUG_RETURN(true);
+ }
+
+ error = row_log_apply(
+ m_prebuilt->trx, index, altered_table,
+ ctx->m_stage);
+
+ if (error != DB_SUCCESS) {
+ goto err_index;
+ }
+
+ row_log_free(index->online_log);
+ index->online_log= nullptr;
+ index->lock.x_unlock();
+ }
+
+ ctx->old_table->indexes.start->online_log= nullptr;
+ }
}
dict_table_t *table_stats = nullptr, *index_stats = nullptr;
@@ -11039,6 +11099,10 @@ lock_fail:
error = lock_table_for_trx(index_stats, trx, LOCK_X);
}
}
+
+ DBUG_EXECUTE_IF("stats_lock_fail",
+ error = DB_LOCK_WAIT;);
+
if (error == DB_SUCCESS) {
error = lock_sys_tables(trx);
}