From 38d63e70d79c129ca4f2d82fab3c53044892f490 Mon Sep 17 00:00:00 2001 From: tuzi_li Date: Sat, 22 Apr 2017 14:23:10 +0800 Subject: [PATCH 1/4] ddl, util/types: make converter correctly convert string to hex or bit. This PR fix #2858 and #2717. Furthermore fix the error that unable to set default value as a hex value just like what happend in #2858. I also noticed that TiDB have some differences when compared to MySQL. create table t1 (a bit(64)); insert t1 value ('10'); select a+0 from t1; In MySQL we can get: +-------+ | a+0 | +-------+ | 12592 | +-------+ But in TiDB we will get: +-------+ | a+0 | +-------+ | 10 | +-------+ As a result of fix #2717, TiDB now behave same as MySQL. --- ddl/ddl_api.go | 5 +++++ ddl/ddl_db_test.go | 18 ++++++++++++++++++ util/types/bit.go | 31 +++++++++++++++++++++++++++++++ util/types/bit_test.go | 4 ++++ util/types/convert_test.go | 5 +++-- util/types/datum.go | 12 +++++++++++- 6 files changed, 72 insertions(+), 3 deletions(-) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index be7c5890d0155..4e6034bc9cb0e 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -19,6 +19,7 @@ package ddl import ( "fmt" + "strconv" "strings" "time" @@ -353,9 +354,13 @@ func getDefaultValue(ctx context.Context, c *ast.ColumnOption, tp byte, fsp int) if err != nil { return nil, errors.Trace(err) } + if v.IsNull() { return nil, nil + } else if v.Kind() == types.KindMysqlHex { + return strconv.FormatInt(v.GetMysqlHex().Value, 10), nil } + return v.ToString() } diff --git a/ddl/ddl_db_test.go b/ddl/ddl_db_test.go index 4ff59884c0bd3..2d7c068e5cd65 100644 --- a/ddl/ddl_db_test.go +++ b/ddl/ddl_db_test.go @@ -1093,3 +1093,21 @@ out: expected := fmt.Sprintf("%d %d", updateCnt, 3) s.tk.MustQuery("select c2, c3 from tnn where c1 = 99").Check(testkit.Rows(expected)) } + +func (s *testDBSuite) TestIssue2858And2717(c *C) { + defer testleak.AfterTest(c)() + s.tk = testkit.NewTestKit(c, s.store) + s.tk.MustExec("use " + s.schemaName) + + s.tk.MustExec("create table t_issue_2858_bit (a bit(64) default b'0')") + s.tk.MustExec("insert into t_issue_2858_bit value ()") + s.tk.MustExec(`insert into t_issue_2858_bit values (100), ('10'), ('\0')`) + s.tk.MustQuery("select a+0 from t_issue_2858_bit").Check([][]interface{}{{0}, {100}, {0x3130}, {0}}) + s.tk.MustExec(`alter table t_issue_2858_bit alter column a set default '\0'`) + + s.tk.MustExec("create table t_issue_2858_hex (a int default 0x123)") + s.tk.MustExec("insert into t_issue_2858_hex value ()") + s.tk.MustExec("insert into t_issue_2858_hex values (123), (0x321)") + s.tk.MustQuery("select a from t_issue_2858_hex").Check([][]interface{}{{0x123}, {123}, {0x321}}) + s.tk.MustExec(`alter table t_issue_2858_hex alter column a set default 0x321`) +} diff --git a/util/types/bit.go b/util/types/bit.go index 8fbcd80e10b9f..f149de59e0e26 100644 --- a/util/types/bit.go +++ b/util/types/bit.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/juju/errors" + "github.com/pingcap/tidb/util/hack" ) // Bit is for mysql bit type. @@ -104,3 +105,33 @@ func ParseBit(s string, width int) (Bit, error) { return Bit{Value: n, Width: width}, nil } + +// ParseBitValue parses the binary string for bit type into uint64. +func ParseBitValue(s string, width int) (uint64, error) { + if len(s) == 0 { + return 0, errors.Errorf("invalid empty string for parsing bit value") + } + + b := hack.Slice(s) + if width == UnspecifiedBitWidth { + width = (len(b) + 7) & ^7 + } + if width == 0 { + width = MinBitWidth + } + if width < MinBitWidth || width > MaxBitWidth { + return 0, errors.Errorf("invalid display width for bit type, must in [1, 64], but %d", width) + } + + var n uint64 + l := len(b) + for i := range b { + n += uint64(b[l-i-1]) << uint(i*8) + } + + if n > (uint64(1)< Date: Mon, 24 Apr 2017 10:17:41 +0800 Subject: [PATCH 2/4] fix: simplify code --- ddl/ddl_api.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 4e6034bc9cb0e..63db5fe6b9bd9 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -357,7 +357,9 @@ func getDefaultValue(ctx context.Context, c *ast.ColumnOption, tp byte, fsp int) if v.IsNull() { return nil, nil - } else if v.Kind() == types.KindMysqlHex { + } + + if v.Kind() == types.KindMysqlHex { return strconv.FormatInt(v.GetMysqlHex().Value, 10), nil } From 9d2432278c611195e8b2c937cc84208e16ece8d6 Mon Sep 17 00:00:00 2001 From: tuzi_li Date: Mon, 24 Apr 2017 16:39:14 +0800 Subject: [PATCH 3/4] fix: rename function and fix the bug of width caculation. --- util/types/bit.go | 6 +++--- util/types/bit_test.go | 2 +- util/types/datum.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/util/types/bit.go b/util/types/bit.go index f149de59e0e26..cf04675df8d49 100644 --- a/util/types/bit.go +++ b/util/types/bit.go @@ -106,15 +106,15 @@ func ParseBit(s string, width int) (Bit, error) { return Bit{Value: n, Width: width}, nil } -// ParseBitValue parses the binary string for bit type into uint64. -func ParseBitValue(s string, width int) (uint64, error) { +// ParseStringToBitValue parses the binary string for bit type into uint64. +func ParseStringToBitValue(s string, width int) (uint64, error) { if len(s) == 0 { return 0, errors.Errorf("invalid empty string for parsing bit value") } b := hack.Slice(s) if width == UnspecifiedBitWidth { - width = (len(b) + 7) & ^7 + width = len(b) * 8 } if width == 0 { width = MinBitWidth diff --git a/util/types/bit_test.go b/util/types/bit_test.go index ebe036f1521bb..e4e0b6aef18c0 100644 --- a/util/types/bit_test.go +++ b/util/types/bit_test.go @@ -44,7 +44,7 @@ func (s *testBitSuite) TestBit(c *C) { c.Assert(b.String(), Equals, t.String) c.Assert(b.ToString(), Equals, t.BitString) - n, err := ParseBitValue(t.BitString, t.Width) + n, err := ParseStringToBitValue(t.BitString, t.Width) c.Assert(err, IsNil) c.Assert(n, Equals, uint64(t.Number)) } diff --git a/util/types/datum.go b/util/types/datum.go index 61c8b172f6135..540cd0d1ba8d6 100644 --- a/util/types/datum.go +++ b/util/types/datum.go @@ -1050,7 +1050,7 @@ func (d *Datum) convertToMysqlBit(sc *variable.StatementContext, target *FieldTy ) if d.Kind() == KindString { var n uint64 - n, err = ParseBitValue(d.GetString(), target.Flen) + n, err = ParseStringToBitValue(d.GetString(), target.Flen) x = NewUintDatum(n) } else { x, err = d.convertToUint(sc, target) From 4d8d8517eebc8de84eeac6168e57d794679e4db3 Mon Sep 17 00:00:00 2001 From: Zejun Li Date: Tue, 25 Apr 2017 12:19:43 +0800 Subject: [PATCH 4/4] fix: edit comment --- util/types/bit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/types/bit.go b/util/types/bit.go index cf04675df8d49..9247c921c7ddd 100644 --- a/util/types/bit.go +++ b/util/types/bit.go @@ -106,7 +106,7 @@ func ParseBit(s string, width int) (Bit, error) { return Bit{Value: n, Width: width}, nil } -// ParseStringToBitValue parses the binary string for bit type into uint64. +// ParseStringToBitValue parses the string for bit type into uint64. func ParseStringToBitValue(s string, width int) (uint64, error) { if len(s) == 0 { return 0, errors.Errorf("invalid empty string for parsing bit value")