summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Malyavin <nikitamalyavin@gmail.com>2021-07-06 10:17:22 +0300
committerNikita Malyavin <nikitamalyavin@gmail.com>2021-07-19 18:26:11 +0300
commit6ad72c42d2d495f604ee15a8d9137a31e41a2cee (patch)
tree02209c1088168e065ffc04254f078345ced6cfa7
parentfa45400d77b07530da87e5a5cd92992b886a2f88 (diff)
downloadmariadb-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.result13
-rw-r--r--mysql-test/suite/gcol/t/innodb_virtual_index.test17
-rw-r--r--storage/innobase/handler/handler0alter.cc35
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