Commit a87e5019 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-18007 innodb.instant_alter_crash: Assertion failed: n < tuple->n_fields

The test innodb.instant_alter_crash is exercising crash recovery
for instant ALTER operations. Unfortunately, for some reason the
redo log was not being flushed as expected (to be analyzed in MDEV-18009)
and this error was not detected earlier.

btr_cur_trim_alter_metadata(): New function for the special case of
rolling back an ALTER TABLE operation such that the table will remain
in the MDEV-15562 format (not rolling back to the MDEV-11369 format).
In this case, we must restore the old number of columns from the
old metadata BLOB page.
parent 330c6218
...@@ -4283,6 +4283,72 @@ btr_cur_update_in_place( ...@@ -4283,6 +4283,72 @@ btr_cur_update_in_place(
return(err); return(err);
} }
/** Trim a metadata record during the rollback of instant ALTER TABLE.
@param[in] entry metadata tuple
@param[in] index primary key
@param[in] update update vector for the rollback */
ATTRIBUTE_COLD
static void btr_cur_trim_alter_metadata(dtuple_t* entry,
const dict_index_t* index,
const upd_t* update)
{
ut_ad(index->is_instant());
ut_ad(update->is_alter_metadata());
ut_ad(entry->is_alter_metadata());
ut_ad(update->fields[0].field_no == index->first_user_field());
ut_ad(update->fields[0].new_val.ext);
ut_ad(update->fields[0].new_val.len == FIELD_REF_SIZE);
ut_ad(entry->n_fields - 1 == index->n_fields);
const byte* ptr = static_cast<const byte*>(
update->fields[0].new_val.data);
ut_ad(!mach_read_from_4(ptr + BTR_EXTERN_LEN));
ut_ad(mach_read_from_4(ptr + BTR_EXTERN_LEN + 4) > 4);
ut_ad(mach_read_from_4(ptr + BTR_EXTERN_OFFSET) == FIL_PAGE_DATA);
ut_ad(mach_read_from_4(ptr + BTR_EXTERN_SPACE_ID)
== index->table->space->id);
ulint n_fields = update->fields[1].field_no;
ut_ad(n_fields <= index->n_fields);
if (n_fields != index->n_uniq) {
ut_ad(n_fields
>= index->n_core_fields);
entry->n_fields = n_fields;
return;
}
/* This is based on dict_table_t::deserialise_columns()
and btr_cur_instant_init_low(). */
mtr_t mtr;
mtr.start();
buf_block_t* block = buf_page_get(
page_id_t(index->table->space->id,
mach_read_from_4(ptr + BTR_EXTERN_PAGE_NO)),
univ_page_size, RW_S_LATCH, &mtr);
buf_block_dbg_add_level(block, SYNC_EXTERN_STORAGE);
ut_ad(fil_page_get_type(block->frame) == FIL_PAGE_TYPE_BLOB);
ut_ad(mach_read_from_4(&block->frame[FIL_PAGE_DATA
+ BTR_BLOB_HDR_NEXT_PAGE_NO])
== FIL_NULL);
ut_ad(mach_read_from_4(&block->frame[FIL_PAGE_DATA
+ BTR_BLOB_HDR_PART_LEN])
== mach_read_from_4(ptr + BTR_EXTERN_LEN + 4));
n_fields = mach_read_from_4(
&block->frame[FIL_PAGE_DATA + BTR_BLOB_HDR_SIZE])
+ index->first_user_field();
/* Rollback should not increase the number of fields. */
ut_ad(n_fields <= index->n_fields);
ut_ad(n_fields + 1 <= entry->n_fields);
/* dict_index_t::clear_instant_alter() cannot be invoked while
rollback of an instant ALTER TABLE transaction is in progress
for an is_alter_metadata() record. */
ut_ad(n_fields >= index->n_core_fields);
mtr.commit();
entry->n_fields = n_fields + 1;
}
/** Trim an update tuple due to instant ADD COLUMN, if needed. /** Trim an update tuple due to instant ADD COLUMN, if needed.
For normal records, the trailing instantly added fields that match For normal records, the trailing instantly added fields that match
the initial default values are omitted. the initial default values are omitted.
...@@ -4309,6 +4375,7 @@ btr_cur_trim( ...@@ -4309,6 +4375,7 @@ btr_cur_trim(
(instant ALTER TABLE on a table where instant ALTER was (instant ALTER TABLE on a table where instant ALTER was
already executed) or rolling back such an operation. */ already executed) or rolling back such an operation. */
ut_ad(!upd_get_nth_field(update, 0)->orig_len); ut_ad(!upd_get_nth_field(update, 0)->orig_len);
ut_ad(entry->is_metadata());
if (thr->graph->trx->in_rollback) { if (thr->graph->trx->in_rollback) {
/* This rollback can occur either as part of /* This rollback can occur either as part of
...@@ -4326,17 +4393,11 @@ btr_cur_trim( ...@@ -4326,17 +4393,11 @@ btr_cur_trim(
innobase_add_instant_try(). */ innobase_add_instant_try(). */
ut_ad(update->n_fields > 2); ut_ad(update->n_fields > 2);
if (update->is_alter_metadata()) { if (update->is_alter_metadata()) {
ut_ad(update->fields[0].field_no btr_cur_trim_alter_metadata(
== index->first_user_field()); entry, index, update);
ut_ad(update->fields[0].new_val.ext);
ut_ad(update->fields[0].new_val.len
== FIELD_REF_SIZE);
ut_ad(entry->n_fields - 1 == index->n_fields);
ulint n_fields = update->fields[1].field_no;
ut_ad(n_fields <= index->n_fields);
entry->n_fields = n_fields;
return; return;
} }
ut_ad(!entry->is_alter_metadata());
ulint n_fields = upd_get_nth_field(update, 0) ulint n_fields = upd_get_nth_field(update, 0)
->field_no; ->field_no;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment