// DecodeDecimal decodes bytes to decimal. // DecodeFloat decodes a float from a byte slice // Decimal decoding: // Byte -> value sign // Byte -> exp sign // DecodeInt -> exp value // DecodeBytes -> abs value bytes func DecodeDecimal(b []byte) ([]byte, mysql.Decimal, error) { var ( r = b d mysql.Decimal err error ) // Decode value sign. valSign := int64(r[0]) r = r[1:] if valSign == zeroSign { d, err = mysql.ParseDecimal("0") return r, d, errors.Trace(err) } // Decode exp sign. expSign := int64(r[0]) r = r[1:] // Decode exp value. expVal := int64(0) r, expVal, err = DecodeInt(r) if err != nil { return r, d, errors.Trace(err) } expVal = decodeExp(expVal, expSign, valSign) // Decode abs value bytes. value := []byte{} r, value, err = DecodeBytes(r) if err != nil { return r, d, errors.Trace(err) } codecValue(value, valSign) // Generate decimal string value. var decimalStr []byte if valSign == negativeSign { decimalStr = append(decimalStr, '-') } if expVal <= 0 { // Like decimal "0.1234" or "0.01234". decimalStr = append(decimalStr, '0') decimalStr = append(decimalStr, '.') decimalStr = append(decimalStr, bytes.Repeat([]byte{'0'}, -int(expVal))...) decimalStr = append(decimalStr, value...) } else { // Like decimal "12.34". decimalStr = append(decimalStr, value[:expVal]...) decimalStr = append(decimalStr, '.') decimalStr = append(decimalStr, value[expVal:]...) } d, err = mysql.ParseDecimal(string(decimalStr)) return r, d, errors.Trace(err) }
// DecodeDecimal decodes bytes to decimal. // DecodeFloat decodes a float from a byte slice // Decimal decoding: // Byte -> value sign // DecodeInt -> exp value // DecodeBytes -> abs value bytes func DecodeDecimal(b []byte) ([]byte, mysql.Decimal, error) { var ( r = b d mysql.Decimal err error ) // Decode value sign. valSign := int64(r[0]) r = r[1:] if valSign == zeroSign { d, err = mysql.ParseDecimal("0") return r, d, errors.Trace(err) } // Decode exp value. expVal := int64(0) r, expVal, err = DecodeInt(r) if err != nil { return r, d, errors.Trace(err) } // Decode abs value bytes. value := []byte{} if valSign == negativeSign { expVal = -expVal r, value, err = DecodeBytesDesc(r) } else { r, value, err = DecodeBytes(r) } if err != nil { return r, d, errors.Trace(err) } // Set decimal sign and point to value. if valSign == negativeSign { value = append([]byte("-0."), value...) } else { value = append([]byte("0."), value...) } numberDecimal, err := mysql.ParseDecimal(string(value)) if err != nil { return r, d, errors.Trace(err) } expDecimal := mysql.NewDecimalFromInt(1, int32(expVal)) d = numberDecimal.Mul(expDecimal) if expDecimal.Exponent() > 0 { // For int64(3), it will be converted to value=0.3 and exp=1 when doing encode. // Its frac will be changed after we run d = numberDecimal.Mul(expDecimal). // So we try to get frac to the original one. d.SetFracDigits(d.FracDigits() - expDecimal.Exponent()) } return r, d, nil }
// DecodeDecimal decodes bytes to decimal. // DecodeFloat decodes a float from a byte slice // Decimal decoding: // Byte -> value sign // DecodeInt -> exp value // DecodeBytes -> abs value bytes func DecodeDecimal(b []byte) ([]byte, mysql.Decimal, error) { var ( r = b d mysql.Decimal err error ) // Decode value sign. valSign := int64(r[0]) r = r[1:] if valSign == zeroSign { d, err = mysql.ParseDecimal("0") return r, d, errors.Trace(err) } // Decode exp value. expVal := int64(0) r, expVal, err = DecodeInt(r) if err != nil { return r, d, errors.Trace(err) } // Decode abs value bytes. value := []byte{} if valSign == negativeSign { expVal = -expVal r, value, err = DecodeBytesDesc(r) } else { r, value, err = DecodeBytes(r) } if err != nil { return r, d, errors.Trace(err) } // Set decimal sign and point to value. if valSign == negativeSign { value = append([]byte("-0."), value...) } else { value = append([]byte("0."), value...) } numberDecimal, err := mysql.ParseDecimal(string(value)) if err != nil { return r, d, errors.Trace(err) } expDecimal := mysql.NewDecimalFromInt(1, int32(expVal)) d = numberDecimal.Mul(expDecimal) return r, d, nil }
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, err := mysql.ParseDecimal(s) return d.GetMysqlDecimal().Cmp(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) } }
// ConvertDatumToDecimal converts datum to decimal. func ConvertDatumToDecimal(d Datum) (mysql.Decimal, error) { switch d.Kind() { case KindInt64: return mysql.NewDecimalFromInt(d.GetInt64(), 0), nil case KindUint64: return mysql.NewDecimalFromUint(d.GetUint64(), 0), nil case KindFloat32: return mysql.NewDecimalFromFloat(float64(d.GetFloat32())), nil case KindFloat64: return mysql.NewDecimalFromFloat(d.GetFloat64()), nil case KindString: return mysql.ParseDecimal(d.GetString()) case KindMysqlDecimal: return d.GetMysqlDecimal(), nil case KindMysqlHex: return mysql.NewDecimalFromInt(int64(d.GetMysqlHex().Value), 0), nil case KindMysqlBit: return mysql.NewDecimalFromUint(uint64(d.GetMysqlBit().Value), 0), nil case KindMysqlEnum: return mysql.NewDecimalFromUint(uint64(d.GetMysqlEnum().Value), 0), nil case KindMysqlSet: return mysql.NewDecimalFromUint(uint64(d.GetMysqlSet().Value), 0), nil default: return mysql.Decimal{}, fmt.Errorf("can't convert %v to decimal", d.GetValue()) } }
func (t *Table) unflatten(rec interface{}, col *column.Col) (interface{}, error) { if rec == nil { return nil, nil } switch col.Tp { case mysql.TypeFloat: return float32(rec.(float64)), nil case mysql.TypeTiny, mysql.TypeShort, mysql.TypeYear, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeBlob, mysql.TypeLongBlob, mysql.TypeVarchar, mysql.TypeString: return rec, nil case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: var t mysql.Time t.Type = col.Tp t.Fsp = col.Decimal err := t.Unmarshal(rec.([]byte)) if err != nil { return nil, errors.Trace(err) } return t, nil case mysql.TypeDuration: return mysql.Duration{Duration: time.Duration(rec.(int64)), Fsp: col.Decimal}, nil case mysql.TypeNewDecimal, mysql.TypeDecimal: return mysql.ParseDecimal(string(rec.([]byte))) case mysql.TypeEnum: return mysql.ParseEnumValue(col.Elems, rec.(uint64)) case mysql.TypeSet: return mysql.ParseSetValue(col.Elems, rec.(uint64)) case mysql.TypeBit: return mysql.Bit{Value: rec.(uint64), Width: col.Flen}, nil } log.Error(col.Tp, rec, reflect.TypeOf(rec)) return nil, nil }
func unflatten(datum types.Datum, tp *types.FieldType) (types.Datum, error) { if datum.IsNull() { return datum, nil } switch tp.Tp { case mysql.TypeFloat: datum.SetFloat32(float32(datum.GetFloat64())) return datum, nil case mysql.TypeTiny, mysql.TypeShort, mysql.TypeYear, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeBlob, mysql.TypeLongBlob, mysql.TypeVarchar, mysql.TypeString: return datum, nil case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: var t mysql.Time t.Type = tp.Tp t.Fsp = tp.Decimal err := t.Unmarshal(datum.GetBytes()) if err != nil { return datum, errors.Trace(err) } datum.SetValue(t) return datum, nil case mysql.TypeDuration: dur := mysql.Duration{Duration: time.Duration(datum.GetInt64())} datum.SetValue(dur) return datum, nil case mysql.TypeNewDecimal, mysql.TypeDecimal: dec, err := mysql.ParseDecimal(datum.GetString()) if err != nil { return datum, errors.Trace(err) } if tp.Decimal >= 0 { dec = dec.Truncate(int32(tp.Decimal)) } datum.SetValue(dec) return datum, nil case mysql.TypeEnum: enum, err := mysql.ParseEnumValue(tp.Elems, datum.GetUint64()) if err != nil { return datum, errors.Trace(err) } datum.SetValue(enum) return datum, nil case mysql.TypeSet: set, err := mysql.ParseSetValue(tp.Elems, datum.GetUint64()) if err != nil { return datum, errors.Trace(err) } datum.SetValue(set) return datum, nil case mysql.TypeBit: bit := mysql.Bit{Value: datum.GetUint64(), Width: tp.Flen} datum.SetValue(bit) return datum, nil } log.Error(tp.Tp, datum) return datum, nil }
func (d *Datum) compareMysqlDecimal(dec mysql.Decimal) (int, error) { switch d.k { case KindMysqlDecimal: return d.GetMysqlDecimal().Cmp(dec), nil case KindString, KindBytes: dDec, err := mysql.ParseDecimal(d.GetString()) return dDec.Cmp(dec), err default: fVal, _ := dec.Float64() return d.compareFloat64(fVal) } }
func (d *Datum) convertToMysqlDecimal(target *FieldType) (Datum, error) { var ret Datum var dec mysql.Decimal switch d.k { case KindInt64: dec = mysql.NewDecimalFromInt(d.GetInt64(), 0) case KindUint64: dec = mysql.NewDecimalFromUint(d.GetUint64(), 0) case KindFloat32, KindFloat64: dec = mysql.NewDecimalFromFloat(d.GetFloat64()) case KindString, KindBytes: var err error dec, err = mysql.ParseDecimal(d.GetString()) if err != nil { return ret, errors.Trace(err) } case KindMysqlDecimal: dec = d.GetMysqlDecimal() case KindMysqlTime: dec = d.GetMysqlTime().ToNumber() case KindMysqlDuration: dec = d.GetMysqlDuration().ToNumber() case KindMysqlBit: dec = mysql.NewDecimalFromFloat(d.GetMysqlBit().ToNumber()) case KindMysqlEnum: dec = mysql.NewDecimalFromFloat(d.GetMysqlEnum().ToNumber()) case KindMysqlHex: dec = mysql.NewDecimalFromFloat(d.GetMysqlHex().ToNumber()) case KindMysqlSet: dec = mysql.NewDecimalFromFloat(d.GetMysqlSet().ToNumber()) default: return invalidConv(d, target.Tp) } if target.Decimal != UnspecifiedLength { dec = dec.Round(int32(target.Decimal)) } ret.SetValue(dec) return ret, nil }
func (s *testCodecSuite) TestDecimal(c *C) { tbl := []string{ "1234.00", "1234", "12.34", "12.340", "0.1234", "0.0", "0", "-0.0", "-0.0000", "-1234.00", "-1234", "-12.34", "-12.340", "-0.1234"} for _, t := range tbl { m, err := mysql.ParseDecimal(t) c.Assert(err, IsNil) b, err := EncodeKey(m) c.Assert(err, IsNil) v, err := DecodeKey(b) c.Assert(err, IsNil) c.Assert(v, HasLen, 1) vv, ok := v[0].(mysql.Decimal) c.Assert(ok, IsTrue) c.Assert(vv.Equals(m), IsTrue) } tblCmp := []struct { Arg1 interface{} Arg2 interface{} Ret int }{ // Test for float type decimal. {"1234", "123400", -1}, {"12340", "123400", -1}, {"1234", "1234.5", -1}, {"1234", "1234.0000", 0}, {"1234", "12.34", 1}, {"12.34", "12.35", -1}, {"0.1234", "12.3400", -1}, {"0.1234", "0.1235", -1}, {"0.123400", "12.34", -1}, {"12.34000", "12.34", 0}, {"0.01234", "0.01235", -1}, {"0.1234", "0", 1}, {"0.0000", "0", 0}, {"0.0001", "0", 1}, {"0.0001", "0.0000", 1}, {"0", "-0.0000", 0}, {"-0.0001", "0", -1}, {"-0.1234", "0", -1}, {"-0.1234", "0.1234", -1}, {"-1.234", "-12.34", 1}, {"-0.1234", "-12.34", 1}, {"-12.34", "1234", -1}, {"-12.34", "-12.35", 1}, {"-0.01234", "-0.01235", 1}, {"-1234", "-123400", 1}, {"-12340", "-123400", 1}, // Test for int type decimal. {int64(-1), int64(1), -1}, {int64(math.MaxInt64), int64(math.MinInt64), 1}, {int64(math.MaxInt64), int64(math.MaxInt32), 1}, {int64(math.MinInt32), int64(math.MaxInt16), -1}, {int64(math.MinInt64), int64(math.MaxInt8), -1}, {int64(0), int64(math.MaxInt8), -1}, {int64(math.MinInt8), int64(0), -1}, {int64(math.MinInt16), int64(math.MaxInt16), -1}, {int64(1), int64(-1), 1}, {int64(1), int64(0), 1}, {int64(-1), int64(0), -1}, {int64(0), int64(0), 0}, {int64(math.MaxInt16), int64(math.MaxInt16), 0}, // Test for uint type decimal. {uint64(0), uint64(0), 0}, {uint64(1), uint64(0), 1}, {uint64(0), uint64(1), -1}, {uint64(math.MaxInt8), uint64(math.MaxInt16), -1}, {uint64(math.MaxUint32), uint64(math.MaxInt32), 1}, {uint64(math.MaxUint8), uint64(math.MaxInt8), 1}, {uint64(math.MaxUint16), uint64(math.MaxInt32), -1}, {uint64(math.MaxUint64), uint64(math.MaxInt64), 1}, {uint64(math.MaxInt64), uint64(math.MaxUint32), 1}, {uint64(math.MaxUint64), uint64(0), 1}, {uint64(0), uint64(math.MaxUint64), -1}, } for _, t := range tblCmp { m1, err := mysql.ConvertToDecimal(t.Arg1) c.Assert(err, IsNil) m2, err := mysql.ConvertToDecimal(t.Arg2) c.Assert(err, IsNil) b1, err := EncodeKey(m1) c.Assert(err, IsNil) b2, err := EncodeKey(m2) c.Assert(err, IsNil) ret := bytes.Compare(b1, b2) c.Assert(ret, Equals, t.Ret) } }
func (s *testCodecSuite) TestDecimal(c *C) { defer testleak.AfterTest(c)() tbl := []string{ "1234.00", "1234", "12.34", "12.340", "0.1234", "0.0", "0", "-0.0", "-0.0000", "-1234.00", "-1234", "-12.34", "-12.340", "-0.1234"} for _, t := range tbl { m, err := mysql.ParseDecimal(t) c.Assert(err, IsNil) b, err := EncodeKey(nil, types.NewDatum(m)) c.Assert(err, IsNil) v, err := Decode(b) c.Assert(err, IsNil) c.Assert(v, HasLen, 1) vv := v[0].GetMysqlDecimal() c.Assert(vv.Equals(m), IsTrue) } tblCmp := []struct { Arg1 interface{} Arg2 interface{} Ret int }{ // Test for float type decimal. {"1234", "123400", -1}, {"12340", "123400", -1}, {"1234", "1234.5", -1}, {"1234", "1234.0000", 0}, {"1234", "12.34", 1}, {"12.34", "12.35", -1}, {"0.12", "0.1234", -1}, {"0.1234", "12.3400", -1}, {"0.1234", "0.1235", -1}, {"0.123400", "12.34", -1}, {"12.34000", "12.34", 0}, {"0.01234", "0.01235", -1}, {"0.1234", "0", 1}, {"0.0000", "0", 0}, {"0.0001", "0", 1}, {"0.0001", "0.0000", 1}, {"0", "-0.0000", 0}, {"-0.0001", "0", -1}, {"-0.1234", "0", -1}, {"-0.1234", "-0.12", -1}, {"-0.12", "-0.1234", 1}, {"-0.12", "-0.1200", 0}, {"-0.1234", "0.1234", -1}, {"-1.234", "-12.34", 1}, {"-0.1234", "-12.34", 1}, {"-12.34", "1234", -1}, {"-12.34", "-12.35", 1}, {"-0.01234", "-0.01235", 1}, {"-1234", "-123400", 1}, {"-12340", "-123400", 1}, // Test for int type decimal. {int64(-1), int64(1), -1}, {int64(math.MaxInt64), int64(math.MinInt64), 1}, {int64(math.MaxInt64), int64(math.MaxInt32), 1}, {int64(math.MinInt32), int64(math.MaxInt16), -1}, {int64(math.MinInt64), int64(math.MaxInt8), -1}, {int64(0), int64(math.MaxInt8), -1}, {int64(math.MinInt8), int64(0), -1}, {int64(math.MinInt16), int64(math.MaxInt16), -1}, {int64(1), int64(-1), 1}, {int64(1), int64(0), 1}, {int64(-1), int64(0), -1}, {int64(0), int64(0), 0}, {int64(math.MaxInt16), int64(math.MaxInt16), 0}, // Test for uint type decimal. {uint64(0), uint64(0), 0}, {uint64(1), uint64(0), 1}, {uint64(0), uint64(1), -1}, {uint64(math.MaxInt8), uint64(math.MaxInt16), -1}, {uint64(math.MaxUint32), uint64(math.MaxInt32), 1}, {uint64(math.MaxUint8), uint64(math.MaxInt8), 1}, {uint64(math.MaxUint16), uint64(math.MaxInt32), -1}, {uint64(math.MaxUint64), uint64(math.MaxInt64), 1}, {uint64(math.MaxInt64), uint64(math.MaxUint32), 1}, {uint64(math.MaxUint64), uint64(0), 1}, {uint64(0), uint64(math.MaxUint64), -1}, } for _, t := range tblCmp { m1, err := mysql.ConvertToDecimal(t.Arg1) c.Assert(err, IsNil) m2, err := mysql.ConvertToDecimal(t.Arg2) c.Assert(err, IsNil) b1, err := EncodeKey(nil, types.NewDatum(m1)) c.Assert(err, IsNil) b2, err := EncodeKey(nil, types.NewDatum(m2)) c.Assert(err, IsNil) ret := bytes.Compare(b1, b2) c.Assert(ret, Equals, t.Ret) } floats := []float64{-123.45, -123.40, -23.45, -1.43, -0.93, -0.4333, -0.068, -0.0099, 0, 0.001, 0.0012, 0.12, 1.2, 1.23, 123.3, 2424.242424} var decs [][]byte for i := range floats { dec := mysql.NewDecimalFromFloat(floats[i]) decs = append(decs, EncodeDecimal(nil, dec)) } for i := 0; i < len(decs)-1; i++ { cmp := bytes.Compare(decs[i], decs[i+1]) c.Assert(cmp, LessEqual, 0) } }
// Unflatten converts a raw datum to a column datum. func Unflatten(datum types.Datum, ft *types.FieldType, inIndex bool) (types.Datum, error) { if datum.IsNull() { return datum, nil } switch ft.Tp { case mysql.TypeFloat: datum.SetFloat32(float32(datum.GetFloat64())) return datum, nil case mysql.TypeTiny, mysql.TypeShort, mysql.TypeYear, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeBlob, mysql.TypeLongBlob, mysql.TypeVarchar, mysql.TypeString: return datum, nil case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: var t mysql.Time t.Type = ft.Tp t.Fsp = ft.Decimal var err error err = t.FromPackedUint(datum.GetUint64()) if err != nil { return datum, errors.Trace(err) } datum.SetMysqlTime(t) return datum, nil case mysql.TypeDuration: dur := mysql.Duration{Duration: time.Duration(datum.GetInt64())} datum.SetValue(dur) return datum, nil case mysql.TypeNewDecimal: if datum.Kind() == types.KindMysqlDecimal { if ft.Decimal >= 0 { dec := datum.GetMysqlDecimal().Truncate(int32(ft.Decimal)) datum.SetMysqlDecimal(dec) } return datum, nil } dec, err := mysql.ParseDecimal(datum.GetString()) if err != nil { return datum, errors.Trace(err) } if ft.Decimal >= 0 { dec = dec.Truncate(int32(ft.Decimal)) } datum.SetValue(dec) return datum, nil case mysql.TypeEnum: enum, err := mysql.ParseEnumValue(ft.Elems, datum.GetUint64()) if err != nil { return datum, errors.Trace(err) } datum.SetValue(enum) return datum, nil case mysql.TypeSet: set, err := mysql.ParseSetValue(ft.Elems, datum.GetUint64()) if err != nil { return datum, errors.Trace(err) } datum.SetValue(set) return datum, nil case mysql.TypeBit: bit := mysql.Bit{Value: datum.GetUint64(), Width: ft.Flen} datum.SetValue(bit) return datum, nil } return datum, nil }