From e53e7cd1343a8a3bccf5627fe1f69be1c820d4d2 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 9 Nov 2023 16:26:11 +0300 Subject: [PATCH] MDEV-20545 Assertion col.vers_sys_end() in dict_index_t::vers_history_row Index values for row_start/row_end was wrongly calculated for inplace ALTER for some layout of virtual fields. Possible impact 1. history row is not detected upon build clustered index for inplace ALTER which may lead to duplicate key errors on auto-increment and FTS index add. 2. foreign key constraint may falsely fail. 3. after inplace ALTER before server restart trx-based system versioning can cause server crash or incorrect data written upon UPDATE. --- mysql-test/suite/versioning/r/alter.result | 59 ++++++++++++++++++++ mysql-test/suite/versioning/t/alter.test | 62 ++++++++++++++++++++++ sql/field.h | 10 ++++ storage/innobase/handler/handler0alter.cc | 10 ++-- 4 files changed, 137 insertions(+), 4 deletions(-) diff --git a/mysql-test/suite/versioning/r/alter.result b/mysql-test/suite/versioning/r/alter.result index a026b68a11b..7891fa60024 100644 --- a/mysql-test/suite/versioning/r/alter.result +++ b/mysql-test/suite/versioning/r/alter.result @@ -786,3 +786,62 @@ with system versioning, modify row_start varchar(8); ERROR HY000: PERIOD FOR SYSTEM_TIME must use columns `row_start` and `row_end` drop table t1; +# +# MDEV-20545 Assertion (col.vers_sys_end()) upon inplace ALTER with virtual columns +# +create table t1 ( +a int, +va int as (a) virtual, +b int, +vb int as (b) virtual, +c int, +vc int as (c) virtual, +d int, +e int, +index(va) +) engine=innodb with system versioning; +replace into t1 () values (),(); +set statement system_versioning_alter_history=keep for alter table t1 drop e; +alter table t1 algorithm=inplace, drop system versioning; +drop table t1; +# +# MDEV-20765 Assertion (type.vers_sys_end()) upon inplace ALTER with virtual columns +# +create table t1 ( +a int, +b int as (a) virtual, +c int, +d int as (c) virtual, +e int, +f int as (e) virtual, +g int, +h int, +i int, +index(d), +key(h), +foreign key (g) references t1 (h) +) engine=innodb with system versioning; +set system_versioning_alter_history= keep; +alter table t1 drop column i; +insert into t1 (g,h) values (1,1); +drop table t1; +# +# MDEV-29034 Assertion (o->ind == vers_start) upon inplace ALTER with virtual columns +# +create table b ( +pk integer auto_increment, +col_int_key integer, +col_varchar_key varchar(1), +o bit, n bit, +h float as ( n + 2 ) virtual, +v bit, +primary key (pk), +key (col_varchar_key, col_int_key) +) engine = innodb; +set `system_versioning_alter_history`= keep; +alter table `b` add system versioning; +alter table `b` add column if not exists ( w bit, v serial ); +Warnings: +Note 1060 Duplicate column name 'v' +alter table `b` add column if not exists ( p bit ); +drop table `b`; diff --git a/mysql-test/suite/versioning/t/alter.test b/mysql-test/suite/versioning/t/alter.test index 311f2d97bdf..ed52d179eaa 100644 --- a/mysql-test/suite/versioning/t/alter.test +++ b/mysql-test/suite/versioning/t/alter.test @@ -677,3 +677,65 @@ alter table t1 modify row_start varchar(8); # cleanup drop table t1; + +--echo # +--echo # MDEV-20545 Assertion (col.vers_sys_end()) upon inplace ALTER with virtual columns +--echo # +create table t1 ( + a int, + va int as (a) virtual, + b int, + vb int as (b) virtual, + c int, + vc int as (c) virtual, + d int, + e int, + index(va) +) engine=innodb with system versioning; +replace into t1 () values (),(); +set statement system_versioning_alter_history=keep for alter table t1 drop e; +alter table t1 algorithm=inplace, drop system versioning; +# cleanup +drop table t1; + +--echo # +--echo # MDEV-20765 Assertion (type.vers_sys_end()) upon inplace ALTER with virtual columns +--echo # +create table t1 ( + a int, + b int as (a) virtual, + c int, + d int as (c) virtual, + e int, + f int as (e) virtual, + g int, + h int, + i int, + index(d), + key(h), + foreign key (g) references t1 (h) +) engine=innodb with system versioning; +set system_versioning_alter_history= keep; +alter table t1 drop column i; +insert into t1 (g,h) values (1,1); +# cleanup +drop table t1; + +--echo # +--echo # MDEV-29034 Assertion (o->ind == vers_start) upon inplace ALTER with virtual columns +--echo # +create table b ( + pk integer auto_increment, + col_int_key integer, + col_varchar_key varchar(1), + o bit, n bit, + h float as ( n + 2 ) virtual, + v bit, + primary key (pk), + key (col_varchar_key, col_int_key) +) engine = innodb; +set `system_versioning_alter_history`= keep; +alter table `b` add system versioning; +alter table `b` add column if not exists ( w bit, v serial ); +alter table `b` add column if not exists ( p bit ); +drop table `b`; diff --git a/sql/field.h b/sql/field.h index a8bb9e2c9cb..8d23a05e720 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1612,6 +1612,16 @@ public: return flags & (VERS_ROW_START | VERS_ROW_END); } + bool vers_sys_start() const + { + return flags & VERS_ROW_START; + } + + bool vers_sys_end() const + { + return flags & VERS_ROW_END; + } + bool vers_update_unversioned() const { return flags & VERS_UPDATE_UNVERSIONED_FLAG; diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 6b2356ff6b3..b61949620f7 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -4556,10 +4556,12 @@ innobase_build_col_map( col_map[old_i - num_old_v] = i; if (old_table->versioned() && altered_table->versioned()) { - if (old_i == old_table->vers_start) { - new_table->vers_start = i + num_v; - } else if (old_i == old_table->vers_end) { - new_table->vers_end = i + num_v; + if (old_i - num_old_v == old_table->vers_start) { + ut_ad(field->vers_sys_start()); + new_table->vers_start = i; + } else if (old_i - num_old_v == old_table->vers_end) { + ut_ad(field->vers_sys_end()); + new_table->vers_end = i; } } goto found_col;