Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: fix a bug that collation does not work for enum and set #17635

Merged
merged 11 commits into from
Jun 5, 2020
2 changes: 1 addition & 1 deletion ddl/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2453,7 +2453,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"))
Expand Down
2 changes: 1 addition & 1 deletion ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
8 changes: 4 additions & 4 deletions executor/aggfuncs/func_first_row_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions types/datum.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
7 changes: 4 additions & 3 deletions types/enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
}
}
Expand Down
32 changes: 30 additions & 2 deletions types/enum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
Expand Down
9 changes: 6 additions & 3 deletions types/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"strings"

"github.com/pingcap/errors"
"github.com/pingcap/tidb/util/collate"
"github.com/pingcap/tidb/util/stringutil"
)

Expand Down Expand Up @@ -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)
Expand Down
24 changes: 21 additions & 3 deletions types/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}

Expand Down