Commit 6e4c840b authored by Marko Mäkelä's avatar Marko Mäkelä

Try to support ADD/DROP/reorder with virtual columns

FIXME: The virtual column code does not really support any reordering
of virtual columns. The function check_v_col_in_order() should be
removed and all the code fixed.

ha_innobase::check_if_supported_inplace_alter(): For now,
forbid ADD COLUMN of a stored column together with ADD/DROP of
virtual columns. This works around an assertion failure in
gcol.innodb_virtual_basic line 541.

dict_table_t::instant_column(): Copy and adjust v_cols[].

dict_table_t::rollback_instant(): Roll back v_cols[].

innobase_build_col_map(): Skip added virtual columns.

prepare_inplace_add_virtual(): Correctly compute num_to_add_vcol.
Remove some unnecessary code. Note that the call to
innodb_base_col_setup() should be executed later.

innobase_add_virtual_try(), innobase_drop_virtual_try(): Let
the caller invoke innodb_update_cols().

ha_innobase::commit_inplace_alter_table(): Add a FIXME comment
that we should not reload the table when virtual columns are added
or dropped. Currently, reloading is the only way to add the base
columns of virtual columns into the InnoDB data dictionary cache.

innobase_instant_try(): If any virtual column is dropped, rewrite
all virtual column metadata. Use a shortcut only for adding
virtual columns. This is because innobase_drop_virtual_try()
assumes that the dropped virtual columns still exist in the cache.
parent ae73b7d8
......@@ -1295,7 +1295,7 @@ inline void dict_index_t::instant_add_field(const dict_index_t& instant)
/** Adjust table metadata for instant ADD/DROP/reorder COLUMN.
@param[in] table altered table (with dropped columns)
@param[in] map mapping from cols[] to table.cols[] */
@param[in] map mapping from cols[] and v_cols[] to table */
void dict_table_t::instant_column(const dict_table_t& table, const ulint* map)
{
DBUG_ASSERT(!table.cached);
......@@ -1308,12 +1308,14 @@ void dict_table_t::instant_column(const dict_table_t& table, const ulint* map)
DBUG_ASSERT(table.n_cols + table.n_dropped() >= n_cols + n_dropped());
ut_ad(mutex_own(&dict_sys->mutex));
const char* end = table.col_names;
for (unsigned i = table.n_cols; i--; ) end += strlen(end) + 1;
{
const char* end = table.col_names;
for (unsigned i = table.n_cols; i--; ) end += strlen(end) + 1;
col_names = static_cast<char*>(
mem_heap_dup(heap, table.col_names,
ulint(end - table.col_names)));
col_names = static_cast<char*>(
mem_heap_dup(heap, table.col_names,
ulint(end - table.col_names)));
}
const dict_col_t* const old_cols = cols;
const dict_col_t* const old_cols_end = cols + n_cols;
cols = static_cast<dict_col_t*>(mem_heap_dup(heap, table.cols,
......@@ -1347,26 +1349,47 @@ void dict_table_t::instant_column(const dict_table_t& table, const ulint* map)
n_t_cols += table.n_cols - n_cols;
n_def = table.n_cols;
/* FIXME: copy v_col_names[] and v_cols[] from table to this
(that is, support ADD/DROP/reorder/rename of VIRTUAL COLUMN).
Here, we wrongly assume that the virtual columns were unchanged. */
DBUG_ASSERT(n_v_def == table.n_v_def);
const dict_v_col_t* const old_v_cols = v_cols;
if (const char* end = table.v_col_names) {
for (unsigned i = table.n_v_cols; i--; ) {
end += strlen(end) + 1;
}
v_col_names = static_cast<char*>(
mem_heap_dup(heap, table.v_col_names,
ulint(end - table.v_col_names)));
v_cols = static_cast<dict_v_col_t*>(
mem_heap_dup(heap, table.v_cols,
table.n_v_cols * sizeof *v_cols));
} else {
ut_ad(table.n_v_cols == 0);
v_col_names = NULL;
v_cols = NULL;
}
n_t_def += table.n_v_cols - n_v_cols;
n_t_cols += table.n_v_cols - n_v_cols;
n_v_def = table.n_v_cols;
for (unsigned i = 0; i < n_v_def; i++) {
dict_v_col_t& v = v_cols[i];
v.m_col.ind = v.m_col.ind - get_n_drop_cols(map, v.m_col.ind);
v.v_indexes = UT_NEW_NOKEY(dict_v_idx_list());
v.base_col = static_cast<dict_col_t**>(
mem_heap_dup(heap, v.base_col,
v.num_base * sizeof *v.base_col));
for (ulint n = v.num_base; n--; ) {
dict_col_t*& base = v.base_col[n];
if (base->is_virtual()) {
} else if (base >= table.cols
&& base <= table.cols + table.n_cols) {
&& base < table.cols + table.n_cols) {
/* The base column was instantly added. */
base += cols - table.cols;
} else {
DBUG_ASSERT(base >= old_cols);
DBUG_ASSERT(base + DATA_N_SYS_COLS
<= old_cols_end);
< old_cols_end);
size_t n = map[base - old_cols];
DBUG_ASSERT(n + DATA_N_SYS_COLS < n_cols);
base = &cols[n];
......@@ -1374,8 +1397,6 @@ void dict_table_t::instant_column(const dict_table_t& table, const ulint* map)
}
}
n_cols = table.n_cols;
dict_index_t* index = dict_table_get_first_index(this);
index->instant_add_field(*dict_table_get_first_index(&table));
......@@ -1440,24 +1461,42 @@ void dict_table_t::instant_column(const dict_table_t& table, const ulint* map)
continue;
}
for (unsigned i = 0; i < index->n_fields; i++) {
dict_field_t& field = index->fields[i];
if (field.col >= table.cols
&& field.col <= table.cols + n_cols) {
dict_field_t& f = index->fields[i];
if (f.col >= table.cols
&& f.col < table.cols + table.n_cols) {
/* This is an instantly added column
in a newly added index. */
DBUG_ASSERT(!field.col->is_virtual());
field.col += cols - table.cols;
field.name = field.col->name(*this);
} else if (field.col < old_cols
|| field.col >= old_cols_end) {
DBUG_ASSERT(field.col->is_virtual());
DBUG_ASSERT(!f.col->is_virtual());
f.col += cols - table.cols;
} else if (f.col >= &table.v_cols->m_col
&& f.col < &table.v_cols[n_v_cols].m_col) {
/* This is an instantly added virtual column
in a newly added index. */
DBUG_ASSERT(f.col->is_virtual());
f.col += v_cols - table.v_cols;
} else if (f.col < old_cols
|| f.col >= old_cols_end) {
DBUG_ASSERT(f.col->is_virtual());
f.col = &v_cols[
map[reinterpret_cast<dict_v_col_t*>(
f.col)
- old_v_cols + n_cols]].m_col;
} else {
field.col = &cols[map[field.col - old_cols]];
DBUG_ASSERT(!field.col->is_virtual());
field.name = field.col->name(*this);
f.col = &cols[map[f.col - old_cols]];
DBUG_ASSERT(!f.col->is_virtual());
}
f.name = f.col->name(*this);
if (f.col->is_virtual()) {
reinterpret_cast<dict_v_col_t*>(f.col)
->v_indexes->push_back(
dict_v_idx_t(index,
index->n_def));
}
}
}
n_cols = table.n_cols;
n_v_cols = table.n_v_cols;
}
/** Serialise metadata of dropped or reordered columns.
......@@ -1603,6 +1642,9 @@ ulint find_old_col_no(
@param[in] old_instant original instant structure
@param[in] old_fields original fields
@param[in] old_n_fields original number of fields
@param[in] old_n_v_cols original n_v_cols
@param[in] old_v_cols original v_cols
@param[in] old_v_col_names original v_col_names
@param[in] col_map column map */
void
dict_table_t::rollback_instant(
......@@ -1612,6 +1654,9 @@ dict_table_t::rollback_instant(
dict_instant_t* old_instant,
dict_field_t* old_fields,
unsigned old_n_fields,
unsigned old_n_v_cols,
dict_v_col_t* old_v_cols,
const char* old_v_col_names,
const ulint* col_map)
{
ut_ad(mutex_own(&dict_sys->mutex));
......@@ -1625,82 +1670,72 @@ dict_table_t::rollback_instant(
DBUG_ASSERT(index->n_core_fields <= index->n_fields);
DBUG_ASSERT(instant || !old_instant);
unsigned n_remove = 0;
unsigned n_add = 0;
if (n_cols > old_n_cols) {
n_remove = n_cols - old_n_cols;
} else {
n_add = old_n_cols - n_cols;
}
instant = old_instant;
index->n_nullable = 0;
for (unsigned i = 0; i < old_n_fields; i++) {
for (unsigned i = old_n_fields; i--; ) {
if (old_fields[i].col->is_nullable()) {
index->n_nullable++;
}
}
for (unsigned i = n_v_cols; i--; ) {
UT_DELETE(v_cols[i].v_indexes);
}
index->n_def = index->n_fields = old_n_fields;
const dict_col_t* const new_cols = cols;
const dict_col_t* const new_cols_end = cols + n_cols;
const dict_v_col_t* const new_v_cols = v_cols;
const dict_v_col_t* const new_v_cols_end = v_cols + n_v_cols;
cols = old_cols;
col_names = old_col_names;
n_cols = old_n_cols;
n_def = old_n_cols;
n_t_def -= n_remove;
n_t_cols -= n_remove;
n_t_def += n_add;
n_t_cols += n_add;
ulint old_col_no = 0;
for (unsigned i = 0; i < n_v_def; i++) {
dict_v_col_t& v = v_cols[i];
v.m_col.ind =find_old_col_no(col_map, v.m_col.ind, n_cols);
for (ulint n = v.num_base; n--; ) {
dict_col_t*& base = v.base_col[n];
if (!base->is_virtual()) {
old_col_no = find_old_col_no(
col_map, (base - new_cols),
n_cols);
ut_ad(old_col_no != ULINT_UNDEFINED);
base = &cols[old_col_no];
}
}
}
v_cols = old_v_cols;
v_col_names = old_v_col_names;
n_def = n_cols = old_n_cols;
n_v_def = n_v_cols = old_n_v_cols;
n_t_def = n_t_cols = n_cols + n_v_cols;
index->fields = old_fields;
while ((index = dict_table_get_next_index(index)) != NULL) {
if (index->to_be_dropped) {
/* instant_column() did not adjust these indexes. */
continue;
}
for (unsigned i = 0; i < index->n_fields; i++) {
dict_field_t& field = index->fields[i];
if (field.col < new_cols
|| field.col >= new_cols_end) {
DBUG_ASSERT(field.col->is_virtual()
|| field.col->is_dropped(*index));
dict_field_t& f = index->fields[i];
if (f.col->is_virtual()) {
DBUG_ASSERT(f.col >= &new_v_cols->m_col);
DBUG_ASSERT(f.col < &new_v_cols_end->m_col);
size_t n = size_t(
reinterpret_cast<dict_v_col_t*>(f.col)
- new_v_cols);
DBUG_ASSERT(n <= n_v_cols);
ulint old_col_no = find_old_col_no(
col_map, n + n_v_cols,
n_cols + n_v_cols);
ut_ad(old_col_no != ULINT_UNDEFINED);
f.col = &v_cols[old_col_no].m_col;
DBUG_ASSERT(f.col->is_virtual());
} else {
DBUG_ASSERT(field.col >= new_cols);
size_t n = size_t(field.col - new_cols);
DBUG_ASSERT(f.col >= new_cols);
DBUG_ASSERT(f.col < new_cols_end);
size_t n = size_t(f.col - new_cols);
DBUG_ASSERT(n <= n_cols);
old_col_no = find_old_col_no(col_map, n, n_cols);
ulint old_col_no = find_old_col_no(col_map,
n, n_cols);
ut_ad(old_col_no != ULINT_UNDEFINED);
field.col = &cols[old_col_no];
DBUG_ASSERT(!field.col->is_virtual());
field.name = field.col->name(*this);
f.col = &cols[old_col_no];
DBUG_ASSERT(!f.col->is_virtual());
}
f.name = f.col->name(*this);
}
}
}
......
......@@ -211,6 +211,12 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx
dict_field_t* const old_fields;
/** size of old_fields */
const unsigned old_n_fields;
/** original number of virtual columns in the table */
const unsigned old_n_v_cols;
/** original virtual columns of the table */
dict_v_col_t* const old_v_cols;
/** original virtual column names of the table */
const char* const old_v_col_names;
/** 0, or 1 + first column whose position changes in instant ALTER */
unsigned first_alter_pos;
/** Allow non-null conversion.
......@@ -272,6 +278,9 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx
old_instant(prebuilt_arg->table->instant),
old_fields(prebuilt_arg->table->indexes.start->fields),
old_n_fields(prebuilt_arg->table->indexes.start->n_fields),
old_n_v_cols(prebuilt_arg->table->n_v_cols),
old_v_cols(prebuilt_arg->table->v_cols),
old_v_col_names(prebuilt_arg->table->v_col_names),
first_alter_pos(0),
allow_not_null(allow_not_null_flag),
page_compression_level(page_compressed
......@@ -305,6 +314,9 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx
rw_lock_free(&index->lock);
dict_mem_index_free(index);
}
for (unsigned i = old_n_v_cols; i--; ) {
UT_DELETE(old_v_cols[i].v_indexes);
}
dict_mem_table_free(instant_table);
}
mem_heap_free(heap);
......@@ -536,6 +548,8 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx
old_cols, old_col_names,
old_instant,
old_fields, old_n_fields,
old_n_v_cols, old_v_cols,
old_v_col_names,
col_map);
}
......@@ -1251,13 +1265,13 @@ ha_innobase::check_if_supported_inplace_alter(
*/
| ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX
| ALTER_DROP_NON_UNIQUE_NON_PRIM_INDEX);
#if 1 /* MDEV-15562 FIXME: enable this, and adjust dict_v_col_t::m_col.ind */
if (supports_instant) {
flags &= ~(ALTER_ADD_STORED_BASE_COLUMN
| ALTER_DROP_STORED_COLUMN
flags &= ~(ALTER_DROP_STORED_COLUMN
#if 0 /* MDEV-15562 FIXME: remove check_v_col_in_order() and fix the code */
| ALTER_ADD_STORED_BASE_COLUMN
#endif
| ALTER_STORED_COLUMN_ORDER);
}
#endif
if (flags != 0
|| IF_PARTITIONING((altered_table->s->partition_info_str
&& altered_table->s->partition_info_str_len), 0)
......@@ -3578,12 +3592,13 @@ innobase_build_col_map(
}
}
ut_ad(!is_v);
innobase_build_col_map_add(
heap, dtuple_get_nth_field(defaults, i),
altered_table->field[i + num_v],
NULL,
dict_table_is_comp(new_table));
if (!is_v) {
innobase_build_col_map_add(
heap, dtuple_get_nth_field(defaults, i),
altered_table->field[i + num_v],
NULL,
dict_table_is_comp(new_table));
}
found_col:
if (is_v) {
num_v++;
......@@ -4051,13 +4066,12 @@ prepare_inplace_add_virtual(
ha_innobase_inplace_ctx* ctx;
ulint i = 0;
ulint j = 0;
const Create_field* new_field;
ctx = static_cast<ha_innobase_inplace_ctx*>
(ha_alter_info->handler_ctx);
ctx->num_to_add_vcol = altered_table->s->fields
+ ctx->num_to_drop_vcol - table->s->fields;
ctx->num_to_add_vcol = altered_table->s->virtual_fields
+ ctx->num_to_drop_vcol - table->s->virtual_fields;
ctx->add_vcol = static_cast<dict_v_col_t*>(
mem_heap_zalloc(ctx->heap, ctx->num_to_add_vcol
......@@ -4069,43 +4083,21 @@ prepare_inplace_add_virtual(
List_iterator_fast<Create_field> cf_it(
ha_alter_info->alter_info->create_list);
while ((new_field = (cf_it++)) != NULL) {
const Field* field = new_field->field;
ulint old_i;
for (old_i = 0; table->field[old_i]; old_i++) {
const Field* n_field = table->field[old_i];
if (field == n_field) {
break;
}
}
i++;
while (const Create_field* new_field = cf_it++) {
const Field* field = altered_table->field[i++];
if (table->field[old_i]) {
if (new_field->field || !innobase_is_v_fld(field)) {
continue;
}
ut_ad(!field);
ulint col_len;
ulint is_unsigned;
ulint field_type;
ulint charset_no;
field = altered_table->field[i - 1];
ulint col_type
= get_innobase_type_from_mysql_type(
&is_unsigned, field);
if (!innobase_is_v_fld(field)) {
continue;
}
col_len = field->pack_length();
field_type = (ulint) field->type();
ulint col_len = field->pack_length();
ulint field_type = (ulint) field->type();
if (!field->real_maybe_null()) {
field_type |= DATA_NOT_NULL;
......@@ -4147,7 +4139,6 @@ prepare_inplace_add_virtual(
}
}
ctx->add_vcol[j].m_col.prtype = dtype_form_prtype(
field_type, charset_no);
......@@ -4166,6 +4157,7 @@ prepare_inplace_add_virtual(
/* No need to track the list */
ctx->add_vcol[j].v_indexes = NULL;
/* FIXME: This should be done on ctx->instant_table later */
innodb_base_col_setup(ctx->old_table, field, &ctx->add_vcol[j]);
j++;
}
......@@ -4446,13 +4438,7 @@ innobase_add_virtual_try(
}
}
ulint n_col = unsigned(user_table->n_cols) - DATA_N_SYS_COLS;
ulint n_v_col = unsigned(user_table->n_v_cols)
+ ctx->num_to_add_vcol - ctx->num_to_drop_vcol;
ulint new_n = dict_table_encode_n_col(n_col, n_v_col)
+ (unsigned(user_table->flags & DICT_TF_COMPACT) << 31);
return innodb_update_cols(user_table, new_n, trx);
return false;
}
/** Add the newly added column in the sys_column system table.
......@@ -4709,14 +4695,7 @@ innobase_drop_virtual_try(
}
}
ulint n_col = unsigned(user_table->n_cols) - DATA_N_SYS_COLS;
ulint n_v_col = unsigned(user_table->n_v_cols)
- ctx->num_to_drop_vcol;
ulint new_n = dict_table_encode_n_col(n_col, n_v_col)
| ((user_table->flags & DICT_TF_COMPACT) << 31);
return innodb_update_cols(user_table, new_n, trx);
return false;
}
/** Construct the metadata record for instant ALTER TABLE.
......@@ -4929,6 +4908,7 @@ static bool innobase_instant_try(
}
if (ctx->first_alter_pos) {
add_all_virtual:
for (uint i = 0; i < user_table->n_v_cols; i++) {
if (innobase_add_one_virtual(
user_table,
......@@ -4937,18 +4917,15 @@ static bool innobase_instant_try(
return true;
}
}
} else {
if ((ha_alter_info->handler_flags & ALTER_DROP_VIRTUAL_COLUMN)
&& innobase_drop_virtual_try(ha_alter_info, user_table,
trx)) {
return true;
}
if ((ha_alter_info->handler_flags & ALTER_ADD_VIRTUAL_COLUMN)
&& innobase_add_virtual_try(ha_alter_info, user_table,
trx)) {
} else if (ha_alter_info->handler_flags & ALTER_DROP_VIRTUAL_COLUMN) {
if (innobase_instant_drop_cols(user_table->id, 65536, trx)) {
return true;
}
goto add_all_virtual;
} else if ((ha_alter_info->handler_flags & ALTER_ADD_VIRTUAL_COLUMN)
&& innobase_add_virtual_try(ha_alter_info, user_table,
trx)) {
return true;
}
unsigned i = unsigned(user_table->n_cols) - DATA_N_SYS_COLS;
......@@ -9455,18 +9432,32 @@ commit_try_norebuild(
}
#endif /* MYSQL_RENAME_INDEX */
if ((ha_alter_info->handler_flags
& ALTER_DROP_VIRTUAL_COLUMN)
&& !ctx->is_instant()
&& innobase_drop_virtual_try(ha_alter_info, ctx->old_table, trx)) {
DBUG_RETURN(true);
}
if (!ctx->is_instant() && ha_alter_info->handler_flags
& (ALTER_DROP_VIRTUAL_COLUMN | ALTER_ADD_VIRTUAL_COLUMN)) {
if ((ha_alter_info->handler_flags & ALTER_DROP_VIRTUAL_COLUMN)
&& innobase_drop_virtual_try(ha_alter_info, ctx->old_table,
trx)) {
DBUG_RETURN(true);
}
if ((ha_alter_info->handler_flags
& ALTER_ADD_VIRTUAL_COLUMN)
&& !ctx->is_instant()
&& innobase_add_virtual_try(ha_alter_info, ctx->old_table, trx)) {
DBUG_RETURN(true);
if ((ha_alter_info->handler_flags & ALTER_ADD_VIRTUAL_COLUMN)
&& innobase_add_virtual_try(ha_alter_info, ctx->old_table,
trx)) {
DBUG_RETURN(true);
}
ulint n_col = unsigned(ctx->old_table->n_cols)
- DATA_N_SYS_COLS;
ulint n_v_col = unsigned(ctx->old_table->n_v_cols)
+ ctx->num_to_add_vcol - ctx->num_to_drop_vcol;
if (innodb_update_cols(
ctx->old_table,
dict_table_encode_n_col(n_col, n_v_col)
| unsigned(ctx->old_table->flags & DICT_TF_COMPACT)
<< 31, trx)) {
DBUG_RETURN(true);
}
}
DBUG_RETURN(innobase_instant_try(ha_alter_info, ctx, altered_table,
......@@ -10336,6 +10327,9 @@ ha_innobase::commit_inplace_alter_table(
}
}
/* FIXME: Avoid this when ctx->is_instant().
Currently dict_load_column_low() is the only place where
num_base for virtual columns is assigned to nonzero. */
if (ctx0->num_to_drop_vcol || ctx0->num_to_add_vcol) {
DBUG_ASSERT(ctx0->old_table->get_ref_count() == 1);
......@@ -10353,6 +10347,12 @@ ha_innobase::commit_inplace_alter_table(
tb_name[strlen(m_prebuilt->table->name.m_name)] = 0;
dict_table_close(m_prebuilt->table, true, false);
if (ctx0->is_instant()) {
for (unsigned i = ctx0->old_n_v_cols; i--; ) {
UT_DELETE(ctx0->old_v_cols[i].v_indexes);
}
const_cast<unsigned&>(ctx0->old_n_v_cols) = 0;
}
dict_table_remove_from_cache(m_prebuilt->table);
m_prebuilt->table = dict_table_open_on_name(
tb_name, TRUE, TRUE, DICT_ERR_IGNORE_NONE);
......
......@@ -1614,7 +1614,8 @@ struct dict_table_t {
/** Adjust table metadata for instant ADD/DROP/reorder COLUMN.
@param[in] table altered table (with dropped columns)
@param[in] map mapping from cols[] to table.cols[] */
@param[in] map mapping from cols[] and v_cols[]
to table */
void instant_column(const dict_table_t& table, const ulint* map);
/** Roll back instant_column().
......@@ -1624,6 +1625,9 @@ struct dict_table_t {
@param[in] old_instant original instant structure
@param[in] old_fields original fields
@param[in] old_n_fields original number of fields
@param[in] old_n_v_cols original n_v_cols
@param[in] old_v_cols original v_cols
@param[in] old_v_col_names original v_col_names
@param[in] col_map column map */
void rollback_instant(
unsigned old_n_cols,
......@@ -1632,6 +1636,9 @@ struct dict_table_t {
dict_instant_t* old_instant,
dict_field_t* old_fields,
unsigned old_n_fields,
unsigned old_n_v_cols,
dict_v_col_t* old_v_cols,
const char* old_v_col_names,
const ulint* col_map);
/** Add the table definition to the data dictionary cache */
......
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