From cc229a01dc94ea00691dee1e268ad489339a84b7 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Sat, 23 Nov 2019 12:51:53 +0530 Subject: MDEV-20190 Instant operation fails when add column and collation change on non-indexed column - Relax the debug assertion when doing instant operation on collation changed column. --- .../suite/innodb/r/instant_alter_bugs.result | 23 +++++++++++++ mysql-test/suite/innodb/t/instant_alter_bugs.test | 17 ++++++++++ storage/innobase/data/data0type.cc | 17 ++++++++++ storage/innobase/handler/handler0alter.cc | 5 +-- storage/innobase/include/data0type.h | 4 +++ storage/innobase/include/dict0mem.h | 38 ++++++++++++++++++---- 6 files changed, 93 insertions(+), 11 deletions(-) diff --git a/mysql-test/suite/innodb/r/instant_alter_bugs.result b/mysql-test/suite/innodb/r/instant_alter_bugs.result index 19262246c9b..78104e1dfa2 100644 --- a/mysql-test/suite/innodb/r/instant_alter_bugs.result +++ b/mysql-test/suite/innodb/r/instant_alter_bugs.result @@ -324,4 +324,27 @@ InnoDB 0 transactions not purged SELECT * FROM t1; a DROP TABLE t1; +# +# MDEV-20190 Instant operation fails when add column and collation +# change on non-indexed column +# +CREATE TABLE t1 (a CHAR)ENGINE=INNODB; +ALTER TABLE t1 DEFAULT COLLATE= latin1_general_cs; +ALTER TABLE t1 ADD COLUMN b INT NOT NULL, MODIFY a CHAR, ALGORITHM=INSTANT; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` char(1) COLLATE latin1_general_cs DEFAULT NULL, + `b` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_cs +DROP TABLE t1; +CREATE TABLE t1 (a CHAR NOT NULL) ENGINE=InnoDB ROW_FORMAT=REDUNDANT; +ALTER TABLE t1 DEFAULT COLLATE = latin1_general_cs; +ALTER TABLE t1 MODIFY a CHAR, ALGORITHM=INSTANT; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` char(1) COLLATE latin1_general_cs DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_cs ROW_FORMAT=REDUNDANT +DROP TABLE t1; SET GLOBAL innodb_purge_rseg_truncate_frequency=@save_frequency; diff --git a/mysql-test/suite/innodb/t/instant_alter_bugs.test b/mysql-test/suite/innodb/t/instant_alter_bugs.test index 090a4aef787..2b47e5bd135 100644 --- a/mysql-test/suite/innodb/t/instant_alter_bugs.test +++ b/mysql-test/suite/innodb/t/instant_alter_bugs.test @@ -348,4 +348,21 @@ ALTER TABLE t1 DROP b, DROP c, DROP d, DROP e; --source include/wait_all_purged.inc SELECT * FROM t1; DROP TABLE t1; + +--echo # +--echo # MDEV-20190 Instant operation fails when add column and collation +--echo # change on non-indexed column +--echo # + +CREATE TABLE t1 (a CHAR)ENGINE=INNODB; +ALTER TABLE t1 DEFAULT COLLATE= latin1_general_cs; +ALTER TABLE t1 ADD COLUMN b INT NOT NULL, MODIFY a CHAR, ALGORITHM=INSTANT; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1 (a CHAR NOT NULL) ENGINE=InnoDB ROW_FORMAT=REDUNDANT; +ALTER TABLE t1 DEFAULT COLLATE = latin1_general_cs; +ALTER TABLE t1 MODIFY a CHAR, ALGORITHM=INSTANT; +SHOW CREATE TABLE t1; +DROP TABLE t1; SET GLOBAL innodb_purge_rseg_truncate_frequency=@save_frequency; diff --git a/storage/innobase/data/data0type.cc b/storage/innobase/data/data0type.cc index 7de4cc026d1..0f55b76bd09 100644 --- a/storage/innobase/data/data0type.cc +++ b/storage/innobase/data/data0type.cc @@ -209,4 +209,21 @@ dtype_print(const dtype_t* type) fprintf(stderr, " len %lu", (ulong) len); } + #endif /* UNIV_DEBUG */ + +bool dtype_is_latin1(ulint prtype) +{ + switch (dtype_get_charset_coll(prtype)) { + case 5: /* latin1_german1_ci */ + case 8: /* latin1_swedish_ci */ + case 15: /* latin1_danish_ci */ + case 31: /* latin1_german2_ci */ + case 47: /* latin1_bin */ + case 48: /* latin1_general_ci */ + case 49: /* latin1_general_cs */ + case 94: /* latin1_spanish_ci */ + return true; + } + return false; +} diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 7bb226b9a2e..e250ea358cf 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -537,10 +537,7 @@ inline bool dict_table_t::instant_column(const dict_table_t& table, if (const dict_col_t* o = find(old_cols, col_map, n_cols, i)) { c.def_val = o->def_val; - DBUG_ASSERT(!((c.prtype ^ o->prtype) - & ~(DATA_NOT_NULL | DATA_VERSIONED - | DATA_LONG_TRUE_VARCHAR))); - DBUG_ASSERT(c.mtype == o->mtype); + DBUG_ASSERT(o->same_mtype(c)); DBUG_ASSERT(c.len >= o->len); if (o->vers_sys_start()) { diff --git a/storage/innobase/include/data0type.h b/storage/innobase/include/data0type.h index 7168c104b1c..cf029390499 100644 --- a/storage/innobase/include/data0type.h +++ b/storage/innobase/include/data0type.h @@ -505,6 +505,10 @@ dtype_print( const dtype_t* type); #endif /* UNIV_DEBUG */ +/** Determine if a MySQL string type is a subset of latin1. +@return whether a subset of latin1 */ +bool dtype_is_latin1(ulint prtype); + /* Structure for an SQL data type. If you add fields to this structure, be sure to initialize them everywhere. This structure is initialized in the following functions: diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 5e1fa0033f7..5849b1f1be7 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -674,19 +674,43 @@ public: def_val.data = NULL; } + /** Determine if the columns have same mtype. + @param[in] other column to compare to + @return whether the column have the same mtype. */ + bool same_mtype(const dict_col_t& other) const { + if (mtype == other.mtype) + return true; + + /* DATA_CHAR and DATA_VARCHAR can be changed to + DATA_MYSQL and DATA_VARMYSQL if charset is of type latin1 + and when collate is changed to/from latin_swedish_ci. */ + if (dtype_is_latin1(prtype) && dtype_is_latin1(other.prtype)) + { + if (dtype_get_charset_coll(prtype) + == dtype_get_charset_coll(other.prtype)) + return false; + + if ((mtype == DATA_VARCHAR && other.mtype == DATA_VARMYSQL) + || (mtype == DATA_VARMYSQL && other.mtype == DATA_VARCHAR) + || (mtype == DATA_CHAR && other.mtype == DATA_MYSQL) + || (mtype == DATA_MYSQL && other.mtype == DATA_CHAR)) + return true; + } + return false; + } + /** Determine if the columns have the same format except for is_nullable() and is_versioned(). @param[in] other column to compare to @return whether the columns have the same format */ bool same_format(const dict_col_t& other) const { - return mtype == other.mtype - && len >= other.len - && mbminlen == other.mbminlen - && mbmaxlen == other.mbmaxlen - && !((prtype ^ other.prtype) - & ~(DATA_NOT_NULL | DATA_VERSIONED - | DATA_LONG_TRUE_VARCHAR)); + if (!same_mtype(other)) + return false; + + return len >= other.len + && mbminlen == other.mbminlen + && mbmaxlen == other.mbmaxlen; } }; -- cgit v1.2.1