From 6bd585f27cd8bc4293bc4b60fc5b600f20f463a7 Mon Sep 17 00:00:00 2001 From: qupeng Date: Fri, 7 Jul 2017 11:02:33 +0800 Subject: [PATCH] json: add uint64 support. (#3648) --- util/types/datum.go | 9 +++++++-- util/types/json/compare.go | 30 +++++++++++++++++------------- util/types/json/functions.go | 2 ++ util/types/json/functions_test.go | 3 +++ util/types/json/json.go | 4 ++++ util/types/json/json_test.go | 4 +++- util/types/json/normalize.go | 3 +++ util/types/json/serdes.go | 7 ++++--- 8 files changed, 43 insertions(+), 19 deletions(-) diff --git a/util/types/datum.go b/util/types/datum.go index f4c0afcd0e..f2bfd7305b 100644 --- a/util/types/datum.go +++ b/util/types/datum.go @@ -1239,9 +1239,12 @@ func (d *Datum) convertToMysqlJSON(sc *variable.StatementContext, target *FieldT if j, err = json.ParseFromString(d.GetString()); err == nil { ret.SetMysqlJSON(j) } - case KindInt64, KindUint64: + case KindInt64: i64 := d.GetInt64() ret.SetMysqlJSON(json.CreateJSON(i64)) + case KindUint64: + u64 := d.GetUint64() + ret.SetMysqlJSON(json.CreateJSON(u64)) case KindFloat32, KindFloat64: f64 := d.GetFloat64() ret.SetMysqlJSON(json.CreateJSON(f64)) @@ -1521,8 +1524,10 @@ func (d *Datum) ToMysqlJSON() (j json.JSON, err error) { case KindMysqlJSON: j = d.x.(json.JSON) return - case KindInt64, KindUint64: + case KindInt64: in = d.GetInt64() + case KindUint64: + in = d.GetUint64() case KindFloat32, KindFloat64: in = d.GetFloat64() case KindMysqlDecimal: diff --git a/util/types/json/compare.go b/util/types/json/compare.go index 25d91af284..dc77787e06 100644 --- a/util/types/json/compare.go +++ b/util/types/json/compare.go @@ -39,25 +39,29 @@ func compareFloat64PrecisionLoss(x, y float64) int { // jsonTypePrecedences is for comparing two json. // See: https://dev.mysql.com/doc/refman/5.7/en/json.html#json-comparison var jsonTypePrecedences = map[string]int{ - "BLOB": -1, - "BIT": -2, - "OPAQUE": -3, - "DATETIME": -4, - "TIME": -5, - "DATE": -6, - "BOOLEAN": -7, - "ARRAY": -8, - "OBJECT": -9, - "STRING": -10, - "INTEGER": -11, - "DOUBLE": -11, - "NULL": -12, + "BLOB": -1, + "BIT": -2, + "OPAQUE": -3, + "DATETIME": -4, + "TIME": -5, + "DATE": -6, + "BOOLEAN": -7, + "ARRAY": -8, + "OBJECT": -9, + "STRING": -10, + "INTEGER": -11, + "UNSIGNED INTEGER": -11, + "DOUBLE": -11, + "NULL": -12, } func i64AsFloat64(i64 int64, typeCode TypeCode) float64 { switch typeCode { case typeCodeLiteral, typeCodeInt64: return float64(i64) + case typeCodeUint64: + u64 := *(*uint64)(unsafe.Pointer(&i64)) + return float64(u64) case typeCodeFloat64: return *(*float64)(unsafe.Pointer(&i64)) default: diff --git a/util/types/json/functions.go b/util/types/json/functions.go index 7b6328905b..5988933f26 100644 --- a/util/types/json/functions.go +++ b/util/types/json/functions.go @@ -40,6 +40,8 @@ func (j JSON) Type() string { } case typeCodeInt64: return "INTEGER" + case typeCodeUint64: + return "UNSIGNED INTEGER" case typeCodeFloat64: return "DOUBLE" case typeCodeString: diff --git a/util/types/json/functions_test.go b/util/types/json/functions_test.go index ed807124b5..520c395885 100644 --- a/util/types/json/functions_test.go +++ b/util/types/json/functions_test.go @@ -35,6 +35,9 @@ func (s *testJSONSuite) TestJSONType(c *C) { j := mustParseFromString(tt.In) c.Assert(j.Type(), Equals, tt.Out) } + // we can't parse '9223372036854775808' to JSON::Uint64 now, + // because go builtin JSON parser treats that as DOUBLE. + c.Assert(CreateJSON(uint64(1<<63)).Type(), Equals, "UNSIGNED INTEGER") } func (s *testJSONSuite) TestJSONExtract(c *C) { diff --git a/util/types/json/json.go b/util/types/json/json.go index 24dc51ef80..a8887a6120 100644 --- a/util/types/json/json.go +++ b/util/types/json/json.go @@ -33,6 +33,7 @@ const ( typeCodeArray TypeCode = 0x03 typeCodeLiteral TypeCode = 0x04 typeCodeInt64 TypeCode = 0x09 + typeCodeUint64 TypeCode = 0x0a typeCodeFloat64 TypeCode = 0x0b typeCodeString TypeCode = 0x0c ) @@ -96,6 +97,9 @@ func (j JSON) MarshalJSON() ([]byte, error) { } case typeCodeInt64: return json.Marshal(j.i64) + case typeCodeUint64: + u64 := *(*uint64)(unsafe.Pointer(&j.i64)) + return json.Marshal(u64) case typeCodeFloat64: f64 := *(*float64)(unsafe.Pointer(&j.i64)) return json.Marshal(f64) diff --git a/util/types/json/json_test.go b/util/types/json/json_test.go index 8eab7001aa..59ec0a1fa1 100644 --- a/util/types/json/json_test.go +++ b/util/types/json/json_test.go @@ -48,6 +48,7 @@ func (s *testJSONSuite) TestParseFromString(c *C) { func (s *testJSONSuite) TestSerializeAndDeserialize(c *C) { var jsonNilValue = CreateJSON(nil) var jsonBoolValue = CreateJSON(true) + var jsonUintValue = CreateJSON(uint64(1 << 63)) var jsonDoubleValue = CreateJSON(3.24) var jsonStringValue = CreateJSON("hello, 世界") j1 := mustParseFromString(`{"aaaaaaaaaaa": [1, "2", {"aa": "bb"}, 4.0], "bbbbbbbbbb": true, "ccccccccc": "d"}`) @@ -60,6 +61,7 @@ func (s *testJSONSuite) TestSerializeAndDeserialize(c *C) { }{ {In: jsonNilValue, Out: jsonNilValue, size: 2}, {In: jsonBoolValue, Out: jsonBoolValue, size: 2}, + {In: jsonUintValue, Out: jsonUintValue, size: 9}, {In: jsonDoubleValue, Out: jsonDoubleValue, size: 9}, {In: jsonStringValue, Out: jsonStringValue, size: 15}, {In: j1, Out: j1, size: 144}, @@ -86,7 +88,7 @@ func (s *testJSONSuite) TestCompareJSON(c *C) { jNull := mustParseFromString(`null`) jBoolTrue := mustParseFromString(`true`) jBoolFalse := mustParseFromString(`false`) - jIntegerLarge := mustParseFromString(`5`) + jIntegerLarge := CreateJSON(uint64(1 << 63)) jIntegerSmall := mustParseFromString(`3`) jStringLarge := mustParseFromString(`"hello, world"`) jStringSmall := mustParseFromString(`"hello"`) diff --git a/util/types/json/normalize.go b/util/types/json/normalize.go index 87dd69cdc5..c39794e480 100644 --- a/util/types/json/normalize.go +++ b/util/types/json/normalize.go @@ -37,6 +37,9 @@ func normalize(in interface{}) (j JSON, err error) { case int64: j.typeCode = typeCodeInt64 j.i64 = t + case uint64: + j.typeCode = typeCodeUint64 + j.i64 = *(*int64)(unsafe.Pointer(&t)) case float64: j.typeCode = typeCodeFloat64 *(*float64)(unsafe.Pointer(&j.i64)) = t diff --git a/util/types/json/serdes.go b/util/types/json/serdes.go index 4fd07a3ade..5e5d204dcd 100644 --- a/util/types/json/serdes.go +++ b/util/types/json/serdes.go @@ -127,7 +127,7 @@ func PeekBytesAsJSON(b []byte) (n int, err error) { n = int(size) + int(reader.Size()) - int(reader.Len()) + typeCodeLen return } - case typeCodeInt64, typeCodeFloat64, typeCodeLiteral: + case typeCodeInt64, typeCodeUint64, typeCodeFloat64, typeCodeLiteral: n = jsonTypeCodeLength[TypeCode(c)] + typeCodeLen return } @@ -151,7 +151,7 @@ func encode(j JSON, buffer *bytes.Buffer) { encodeJSONArray(j.array, buffer) case typeCodeLiteral: encodeJSONLiteral(byte(j.i64), buffer) - case typeCodeInt64: + case typeCodeInt64, typeCodeUint64: encodeJSONInt64(j.i64, buffer) case typeCodeFloat64: f64 := *(*float64)(unsafe.Pointer(&j.i64)) @@ -179,7 +179,7 @@ func decode(typeCode byte, data []byte) (j JSON, err error) { case typeCodeLiteral: pbyte := (*byte)(unsafe.Pointer(&j.i64)) err = decodeJSONLiteral(pbyte, data) - case typeCodeInt64: + case typeCodeInt64, typeCodeUint64: err = decodeJSONInt64(&j.i64, data) case typeCodeFloat64: pfloat := (*float64)(unsafe.Pointer(&j.i64)) @@ -388,6 +388,7 @@ var jsonTypeCodeLength = map[TypeCode]int{ typeCodeArray: -1, typeCodeLiteral: 1, typeCodeInt64: 8, + typeCodeUint64: 8, typeCodeFloat64: 8, typeCodeString: -1, }