From 89ea13880cce951441d259908b5b6999ced6bc83 Mon Sep 17 00:00:00 2001 From: wjHuang Date: Fri, 5 Jun 2020 12:14:42 +0800 Subject: [PATCH] cherry pick #17635 to release-4.0 Signed-off-by: sre-bot --- ddl/db_test.go | 2 +- ddl/ddl_api.go | 2 +- executor/aggfuncs/func_first_row_test.go | 8 +++--- types/datum.go | 4 +-- types/enum.go | 7 +++--- types/enum_test.go | 32 ++++++++++++++++++++++-- types/set.go | 9 ++++--- types/set_test.go | 24 +++++++++++++++--- 8 files changed, 69 insertions(+), 19 deletions(-) diff --git a/ddl/db_test.go b/ddl/db_test.go index e2a81cfdc47ab..0c56967f9981b 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -2370,7 +2370,7 @@ func (s *testDBSuite2) TestCreateTableWithSetCol(c *C) { " `b` set('e') DEFAULT ''\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) s.tk.MustExec("drop table t_set") - s.tk.MustExec("create table t_set (a set('a', 'b', 'c', 'd') default 'a,C,c');") + s.tk.MustExec("create table t_set (a set('a', 'b', 'c', 'd') default 'a,c,c');") s.tk.MustQuery("show create table t_set").Check(testkit.Rows("t_set CREATE TABLE `t_set` (\n" + " `a` set('a','b','c','d') DEFAULT 'a,c'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 5fa6f361a4e88..fe90fbcf11448 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -778,7 +778,7 @@ func setSetDefaultValue(v types.Datum, col *table.Column) (string, error) { if existCnt != len(valMap) { return "", ErrInvalidDefaultValue.GenWithStackByArgs(col.Name.O) } - setVal, err := types.ParseSetName(col.Elems, str) + setVal, err := types.ParseSetName(col.Elems, str, col.Collate) if err != nil { return "", ErrInvalidDefaultValue.GenWithStackByArgs(col.Name.O) } diff --git a/executor/aggfuncs/func_first_row_test.go b/executor/aggfuncs/func_first_row_test.go index 357f651beb26c..9980454e61659 100644 --- a/executor/aggfuncs/func_first_row_test.go +++ b/executor/aggfuncs/func_first_row_test.go @@ -25,11 +25,11 @@ import ( func (s *testSuite) TestMergePartialResult4FirstRow(c *C) { elems := []string{"a", "b", "c", "d", "e"} - enumA, _ := types.ParseEnumName(elems, "a") - enumC, _ := types.ParseEnumName(elems, "c") + enumA, _ := types.ParseEnumName(elems, "a", mysql.DefaultCollationName) + enumC, _ := types.ParseEnumName(elems, "c", mysql.DefaultCollationName) - setA, _ := types.ParseSetName(elems, "a") - setAB, _ := types.ParseSetName(elems, "a,b") + setA, _ := types.ParseSetName(elems, "a", mysql.DefaultCollationName) + setAB, _ := types.ParseSetName(elems, "a,b", mysql.DefaultCollationName) tests := []aggTest{ buildAggTester(ast.AggFuncFirstRow, mysql.TypeLonglong, 5, 0, 2, 0), diff --git a/types/datum.go b/types/datum.go index 99fa9a575cd27..e178ad515afb5 100644 --- a/types/datum.go +++ b/types/datum.go @@ -1346,7 +1346,7 @@ func (d *Datum) convertToMysqlEnum(sc *stmtctx.StatementContext, target *FieldTy ) switch d.k { case KindString, KindBytes: - e, err = ParseEnumName(target.Elems, d.GetString()) + e, err = ParseEnumName(target.Elems, d.GetString(), target.Collate) default: var uintDatum Datum uintDatum, err = d.convertToUint(sc, target) @@ -1371,7 +1371,7 @@ func (d *Datum) convertToMysqlSet(sc *stmtctx.StatementContext, target *FieldTyp ) switch d.k { case KindString, KindBytes: - s, err = ParseSetName(target.Elems, d.GetString()) + s, err = ParseSetName(target.Elems, d.GetString(), target.Collate) default: var uintDatum Datum uintDatum, err = d.convertToUint(sc, target) diff --git a/types/enum.go b/types/enum.go index 1aafdc345bcf4..841b2cdc39beb 100644 --- a/types/enum.go +++ b/types/enum.go @@ -15,9 +15,9 @@ package types import ( "strconv" - "strings" "github.com/pingcap/errors" + "github.com/pingcap/tidb/util/collate" "github.com/pingcap/tidb/util/stringutil" ) @@ -46,9 +46,10 @@ func (e Enum) ToNumber() float64 { } // ParseEnumName creates a Enum with item name. -func ParseEnumName(elems []string, name string) (Enum, error) { +func ParseEnumName(elems []string, name string, collation string) (Enum, error) { + ctor := collate.GetCollator(collation) for i, n := range elems { - if strings.EqualFold(n, name) { + if ctor.Compare(n, name) == 0 { return Enum{Name: n, Value: uint64(i) + 1}, nil } } diff --git a/types/enum_test.go b/types/enum_test.go index ec7d24dd734f1..6ce7a864fa450 100644 --- a/types/enum_test.go +++ b/types/enum_test.go @@ -15,16 +15,20 @@ package types import ( . "github.com/pingcap/check" + "github.com/pingcap/parser/mysql" + "github.com/pingcap/tidb/util/collate" "github.com/pingcap/tidb/util/testleak" ) -var _ = Suite(&testEnumSuite{}) +var _ = SerialSuites(&testEnumSuite{}) type testEnumSuite struct { } func (s *testEnumSuite) TestEnum(c *C) { defer testleak.AfterTest(c)() + collate.SetNewCollationEnabledForTest(true) + defer collate.SetNewCollationEnabledForTest(false) tbl := []struct { Elems []string Name string @@ -34,9 +38,33 @@ func (s *testEnumSuite) TestEnum(c *C) { {[]string{"a"}, "b", 0}, {[]string{"a"}, "1", 1}, } + citbl := []struct { + Elems []string + Name string + Expected int + }{ + {[]string{"a", "b"}, "A ", 1}, + {[]string{"a"}, "A", 1}, + {[]string{"a"}, "b", 0}, + {[]string{"啊"}, "啊", 1}, + {[]string{"a"}, "1", 1}, + } for _, t := range tbl { - e, err := ParseEnumName(t.Elems, t.Name) + e, err := ParseEnumName(t.Elems, t.Name, mysql.DefaultCollationName) + if t.Expected == 0 { + c.Assert(err, NotNil) + c.Assert(e.ToNumber(), Equals, float64(0)) + c.Assert(e.String(), Equals, "") + continue + } + + c.Assert(err, IsNil) + c.Assert(e.String(), Equals, t.Elems[t.Expected-1]) + c.Assert(e.ToNumber(), Equals, float64(t.Expected)) + } + for _, t := range citbl { + e, err := ParseEnumName(t.Elems, t.Name, "utf8_general_ci") if t.Expected == 0 { c.Assert(err, NotNil) c.Assert(e.ToNumber(), Equals, float64(0)) diff --git a/types/set.go b/types/set.go index 57145855c9c43..48a6d2a84a6c0 100644 --- a/types/set.go +++ b/types/set.go @@ -18,6 +18,7 @@ import ( "strings" "github.com/pingcap/errors" + "github.com/pingcap/tidb/util/collate" "github.com/pingcap/tidb/util/stringutil" ) @@ -48,21 +49,23 @@ func (e Set) Copy() Set { } // ParseSetName creates a Set with name. -func ParseSetName(elems []string, name string) (Set, error) { +func ParseSetName(elems []string, name string, collation string) (Set, error) { if len(name) == 0 { return zeroSet, nil } + ctor := collate.GetCollator(collation) + seps := strings.Split(name, ",") marked := make(map[string]struct{}, len(seps)) for _, s := range seps { - marked[strings.ToLower(s)] = struct{}{} + marked[string(ctor.Key(s))] = struct{}{} } items := make([]string, 0, len(seps)) value := uint64(0) for i, n := range elems { - key := strings.ToLower(n) + key := string(ctor.Key(n)) if _, ok := marked[key]; ok { value |= 1 << uint64(i) delete(marked, key) diff --git a/types/set_test.go b/types/set_test.go index f3fe3260a921f..8a987252badcf 100644 --- a/types/set_test.go +++ b/types/set_test.go @@ -15,16 +15,20 @@ package types import ( . "github.com/pingcap/check" + "github.com/pingcap/parser/mysql" + "github.com/pingcap/tidb/util/collate" "github.com/pingcap/tidb/util/testleak" ) -var _ = Suite(&testSetSuite{}) +var _ = SerialSuites(&testSetSuite{}) type testSetSuite struct { } func (s *testSetSuite) TestSet(c *C) { defer testleak.AfterTest(c)() + collate.SetNewCollationEnabledForTest(true) + defer collate.SetNewCollationEnabledForTest(false) elems := []string{"a", "b", "c", "d"} tbl := []struct { Name string @@ -39,9 +43,23 @@ func (s *testSetSuite) TestSet(c *C) { {"", 0, ""}, {"0", 0, ""}, } + citbl := []struct { + Name string + ExpectedValue uint64 + ExpectedName string + }{ + {"A ", 1, "a"}, + {"a,B,a", 3, "a,b"}, + } for _, t := range tbl { - e, err := ParseSetName(elems, t.Name) + e, err := ParseSetName(elems, t.Name, mysql.DefaultCollationName) + c.Assert(err, IsNil) + c.Assert(e.ToNumber(), Equals, float64(t.ExpectedValue)) + c.Assert(e.String(), Equals, t.ExpectedName) + } + for _, t := range citbl { + e, err := ParseSetName(elems, t.Name, "utf8_general_ci") c.Assert(err, IsNil) c.Assert(e.ToNumber(), Equals, float64(t.ExpectedValue)) c.Assert(e.String(), Equals, t.ExpectedName) @@ -69,7 +87,7 @@ func (s *testSetSuite) TestSet(c *C) { "e.f", } for _, t := range tblErr { - _, err := ParseSetName(elems, t) + _, err := ParseSetName(elems, t, mysql.DefaultCollationName) c.Assert(err, NotNil) }