diff options
author | Nikita Malyavin <nikitamalyavin@gmail.com> | 2021-07-06 10:17:22 +0300 |
---|---|---|
committer | Nikita Malyavin <nikitamalyavin@gmail.com> | 2021-07-19 18:26:11 +0300 |
commit | 6ad72c42d2d495f604ee15a8d9137a31e41a2cee (patch) | |
tree | 02209c1088168e065ffc04254f078345ced6cfa7 | |
parent | fa45400d77b07530da87e5a5cd92992b886a2f88 (diff) | |
download | mariadb-git-bb-10.6-MDEV-20154.tar.gz |
MDEV-20154 Assertion `len <= col->len | ...` failed in row_merge_buf_addbb-10.6-MDEV-20154
len was containing garbage, since vctempl->mysql_col_offset was containing
old value while calling `row_mysql_store_col_in_innobase_format` from
`innobase_get_computed_value()`.
It was not updated after the first ALTER TABLE call, because it's INPLACE
logic considered there's nothing to update, and exited immediately from
`ha_innobase::inplace_alter_table()`.
However, vcol metadata needs an update, since vcols structure is changed
in mysql record.
There may be a plenty of solutions, but the simplest one is:
if vcol structure is changed, drop vc_templ; it will be recreated on next
`open()` call.
-rw-r--r-- | mysql-test/suite/gcol/r/innodb_virtual_index.result | 13 | ||||
-rw-r--r-- | mysql-test/suite/gcol/t/innodb_virtual_index.test | 17 | ||||
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 35 |
3 files changed, 57 insertions, 8 deletions
diff --git a/mysql-test/suite/gcol/r/innodb_virtual_index.result b/mysql-test/suite/gcol/r/innodb_virtual_index.result index 2e9b762500d..8f8e7946b87 100644 --- a/mysql-test/suite/gcol/r/innodb_virtual_index.result +++ b/mysql-test/suite/gcol/r/innodb_virtual_index.result @@ -296,3 +296,16 @@ Table Op Msg_type Msg_text test.t1 optimize note Table does not support optimize, doing recreate + analyze instead test.t1 optimize status OK DROP TABLE t1; +# +# MDEV-20154 Assertion `len <= col->len || ((col->mtype) == 5 +# || (col->mtype) == 14)' failed in row_merge_buf_add +# +CREATE TABLE t1 ( +a VARCHAR(2500), +b VARCHAR(2499) AS (a) VIRTUAL +) ENGINE=InnoDB; +INSERT INTO t1 (a) VALUES ('foo'); +ALTER TABLE t1 MODIFY a VARCHAR(2600), ALGORITHM=INPLACE; +ALTER TABLE t1 ADD KEY (b), ALGORITHM=INPLACE; +# Cleanup +DROP TABLE t1; diff --git a/mysql-test/suite/gcol/t/innodb_virtual_index.test b/mysql-test/suite/gcol/t/innodb_virtual_index.test index bb47379f3b2..f7796bd8a86 100644 --- a/mysql-test/suite/gcol/t/innodb_virtual_index.test +++ b/mysql-test/suite/gcol/t/innodb_virtual_index.test @@ -321,3 +321,20 @@ CREATE TABLE t1 (id INT PRIMARY KEY, a CHAR(3), INSERT INTO t1 (id,a) VALUES (1,'foo'); OPTIMIZE TABLE t1; DROP TABLE t1; + +--echo # +--echo # MDEV-20154 Assertion `len <= col->len || ((col->mtype) == 5 +--echo # || (col->mtype) == 14)' failed in row_merge_buf_add +--echo # + +CREATE TABLE t1 ( + a VARCHAR(2500), + b VARCHAR(2499) AS (a) VIRTUAL +) ENGINE=InnoDB; +INSERT INTO t1 (a) VALUES ('foo'); + +ALTER TABLE t1 MODIFY a VARCHAR(2600), ALGORITHM=INPLACE; +ALTER TABLE t1 ADD KEY (b), ALGORITHM=INPLACE; + +--echo # Cleanup +DROP TABLE t1; diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 62f9d103aa1..9b9d986cf7a 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -8301,8 +8301,7 @@ alter_templ_needs_rebuild( for (ulint j=0; j < table->n_cols; j++) { dict_col_t* cols = dict_table_get_nth_col(table, j); - if (cf.length > cols->len - && dict_col_in_v_indexes(table, cols)) { + if (cf.length > cols->len) { return(true); } } @@ -8363,7 +8362,32 @@ ha_innobase::inplace_alter_table( DEBUG_SYNC(m_user_thd, "innodb_inplace_alter_table_enter"); + ha_innobase_inplace_ctx* ctx =static_cast<ha_innobase_inplace_ctx*> + (ha_alter_info->handler_ctx); + + bool field_len_changed= ctx ? + alter_templ_needs_rebuild(altered_table, + ha_alter_info, + ctx->new_table) : + 0; + if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA)) { + if (field_len_changed && ctx->new_table->n_v_cols > 0) { + /* Changing mysql record structure may end up here if + only virtual fields were altered. In this case, + however, vc_templ should be rebuilt. Since we don't + actually change any stored data, we can just dispose + vc_templ; it will be recreated on next + ha_innobase::open(). */ + + dict_free_vc_templ(ctx->new_table->vc_templ); + UT_DELETE(ctx->new_table->vc_templ); + + DBUG_ASSERT(ctx->new_table->vc_templ == + ctx->old_table->vc_templ); + ctx->new_table->vc_templ = NULL; + ctx->old_table->vc_templ = NULL; + } ok_exit: DEBUG_SYNC(m_user_thd, "innodb_after_inplace_alter_table"); DBUG_RETURN(false); @@ -8377,10 +8401,6 @@ ok_exit: goto ok_exit; } - ha_innobase_inplace_ctx* ctx - = static_cast<ha_innobase_inplace_ctx*> - (ha_alter_info->handler_ctx); - DBUG_ASSERT(ctx); DBUG_ASSERT(ctx->trx); DBUG_ASSERT(ctx->prebuilt == m_prebuilt); @@ -8412,8 +8432,7 @@ ok_exit: = ctx->need_rebuild() || ((ha_alter_info->handler_flags & ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE) - && alter_templ_needs_rebuild( - altered_table, ha_alter_info, ctx->new_table)); + && field_len_changed); if ((ctx->new_table->n_v_cols > 0) && rebuild_templ) { /* Save the templ if isn't NULL so as to restore the |