Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mysql-test/suite/innodb/r/import_tablespace_race.result
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ END REPEAT;
END
$
connection default;
ALTER TABLE t NOWAIT ADD INDEX (c);
ALTER TABLE t NOWAIT ADD INDEX (c(20));
connection con1;
connection default;
FLUSH TABLE t FOR EXPORT;
Expand Down
25 changes: 25 additions & 0 deletions mysql-test/suite/innodb/r/innodb-virtual-columns-debug.result
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,28 @@ disconnect con1;
connection default;
SET debug_sync= "RESET";
DROP TABLE t1;
#
# MDEV-26057 Assertion `!vcol->v_indexes.empty() in trx_undo_log_v_idx
#
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL,
f3 INT AS (f2) VIRTUAL) ENGINE=InnoDB;
INSERT INTO t1(f1, f2) VALUES(1, 1);
SET DEBUG_SYNC="alter_rollback_complete SIGNAL dml_start WAIT_FOR default_resume";
set STATEMENT DEBUG_DBUG="d,innodb_OOM_inplace_alter" FOR alter table t1 add index(f3), algorithm=inplace;
connect con1,localhost,root,,,;
SET DEBUG_SYNC="now WAIT_FOR dml_start";
INSERT INTO t1(f1, f2) VALUES(2, 2);
SET DEBUG_SYNC="now SIGNAL default_resume";
connection default;
ERROR HY000: Out of memory.
connection default;
disconnect con1;
SET DEBUG_SYNC="RESET";
SELECT * FROM t1;
f1 f2 f3
1 1 1
2 2 2
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
DROP TABLE t1;
2 changes: 1 addition & 1 deletion mysql-test/suite/innodb/t/import_tablespace_race.test
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ $

--connection default
--error 0,ER_LOCK_WAIT_TIMEOUT
ALTER TABLE t NOWAIT ADD INDEX (c);
ALTER TABLE t NOWAIT ADD INDEX (c(20));

--connection con1
--reap
Expand Down
25 changes: 25 additions & 0 deletions mysql-test/suite/innodb/t/innodb-virtual-columns-debug.test
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,28 @@ SET debug_sync= "now SIGNAL another_open";
--connection default
SET debug_sync= "RESET";
DROP TABLE t1;

--echo #
--echo # MDEV-26057 Assertion `!vcol->v_indexes.empty() in trx_undo_log_v_idx
Comment thread
iMineLink marked this conversation as resolved.
--echo #
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL,
f3 INT AS (f2) VIRTUAL) ENGINE=InnoDB;
INSERT INTO t1(f1, f2) VALUES(1, 1);

SET DEBUG_SYNC="alter_rollback_complete SIGNAL dml_start WAIT_FOR default_resume";
send set STATEMENT DEBUG_DBUG="d,innodb_OOM_inplace_alter" FOR alter table t1 add index(f3), algorithm=inplace;

connect(con1,localhost,root,,,);
SET DEBUG_SYNC="now WAIT_FOR dml_start";
Comment thread
iMineLink marked this conversation as resolved.
INSERT INTO t1(f1, f2) VALUES(2, 2);
SET DEBUG_SYNC="now SIGNAL default_resume";

connection default;
--error ER_OUT_OF_RESOURCES
--reap
connection default;
disconnect con1;
SET DEBUG_SYNC="RESET";
SELECT * FROM t1;
CHECK TABLE t1;
DROP TABLE t1;
25 changes: 3 additions & 22 deletions storage/innobase/handler/handler0alter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9108,7 +9108,6 @@ argument passed.
@param[in] table table
@param[in] col_no column number
@param[in] is_v if this is a virtual column
@param[in] only_committed whether to consider only committed indexes
@retval true column exists
@retval false column does not exist, true if column is system column or
it is in the index. */
Expand All @@ -9117,8 +9116,7 @@ bool
check_col_exists_in_indexes(
const dict_table_t* table,
ulint col_no,
bool is_v,
bool only_committed = false)
bool is_v)
{
/* This function does not check system columns */
if (!is_v && dict_table_get_nth_col(table, col_no)->mtype == DATA_SYS) {
Expand All @@ -9129,9 +9127,7 @@ check_col_exists_in_indexes(
index;
index = dict_table_get_next_index(index)) {

if (only_committed
? !index->is_committed()
: index->to_be_dropped) {
if (index->to_be_dropped) {
continue;
}

Expand Down Expand Up @@ -9342,6 +9338,7 @@ inline bool rollback_inplace_alter_table(Alter_inplace_info *ha_alter_info,
ctx->trx->free();
ctx->trx= nullptr;

DEBUG_SYNC_C("alter_rollback_complete");
dict_sys.lock(SRW_LOCK_CALL);

if (ctx->add_vcol)
Expand Down Expand Up @@ -9369,22 +9366,6 @@ inline bool rollback_inplace_alter_table(Alter_inplace_info *ha_alter_info,
DBUG_ASSERT(prebuilt->table->indexes.start->online_status ==
ONLINE_INDEX_COMPLETE);

/* Reset dict_col_t::ord_part for unindexed columns */
for (ulint i= 0; i < dict_table_get_n_cols(prebuilt->table); i++)
{
dict_col_t &col= prebuilt->table->cols[i];
if (col.ord_part && !check_col_exists_in_indexes(prebuilt->table, i, false,
true))
col.ord_part= 0;
}

for (ulint i = 0; i < dict_table_get_n_v_cols(prebuilt->table); i++)
{
dict_col_t &col = prebuilt->table->v_cols[i].m_col;
if (col.ord_part && !check_col_exists_in_indexes(prebuilt->table, i, true,
true))
col.ord_part= 0;
}
dict_sys.unlock();
trx_commit_for_mysql(prebuilt->trx);
prebuilt->trx_id = 0;
Expand Down
45 changes: 45 additions & 0 deletions storage/innobase/row/row0merge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4014,6 +4014,43 @@ static void row_merge_drop_fulltext_indexes(trx_t *trx, dict_table_t *table)
DICT_TF2_FLAG_UNSET(table, DICT_TF2_FTS);
}

/** @param table table
@param col a stored column of the table
@return whether the column is a field of any index of the table */
static bool row_merge_col_is_indexed(const dict_table_t &table,
const dict_col_t &col)
{
for (const dict_index_t *index= dict_table_get_first_index(&table); index;
index= dict_table_get_next_index(index))
for (unsigned f= 0; f < index->n_uniq; f++)
if (dict_index_get_nth_col(index, f) == &col)
return true;
return false;
}

/** Reset dict_col_t::ord_part for the columns that are no
longer a field of any index that remains in the data dictionary cache.
@param table table whose columns are to be adjusted */
static void row_merge_reset_ord_part(dict_table_t *table)
{
ut_ad(dict_sys.locked());

for (unsigned c= 0; c < table->n_v_def; c++)
{
dict_v_col_t &v= table->v_cols[c];
if (v.m_col.ord_part && v.v_indexes.empty())
v.m_col.ord_part= 0;
}

for (unsigned c= 0; c < table->n_cols; c++)
{
dict_col_t &col= table->cols[c];
if (col.ord_part && col.mtype != DATA_SYS &&
!row_merge_col_is_indexed(*table, col))
col.ord_part= 0;
}
}

/** Drop indexes that were created before an error occurred.
The data dictionary must have been locked exclusively by the caller,
because the transaction will not be committed.
Expand Down Expand Up @@ -4188,6 +4225,14 @@ row_merge_drop_indexes(
}

row_merge_drop_fulltext_indexes(trx, table);
/* The aborted indexes are gone from the cache.
Adjust dict_col_t::ord_part while still holding the
same dict_sys.latch, so that DML will never observe a
column with ord_part set and an empty dict_v_col_t::v_indexes.
In the lazy drop above, the indexes remain in the cache,
and nothing must be reset; */
ut_ad(locked || table->get_ref_count() == 1);
row_merge_reset_ord_part(table);
table->drop_aborted = FALSE;
ut_d(dict_table_check_for_dup_indexes(table, CHECK_ALL_COMPLETE));
}
Expand Down
57 changes: 55 additions & 2 deletions storage/innobase/row/row0quiesce.cc
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,54 @@ row_quiesce_write_indexes(
return(err);
}

/** Compute dict_col_t::ord_part and dict_col_t::max_prefix
of a column from the committed indexes that are present in
the data dictionary cache, in the same way as
dict_index_add_to_cache() would compute them when the table
definition is being loaded from SYS_INDEXES.

The cached values can be stale. A rolled back ADD INDEX
can leave dict_col_t::ord_part set until the aborted index
is removed from the dictionary cache by a subsequent DDL
operation, and dict_col_t::max_prefix is never reset when an
index is being dropped.
@param table table
@param col a column of the table
@param max_prefix computed dict_col_t::max_prefix
@return computed dict_col_t::ord_part */
static bool row_quiesce_col_ord_part(const dict_table_t &table,
const dict_col_t &col,
ulint &max_prefix)
{
bool ord_part= false;
max_prefix= 0;

for (const dict_index_t* index= UT_LIST_GET_FIRST(table.indexes);
index; index= UT_LIST_GET_NEXT(indexes, index))
{
if (!index->is_committed())
continue;

for (unsigned i= 0; i < index->n_uniq; i++)
{
const dict_field_t& field= index->fields[i];
if (field.col != &col)
continue;

if (!field.prefix_len)
{
max_prefix= 0;
return true;
}

if (field.prefix_len > max_prefix)
max_prefix= field.prefix_len;
ord_part= true;
}
}
return ord_part;
}

/*********************************************************************//**
Write the meta data (table columns) config file. Serialise the contents of
dict_col_t structure, along with the column name. All fields are serialized
Expand Down Expand Up @@ -251,10 +299,15 @@ row_quiesce_write_table(
mach_write_to_4(ptr, col->ind);
ptr += sizeof(ib_uint32_t);

mach_write_to_4(ptr, col->ord_part);
/* Derive the values from the indexes rather than using
the cached dict_col_t fields, which can be stale. */
ulint max_prefix;

mach_write_to_4(ptr, row_quiesce_col_ord_part(*table, *col,
max_prefix));
ptr += sizeof(ib_uint32_t);

mach_write_to_4(ptr, col->max_prefix);
mach_write_to_4(ptr, max_prefix);

DBUG_EXECUTE_IF("ib_export_io_write_failure_2",
close(fileno(file)););
Expand Down
Loading