diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 6cbbb92805872..9245f3fbb8ef6 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -6041,6 +6041,10 @@ func checkAlterTableCharset(tblInfo *model.TableInfo, dbInfo *model.DBInfo, toCh return doNothing, nil } + if err = checkIndexLengthWithNewCharset(tblInfo, toCharset, toCollate); err != nil { + return doNothing, err + } + for _, col := range tblInfo.Columns { if col.GetType() == mysql.TypeVarchar { if err = types.IsVarcharTooBigFieldLength(col.GetFlen(), col.Name.O, toCharset); err != nil { @@ -6064,6 +6068,30 @@ func checkAlterTableCharset(tblInfo *model.TableInfo, dbInfo *model.DBInfo, toCh return doNothing, nil } +func checkIndexLengthWithNewCharset(tblInfo *model.TableInfo, toCharset, toCollate string) error { + // Copy all columns and replace the charset and collate. + columns := make([]*model.ColumnInfo, 0, len(tblInfo.Columns)) + for _, col := range tblInfo.Columns { + newCol := col.Clone() + if field_types.HasCharset(&newCol.FieldType) { + newCol.SetCharset(toCharset) + newCol.SetCollate(toCollate) + } else { + newCol.SetCharset(charset.CharsetBin) + newCol.SetCollate(charset.CharsetBin) + } + columns = append(columns, newCol) + } + + for _, indexInfo := range tblInfo.Indices { + err := checkIndexPrefixLength(columns, indexInfo.Columns) + if err != nil { + return err + } + } + return nil +} + // RenameIndex renames an index. // In TiDB, indexes are case-insensitive (so index 'a' and 'A" are considered the same index), // but index names are case-sensitive (we can rename index 'a' to 'A') diff --git a/ddl/ddl_api_test.go b/ddl/ddl_api_test.go index 3ae596031a9a6..b3a6e425f48b1 100644 --- a/ddl/ddl_api_test.go +++ b/ddl/ddl_api_test.go @@ -267,3 +267,15 @@ func TestCreateDropCreateTable(t *testing.T) { require.Less(t, create0TS, dropTS, "first create should finish before drop") require.Less(t, dropTS, create1TS, "second create should finish after drop") } + +func TestFix56930(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t; create table posts (id int auto_increment primary key, title varchar(500) character set utf8, subtitle varchar(500) character set utf8, unique key(title, subtitle));") + tk.MustGetErrMsg("alter table posts convert to character set utf8mb4;", "[ddl:1071]Specified key was too long (4000 bytes); max key length is 3072 bytes") + tk.MustExec("drop table if exists t; create table t(a varchar(1000) character set utf8, primary key(a));") + tk.MustGetErrMsg("alter table t convert to character set utf8mb4;", "[ddl:1071]Specified key was too long (4000 bytes); max key length is 3072 bytes") + tk.MustExec("drop table if exists t; create table t(a varchar(1000) character set utf8, key(a));") + tk.MustGetErrMsg("alter table t convert to character set utf8mb4;", "[ddl:1071]Specified key was too long (4000 bytes); max key length is 3072 bytes") +}