func (s *testTypeConvertSuite) TestConvertToString(c *check.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, check.IsNil) testToString(c, t, "2011-11-10 11:11:11.999999") td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, check.IsNil) testToString(c, td, "11:11:11.999999") ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(3.1415926, ft) c.Assert(err, check.IsNil) testToString(c, v, "3.14159") _, err = ToString(&invalidMockType{}) c.Assert(err, check.NotNil) }
func (d *Datum) compareString(s string) (int, error) { switch d.k { case KindNull, KindMinNotNull: return -1, nil case KindMaxValue: return 1, nil case KindString, KindBytes: return CompareString(d.GetString(), s), nil case KindMysqlDecimal: dec := new(mysql.MyDecimal) err := dec.FromString([]byte(s)) return d.GetMysqlDecimal().Compare(dec), err case KindMysqlTime: dt, err := mysql.ParseDatetime(s) return d.GetMysqlTime().Compare(dt), err case KindMysqlDuration: dur, err := mysql.ParseDuration(s, mysql.MaxFsp) return d.GetMysqlDuration().Compare(dur), err case KindMysqlBit: return CompareString(d.GetMysqlBit().ToString(), s), nil case KindMysqlHex: return CompareString(d.GetMysqlHex().ToString(), s), nil case KindMysqlSet: return CompareString(d.GetMysqlSet().String(), s), nil case KindMysqlEnum: return CompareString(d.GetMysqlEnum().String(), s), nil default: fVal, err := StrToFloat(s) if err != nil { return 0, err } return d.compareFloat64(fVal) } }
func (s *testTypeConvertSuite) TestConvertToBool(c *check.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, check.IsNil) testToBool(c, t, 1) td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, check.IsNil) testToBool(c, td, 1) ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(3.1415926, ft) c.Assert(err, check.IsNil) testToBool(c, v, 1) _, err = ToBool(&invalidMockType{}) c.Assert(err, check.NotNil) }
func (ts *testDatumSuite) TestToBool(c *C) { testDatumToBool(c, int(0), 0) testDatumToBool(c, int64(0), 0) testDatumToBool(c, uint64(0), 0) testDatumToBool(c, float32(0.1), 0) testDatumToBool(c, float64(0.1), 0) testDatumToBool(c, "", 0) testDatumToBool(c, "0.1", 0) testDatumToBool(c, []byte{}, 0) testDatumToBool(c, []byte("0.1"), 0) testDatumToBool(c, mysql.Hex{Value: 0}, 0) testDatumToBool(c, mysql.Bit{Value: 0, Width: 8}, 0) testDatumToBool(c, mysql.Enum{Name: "a", Value: 1}, 1) testDatumToBool(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) testDatumToBool(c, t, 1) td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, IsNil) testDatumToBool(c, td, 1) ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(0.1415926, ft) c.Assert(err, IsNil) testDatumToBool(c, v, 0) d := NewDatum(&invalidMockType{}) _, err = d.ToBool() c.Assert(err, NotNil) }
func (s *testTypeConvertSuite) TestConvertToFloat64(c *check.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, check.IsNil) testToFloat64(c, t, float64(20111110111111.999999)) td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, check.IsNil) testToFloat64(c, td, float64(111111.999999)) ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(3.1415926, ft) c.Assert(err, check.IsNil) testToFloat64(c, v, float64(3.14159)) _, err = ToFloat64(&invalidMockType{}) c.Assert(err, check.NotNil) }
func (ts *testTypeConvertSuite) TestToInt64(c *C) { testDatumToInt64(c, "0", int64(0)) testDatumToInt64(c, int(0), int64(0)) testDatumToInt64(c, int64(0), int64(0)) testDatumToInt64(c, uint64(0), int64(0)) testDatumToInt64(c, float32(3.1), int64(3)) testDatumToInt64(c, float64(3.1), int64(3)) testDatumToInt64(c, mysql.Hex{Value: 100}, int64(100)) testDatumToInt64(c, mysql.Bit{Value: 100, Width: 8}, int64(100)) testDatumToInt64(c, mysql.Enum{Name: "a", Value: 1}, int64(1)) testDatumToInt64(c, mysql.Set{Name: "a", Value: 1}, int64(1)) t, err := mysql.ParseTime("2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 0) c.Assert(err, IsNil) testDatumToInt64(c, t, int64(20111110111112)) td, err := mysql.ParseDuration("11:11:11.999999", 6) c.Assert(err, IsNil) testDatumToInt64(c, td, int64(111112)) ft := NewFieldType(mysql.TypeNewDecimal) ft.Decimal = 5 v, err := Convert(3.1415926, ft) c.Assert(err, IsNil) testDatumToInt64(c, v, int64(3)) }
func (d *Datum) compareMysqlDuration(dur mysql.Duration) (int, error) { switch d.k { case KindMysqlDuration: return d.GetMysqlDuration().Compare(dur), nil case KindString, KindBytes: dDur, err := mysql.ParseDuration(d.GetString(), mysql.MaxFsp) return dDur.Compare(dur), err default: return d.compareFloat64(dur.Seconds()) } }
func (s *testUtilSuite) TestDumpBinaryTime(c *C) { t, err := mysql.ParseTimestamp("0000-00-00 00:00:00.0000000") c.Assert(err, IsNil) d := dumpBinaryDateTime(t, nil) c.Assert(d, DeepEquals, []byte{11, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}) t, err = mysql.ParseDatetime("0000-00-00 00:00:00.0000000") c.Assert(err, IsNil) d = dumpBinaryDateTime(t, nil) c.Assert(d, DeepEquals, []byte{11, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}) t, err = mysql.ParseDate("0000-00-00") c.Assert(err, IsNil) d = dumpBinaryDateTime(t, nil) c.Assert(d, DeepEquals, []byte{4, 1, 0, 1, 1}) myDuration, err := mysql.ParseDuration("0000-00-00 00:00:00.0000000", 6) c.Assert(err, IsNil) d = dumpBinaryTime(myDuration.Duration) c.Assert(d, DeepEquals, []byte{0}) }
func (d *Datum) convertToMysqlDuration(target *FieldType) (Datum, error) { tp := target.Tp fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } var ret Datum switch d.k { case KindMysqlTime: dur, err := d.GetMysqlTime().ConvertToDuration() if err != nil { ret.SetValue(dur) return ret, errors.Trace(err) } dur, err = dur.RoundFrac(fsp) ret.SetValue(dur) if err != nil { return ret, errors.Trace(err) } case KindMysqlDuration: dur, err := d.GetMysqlDuration().RoundFrac(fsp) ret.SetValue(dur) if err != nil { return ret, errors.Trace(err) } case KindString, KindBytes: t, err := mysql.ParseDuration(d.GetString(), fsp) ret.SetValue(t) if err != nil { return ret, errors.Trace(err) } default: return invalidConv(d, tp) } return ret, nil }
func parseDuration(c *C, s string) mysql.Duration { m, err := mysql.ParseDuration(s, mysql.DefaultFsp) c.Assert(err, IsNil) return m }
// 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 } vdi, ok := val.(*DataItem) if ok { return Convert(vdi.Data, target) } 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 case mysql.TypeNull: return nil, 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{}) { 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) case *DataItem: return Cast(x.Data, target) } 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) case *DataItem: return Cast(x.Data, target) } 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 (s *testTypeConvertSuite) TestConvertToString(c *C) { defer testleak.AfterTest(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) // test truncate cases := []struct { flen int charset string input string output string }{ {5, "utf8", "你好,世界", "你好,世界"}, {5, "utf8mb4", "你好,世界", "你好,世界"}, {4, "utf8", "你好,世界", "你好,世"}, {4, "utf8mb4", "你好,世界", "你好,世"}, {15, "binary", "你好,世界", "你好,世界"}, {12, "binary", "你好,世界", "你好,世"}, {0, "binary", "你好,世界", ""}, } for _, ca := range cases { ft = NewFieldType(mysql.TypeVarchar) ft.Flen = ca.flen ft.Charset = ca.charset inputDatum := NewStringDatum(ca.input) outputDatum, err := inputDatum.ConvertTo(ft) if ca.input != ca.output { c.Assert(ErrDataTooLong.Equal(err), IsTrue) } else { c.Assert(err, IsNil) } c.Assert(outputDatum.GetString(), Equals, ca.output) } }