Skip to content

Commit

Permalink
ddl: resolve table charset option from collation option (pingcap#13506)
Browse files Browse the repository at this point in the history
  • Loading branch information
bb7133 authored and sre-bot committed Nov 20, 2019
1 parent 889ad61 commit b75c389
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 96 deletions.
36 changes: 8 additions & 28 deletions ddl/db_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ func (s *testIntegrationSuite3) TestChangingCharsetToUtf8(c *C) {
tk.MustExec("use test")
tk.MustExec("create table t1(a varchar(20) charset utf8)")
tk.MustExec("insert into t1 values (?)", "t1_value")

tk.MustExec("alter table t1 collate uTf8mB4_uNiCoDe_Ci charset Utf8mB4 charset uTF8Mb4 collate UTF8MB4_BiN")
tk.MustExec("alter table t1 modify column a varchar(20) charset utf8mb4")
tk.MustQuery("select * from t1;").Check(testkit.Rows("t1_value"))

Expand All @@ -624,34 +624,14 @@ func (s *testIntegrationSuite3) TestChangingCharsetToUtf8(c *C) {
tk.MustExec("alter table t modify column a varchar(20) charset latin1")
tk.MustQuery("select * from t;").Check(testkit.Rows("t_value"))

rs, err := tk.Exec("alter table t modify column a varchar(20) charset utf8")
if rs != nil {
rs.Close()
}
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[ddl:8200]Unsupported modify charset from latin1 to utf8")
rs, err = tk.Exec("alter table t modify column a varchar(20) charset utf8mb4")
if rs != nil {
rs.Close()
}
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[ddl:8200]Unsupported modify charset from latin1 to utf8mb4")
tk.MustGetErrCode("alter table t modify column a varchar(20) charset utf8", mysql.ErrUnsupportedDDLOperation)
tk.MustGetErrCode("alter table t modify column a varchar(20) charset utf8mb4", mysql.ErrUnsupportedDDLOperation)
tk.MustGetErrCode("alter table t modify column a varchar(20) charset utf8 collate utf8_bin", mysql.ErrUnsupportedDDLOperation)
tk.MustGetErrCode("alter table t modify column a varchar(20) charset utf8mb4 collate utf8mb4_general_ci", mysql.ErrUnsupportedDDLOperation)

rs, err = tk.Exec("alter table t modify column a varchar(20) charset utf8mb4 collate utf8bin")
if rs != nil {
rs.Close()
}
c.Assert(err, NotNil)
rs, err = tk.Exec("alter table t modify column a varchar(20) charset utf8 collate utf8_bin")
if rs != nil {
rs.Close()
}
c.Assert(err, NotNil)
rs, err = tk.Exec("alter table t modify column a varchar(20) charset utf8mb4 collate utf8mb4_general_ci")
if rs != nil {
rs.Close()
}
c.Assert(err, NotNil)
tk.MustGetErrCode("alter table t modify column a varchar(20) charset utf8mb4 collate utf8bin", mysql.ErrUnknownCollation)
tk.MustGetErrCode("alter table t collate LATIN1_GENERAL_CI charset utf8 collate utf8_bin", mysql.ErrConflictingDeclarations)
tk.MustGetErrCode("alter table t collate LATIN1_GENERAL_CI collate UTF8MB4_UNICODE_ci collate utf8_bin", mysql.ErrCollationCharsetMismatch)
}

func (s *testIntegrationSuite4) TestChangingTableCharset(c *C) {
Expand Down
23 changes: 13 additions & 10 deletions ddl/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1953,21 +1953,24 @@ func (s *testDBSuite1) TestCreateTable(c *C) {

s.tk.MustExec("drop table t")

_, err = s.tk.Exec("CREATE TABLE `t` (`a` int) DEFAULT CHARSET=abcdefg")
c.Assert(err, NotNil)
s.tk.MustGetErrCode("CREATE TABLE `t` (`a` int) DEFAULT CHARSET=abcdefg", mysql.ErrUnknownCharacterSet)

_, err = s.tk.Exec("CREATE TABLE `collateTest` (`a` int, `b` varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_slovak_ci")
c.Assert(err, IsNil)
result := s.tk.MustQuery("show create table collateTest")
got := result.Rows()[0][1]
c.Assert(got, Equals, "CREATE TABLE `collateTest` (\n `a` int(11) DEFAULT NULL,\n `b` varchar(10) COLLATE utf8_slovak_ci DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_slovak_ci")
s.tk.MustExec("CREATE TABLE `collateTest` (`a` int, `b` varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_slovak_ci")
expects := "CREATE TABLE `collateTest` (\n `a` int(11) DEFAULT NULL,\n `b` varchar(10) COLLATE utf8_slovak_ci DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_slovak_ci"
s.tk.MustQuery("show create table collateTest").Check(testkit.Rows(expects))

s.tk.MustGetErrCode("CREATE TABLE `collateTest2` (`a` int) CHARSET utf8 COLLATE utf8mb4_unicode_ci", mysql.ErrCollationCharsetMismatch)
s.tk.MustGetErrCode("CREATE TABLE `collateTest3` (`a` int) COLLATE utf8mb4_unicode_ci CHARSET utf8", mysql.ErrConflictingDeclarations)

s.tk.MustExec("CREATE TABLE `collateTest4` (`a` int) COLLATE utf8_uniCOde_ci")
expects = "CREATE TABLE `collateTest4` (\n `a` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci"
s.tk.MustQuery("show create table collateTest4").Check(testkit.Rows(expects))

s.tk.MustExec("create database test2 default charset utf8 collate utf8_general_ci")
s.tk.MustExec("use test2")
s.tk.MustExec("create table dbCollateTest (a varchar(10))")
result = s.tk.MustQuery("show create table dbCollateTest")
got = result.Rows()[0][1]
c.Assert(got, Equals, "CREATE TABLE `dbCollateTest` (\n `a` varchar(10) COLLATE utf8_general_ci DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci")
expects = "CREATE TABLE `dbCollateTest` (\n `a` varchar(10) COLLATE utf8_general_ci DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci"
s.tk.MustQuery("show create table dbCollateTest").Check(testkit.Rows(expects))

// test for enum column
s.tk.MustExec("use test")
Expand Down
85 changes: 28 additions & 57 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1309,7 +1309,11 @@ func buildTableInfoWithCheck(ctx sessionctx.Context, d *ddl, s *ast.CreateTableS
return nil, errors.Trace(err)
}

tableCharset, tableCollate := findTableOptionCharsetAndCollate(s.Options)
tableCharset, tableCollate, err := getCharsetAndCollateInTableOption(0, s.Options)
if err != nil {
return nil, err
}

// The column charset haven't been resolved here.
cols, newConstraints, err := buildColumnsAndConstraints(ctx, colDefs, s.Constraints, tableCharset, tableCollate, dbCharset, dbCollate)
if err != nil {
Expand All @@ -1326,6 +1330,8 @@ func buildTableInfoWithCheck(ctx sessionctx.Context, d *ddl, s *ast.CreateTableS
if err != nil {
return nil, errors.Trace(err)
}
tbInfo.Collate = tableCollate
tbInfo.Charset = tableCharset

pi, err := buildTablePartitionInfo(ctx, d, s)
if err != nil {
Expand All @@ -1348,7 +1354,6 @@ func buildTableInfoWithCheck(ctx sessionctx.Context, d *ddl, s *ast.CreateTableS
tbInfo.Partition = pi
}

// The specified charset will be handled in handleTableOptions
if err = handleTableOptions(s.Options, tbInfo); err != nil {
return nil, errors.Trace(err)
}
Expand Down Expand Up @@ -1759,32 +1764,6 @@ func resolveDefaultTableCharsetAndCollation(tbInfo *model.TableInfo, dbCharset,
return
}

func findTableOptionCharsetAndCollate(options []*ast.TableOption) (tableCharset, tableCollate string) {
var findCnt int
for i := len(options) - 1; i >= 0; i-- {
op := options[i]
if len(tableCharset) == 0 && op.Tp == ast.TableOptionCharset {
// find the last one.
tableCharset = op.StrValue
findCnt++
if findCnt == 2 {
break
}
continue
}
if len(tableCollate) == 0 && op.Tp == ast.TableOptionCollate {
// find the last one.
tableCollate = op.StrValue
findCnt++
if findCnt == 2 {
break
}
continue
}
}
return tableCharset, tableCollate
}

// handleTableOptions updates tableInfo according to table options.
func handleTableOptions(options []*ast.TableOption, tbInfo *model.TableInfo) error {
for _, op := range options {
Expand All @@ -1793,10 +1772,6 @@ func handleTableOptions(options []*ast.TableOption, tbInfo *model.TableInfo) err
tbInfo.AutoIncID = int64(op.UintValue)
case ast.TableOptionComment:
tbInfo.Comment = op.StrValue
case ast.TableOptionCharset:
tbInfo.Charset = op.StrValue
case ast.TableOptionCollate:
tbInfo.Collate = op.StrValue
case ast.TableOptionCompression:
tbInfo.Compression = op.StrValue
case ast.TableOptionShardRowID:
Expand All @@ -1815,7 +1790,6 @@ func handleTableOptions(options []*ast.TableOption, tbInfo *model.TableInfo) err
if tbInfo.PreSplitRegions > tbInfo.ShardRowIDBits {
tbInfo.PreSplitRegions = tbInfo.ShardRowIDBits
}

return nil
}

Expand All @@ -1829,40 +1803,37 @@ func isIgnorableSpec(tp ast.AlterTableType) bool {
// getCharsetAndCollateInTableOption will iterate the charset and collate in the options,
// and returns the last charset and collate in options. If there is no charset in the options,
// the returns charset will be "", the same as collate.
func getCharsetAndCollateInTableOption(startIdx int, options []*ast.TableOption) (ca, co string, err error) {
charsets := make([]string, 0, len(options))
collates := make([]string, 0, len(options))
func getCharsetAndCollateInTableOption(startIdx int, options []*ast.TableOption) (chs, coll string, err error) {
for i := startIdx; i < len(options); i++ {
opt := options[i]
// we set the charset to the last option. example: alter table t charset latin1 charset utf8 collate utf8_bin;
// the charset will be utf8, collate will be utf8_bin
switch opt.Tp {
case ast.TableOptionCharset:
charsets = append(charsets, opt.StrValue)
info, err := charset.GetCharsetDesc(opt.StrValue)
if err != nil {
return "", "", err
}
if len(chs) == 0 {
chs = info.Name
} else if chs != info.Name {
return "", "", ErrConflictingDeclarations.GenWithStackByArgs(chs, info.Name)
}
if len(coll) == 0 {
coll = info.DefaultCollation
}
case ast.TableOptionCollate:
collates = append(collates, opt.StrValue)
}
}

if len(charsets) > 1 {
return "", "", ErrConflictingDeclarations.GenWithStackByArgs(charsets[0], charsets[1])
}
if len(charsets) == 1 {
if charsets[0] == "" {
return "", "", ErrUnknownCharacterSet.GenWithStackByArgs("")
}
ca = charsets[0]
}
if len(collates) != 0 {
for i := range collates {
if collates[i] == "" {
return "", "", ErrUnknownCollation.GenWithStackByArgs("")
info, err := charset.GetCollationByName(opt.StrValue)
if err != nil {
return "", "", err
}
if len(ca) != 0 && !charset.ValidCharsetAndCollation(ca, collates[i]) {
return "", "", ErrCollationCharsetMismatch.GenWithStackByArgs(collates[i], ca)
if len(chs) == 0 {
chs = info.CharsetName
} else if chs != info.CharsetName {
return "", "", ErrCollationCharsetMismatch.GenWithStackByArgs(info.Name, chs)
}
coll = info.Name
}
co = collates[len(collates)-1]
}
return
}
Expand Down
1 change: 0 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ github.com/pingcap/tipb v0.0.0-20191120020146-6161b015e21e/go.mod h1:RtkHW8WbcNx
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
Expand Down

0 comments on commit b75c389

Please sign in to comment.