diff options
author | Eugene Kosov <claprix@yandex.ru> | 2019-06-14 12:18:49 +0300 |
---|---|---|
committer | Eugene Kosov <claprix@yandex.ru> | 2019-06-22 03:53:00 +0300 |
commit | b6e88535d42d01ab6c566967159d1b4c8fd0fa11 (patch) | |
tree | 126642a757db72f89788bec10cfb1c1be5f42772 | |
parent | e9a692fe1ecd82e0812f4986a0010ca0db9295b7 (diff) | |
download | mariadb-git-b6e88535d42d01ab6c566967159d1b4c8fd0fa11.tar.gz |
MDEV-17301 Change of COLLATE unnecessarily requires ALGORITHM=COPY
Patch is about two cases:
1) On some collate changes it's possible to rebuild only secondary indexes
2) For non-indexed columns collate can be changed INSTANTly
Implemented mostly in Field_{string,varstring,blob}::is_equal().
Make this method return how exactly collationa differs.
This information is later used by fill_alter_inplace_info() to pass
correct info to engine.
-rw-r--r-- | mysql-test/suite/innodb/r/instant_alter_charset,redundant.rdiff | 49 | ||||
-rw-r--r-- | mysql-test/suite/innodb/r/instant_alter_charset.result | 113 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/instant_alter_charset.test | 111 | ||||
-rw-r--r-- | sql/field.cc | 97 | ||||
-rw-r--r-- | sql/field.h | 3 | ||||
-rw-r--r-- | sql/sql_priv.h | 8 | ||||
-rw-r--r-- | sql/sql_string.h | 2 | ||||
-rw-r--r-- | sql/sql_table.cc | 44 | ||||
-rw-r--r-- | sql/sql_type.cc | 14 | ||||
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 16 |
10 files changed, 377 insertions, 80 deletions
diff --git a/mysql-test/suite/innodb/r/instant_alter_charset,redundant.rdiff b/mysql-test/suite/innodb/r/instant_alter_charset,redundant.rdiff index d7d2a32d742..22b7e46ea9c 100644 --- a/mysql-test/suite/innodb/r/instant_alter_charset,redundant.rdiff +++ b/mysql-test/suite/innodb/r/instant_alter_charset,redundant.rdiff @@ -1,10 +1,51 @@ ---- instant_alter_charset.result 2019-04-23 17:42:23.324326518 +0400 -+++ instant_alter_charset,redundant.result 2019-04-23 17:42:46.047531591 +0400 +--- instant_alter_charset.result 2019-06-17 14:36:02.311515062 +0300 ++++ instant_alter_charset,redundant.result 2019-06-17 14:50:11.888705725 +0300 @@ -279,7 +279,6 @@ alter table boundary_255 modify a varchar(70) charset utf8mb4, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY drop table boundary_255; - create table fully_compatible ( - id int auto_increment unique key, + create table t ( + a char(10) collate utf8mb3_general_ci, +@@ -297,32 +296,21 @@ + repeat('a', 10), repeat('a', 10) + ); + alter table t modify a char(10) collate utf8mb4_general_ci, algorithm=instant; +-check table t; +-Table Op Msg_type Msg_text +-test.t check status OK ++ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY + alter table t modify b char(70) collate utf8mb4_general_ci, algorithm=instant; +-check table t; +-Table Op Msg_type Msg_text +-test.t check status OK ++ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY + alter table t modify c char(100) collate utf8mb4_general_ci, algorithm=instant; +-check table t; +-Table Op Msg_type Msg_text +-test.t check status OK ++ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY + alter table t modify aa char(10) collate utf8mb4_general_ci, algorithm=instant; +-check table t; +-Table Op Msg_type Msg_text +-test.t check status OK ++ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY + alter table t modify bb char(70) collate utf8mb4_general_ci, algorithm=instant; +-check table t; +-Table Op Msg_type Msg_text +-test.t check status OK ++ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY + alter table t modify cc char(100) collate utf8mb4_general_ci, algorithm=instant; +-check table t; +-Table Op Msg_type Msg_text +-test.t check status OK ++ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY + alter table t modify d char(10) collate utf8mb4_spanish_ci, algorithm=instant; ++ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY + alter table t modify dd char(10) collate utf8mb4_spanish_ci, algorithm=instant; +-ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY ++ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY + select * from t; + a b c aa bb cc d dd + aaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaa aaaaaaaaaa diff --git a/mysql-test/suite/innodb/r/instant_alter_charset.result b/mysql-test/suite/innodb/r/instant_alter_charset.result index 2e278ac54f4..be15b02a8c6 100644 --- a/mysql-test/suite/innodb/r/instant_alter_charset.result +++ b/mysql-test/suite/innodb/r/instant_alter_charset.result @@ -281,6 +281,52 @@ modify a varchar(70) charset utf8mb4, algorithm=instant; ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY drop table boundary_255; +create table t ( +a char(10) collate utf8mb3_general_ci, +b char(70) collate utf8mb3_general_ci, +c char(100) collate utf8mb3_general_ci, +aa char(10) collate utf8mb3_general_ci unique, +bb char(70) collate utf8mb3_general_ci unique, +cc char(100) collate utf8mb3_general_ci unique, +d char(10) collate utf8mb3_general_ci, +dd char(10) collate utf8mb3_general_ci unique +) engine=innodb; +insert into t values +(repeat('a', 10), repeat('a', 70), repeat('a', 100), +repeat('a', 10), repeat('a', 70), repeat('a', 100), +repeat('a', 10), repeat('a', 10) +); +alter table t modify a char(10) collate utf8mb4_general_ci, algorithm=instant; +check table t; +Table Op Msg_type Msg_text +test.t check status OK +alter table t modify b char(70) collate utf8mb4_general_ci, algorithm=instant; +check table t; +Table Op Msg_type Msg_text +test.t check status OK +alter table t modify c char(100) collate utf8mb4_general_ci, algorithm=instant; +check table t; +Table Op Msg_type Msg_text +test.t check status OK +alter table t modify aa char(10) collate utf8mb4_general_ci, algorithm=instant; +check table t; +Table Op Msg_type Msg_text +test.t check status OK +alter table t modify bb char(70) collate utf8mb4_general_ci, algorithm=instant; +check table t; +Table Op Msg_type Msg_text +test.t check status OK +alter table t modify cc char(100) collate utf8mb4_general_ci, algorithm=instant; +check table t; +Table Op Msg_type Msg_text +test.t check status OK +alter table t modify d char(10) collate utf8mb4_spanish_ci, algorithm=instant; +alter table t modify dd char(10) collate utf8mb4_spanish_ci, algorithm=instant; +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY +select * from t; +a b c aa bb cc d dd +aaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaa aaaaaaaaaa +drop table t; create table fully_compatible ( id int auto_increment unique key, from_charset char(255), @@ -784,7 +830,7 @@ algorithm=instant; alter table tmp modify b varchar(50) charset utf8mb4 collate utf8mb4_vietnamese_ci, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY alter table tmp modify c varchar(50) charset utf8mb4 collate utf8mb4_vietnamese_ci, algorithm=instant; @@ -801,7 +847,7 @@ algorithm=instant; alter table tmp modify b varchar(50) charset utf8mb4 collate utf8mb4_vietnamese_ci, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY alter table tmp modify c varchar(50) charset utf8mb4 collate utf8mb4_vietnamese_ci, algorithm=instant; @@ -818,7 +864,7 @@ algorithm=instant; alter table tmp modify b varchar(50) charset utf8mb4 collate utf8mb4_vietnamese_ci, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY alter table tmp modify c varchar(50) charset utf8mb4 collate utf8mb4_vietnamese_ci, algorithm=instant; @@ -835,7 +881,7 @@ algorithm=instant; alter table tmp modify b varchar(50) charset utf8mb4 collate utf8mb4_vietnamese_ci, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY alter table tmp modify c varchar(50) charset utf8mb4 collate utf8mb4_vietnamese_ci, algorithm=instant; @@ -852,7 +898,7 @@ algorithm=instant; alter table tmp modify b varchar(50) charset ascii collate ascii_bin, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY alter table tmp modify c varchar(50) charset ascii collate ascii_bin, algorithm=instant; @@ -869,7 +915,7 @@ algorithm=instant; alter table tmp modify b varchar(50) charset utf8mb3 collate utf8mb3_lithuanian_ci, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY alter table tmp modify c varchar(50) charset utf8mb3 collate utf8mb3_lithuanian_ci, algorithm=instant; @@ -886,7 +932,7 @@ algorithm=instant; alter table tmp modify b varchar(50) charset utf8mb4 collate utf8mb4_persian_ci, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY alter table tmp modify c varchar(50) charset utf8mb4 collate utf8mb4_persian_ci, algorithm=instant; @@ -903,7 +949,7 @@ algorithm=instant; alter table tmp modify b varchar(50) charset utf8mb4 collate utf8mb4_german2_ci, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY alter table tmp modify c varchar(50) charset utf8mb4 collate utf8mb4_german2_ci, algorithm=instant; @@ -920,7 +966,7 @@ algorithm=instant; alter table tmp modify b varchar(50) charset utf8mb3 collate utf8mb3_unicode_ci, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY alter table tmp modify c varchar(50) charset utf8mb3 collate utf8mb3_unicode_ci, algorithm=instant; @@ -937,7 +983,7 @@ algorithm=instant; alter table tmp modify b varchar(50) charset latin1 collate latin1_general_ci, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY alter table tmp modify c varchar(50) charset latin1 collate latin1_general_ci, algorithm=instant; @@ -954,7 +1000,7 @@ algorithm=instant; alter table tmp modify b varchar(50) charset utf16 collate utf16_german2_ci, algorithm=instant; -ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY alter table tmp modify c varchar(50) charset utf16 collate utf16_german2_ci, algorithm=instant; @@ -1827,3 +1873,48 @@ CREATE TABLE t1 (a VARCHAR(1), UNIQUE(a)) ENGINE=InnoDB; ALTER TABLE t1 MODIFY a INT, ADD b INT, ADD UNIQUE (b), ALGORITHM=INSTANT; ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY DROP TABLE t1; +# +# MDEV-17301 Change of COLLATE unnecessarily requires ALGORITHM=COPY +# +create table t ( +a char(10) collate latin1_general_ci primary key, +b char(10) collate latin1_general_ci, +c char(10) collate latin1_general_ci, +unique key b_key(b) +) engine=innodb; +insert into t values +('aaa', 'aaa', 'aaa'), ('ccc', 'ccc', 'ccc'), ('bbb', 'bbb', 'bbb'); +alter table t modify a char(10) collate latin1_general_cs, algorithm=inplace; +ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +alter table t modify b char(10) collate latin1_general_cs, algorithm=instant; +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY +alter table t modify b char(10) collate latin1_general_cs, algorithm=nocopy; +check table t; +Table Op Msg_type Msg_text +test.t check status OK +alter table t modify c char(10) collate latin1_general_cs, algorithm=instant; +check table t; +Table Op Msg_type Msg_text +test.t check status OK +drop table t; +create table t ( +a varchar(10) collate latin1_general_ci primary key, +b varchar(10) collate latin1_general_ci, +c varchar(10) collate latin1_general_ci, +unique key b_key(b) +) engine=innodb; +insert into t values +('aaa', 'aaa', 'aaa'), ('ccc', 'ccc', 'ccc'), ('bbb', 'bbb', 'bbb'); +alter table t modify a varchar(10) collate latin1_general_cs, algorithm=inplace; +ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY +alter table t modify b varchar(10) collate latin1_general_cs, algorithm=instant; +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: ADD INDEX. Try ALGORITHM=NOCOPY +alter table t modify b varchar(10) collate latin1_general_cs, algorithm=nocopy; +check table t; +Table Op Msg_type Msg_text +test.t check status OK +alter table t modify c varchar(10) collate latin1_general_cs, algorithm=instant; +check table t; +Table Op Msg_type Msg_text +test.t check status OK +drop table t; diff --git a/mysql-test/suite/innodb/t/instant_alter_charset.test b/mysql-test/suite/innodb/t/instant_alter_charset.test index 8259689a139..b6e45cb6924 100644 --- a/mysql-test/suite/innodb/t/instant_alter_charset.test +++ b/mysql-test/suite/innodb/t/instant_alter_charset.test @@ -321,6 +321,67 @@ alter table boundary_255 drop table boundary_255; + +create table t ( + a char(10) collate utf8mb3_general_ci, + b char(70) collate utf8mb3_general_ci, + c char(100) collate utf8mb3_general_ci, + + aa char(10) collate utf8mb3_general_ci unique, + bb char(70) collate utf8mb3_general_ci unique, + cc char(100) collate utf8mb3_general_ci unique, + + d char(10) collate utf8mb3_general_ci, + dd char(10) collate utf8mb3_general_ci unique +) engine=innodb; +insert into t values + (repeat('a', 10), repeat('a', 70), repeat('a', 100), + repeat('a', 10), repeat('a', 70), repeat('a', 100), + repeat('a', 10), repeat('a', 10) +); +if ($row_format != 'redundant') { +alter table t modify a char(10) collate utf8mb4_general_ci, algorithm=instant; +check table t; +alter table t modify b char(70) collate utf8mb4_general_ci, algorithm=instant; +check table t; +alter table t modify c char(100) collate utf8mb4_general_ci, algorithm=instant; +check table t; + +alter table t modify aa char(10) collate utf8mb4_general_ci, algorithm=instant; +check table t; +alter table t modify bb char(70) collate utf8mb4_general_ci, algorithm=instant; +check table t; +alter table t modify cc char(100) collate utf8mb4_general_ci, algorithm=instant; +check table t; + +alter table t modify d char(10) collate utf8mb4_spanish_ci, algorithm=instant; +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify dd char(10) collate utf8mb4_spanish_ci, algorithm=instant; +} +if ($row_format == 'redundant') { +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify a char(10) collate utf8mb4_general_ci, algorithm=instant; +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify b char(70) collate utf8mb4_general_ci, algorithm=instant; +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify c char(100) collate utf8mb4_general_ci, algorithm=instant; + +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify aa char(10) collate utf8mb4_general_ci, algorithm=instant; +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify bb char(70) collate utf8mb4_general_ci, algorithm=instant; +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify cc char(100) collate utf8mb4_general_ci, algorithm=instant; + +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify d char(10) collate utf8mb4_spanish_ci, algorithm=instant; +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify dd char(10) collate utf8mb4_spanish_ci, algorithm=instant; +} +select * from t; +drop table t; + + create table fully_compatible ( id int auto_increment unique key, from_charset char(255), @@ -604,3 +665,53 @@ CREATE TABLE t1 (a VARCHAR(1), UNIQUE(a)) ENGINE=InnoDB; ALTER TABLE t1 MODIFY a INT, ADD b INT, ADD UNIQUE (b), ALGORITHM=INSTANT; DROP TABLE t1; + +--echo # +--echo # MDEV-17301 Change of COLLATE unnecessarily requires ALGORITHM=COPY +--echo # + +create table t ( + a char(10) collate latin1_general_ci primary key, + b char(10) collate latin1_general_ci, + c char(10) collate latin1_general_ci, + unique key b_key(b) +) engine=innodb; + +insert into t values + ('aaa', 'aaa', 'aaa'), ('ccc', 'ccc', 'ccc'), ('bbb', 'bbb', 'bbb'); + +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify a char(10) collate latin1_general_cs, algorithm=inplace; + +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify b char(10) collate latin1_general_cs, algorithm=instant; +alter table t modify b char(10) collate latin1_general_cs, algorithm=nocopy; +check table t; + +alter table t modify c char(10) collate latin1_general_cs, algorithm=instant; +check table t; + +drop table t; + +create table t ( + a varchar(10) collate latin1_general_ci primary key, + b varchar(10) collate latin1_general_ci, + c varchar(10) collate latin1_general_ci, + unique key b_key(b) +) engine=innodb; + +insert into t values + ('aaa', 'aaa', 'aaa'), ('ccc', 'ccc', 'ccc'), ('bbb', 'bbb', 'bbb'); + +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify a varchar(10) collate latin1_general_cs, algorithm=inplace; + +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t modify b varchar(10) collate latin1_general_cs, algorithm=instant; +alter table t modify b varchar(10) collate latin1_general_cs, algorithm=nocopy; +check table t; + +alter table t modify c varchar(10) collate latin1_general_cs, algorithm=instant; +check table t; + +drop table t; diff --git a/sql/field.cc b/sql/field.cc index 1c874c1021a..761e2c66641 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7073,36 +7073,40 @@ int Field_str::store(double nr) return store(buff, (uint)length, &my_charset_numeric); } - -bool Field_longstr:: - csinfo_change_allows_instant_alter(const Create_field *to) const +uint Field_longstr:: + is_equal_for_different_charsets(const Column_definition &to) const { - Charset cs(field_charset); - const bool part_of_a_key= !to->field->part_of_key.is_clear_all(); - return part_of_a_key ? - cs.encoding_and_order_allow_reinterpret_as(to->charset) : - cs.encoding_allows_reinterpret_as(to->charset); -} + Charset field_cs(field_charset); + if (!field_cs.encoding_allows_reinterpret_as(to.charset)) + return IS_EQUAL_NO; + if (!field_cs.eq_collation_specific_names(to.charset)) + return IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET_BUT_COLLATE; + + return IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET; +} uint Field_string::is_equal(Create_field *new_field) { DBUG_ASSERT(!compression_method()); if (new_field->type_handler() != type_handler()) return IS_EQUAL_NO; - if (new_field->length < max_display_length()) - return IS_EQUAL_NO; if (new_field->char_length < char_length()) return IS_EQUAL_NO; - if (!csinfo_change_allows_instant_alter(new_field)) - return IS_EQUAL_NO; + if (new_field->charset != field_charset) + { + if (new_field->length != max_display_length() && + table->file->ha_table_flags() & HA_EXTENDED_TYPES_CONVERSION) + return IS_EQUAL_NO; - if (new_field->length == max_display_length()) - return new_field->charset == field_charset - ? IS_EQUAL_YES : IS_EQUAL_PACK_LENGTH; + return is_equal_for_different_charsets(*new_field); + } - return IS_EQUAL_NO; + if (new_field->length != max_display_length()) + return IS_EQUAL_NO; + + return IS_EQUAL_YES; } @@ -7936,6 +7940,20 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table, return res; } +/* + This check is InnoDB specific. ROW_FORMAT=REDUNDANT always allows such + enlargement. But other row formats can do this only for particular current + and new lengths. This is because InnoDB stores VARCHAR length in one or two + bytes. +*/ +static bool supports_such_enlargement(const Field *field, + const Create_field *new_field) +{ + return field->field_length <= 127 || new_field->length <= 255 || + field->field_length > 255 || + (field->table->file->ha_table_flags() & HA_EXTENDED_TYPES_CONVERSION); +} + uint Field_varstring::is_equal(Create_field *new_field) { if (new_field->length < field_length) @@ -7944,24 +7962,26 @@ uint Field_varstring::is_equal(Create_field *new_field) return IS_EQUAL_NO; if (!new_field->compression_method() != !compression_method()) return IS_EQUAL_NO; - - if (!csinfo_change_allows_instant_alter(new_field)) + if (new_field->type_handler() != type_handler()) return IS_EQUAL_NO; - const Type_handler *new_type_handler= new_field->type_handler(); - if (new_type_handler == type_handler()) + if (new_field->charset != field_charset) { - if (new_field->length == field_length) - return new_field->charset == field_charset - ? IS_EQUAL_YES : IS_EQUAL_PACK_LENGTH; - if (field_length <= 127 || - new_field->length <= 255 || - field_length > 255 || - (table->file->ha_table_flags() & HA_EXTENDED_TYPES_CONVERSION)) - return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer length + if (!supports_such_enlargement(this, new_field)) + return IS_EQUAL_NO; + + return is_equal_for_different_charsets(*new_field); } - return IS_EQUAL_NO; + if (new_field->length != field_length) + { + if (!supports_such_enlargement(this, new_field)) + return IS_EQUAL_NO; + + return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer length + } + + return IS_EQUAL_YES; } @@ -8728,25 +8748,16 @@ uint Field_blob::max_packed_col_length(uint max_length) uint Field_blob::is_equal(Create_field *new_field) { if (new_field->type_handler() != type_handler()) - { return IS_EQUAL_NO; - } + if (!new_field->compression_method() != !compression_method()) - { - return IS_EQUAL_NO; - } - if (new_field->pack_length != pack_length()) - { return IS_EQUAL_NO; - } - if (!csinfo_change_allows_instant_alter(new_field)) + if (new_field->pack_length != pack_length()) return IS_EQUAL_NO; if (field_charset != new_field->charset) - { - return IS_EQUAL_PACK_LENGTH; - } + return is_equal_for_different_charsets(*new_field); return IS_EQUAL_YES; } @@ -11325,4 +11336,4 @@ void Field_blob::print_key_value(String *out, uint32 length) void Field::print_key_value_binary(String *out, const uchar* key, uint32 length) { out->append_semi_hex((const char*)key, length, charset()); -}
\ No newline at end of file +} diff --git a/sql/field.h b/sql/field.h index 1aeca8a34c9..8c46633d550 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1928,7 +1928,8 @@ protected: CHARSET_INFO *cs, size_t nchars); String *uncompress(String *val_buffer, String *val_ptr, const uchar *from, uint from_length); - bool csinfo_change_allows_instant_alter(const Create_field *to) const; + uint is_equal_for_different_charsets(const Column_definition &to) const; + public: Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, diff --git a/sql/sql_priv.h b/sql/sql_priv.h index 0d1c9881c17..0b06985f431 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -354,6 +354,14 @@ data dictionary without changing table rows */ #define IS_EQUAL_PACK_LENGTH 2 +/** + charsets are the same or compatible, collates are the same, the rest is equal +*/ +#define IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET 3 +/** + charsets are the same or compatible, collates are different, the rest is equal +*/ +#define IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET_BUT_COLLATE 4 enum enum_parsing_place { diff --git a/sql/sql_string.h b/sql/sql_string.h index f21f23e42ef..10af5a9426f 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -167,7 +167,7 @@ public: */ LEX_CSTRING collation_specific_name() const; bool encoding_allows_reinterpret_as(CHARSET_INFO *cs) const; - bool encoding_and_order_allow_reinterpret_as(CHARSET_INFO *cs) const; + bool eq_collation_specific_names(CHARSET_INFO *cs) const; }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 8a8d464845c..b6cc3a4290e 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6568,15 +6568,19 @@ Compare_keys compare_keys_but_name(const KEY *table_key, const KEY *new_key, ((Field_varstring *) old_field)->length_bytes); } + uint is_equal= key_part->field->is_equal(new_field); if (key_part->length == old_field_len && key_part->length < new_part->length && - (key_part->field->is_equal((Create_field *) new_field) == - IS_EQUAL_PACK_LENGTH)) + (is_equal == IS_EQUAL_PACK_LENGTH || + is_equal == IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET)) { result= Compare_keys::EqualButKeyPartLength; } else if (key_part->length != new_part->length) return Compare_keys::NotEqual; + + if (is_equal == IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET_BUT_COLLATE) + return Compare_keys::NotEqual; } /* @@ -6599,6 +6603,40 @@ Compare_keys compare_keys_but_name(const KEY *table_key, const KEY *new_key, } /** + Change Field::is_equal() result depending on field being a part of some index. +*/ +static uint process_is_equal_result_for_key_parts(uint is_equal, + const Field *old_field, + const Create_field *new_field) +{ + if (is_equal == IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET) + return IS_EQUAL_PACK_LENGTH; + + if (is_equal == IS_EQUAL_WITH_REINTERPRET_COMPATIBLE_CHARSET_BUT_COLLATE) + { + bool part_of_a_key= !new_field->field->part_of_key.is_clear_all(); + + if (part_of_a_key) + { + const TABLE_SHARE *s= new_field->field->table->s; + + bool part_of_a_primary_key= + s->primary_key != MAX_KEY && + new_field->field->part_of_key.is_set(s->primary_key); + + if (part_of_a_primary_key) + return IS_EQUAL_NO; + + return IS_EQUAL_PACK_LENGTH; + } + + return IS_EQUAL_PACK_LENGTH; + } + + return is_equal; +} + +/** Compare original and new versions of a table and fill Alter_inplace_info describing differences between those versions. @@ -6735,7 +6773,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, Check if type of column has changed to some incompatible type. */ uint is_equal= field->is_equal(new_field); - switch (is_equal) + switch (process_is_equal_result_for_key_parts(is_equal, field, new_field)) { case IS_EQUAL_NO: /* New column type is incompatible with old one. */ diff --git a/sql/sql_type.cc b/sql/sql_type.cc index fc33b6d1427..446e753e006 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -8363,20 +8363,10 @@ Charset::encoding_allows_reinterpret_as(const CHARSET_INFO *cs) const bool -Charset::encoding_and_order_allow_reinterpret_as(CHARSET_INFO *cs) const +Charset::eq_collation_specific_names(CHARSET_INFO *cs) const { - /* - Test quickly if we have two exactly equal CHARSET_INFO pointers. - This also handles a special case with my_charset_bin: - it does not have a collation name specific part in CHARSET_INFO::name, - which is just "binary" (without a character set name prefix), - so the code with collation_specific_name() below won't work for it. - */ - if (m_charset == cs) - return true; - if (!encoding_allows_reinterpret_as(cs)) - return false; LEX_CSTRING name0= collation_specific_name(); LEX_CSTRING name1= Charset(cs).collation_specific_name(); + /* Empty collations are not equal */ return name0.length && !cmp(&name0, &name1); } diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 39d97f8f8af..e4f5a04f668 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -9120,14 +9120,20 @@ innobase_rename_or_enlarge_column_try( DBUG_ASSERT(col->len <= len); #ifdef UNIV_DEBUG + ut_ad(col->mbminlen <= col->mbmaxlen); switch (mtype) { + case DATA_MYSQL: + if (!(prtype & DATA_BINARY_TYPE) || user_table->not_redundant() + || col->mbminlen != col->mbmaxlen) { + /* NOTE: we could allow this when !(prtype & + DATA_BINARY_TYPE) and ROW_FORMAT is not REDUNDANT and + mbminlen<mbmaxlen. That is, we treat a UTF-8 CHAR(n) + column somewhat like a VARCHAR. */ + break; + } + /* fall through */ case DATA_FIXBINARY: case DATA_CHAR: - case DATA_MYSQL: - /* NOTE: we could allow this when !(prtype & DATA_BINARY_TYPE) - and ROW_FORMAT is not REDUNDANT and mbminlen<mbmaxlen. - That is, we treat a UTF-8 CHAR(n) column somewhat like - a VARCHAR. */ ut_ad(col->len == len); break; case DATA_BINARY: |