func (s *testTypeConvertSuite) TestConvertToBool(c *C) { testToBool(c, int(0), 0) testToBool(c, int64(0), 0) testToBool(c, uint64(0), 0) testToBool(c, float32(0), 0) testToBool(c, float64(0), 0) testToBool(c, "", 0) testToBool(c, "0", 0) testToBool(c, []byte{}, 0) testToBool(c, []byte("0"), 0) testToBool(c, mysql.Hex{Value: 0}, 0) testToBool(c, mysql.Bit{Value: 0, Width: 8}, 0) testToBool(c, mysql.Enum{Name: "a", Value: 1}, 1) testToBool(c, mysql.Set{Name: "a", Value: 1}, 1) t, err := mysql.ParseTime("2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 6) c.Assert(err, IsNil) testToBool(c, t, 1) td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, IsNil) testToBool(c, td, 1) ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(3.1415926, ft) c.Assert(err, IsNil) testToBool(c, v, 1) _, err = ToBool(&invalidMockType{}) c.Assert(err, NotNil) }
func (s *testTypeConvertSuite) TestConvertToString(c *C) { testToString(c, "0", "0") testToString(c, true, "1") testToString(c, "false", "false") testToString(c, int(0), "0") testToString(c, int8(0), "0") testToString(c, int16(0), "0") testToString(c, int32(0), "0") testToString(c, int64(0), "0") testToString(c, uint(0), "0") testToString(c, uint8(0), "0") testToString(c, uint16(0), "0") testToString(c, uint32(0), "0") testToString(c, uint64(0), "0") testToString(c, float32(1.6), "1.6") testToString(c, float64(-0.6), "-0.6") testToString(c, []byte{1}, "\x01") t, err := mysql.ParseTime("2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 6) c.Assert(err, IsNil) testToString(c, t, "2011-11-10 11:11:11.999999") td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, IsNil) testToString(c, td, "11:11:11.999999") ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(3.1415926, ft) c.Assert(err, IsNil) testToString(c, v, "3.14159") _, err = ToString(&invalidMockType{}) c.Assert(err, NotNil) }
func (s *testTypeConvertSuite) TestConvertToString(c *C) { testToString(c, "0", "0") testToString(c, true, "1") testToString(c, "false", "false") testToString(c, int(0), "0") testToString(c, int64(0), "0") testToString(c, uint64(0), "0") testToString(c, float32(1.6), "1.6") testToString(c, float64(-0.6), "-0.6") testToString(c, []byte{1}, "\x01") testToString(c, mysql.Hex{Value: 0x4D7953514C}, "MySQL") testToString(c, mysql.Bit{Value: 0x41, Width: 8}, "A") testToString(c, mysql.Enum{Name: "a", Value: 1}, "a") testToString(c, mysql.Set{Name: "a", Value: 1}, "a") t, err := mysql.ParseTime("2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 6) c.Assert(err, IsNil) testToString(c, t, "2011-11-10 11:11:11.999999") td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, IsNil) testToString(c, td, "11:11:11.999999") ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(3.1415926, ft) c.Assert(err, IsNil) testToString(c, v, "3.14159") _, err = ToString(&invalidMockType{}) c.Assert(err, NotNil) }
func (s *testTypeConvertSuite) TestConvertToFloat64(c *C) { testToFloat64(c, "0", float64(0)) testToFloat64(c, int(0), float64(0)) testToFloat64(c, int64(0), float64(0)) testToFloat64(c, uint64(0), float64(0)) // TODO: check this //testToFloat64(c, float32(3.1), float64(3.1)) testToFloat64(c, float64(3.1), float64(3.1)) testToFloat64(c, mysql.Hex{Value: 100}, float64(100)) testToFloat64(c, mysql.Bit{Value: 100, Width: 8}, float64(100)) testToFloat64(c, mysql.Enum{Name: "a", Value: 1}, float64(1)) testToFloat64(c, mysql.Set{Name: "a", Value: 1}, float64(1)) t, err := mysql.ParseTime("2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 6) c.Assert(err, IsNil) testToFloat64(c, t, float64(20111110111111.999999)) td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, IsNil) testToFloat64(c, td, float64(111111.999999)) ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(3.1415926, ft) c.Assert(err, IsNil) testToFloat64(c, v, float64(3.14159)) _, err = ToFloat64(&invalidMockType{}) c.Assert(err, NotNil) }
func (s *testTypeConvertSuite) TestConvertToInt64(c *C) { testToInt64(c, "0", int64(0)) testToInt64(c, int(0), int64(0)) testToInt64(c, int64(0), int64(0)) testToInt64(c, uint64(0), int64(0)) testToInt64(c, float32(3.1), int64(3)) testToInt64(c, float64(3.1), int64(3)) testToInt64(c, mysql.Hex{Value: 100}, int64(100)) testToInt64(c, mysql.Bit{Value: 100, Width: 8}, int64(100)) t, err := mysql.ParseTime("2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 0) c.Assert(err, IsNil) testToInt64(c, t, int64(20111110111112)) td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, IsNil) testToInt64(c, td, int64(111112)) ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(3.1415926, ft) c.Assert(err, IsNil) testToInt64(c, v, int64(3)) _, err = ToInt64(&invalidMockType{}) c.Assert(err, NotNil) }
func (s *testTypeConvertSuite) TestConvertToFloat64(c *C) { testToFloat64(c, "0", float64(0)) testToFloat64(c, false, float64(0)) testToFloat64(c, true, float64(1)) testToFloat64(c, int(0), float64(0)) testToFloat64(c, int8(0), float64(0)) testToFloat64(c, int16(0), float64(0)) testToFloat64(c, int32(0), float64(0)) testToFloat64(c, int64(0), float64(0)) testToFloat64(c, uint(0), float64(0)) testToFloat64(c, uint8(0), float64(0)) testToFloat64(c, uint16(0), float64(0)) testToFloat64(c, uint32(0), float64(0)) testToFloat64(c, uint64(0), float64(0)) // TODO: check this //testToFloat64(c, float32(3.1), float64(3.1)) testToFloat64(c, float64(3.1), float64(3.1)) t, err := mysql.ParseTime("2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 6) c.Assert(err, IsNil) testToFloat64(c, t, float64(20111110111111.999999)) td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, IsNil) testToFloat64(c, td, float64(111111.999999)) ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(3.1415926, ft) c.Assert(err, IsNil) testToFloat64(c, v, float64(3.14159)) _, err = ToFloat64(&invalidMockType{}) c.Assert(err, NotNil) }
func (s *testTypeConvertSuite) TestConvertToBool(c *C) { testToBool(c, false, int8(0)) testToBool(c, int(0), int8(0)) testToBool(c, int8(0), int8(0)) testToBool(c, int16(0), int8(0)) testToBool(c, int32(0), int8(0)) testToBool(c, int64(0), int8(0)) testToBool(c, uint(0), int8(0)) testToBool(c, uint8(0), int8(0)) testToBool(c, uint16(0), int8(0)) testToBool(c, uint32(0), int8(0)) testToBool(c, uint64(0), int8(0)) testToBool(c, float32(0), int8(0)) testToBool(c, float64(0), int8(0)) testToBool(c, "", int8(0)) testToBool(c, "0", int8(0)) testToBool(c, []byte{}, int8(0)) testToBool(c, []byte("0"), int8(0)) t, err := mysql.ParseTime("2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 6) c.Assert(err, IsNil) testToBool(c, t, int8(1)) td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, IsNil) testToBool(c, td, int8(1)) ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(3.1415926, ft) c.Assert(err, IsNil) testToBool(c, v, int8(1)) _, err = ToBool(&invalidMockType{}) c.Assert(err, NotNil) }
// Convert converts the val with type tp. func Convert(val interface{}, target *FieldType) (v interface{}, err error) { //NTYPE tp := target.Tp if val == nil { return nil, nil } switch tp { // TODO: implement mysql types convert when "CAST() AS" syntax are supported. case mysql.TypeFloat: x, err := ToFloat64(val) if err != nil { return invConv(val, tp) } if target.Flen != UnspecifiedLength { x, err = TruncateFloat(x, target.Flen, target.Decimal) if err != nil { return nil, errors.Trace(err) } } return float32(x), nil case mysql.TypeDouble: x, err := ToFloat64(val) if err != nil { return invConv(val, tp) } if target.Flen != UnspecifiedLength { x, err = TruncateFloat(x, target.Flen, target.Decimal) if err != nil { return nil, errors.Trace(err) } } return float64(x), nil case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString: x, err := ToString(val) if err != nil { return invConv(val, tp) } // TODO: consider target.Charset/Collate x = truncateStr(x, target.Flen) if target.Charset == charset.CharsetBin { return []byte(x), nil } return x, nil case mysql.TypeDuration: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Duration: return x.RoundFrac(fsp) case mysql.Time: t, err := x.ConvertToDuration() if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case string: return mysql.ParseDuration(x, fsp) default: return invConv(val, tp) } case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDate: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Time: t, err := x.Convert(tp) if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case mysql.Duration: t, err := x.ConvertToTime(tp) if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case string: return mysql.ParseTime(x, tp, fsp) case int64: return mysql.ParseTimeFromNum(x, tp, fsp) default: return invConv(val, tp) } case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: unsigned := mysql.HasUnsignedFlag(target.Flag) if unsigned { return convertToUint(val, target) } return convertToInt(val, target) case mysql.TypeBit: x, err := convertToUint(val, target) if err != nil { return x, errors.Trace(err) } // check bit boundary, if bit has n width, the boundary is // in [0, (1 << n) - 1] width := target.Flen if width == 0 { width = mysql.MinBitWidth } else if width == mysql.UnspecifiedBitWidth { width = mysql.MaxBitWidth } maxValue := uint64(1)<<uint64(width) - 1 if x > maxValue { return maxValue, overflow(val, tp) } return x, nil case mysql.TypeDecimal, mysql.TypeNewDecimal: x, err := ToDecimal(val) if err != nil { return invConv(val, tp) } if target.Decimal != UnspecifiedLength { x = x.Round(int32(target.Decimal)) } // TODO: check Flen return x, nil case mysql.TypeYear: var ( intVal int64 err error ) switch x := val.(type) { case string: intVal, err = StrToInt(x) case mysql.Time: return int64(x.Year()), nil case mysql.Duration: return int64(time.Now().Year()), nil default: intVal, err = ToInt64(x) } if err != nil { return invConv(val, tp) } y, err := mysql.AdjustYear(int(intVal)) if err != nil { return invConv(val, tp) } return int64(y), nil default: panic("should never happen") } }
// Cast casts val to certain types and does not return error. func Cast(val interface{}, target *FieldType) (v interface{}) { //NTYPE tp := target.Tp switch tp { case mysql.TypeString: x, _ := ToString(val) // TODO: consider target.Charset/Collate x = truncateStr(x, target.Flen) if target.Charset == charset.CharsetBin { return []byte(x) } return x case mysql.TypeDuration: var dur mysql.Duration fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Duration: dur, _ = x.RoundFrac(fsp) case mysql.Time: dur, _ = x.ConvertToDuration() dur, _ = dur.RoundFrac(fsp) case string: dur, _ = mysql.ParseDuration(x, fsp) } return dur case mysql.TypeDatetime, mysql.TypeDate: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } var t mysql.Time t.Type = tp switch x := val.(type) { case mysql.Time: t, _ = x.Convert(tp) t, _ = t.RoundFrac(fsp) case mysql.Duration: t, _ = x.ConvertToTime(tp) t, _ = t.RoundFrac(fsp) case string: t, _ = mysql.ParseTime(x, tp, fsp) case int64: t, _ = mysql.ParseTimeFromNum(x, tp, fsp) } return t case mysql.TypeLonglong: if mysql.HasUnsignedFlag(target.Flag) { v, _ = ToUint64(val) } else { v, _ = ToInt64(val) } return case mysql.TypeNewDecimal: x, _ := ToDecimal(val) if target.Decimal != UnspecifiedLength { x = x.Round(int32(target.Decimal)) } // TODO: check Flen return x default: panic("should never happen") } }
func parseTime(c *C, s string) mysql.Time { m, err := mysql.ParseTime(s, mysql.TypeDatetime, mysql.DefaultFsp) c.Assert(err, IsNil) return m }
func (s *testTypeConvertSuite) TestConvertType(c *C) { ft := NewFieldType(mysql.TypeBlob) ft.Flen = 4 ft.Charset = "utf8" v, err := Convert("123456", ft) c.Assert(err, IsNil) c.Assert(v, Equals, "1234") ft = NewFieldType(mysql.TypeString) ft.Flen = 4 ft.Charset = charset.CharsetBin v, err = Convert("12345", ft) c.Assert(err, IsNil) c.Assert(v, DeepEquals, []byte("1234")) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(111.114, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(111.11)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(999.999, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(999.99)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(-999.999, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(-999.99)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(1111.11, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(999.99)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(999.916, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(999.92)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(999.915, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(999.91)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(999.9155, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(999.92)) // For TypeBlob ft = NewFieldType(mysql.TypeBlob) v, err = Convert(&invalidMockType{}, ft) c.Assert(err, NotNil) // Nil ft = NewFieldType(mysql.TypeBlob) v, err = Convert(nil, ft) c.Assert(err, IsNil) c.Assert(v, IsNil) // TypeDouble ft = NewFieldType(mysql.TypeDouble) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(999.9155, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float64(999.92)) // For TypeString ft = NewFieldType(mysql.TypeString) ft.Flen = 3 v, err = Convert("12345", ft) c.Assert(err, IsNil) c.Assert(v, Equals, "123") ft = NewFieldType(mysql.TypeString) ft.Flen = 3 ft.Charset = charset.CharsetBin v, err = Convert("12345", ft) c.Assert(err, IsNil) c.Assert(v, DeepEquals, []byte("123")) // For TypeDuration ft = NewFieldType(mysql.TypeDuration) ft.Decimal = 3 v, err = Convert("10:11:12.123456", ft) c.Assert(err, IsNil) c.Assert(v.(mysql.Duration).String(), Equals, "10:11:12.123") ft.Decimal = 1 vv, err := Convert(v, ft) c.Assert(err, IsNil) c.Assert(vv.(mysql.Duration).String(), Equals, "10:11:12.1") vt, err := mysql.ParseTime("2010-10-10 10:11:11.12345", mysql.TypeTimestamp, 2) c.Assert(vt.String(), Equals, "2010-10-10 10:11:11.12") c.Assert(err, IsNil) v, err = Convert(vt, ft) c.Assert(err, IsNil) c.Assert(v.(mysql.Duration).String(), Equals, "10:11:11.1") // For mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDate ft = NewFieldType(mysql.TypeTimestamp) ft.Decimal = 3 v, err = Convert("2010-10-10 10:11:11.12345", ft) c.Assert(err, IsNil) c.Assert(v.(mysql.Time).String(), Equals, "2010-10-10 10:11:11.123") ft.Decimal = 1 vv, err = Convert(v, ft) c.Assert(err, IsNil) c.Assert(vv.(mysql.Time).String(), Equals, "2010-10-10 10:11:11.1") // For TypeLonglong ft = NewFieldType(mysql.TypeLonglong) v, err = Convert("100", ft) c.Assert(err, IsNil) c.Assert(v, Equals, int64(100)) ft = NewFieldType(mysql.TypeLonglong) ft.Flag |= mysql.UnsignedFlag v, err = Convert("100", ft) c.Assert(err, IsNil) c.Assert(v, Equals, uint64(100)) // For TypeNewDecimal ft = NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err = Convert(3.1415926, ft) c.Assert(err, IsNil) c.Assert(v.(mysql.Decimal).String(), Equals, "3.14159") // For TypeYear ft = NewFieldType(mysql.TypeYear) v, err = Convert("2015-11-11", ft) c.Assert(err, IsNil) c.Assert(v, Equals, int16(2015)) v, err = Convert(2015, ft) c.Assert(err, IsNil) c.Assert(v, Equals, int16(2015)) v, err = Convert(1800, ft) c.Assert(err, NotNil) dt, err := mysql.ParseDate("2015-11-11") c.Assert(err, IsNil) v, err = Convert(dt, ft) c.Assert(v, Equals, int16(2015)) v, err = Convert(mysql.ZeroDuration, ft) c.Assert(v, Equals, int16(time.Now().Year())) }
// Compare returns an integer comparing the interface a to b. // TODO: compare should return errors instead of panicing. func Compare(a, b interface{}) int { switch x := a.(type) { case nil: if b != nil { return -1 } return 0 case bool: switch y := b.(type) { case nil: return 1 case bool: if !x && y { return -1 } if x == y { return 0 } return 1 default: // Make bool collate before anything except nil and // other bool for index seeking first non NULL value. return -1 } case float32: return compareFloat64With(float64(x), b) case float64: return compareFloat64With(float64(x), b) case int8: return compareInt64With(int64(x), b) case int16: return compareInt64With(int64(x), b) case int32: return compareInt64With(int64(x), b) case int: return compareInt64With(int64(x), b) case int64: return compareInt64With(int64(x), b) case uint8: return compareUint64With(uint64(x), b) case uint16: return compareUint64With(uint64(x), b) case uint: return compareUint64With(uint64(x), b) case uint32: return compareUint64With(uint64(x), b) case uint64: return compareUint64With(uint64(x), b) case string: switch y := b.(type) { case nil: return 1 case string: if x < y { return -1 } if x == y { return 0 } return 1 default: panic("should never happen") } case []byte: switch y := b.(type) { case nil: return 1 case []byte: return bytes.Compare(x, y) default: panic("should never happen") } case mysql.Time: switch y := b.(type) { case nil: return 1 case mysql.Time: return x.Compare(y) case string: t, err := mysql.ParseTime(y, x.Type, x.Fsp) if err != nil { log.Warnf("Failed to convert %s to mysql.Time with err %v", y, err) return 1 } return x.Compare(t) default: panic("should never happen") } case mysql.Duration: switch y := b.(type) { case nil: return 1 case mysql.Duration: if x.Duration < y.Duration { return -1 } if x.Duration == y.Duration { return 0 } return 1 default: panic("should never happen") } case mysql.Decimal: y, err := ToDecimal(b) if err != nil { panic(fmt.Sprintf("should never happen, err: %v", err)) } return x.Cmp(y) default: panic("should never happen") } }
// Convert converts the val with type tp. func Convert(val interface{}, target *FieldType) (v interface{}, err error) { //NTYPE tp := target.Tp if val == nil { return nil, nil } switch tp { // TODO: implement mysql types convert when "CAST() AS" syntax are supported. case mysql.TypeFloat: x, err := ToFloat64(val) if err != nil { return InvConv(val, tp) } if target.Flen != UnspecifiedLength { x, err = TruncateFloat(x, target.Flen, target.Decimal) if err != nil { return nil, errors.Trace(err) } } return float32(x), nil case mysql.TypeDouble: x, err := ToFloat64(val) if err != nil { return InvConv(val, tp) } if target.Flen != UnspecifiedLength { x, err = TruncateFloat(x, target.Flen, target.Decimal) if err != nil { return nil, errors.Trace(err) } } return float64(x), nil case mysql.TypeString: x, err := ToString(val) if err != nil { return InvConv(val, tp) } // TODO: consider target.Charset/Collate x = truncateStr(x, target.Flen) if target.Charset == charset.CharsetBin { return []byte(x), nil } return x, nil case mysql.TypeBlob: x, err := ToString(val) if err != nil { return InvConv(val, tp) } x = truncateStr(x, target.Flen) if target.Charset == charset.CharsetBin { return []byte(x), nil } return x, nil case mysql.TypeDuration: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Duration: return x.RoundFrac(fsp) case mysql.Time: t, err := x.ConvertToDuration() if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case string: return mysql.ParseDuration(x, fsp) default: return InvConv(val, tp) } case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDate: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Time: t, err := x.Convert(tp) if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case mysql.Duration: t, err := x.ConvertToTime(tp) if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case string: return mysql.ParseTime(x, tp, fsp) default: return InvConv(val, tp) } case mysql.TypeLonglong: x, err := ToInt64(val) if err != nil { return InvConv(val, tp) } // TODO: We should first convert to uint64 then check unsigned flag. if mysql.HasUnsignedFlag(target.Flag) { return uint64(x), nil } return x, nil case mysql.TypeNewDecimal: x, err := ToDecimal(val) if err != nil { return InvConv(val, tp) } if target.Decimal != UnspecifiedLength { x = x.Round(int32(target.Decimal)) } // TODO: check Flen return x, nil case mysql.TypeYear: var ( intVal int64 err error ) switch x := val.(type) { case string: intVal, err = StrToInt(x) case mysql.Time: return int16(x.Year()), nil case mysql.Duration: return int16(time.Now().Year()), nil default: intVal, err = ToInt64(x) } if err != nil { return InvConv(val, tp) } y, err := mysql.AdjustYear(int(intVal)) if err != nil { return InvConv(val, tp) } return int16(y), nil default: panic("should never happen") } }
func (s *testTypeConvertSuite) TestConvertType(c *C) { ft := NewFieldType(mysql.TypeBlob) ft.Flen = 4 ft.Charset = "utf8" v, err := Convert("123456", ft) c.Assert(err, IsNil) c.Assert(v, Equals, "1234") ft = NewFieldType(mysql.TypeString) ft.Flen = 4 ft.Charset = charset.CharsetBin v, err = Convert("12345", ft) c.Assert(err, IsNil) c.Assert(v, DeepEquals, []byte("1234")) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(111.114, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(111.11)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(999.999, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(999.99)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(-999.999, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(-999.99)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(1111.11, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(999.99)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(999.916, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(999.92)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(999.915, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(999.91)) ft = NewFieldType(mysql.TypeFloat) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(999.9155, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float32(999.92)) // For TypeBlob ft = NewFieldType(mysql.TypeBlob) v, err = Convert(&invalidMockType{}, ft) c.Assert(err, NotNil) // Nil ft = NewFieldType(mysql.TypeBlob) v, err = Convert(nil, ft) c.Assert(err, IsNil) c.Assert(v, IsNil) // TypeDouble ft = NewFieldType(mysql.TypeDouble) ft.Flen = 5 ft.Decimal = 2 v, err = Convert(999.9155, ft) c.Assert(err, IsNil) c.Assert(v, Equals, float64(999.92)) // For TypeString ft = NewFieldType(mysql.TypeString) ft.Flen = 3 v, err = Convert("12345", ft) c.Assert(err, IsNil) c.Assert(v, Equals, "123") ft = NewFieldType(mysql.TypeString) ft.Flen = 3 ft.Charset = charset.CharsetBin v, err = Convert("12345", ft) c.Assert(err, IsNil) c.Assert(v, DeepEquals, []byte("123")) // For TypeDuration ft = NewFieldType(mysql.TypeDuration) ft.Decimal = 3 v, err = Convert("10:11:12.123456", ft) c.Assert(err, IsNil) c.Assert(v.(mysql.Duration).String(), Equals, "10:11:12.123") ft.Decimal = 1 vv, err := Convert(v, ft) c.Assert(err, IsNil) c.Assert(vv.(mysql.Duration).String(), Equals, "10:11:12.1") vt, err := mysql.ParseTime("2010-10-10 10:11:11.12345", mysql.TypeTimestamp, 2) c.Assert(vt.String(), Equals, "2010-10-10 10:11:11.12") c.Assert(err, IsNil) v, err = Convert(vt, ft) c.Assert(err, IsNil) c.Assert(v.(mysql.Duration).String(), Equals, "10:11:11.1") // For mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDate ft = NewFieldType(mysql.TypeTimestamp) ft.Decimal = 3 v, err = Convert("2010-10-10 10:11:11.12345", ft) c.Assert(err, IsNil) c.Assert(v.(mysql.Time).String(), Equals, "2010-10-10 10:11:11.123") ft.Decimal = 1 vv, err = Convert(v, ft) c.Assert(err, IsNil) c.Assert(vv.(mysql.Time).String(), Equals, "2010-10-10 10:11:11.1") // For TypeLonglong ft = NewFieldType(mysql.TypeLonglong) v, err = Convert("100", ft) c.Assert(err, IsNil) c.Assert(v, Equals, int64(100)) ft = NewFieldType(mysql.TypeLonglong) ft.Flag |= mysql.UnsignedFlag v, err = Convert("100", ft) c.Assert(err, IsNil) c.Assert(v, Equals, uint64(100)) // For TypeBit ft = NewFieldType(mysql.TypeBit) ft.Flen = 8 v, err = Convert("100", ft) c.Assert(err, IsNil) c.Assert(v, Equals, mysql.Bit{Value: 100, Width: 8}) v, err = Convert(mysql.Hex{Value: 100}, ft) c.Assert(err, IsNil) c.Assert(v, Equals, mysql.Bit{Value: 100, Width: 8}) v, err = Convert(mysql.Bit{Value: 100, Width: 8}, ft) c.Assert(err, IsNil) c.Assert(v, Equals, mysql.Bit{Value: 100, Width: 8}) ft.Flen = 1 v, err = Convert(1, ft) c.Assert(err, IsNil) c.Assert(v, Equals, mysql.Bit{Value: 1, Width: 1}) _, err = Convert(2, ft) c.Assert(err, NotNil) ft.Flen = 0 _, err = Convert(2, ft) c.Assert(err, NotNil) // For TypeNewDecimal ft = NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err = Convert(3.1415926, ft) c.Assert(err, IsNil) c.Assert(v.(mysql.Decimal).String(), Equals, "3.14159") // For TypeYear ft = NewFieldType(mysql.TypeYear) v, err = Convert("2015-11-11", ft) c.Assert(err, IsNil) c.Assert(v, Equals, int64(2015)) v, err = Convert(2015, ft) c.Assert(err, IsNil) c.Assert(v, Equals, int64(2015)) v, err = Convert(1800, ft) c.Assert(err, NotNil) dt, err := mysql.ParseDate("2015-11-11") c.Assert(err, IsNil) v, err = Convert(dt, ft) c.Assert(v, Equals, int64(2015)) v, err = Convert(mysql.ZeroDuration, ft) c.Assert(v, Equals, int64(time.Now().Year())) // For enum ft = NewFieldType(mysql.TypeEnum) ft.Elems = []string{"a", "b", "c"} v, err = Convert("a", ft) c.Assert(err, IsNil) c.Assert(v, DeepEquals, mysql.Enum{Name: "a", Value: 1}) v, err = Convert(2, ft) c.Assert(err, IsNil) c.Assert(v, DeepEquals, mysql.Enum{Name: "b", Value: 2}) _, err = Convert("d", ft) c.Assert(err, NotNil) _, err = Convert(4, ft) c.Assert(err, NotNil) ft = NewFieldType(mysql.TypeSet) ft.Elems = []string{"a", "b", "c"} v, err = Convert("a", ft) c.Assert(err, IsNil) c.Assert(v, DeepEquals, mysql.Set{Name: "a", Value: 1}) v, err = Convert(2, ft) c.Assert(err, IsNil) c.Assert(v, DeepEquals, mysql.Set{Name: "b", Value: 2}) v, err = Convert(3, ft) c.Assert(err, IsNil) c.Assert(v, DeepEquals, mysql.Set{Name: "a,b", Value: 3}) _, err = Convert("d", ft) c.Assert(err, NotNil) _, err = Convert(9, ft) c.Assert(err, NotNil) }
// CastValue casts a value based on column's type. func (c *Col) CastValue(ctx context.Context, val interface{}) (casted interface{}, err error) { if val == nil { return } switch c.Tp { case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear, mysql.TypeBit: intVal, errCode := c.normalizeIntegerValue(val) if errCode == errCodeType { casted = intVal err = c.TypeError(val) return } return c.castIntegerValue(intVal, errCode) case mysql.TypeFloat, mysql.TypeDouble: return c.castFloatValue(val) case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: switch v := val.(type) { case int64: casted, err = mysql.ParseTimeFromNum(v, c.Tp, c.Decimal) if err != nil { err = newParseColError(err, c) } case string: casted, err = mysql.ParseTime(v, c.Tp, c.Decimal) if err != nil { err = newParseColError(err, c) } case mysql.Time: var t mysql.Time t, err = v.Convert(c.Tp) if err != nil { err = newParseColError(err, c) return } casted, err = t.RoundFrac(c.Decimal) if err != nil { err = newParseColError(err, c) } default: err = c.TypeError(val) } case mysql.TypeDuration: switch v := val.(type) { case string: casted, err = mysql.ParseDuration(v, c.Decimal) if err != nil { err = newParseColError(err, c) } case mysql.Time: var t mysql.Duration t, err = v.ConvertToDuration() if err != nil { err = newParseColError(err, c) return } casted, err = t.RoundFrac(c.Decimal) if err != nil { err = newParseColError(err, c) } case mysql.Duration: casted, err = v.RoundFrac(c.Decimal) default: err = c.TypeError(val) } case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString: strV := "" switch v := val.(type) { case mysql.Time: strV = v.String() case mysql.Duration: strV = v.String() case []byte: if c.Charset == charset.CharsetBin { casted = v return } strV = string(v) default: strV = fmt.Sprintf("%v", val) } if (c.Flen != types.UnspecifiedLength) && (len(strV) > c.Flen) { strV = strV[:c.Flen] } casted = strV case mysql.TypeDecimal, mysql.TypeNewDecimal: switch v := val.(type) { case string: casted, err = mysql.ParseDecimal(v) if err != nil { err = newParseColError(err, c) } case int8: casted = mysql.NewDecimalFromInt(int64(v), 0) case int16: casted = mysql.NewDecimalFromInt(int64(v), 0) case int32: casted = mysql.NewDecimalFromInt(int64(v), 0) case int64: casted = mysql.NewDecimalFromInt(int64(v), 0) case int: casted = mysql.NewDecimalFromInt(int64(v), 0) case uint8: casted = mysql.NewDecimalFromUint(uint64(v), 0) case uint16: casted = mysql.NewDecimalFromUint(uint64(v), 0) case uint32: casted = mysql.NewDecimalFromUint(uint64(v), 0) case uint64: casted = mysql.NewDecimalFromUint(uint64(v), 0) case uint: casted = mysql.NewDecimalFromUint(uint64(v), 0) case float32: casted = mysql.NewDecimalFromFloat(float64(v)) case float64: casted = mysql.NewDecimalFromFloat(float64(v)) case mysql.Decimal: casted = v } default: err = c.TypeError(val) } return }
// Convert converts the val with type tp. func Convert(val interface{}, target *FieldType) (v interface{}, err error) { //NTYPE tp := target.Tp if val == nil { return nil, nil } switch tp { // TODO: implement mysql types convert when "CAST() AS" syntax are supported. case mysql.TypeFloat: x, err := ToFloat64(val) if err != nil { return InvConv(val, tp) } if target.Flen != UnspecifiedLength { x, err = TruncateFloat(x, target.Flen, target.Decimal) if err != nil { return nil, errors.Trace(err) } } return float32(x), nil case mysql.TypeDouble: x, err := ToFloat64(val) if err != nil { return InvConv(val, tp) } if target.Flen != UnspecifiedLength { x, err = TruncateFloat(x, target.Flen, target.Decimal) if err != nil { return nil, errors.Trace(err) } } return float64(x), nil case mysql.TypeString: x, err := ToString(val) if err != nil { return InvConv(val, tp) } // TODO: consider target.Charset/Collate x = truncateStr(x, target.Flen) return x, nil case mysql.TypeBlob: x, err := ToString(val) if err != nil { return InvConv(val, tp) } x = truncateStr(x, target.Flen) return x, nil case mysql.TypeDuration: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Duration: return x.RoundFrac(fsp) case mysql.Time: t, err := x.ConvertToDuration() if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case string: return mysql.ParseDuration(x, fsp) default: return InvConv(val, tp) } case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDate: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Time: t, err := x.Convert(tp) if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case mysql.Duration: t, err := x.ConvertToTime(tp) if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case string: return mysql.ParseTime(x, tp, fsp) default: return InvConv(val, tp) } case mysql.TypeLonglong: x, err := ToInt64(val) if err != nil { return InvConv(val, tp) } return x, nil case mysql.TypeNewDecimal: x, err := ToDecimal(val) if err != nil { return InvConv(val, tp) } if target.Decimal != UnspecifiedLength { x = x.Round(int32(target.Decimal)) } // TODO: check Flen return x, nil default: panic("should never happen") } }
func getTimeValue(ctx context.Context, v interface{}, tp byte, fsp int) (interface{}, error) { value := mysql.Time{ Type: tp, Fsp: fsp, } defaultTime, err := getSystemTimestamp(ctx) if err != nil { return nil, errors.Trace(err) } switch x := v.(type) { case string: if x == CurrentTimestamp { value.Time = defaultTime } else if x == ZeroTimestamp { value, _ = mysql.ParseTimeFromNum(0, tp, fsp) } else { value, err = mysql.ParseTime(x, tp, fsp) if err != nil { return nil, errors.Trace(err) } } case Value: switch xval := x.Val.(type) { case string: value, err = mysql.ParseTime(xval, tp, fsp) if err != nil { return nil, errors.Trace(err) } case int64: value, err = mysql.ParseTimeFromNum(int64(xval), tp, fsp) if err != nil { return nil, errors.Trace(err) } case nil: return nil, nil default: return nil, errors.Trace(errDefaultValue) } case *Ident: if x.Equal(CurrentTimeExpr) { return CurrentTimestamp, nil } return nil, errors.Trace(errDefaultValue) case *UnaryOperation: // support some expression, like `-1` m := map[interface{}]interface{}{} v := Eval(x, nil, m) ft := types.NewFieldType(mysql.TypeLonglong) xval, err := types.Convert(v, ft) if err != nil { return nil, errors.Trace(err) } value, err = mysql.ParseTimeFromNum(xval.(int64), tp, fsp) if err != nil { return nil, errors.Trace(err) } default: return nil, nil } return value, nil }
// Convert converts the val with type tp. func Convert(val interface{}, target *FieldType) (v interface{}, err error) { tp := target.Tp if val == nil { return nil, nil } switch tp { // TODO: implement mysql types convert when "CAST() AS" syntax are supported. case mysql.TypeFloat: x, err := ToFloat64(val) if err != nil { return invConv(val, tp) } // For float and following double type, we will only truncate it for float(M, D) format. // If no D is set, we will handle it like origin float whether M is set or not. if target.Flen != UnspecifiedLength && target.Decimal != UnspecifiedLength { x, err = TruncateFloat(x, target.Flen, target.Decimal) if err != nil { return nil, errors.Trace(err) } } return float32(x), nil case mysql.TypeDouble: x, err := ToFloat64(val) if err != nil { return invConv(val, tp) } if target.Flen != UnspecifiedLength && target.Decimal != UnspecifiedLength { x, err = TruncateFloat(x, target.Flen, target.Decimal) if err != nil { return nil, errors.Trace(err) } } return float64(x), nil case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString: x, err := ToString(val) if err != nil { return invConv(val, tp) } // TODO: consider target.Charset/Collate x = truncateStr(x, target.Flen) if target.Charset == charset.CharsetBin { return []byte(x), nil } return x, nil case mysql.TypeDuration: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Duration: return x.RoundFrac(fsp) case mysql.Time: t, err := x.ConvertToDuration() if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case string: return mysql.ParseDuration(x, fsp) default: return invConv(val, tp) } case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDate: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Time: t, err := x.Convert(tp) if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case mysql.Duration: t, err := x.ConvertToTime(tp) if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case string: return mysql.ParseTime(x, tp, fsp) case int64: return mysql.ParseTimeFromNum(x, tp, fsp) default: return invConv(val, tp) } case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: unsigned := mysql.HasUnsignedFlag(target.Flag) if unsigned { return convertToUint(val, target) } return convertToInt(val, target) case mysql.TypeBit: x, err := convertToUint(val, target) if err != nil { return x, errors.Trace(err) } // check bit boundary, if bit has n width, the boundary is // in [0, (1 << n) - 1] width := target.Flen if width == 0 || width == mysql.UnspecifiedBitWidth { width = mysql.MinBitWidth } maxValue := uint64(1)<<uint64(width) - 1 if x > maxValue { return maxValue, overflow(val, tp) } return mysql.Bit{Value: x, Width: width}, nil case mysql.TypeDecimal, mysql.TypeNewDecimal: x, err := ToDecimal(val) if err != nil { return invConv(val, tp) } if target.Decimal != UnspecifiedLength { x = x.Round(int32(target.Decimal)) } // TODO: check Flen return x, nil case mysql.TypeYear: var ( intVal int64 err error ) switch x := val.(type) { case string: intVal, err = StrToInt(x) case mysql.Time: return int64(x.Year()), nil case mysql.Duration: return int64(time.Now().Year()), nil default: intVal, err = ToInt64(x) } if err != nil { return invConv(val, tp) } y, err := mysql.AdjustYear(int(intVal)) if err != nil { return invConv(val, tp) } return int64(y), nil case mysql.TypeEnum: var ( e mysql.Enum err error ) switch x := val.(type) { case string: e, err = mysql.ParseEnumName(target.Elems, x) case []byte: e, err = mysql.ParseEnumName(target.Elems, string(x)) default: var number uint64 number, err = ToUint64(x) if err != nil { return nil, errors.Trace(err) } e, err = mysql.ParseEnumValue(target.Elems, number) } if err != nil { return invConv(val, tp) } return e, nil case mysql.TypeSet: var ( s mysql.Set err error ) switch x := val.(type) { case string: s, err = mysql.ParseSetName(target.Elems, x) case []byte: s, err = mysql.ParseSetName(target.Elems, string(x)) default: var number uint64 number, err = ToUint64(x) if err != nil { return nil, errors.Trace(err) } s, err = mysql.ParseSetValue(target.Elems, number) } if err != nil { return invConv(val, tp) } return s, nil default: panic("should never happen") } }