From 0a7faed75ad49b99bace0882f89e08639c85679b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 20 Jul 2020 14:15:56 +0300 Subject: [PATCH] MDEV-22771 Instant extension of CHAR column is wrongly allowed commit 854c219a7f0e1878517d5a821992f650342380dd (MDEV-17301) broke a constraint: Fixed-length columns cannot be extended in InnoDB without rebuilding the table. ha_innobase::can_convert_string(): Correct the condition. We must not allow any instantaneous change to the length of CHAR columns measured in characters. For any format other than ROW_FORMAT=REDUNDANT, we can allow the length in bytes to be extended if mbminlen 9115 bytes .../suite/innodb/t/instant_alter_extend.test | 9 ++++ storage/innobase/handler/ha_innodb.cc | 50 ++++++++---------- 4 files changed, 50 insertions(+), 40 deletions(-) diff --git a/mysql-test/suite/innodb/r/instant_alter_extend,utf8.rdiff b/mysql-test/suite/innodb/r/instant_alter_extend,utf8.rdiff index 596dfe43ab8..08f9afa696a 100644 --- a/mysql-test/suite/innodb/r/instant_alter_extend,utf8.rdiff +++ b/mysql-test/suite/innodb/r/instant_alter_extend,utf8.rdiff @@ -1,29 +1,38 @@ --- instant_alter_convert.result +++ instant_alter_convert,utf8.result -@@ -37,7 +37,7 @@ - test.t check status OK +@@ -38,7 +38,7 @@ + best.t check status OK call check_table('t'); name mtype prtype len -a 2 800FE 200 +a 13 2100FE 600 # CHAR enlargement - alter table t modify a char(220), algorithm=instant; - select count(a) from t where a = @bigval; -@@ -51,7 +51,7 @@ - test.t check status OK + alter table t modify a char(220); + affected rows: 2 +@@ -54,7 +54,7 @@ + best.t check status OK call check_table('t'); name mtype prtype len -a 2 800FE 220 +a 13 2100FE 660 + ALTER TABLE t CHANGE COLUMN a a CHAR(230) BINARY; + affected rows: 2 + info: Records: 2 Duplicates: 0 Warnings: 0 +@@ -69,7 +69,7 @@ + best.t check status OK + call check_table('t'); + name mtype prtype len +-a 13 2F00FE 230 ++a 13 5300FE 690 # Convert from VARCHAR to a bigger CHAR - alter table t modify a varchar(200), algorithm=instant; - ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY -@@ -72,7 +72,7 @@ - test.t check status OK + alter table t modify a varchar(200); + affected rows: 2 +@@ -92,7 +92,7 @@ + best.t check status OK call check_table('t'); name mtype prtype len -a 2 800FE 255 +a 13 2100FE 765 # BINARY/VARBINARY test create or replace table t (a varbinary(300)); - alter table t modify a binary(255), algorithm=instant; + insert into t values(NULL); diff --git a/mysql-test/suite/innodb/r/instant_alter_extend.result b/mysql-test/suite/innodb/r/instant_alter_extend.result index fb03ef9a18299127f66988953cf2ab35fd9b148d..33a5f57c7b64609b71ec4960780c7a545e240e38 100644 GIT binary patch delta 148 zcmaFkJllOkI-7x`Pl#)fLWrZ2kE=q7g0qLCpS!Dqv%gQMub)C95CTPlG>nW5G!>jY z{TzcLC-;jxsGw?cba8>{NK)|h3sG?M3FJjI@@M3j#g;^BY6a% diff --git a/mysql-test/suite/innodb/t/instant_alter_extend.test b/mysql-test/suite/innodb/t/instant_alter_extend.test index 4320d9bae05..7258ba6d238 100644 --- a/mysql-test/suite/innodb/t/instant_alter_extend.test +++ b/mysql-test/suite/innodb/t/instant_alter_extend.test @@ -64,6 +64,15 @@ select a, length(a) from t where a = 'z'; check table t extended; call check_table('t'); +--enable_info +ALTER TABLE t CHANGE COLUMN a a CHAR(230) BINARY; +ALTER TABLE t ADD COLUMN b INT FIRST; +ALTER TABLE t DROP b; +--disable_info + +check table t extended; +call check_table('t'); + --echo # Convert from VARCHAR to a bigger CHAR --enable_info alter table t modify a varchar(200); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index f85ee3f26fb..6d100576c20 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -20986,43 +20986,35 @@ is_part_of_a_primary_key(const Field* field) && field->part_of_key.is_set(s->primary_key); } -bool -ha_innobase::can_convert_string(const Field_string* field, - const Column_definition& new_type) const +bool ha_innobase::can_convert_string(const Field_string *field, + const Column_definition &new_type) const { - DBUG_ASSERT(!field->compression_method()); - if (new_type.type_handler() != field->type_handler()) { - return false; - } + DBUG_ASSERT(!field->compression_method()); + if (new_type.type_handler() != field->type_handler()) + return false; - if (new_type.char_length < field->char_length()) { - return false; - } + if (new_type.char_length != field->char_length()) + return false; - if (new_type.charset != field->charset()) { - if (new_type.length != field->max_display_length() - && !m_prebuilt->table->not_redundant()) { - return IS_EQUAL_NO; - } + const Charset field_cs(field->charset()); - Charset field_cs(field->charset()); - if (!field_cs.encoding_allows_reinterpret_as( - new_type.charset)) { - return false; - } + if (new_type.length != field->max_display_length() && + (!m_prebuilt->table->not_redundant() || + field_cs.mbminlen() == field_cs.mbmaxlen())) + return false; - if (!field_cs.eq_collation_specific_names(new_type.charset)) { - return !is_part_of_a_primary_key(field); - } + if (new_type.charset != field->charset()) + { + if (!field_cs.encoding_allows_reinterpret_as(new_type.charset)) + return false; - return true; - } + if (!field_cs.eq_collation_specific_names(new_type.charset)) + return !is_part_of_a_primary_key(field); - if (new_type.length != field->max_display_length()) { - return false; - } + return true; + } - return true; + return true; } static bool