Skip to content

Commit

Permalink
*: rewrite hex and bit literals (#4415)
Browse files Browse the repository at this point in the history
  • Loading branch information
breezewish authored and hanfei1991 committed Sep 7, 2017
1 parent 20a928e commit fc209cb
Show file tree
Hide file tree
Showing 39 changed files with 805 additions and 726 deletions.
26 changes: 23 additions & 3 deletions ddl/ddl_api.go
Expand Up @@ -19,7 +19,6 @@ package ddl

import (
"fmt"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -329,6 +328,11 @@ func columnDefToCol(ctx context.Context, offset int, colDef *ast.ColumnDef) (*ta
if col.Charset == charset.CharsetBin {
col.Flag |= mysql.BinaryFlag
}
if col.Tp == mysql.TypeBit {
// For BIT field, it's charset is binary but does not have binary flag.
col.Flag &= ^mysql.BinaryFlag
col.Flag |= mysql.UnsignedFlag
}
err := checkDefaultValue(ctx, col, hasDefaultValue)
if err != nil {
return nil, nil, errors.Trace(err)
Expand Down Expand Up @@ -365,8 +369,24 @@ func getDefaultValue(ctx context.Context, c *ast.ColumnOption, tp byte, fsp int)
return nil, nil
}

if v.Kind() == types.KindMysqlHex {
return strconv.FormatInt(v.GetMysqlHex().Value, 10), nil
if v.Kind() == types.KindBinaryLiteral || v.Kind() == types.KindMysqlBit {
if tp == mysql.TypeBit ||
tp == mysql.TypeString || tp == mysql.TypeVarchar || tp == mysql.TypeVarString ||
tp == mysql.TypeBlob || tp == mysql.TypeLongBlob || tp == mysql.TypeMediumBlob || tp == mysql.TypeTinyBlob ||
tp == mysql.TypeJSON {
// For BinaryLiteral / string fields, when getting default value we cast the value into BinaryLiteral{}, thus we return
// its raw string content here.
return v.GetBinaryLiteral().ToString(), nil
}
// For other kind of fields (e.g. INT), we supply its integer value so that it acts as integers.
return v.GetBinaryLiteral().ToInt()
}

if tp == mysql.TypeBit {
if v.Kind() == types.KindInt64 || v.Kind() == types.KindUint64 {
// For BIT fields, convert int into BinaryLiteral.
return types.NewBinaryLiteralFromUint(v.GetUint64(), -1).ToString(), nil
}
}

return v.ToString()
Expand Down
26 changes: 26 additions & 0 deletions ddl/ddl_db_test.go
Expand Up @@ -1347,6 +1347,32 @@ func (s *testDBSuite) TestIssue2858And2717(c *C) {
s.tk.MustExec(`alter table t_issue_2858_hex alter column a set default 0x321`)
}

func (s *testDBSuite) TestIssue4432(c *C) {
defer testleak.AfterTest(c)()
s.tk = testkit.NewTestKit(c, s.store)
s.tk.MustExec("use " + s.schemaName)

s.tk.MustExec("create table tx (col bit(10) default 'a')")
s.tk.MustExec("insert into tx value ()")
s.tk.MustQuery("select * from tx").Check(testkit.Rows("\x00a"))
s.tk.MustExec("drop table tx")

s.tk.MustExec("create table tx (col bit(10) default 0x61)")
s.tk.MustExec("insert into tx value ()")
s.tk.MustQuery("select * from tx").Check(testkit.Rows("\x00a"))
s.tk.MustExec("drop table tx")

s.tk.MustExec("create table tx (col bit(10) default 97)")
s.tk.MustExec("insert into tx value ()")
s.tk.MustQuery("select * from tx").Check(testkit.Rows("\x00a"))
s.tk.MustExec("drop table tx")

s.tk.MustExec("create table tx (col bit(10) default 0b1100001)")
s.tk.MustExec("insert into tx value ()")
s.tk.MustQuery("select * from tx").Check(testkit.Rows("\x00a"))
s.tk.MustExec("drop table tx")
}

func (s *testDBSuite) TestChangeColumnPosition(c *C) {
defer testleak.AfterTest(c)()
s.tk = testkit.NewTestKit(c, s.store)
Expand Down
3 changes: 1 addition & 2 deletions ddl/index.go
Expand Up @@ -14,7 +14,6 @@
package ddl

import (
"math"
"sort"
"sync"
"time"
Expand Down Expand Up @@ -86,7 +85,7 @@ func buildIndexColumns(columns []*model.ColumnInfo, idxColNames []*ast.IndexColN
if col.Flen != types.UnspecifiedLength {
// Special case for the bit type.
if col.FieldType.Tp == mysql.TypeBit {
sumLength += int(math.Ceil(float64(col.Flen+7) / float64(8)))
sumLength += (col.Flen + 7) >> 3
} else {
sumLength += col.Flen
}
Expand Down
7 changes: 2 additions & 5 deletions executor/show.go
Expand Up @@ -15,7 +15,6 @@ package executor

import (
"bytes"
"encoding/binary"
"fmt"
"sort"
"strings"
Expand Down Expand Up @@ -432,10 +431,8 @@ func (e *ShowExec) fetchShowCreateTable() error {
default:
defaultValStr := fmt.Sprintf("%v", col.DefaultValue)
if col.Tp == mysql.TypeBit {
bytes := make([]byte, 8)
copy(bytes[8-len(defaultValStr):8], []byte(defaultValStr))
intValue := binary.BigEndian.Uint64(bytes)
buf.WriteString(fmt.Sprintf(" DEFAULT b'%b'", intValue))
defaultValBinaryLiteral := types.BinaryLiteral(defaultValStr)
buf.WriteString(fmt.Sprintf(" DEFAULT %s", defaultValBinaryLiteral.ToBitLiteralString(true)))
} else {
buf.WriteString(fmt.Sprintf(" DEFAULT '%s'", format.OutputFormat(defaultValStr)))
}
Expand Down
4 changes: 2 additions & 2 deletions expression/builtin_cast_test.go
Expand Up @@ -1076,9 +1076,9 @@ func (s *testEvaluatorSuite) TestWrapWithCastAsTypesClasses(c *C) {
123, 123, types.NewDecFromStringForTest("123"), "a",
},
{
&Constant{RetType: types.NewFieldType(mysql.TypeVarString), Value: types.NewDatum(types.Hex{Value: 0x61})},
&Constant{RetType: types.NewFieldType(mysql.TypeVarString), Value: types.NewBinaryLiteralDatum(types.NewBinaryLiteralFromUint(0x61, -1))},
nil,
97, 97, types.NewDecFromInt(97), "a",
97, 97, types.NewDecFromInt(0x61), "a",
},
}
for _, t := range cases {
Expand Down
3 changes: 1 addition & 2 deletions expression/builtin_control_test.go
Expand Up @@ -111,8 +111,7 @@ func (s *testEvaluatorSuite) TestIfNull(c *C) {
{tm, nil, tm, false, false},
{nil, duration, duration, false, false},
{nil, types.NewDecFromFloatForTest(123.123), types.NewDecFromFloatForTest(123.123), false, false},
{nil, types.Bit{Value: 1, Width: 8}, "\x01", false, false},
{nil, types.Hex{Value: 1}, "\x01", false, false},
{nil, types.NewBinaryLiteralFromUint(0x01, -1), uint64(1), false, false},
{nil, types.Set{Value: 1, Name: "abc"}, "abc", false, false},
{nil, jsonInt.GetMysqlJSON(), jsonInt.GetMysqlJSON(), false, false},
{"abc", nil, "abc", false, false},
Expand Down
15 changes: 7 additions & 8 deletions expression/builtin_string_test.go
Expand Up @@ -44,8 +44,7 @@ func (s *testEvaluatorSuite) TestLength(c *C) {
{3.14, 4, false, false},
{types.NewDecFromFloatForTest(123.123), 7, false, false},
{types.Time{Time: types.FromGoTime(time.Now()), Fsp: 6, Type: mysql.TypeDatetime}, 26, false, false},
{types.Bit{Value: 1, Width: 8}, 1, false, false},
{types.Hex{Value: 1}, 1, false, false},
{types.NewBinaryLiteralFromUint(0x01, -1), 1, false, false},
{types.Set{Value: 1, Name: "abc"}, 3, false, false},
{types.Duration{Duration: time.Duration(12*time.Hour + 1*time.Minute + 1*time.Second), Fsp: types.DefaultFsp}, 8, false, false},
{nil, 0, true, false},
Expand Down Expand Up @@ -274,7 +273,7 @@ func (s *testEvaluatorSuite) TestLeft(c *C) {
{[]interface{}{"abcde", "a"}, false, false, ""},
{[]interface{}{1234, 3}, false, false, "123"},
{[]interface{}{12.34, 3}, false, false, "12."},
{[]interface{}{types.Bit{Value: 0x0102, Width: 16}, 1}, false, false, string([]byte{0x01})},
{[]interface{}{types.NewBinaryLiteralFromUint(0x0102, -1), 1}, false, false, string([]byte{0x01})},
{[]interface{}{errors.New("must err"), 0}, false, true, ""},
}
for _, t := range cases {
Expand Down Expand Up @@ -325,7 +324,7 @@ func (s *testEvaluatorSuite) TestRight(c *C) {
{[]interface{}{"abcde", "a"}, false, false, ""},
{[]interface{}{1234, 3}, false, false, "234"},
{[]interface{}{12.34, 3}, false, false, ".34"},
{[]interface{}{types.Bit{Value: 0x0102, Width: 16}, 1}, false, false, string([]byte{0x02})},
{[]interface{}{types.NewBinaryLiteralFromUint(0x0102, -1), 1}, false, false, string([]byte{0x02})},
{[]interface{}{errors.New("must err"), 0}, false, true, ""},
}
for _, t := range cases {
Expand Down Expand Up @@ -983,7 +982,7 @@ func (s *testEvaluatorSuite) TestHexFunc(c *C) {
{-1, false, false, "FFFFFFFFFFFFFFFF"},
{-12.3, false, false, "FFFFFFFFFFFFFFF4"},
{-12.8, false, false, "FFFFFFFFFFFFFFF3"},
{types.Bit{Value: 0xC, Width: 4}, false, false, "0C"},
{types.NewBinaryLiteralFromUint(0xC, -1), false, false, "C"},
{0x12, false, false, "12"},
{nil, true, false, ""},
{errors.New("must err"), false, true, ""},
Expand Down Expand Up @@ -1392,9 +1391,9 @@ func (s *testEvaluatorSuite) TestOct(c *C) {
//overflow uint64
{"9999999999999999999999999", "1777777777777777777777"},
{"-9999999999999999999999999", "1777777777777777777777"},
{types.Bit{Value: 255, Width: 8}, "377"}, // b'11111111'
{types.Bit{Value: 10, Width: 4}, "12"}, // b'1010'
{types.Bit{Value: 5, Width: 4}, "5"}, // b'0101'
{types.NewBinaryLiteralFromUint(255, -1), "377"}, // b'11111111'
{types.NewBinaryLiteralFromUint(10, -1), "12"}, // b'1010'
{types.NewBinaryLiteralFromUint(5, -1), "5"}, // b'0101'
}
fc := funcs[ast.Oct]
for _, tt := range octTests {
Expand Down
10 changes: 4 additions & 6 deletions expression/evaluator_test.go
Expand Up @@ -273,8 +273,7 @@ func (s *testEvaluatorSuite) TestBinopNumeric(c *C) {
{uint64(1), ast.Plus, uint64(1), 2},
{uint64(1), ast.Plus, -1, 0},
{1, ast.Plus, []byte("1"), 2},
{1, ast.Plus, types.Hex{Value: 1}, 2},
{1, ast.Plus, types.Bit{Value: 1, Width: 1}, 2},
{1, ast.Plus, types.NewBinaryLiteralFromUint(1, -1), 2},
{1, ast.Plus, types.Enum{Name: "a", Value: 1}, 2},
{1, ast.Plus, types.Set{Name: "a", Value: 1}, 2},

Expand Down Expand Up @@ -465,8 +464,8 @@ func (s *testEvaluatorSuite) TestUnaryOp(c *C) {
{1, ast.UnaryNot, int64(0)},
{0, ast.UnaryNot, int64(1)},
{nil, ast.UnaryNot, nil},
{types.Hex{Value: 0}, ast.UnaryNot, int64(1)},
{types.Bit{Value: 0, Width: 1}, ast.UnaryNot, int64(1)},
{types.NewBinaryLiteralFromUint(0, -1), ast.UnaryNot, int64(1)},
{types.NewBinaryLiteralFromUint(1, -1), ast.UnaryNot, int64(0)},
{types.Enum{Name: "a", Value: 1}, ast.UnaryNot, int64(0)},
{types.Set{Name: "a", Value: 1}, ast.UnaryNot, int64(0)},

Expand All @@ -482,8 +481,7 @@ func (s *testEvaluatorSuite) TestUnaryOp(c *C) {
{uint64(1), ast.UnaryMinus, -int64(1)},
{"1.0", ast.UnaryMinus, -1.0},
{[]byte("1.0"), ast.UnaryMinus, -1.0},
{types.Hex{Value: 1}, ast.UnaryMinus, -1.0},
{types.Bit{Value: 1, Width: 1}, ast.UnaryMinus, -1.0},
{types.NewBinaryLiteralFromUint(1, -1), ast.UnaryMinus, -1.0},
{true, ast.UnaryMinus, int64(-1)},
{false, ast.UnaryMinus, int64(0)},
{types.Enum{Name: "a", Value: 1}, ast.UnaryMinus, -1.0},
Expand Down
6 changes: 3 additions & 3 deletions expression/expression.go
Expand Up @@ -161,11 +161,11 @@ func evalExprToInt(expr Expression, row []types.Datum, sc *variable.StatementCon
if val.IsNull() || err != nil {
return res, val.IsNull(), errors.Trace(err)
}
if expr.GetTypeClass() == types.ClassInt {
return val.GetInt64(), false, nil
} else if IsHybridType(expr) {
if IsHybridType(expr) {
res, err = val.ToInt64(sc)
return res, false, errors.Trace(err)
} else if expr.GetTypeClass() == types.ClassInt {
return val.GetInt64(), false, nil
}
panic(fmt.Sprintf("cannot get INT result from %s expression", types.TypeStr(expr.GetType().Tp)))
}
Expand Down
6 changes: 3 additions & 3 deletions expression/typeinferer.go
Expand Up @@ -491,18 +491,18 @@ func (v *typeInferrer) addCastToString(expr ast.ExprNode) ast.ExprNode {
// For ENUM/SET which is consist of a string attribute `Name` and an int attribute `Value`,
// it will cause an error if we convert ENUM/SET to int as a string value.
//
// For Bit/Hex, we will get a wrong result if we convert it to int as a string value.
// For BinaryLiteral/MysqlBit, we will get a wrong result if we convert it to int as a string value.
// For example, when convert `0b101` to int, the result should be 5, but we will get 101 if we regard it as a string.
func IsHybridType(expr Expression) bool {
switch expr.GetType().Tp {
case mysql.TypeEnum, mysql.TypeBit, mysql.TypeSet:
return true
}

// For a constant, the field type will be inferred as `VARCHAR` when the kind of it is `HEX` or `BIT`.
// For a constant, the field type will be inferred as `VARCHAR` when the kind of it is BinaryLiteral
if con, ok := expr.(*Constant); ok {
switch con.Value.Kind() {
case types.KindMysqlHex, types.KindMysqlBit:
case types.KindBinaryLiteral:
return true
}
}
Expand Down
6 changes: 3 additions & 3 deletions expression/typeinferer_test.go
Expand Up @@ -66,7 +66,7 @@ func (ts *testTypeInferrerSuite) TestInferType(c *C) {
expr string
tp byte
chs string
flag int
flag uint
}{
{"c_int", mysql.TypeLong, charset.CharsetBin, mysql.BinaryFlag},
{"+1", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag},
Expand Down Expand Up @@ -410,8 +410,8 @@ func (s *testTypeInferrerSuite) TestIsHybridType(c *C) {
{"c_enum", mysql.TypeEnum, true},
{"c_set", mysql.TypeSet, true},
{"c_bit", mysql.TypeBit, true},
{"0b1001", mysql.TypeVarchar, true},
{"0xFFFF", mysql.TypeVarchar, true},
{"0b1001", mysql.TypeVarString, true},
{"0xFFFF", mysql.TypeVarString, true},
{"c_dt", mysql.TypeDatetime, false},
{"c_date", mysql.TypeDate, false},
{"c_time", mysql.TypeDuration, false},
Expand Down
8 changes: 4 additions & 4 deletions expression/util.go
Expand Up @@ -97,8 +97,6 @@ func kindToFieldType(kind byte) types.FieldType {
case types.KindUint64:
ft.Tp = mysql.TypeLonglong
ft.Flag |= mysql.UnsignedFlag
case types.KindMysqlHex:
ft.Tp = mysql.TypeLonglong
case types.KindMinNotNull:
ft.Tp = mysql.TypeLonglong
case types.KindMaxValue:
Expand All @@ -111,8 +109,6 @@ func kindToFieldType(kind byte) types.FieldType {
ft.Tp = mysql.TypeVarString
case types.KindBytes:
ft.Tp = mysql.TypeVarString
case types.KindMysqlBit:
ft.Tp = mysql.TypeBit
case types.KindMysqlEnum:
ft.Tp = mysql.TypeEnum
case types.KindMysqlSet:
Expand All @@ -127,6 +123,10 @@ func kindToFieldType(kind byte) types.FieldType {
ft.Tp = mysql.TypeDuration
case types.KindMysqlTime:
ft.Tp = mysql.TypeDatetime
case types.KindBinaryLiteral:
ft.Tp = mysql.TypeVarString
case types.KindMysqlBit:
ft.Tp = mysql.TypeBit
}
return ft
}
Expand Down
44 changes: 22 additions & 22 deletions mysql/type.go
Expand Up @@ -52,28 +52,28 @@ const TypeUnspecified = TypeDecimal

// Flag information.
const (
NotNullFlag = 1 /* Field can't be NULL */
PriKeyFlag = 2 /* Field is part of a primary key */
UniqueKeyFlag = 4 /* Field is part of a unique key */
MultipleKeyFlag = 8 /* Field is part of a key */
BlobFlag = 16 /* Field is a blob */
UnsignedFlag = 32 /* Field is unsigned */
ZerofillFlag = 64 /* Field is zerofill */
BinaryFlag = 128 /* Field is binary */

EnumFlag = 256 /* Field is an enum */
AutoIncrementFlag = 512 /* Field is an auto increment field */
TimestampFlag = 1024 /* Field is a timestamp */
SetFlag = 2048 /* Field is a set */
NoDefaultValueFlag = 4096 /* Field doesn't have a default value */
OnUpdateNowFlag = 8192 /* Field is set to NOW on UPDATE */
NumFlag = 32768 /* Field is a num (for clients) */
PartKeyFlag = 16384 /* Intern: Part of some keys */
GroupFlag = 32768 /* Intern: Group field */
UniqueFlag = 65536 /* Intern: Used by sql_yacc */
BinCmpFlag = 131072 /* Intern: Used by sql_yacc */
ParseToJSONFlag = 262144 /* Intern: Used when we want to parse string to JSON in CAST */
IsBooleanFlag = 524288 /* Intern: Used for telling boolean literal from integer */
NotNullFlag uint = 1 /* Field can't be NULL */
PriKeyFlag uint = 2 /* Field is part of a primary key */
UniqueKeyFlag uint = 4 /* Field is part of a unique key */
MultipleKeyFlag uint = 8 /* Field is part of a key */
BlobFlag uint = 16 /* Field is a blob */
UnsignedFlag uint = 32 /* Field is unsigned */
ZerofillFlag uint = 64 /* Field is zerofill */
BinaryFlag uint = 128 /* Field is binary */

EnumFlag uint = 256 /* Field is an enum */
AutoIncrementFlag uint = 512 /* Field is an auto increment field */
TimestampFlag uint = 1024 /* Field is a timestamp */
SetFlag uint = 2048 /* Field is a set */
NoDefaultValueFlag uint = 4096 /* Field doesn't have a default value */
OnUpdateNowFlag uint = 8192 /* Field is set to NOW on UPDATE */
NumFlag uint = 32768 /* Field is a num (for clients) */
PartKeyFlag uint = 16384 /* Intern: Part of some keys */
GroupFlag uint = 32768 /* Intern: Group field */
UniqueFlag uint = 65536 /* Intern: Used by sql_yacc */
BinCmpFlag uint = 131072 /* Intern: Used by sql_yacc */
ParseToJSONFlag uint = 262144 /* Intern: Used when we want to parse string to JSON in CAST */
IsBooleanFlag uint = 524288 /* Intern: Used for telling boolean literal from integer */
)

// TypeInt24 bounds.
Expand Down
4 changes: 0 additions & 4 deletions parser/parser.y
Expand Up @@ -2647,10 +2647,6 @@ Literal:
| floatLit
| decLit
| intLit
{
expr := ast.NewValueExpr($1)
$$ = expr
}
| StringLiteral %prec lowerThanStringLitToken
{
$$ = $1
Expand Down
6 changes: 3 additions & 3 deletions parser/parser_test.go
Expand Up @@ -183,9 +183,9 @@ func (s *testParserSuite) TestSimple(c *C) {
_, err = parser.ParseOneStmt(src, "", "")
c.Assert(err, IsNil)

src = "select 0b'';"
_, err = parser.ParseOneStmt(src, "", "")
c.Assert(err, NotNil)
// src = "select 0b'';"
// _, err = parser.ParseOneStmt(src, "", "")
// c.Assert(err, NotNil)
}

type testCase struct {
Expand Down

0 comments on commit fc209cb

Please sign in to comment.