github.com/XiaoMi/Gaea@v1.2.5/parser/tidb-types/datum_test.go (about) 1 // Copyright 2016 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package types 15 16 import ( 17 "reflect" 18 "testing" 19 "time" 20 21 . "github.com/pingcap/check" 22 23 "github.com/XiaoMi/Gaea/mysql" 24 "github.com/XiaoMi/Gaea/parser/stmtctx" 25 "github.com/XiaoMi/Gaea/parser/tidb-types/json" 26 ) 27 28 var _ = Suite(&testDatumSuite{}) 29 30 type testDatumSuite struct { 31 } 32 33 func (ts *testDatumSuite) TestDatum(c *C) { 34 values := []interface{}{ 35 int64(1), 36 uint64(1), 37 1.1, 38 "abc", 39 []byte("abc"), 40 []int{1}, 41 } 42 for _, val := range values { 43 var d Datum 44 d.SetValue(val) 45 x := d.GetValue() 46 c.Assert(x, DeepEquals, val) 47 } 48 } 49 50 func testDatumToBool(c *C, in interface{}, res int) { 51 datum := NewDatum(in) 52 res64 := int64(res) 53 sc := new(stmtctx.StatementContext) 54 sc.IgnoreTruncate = true 55 b, err := datum.ToBool(sc) 56 c.Assert(err, IsNil) 57 c.Assert(b, Equals, res64) 58 } 59 60 func (ts *testDatumSuite) TestToBool(c *C) { 61 testDatumToBool(c, int(0), 0) 62 testDatumToBool(c, int64(0), 0) 63 testDatumToBool(c, uint64(0), 0) 64 testDatumToBool(c, float32(0.1), 0) 65 testDatumToBool(c, float64(0.1), 0) 66 testDatumToBool(c, "", 0) 67 testDatumToBool(c, "0.1", 0) 68 testDatumToBool(c, []byte{}, 0) 69 testDatumToBool(c, []byte("0.1"), 0) 70 testDatumToBool(c, NewBinaryLiteralFromUint(0, -1), 0) 71 testDatumToBool(c, Enum{Name: "a", Value: 1}, 1) 72 testDatumToBool(c, Set{Name: "a", Value: 1}, 1) 73 74 t, err := ParseTime(&stmtctx.StatementContext{TimeZone: time.UTC}, "2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 6) 75 c.Assert(err, IsNil) 76 testDatumToBool(c, t, 1) 77 78 td, err := ParseDuration(nil, "11:11:11.999999", 6) 79 c.Assert(err, IsNil) 80 testDatumToBool(c, td, 1) 81 82 ft := NewFieldType(mysql.TypeNewDecimal) 83 ft.Decimal = 5 84 v, err := Convert(0.1415926, ft) 85 c.Assert(err, IsNil) 86 testDatumToBool(c, v, 0) 87 d := NewDatum(&invalidMockType{}) 88 sc := new(stmtctx.StatementContext) 89 sc.IgnoreTruncate = true 90 _, err = d.ToBool(sc) 91 c.Assert(err, NotNil) 92 } 93 94 func (ts *testDatumSuite) TestEqualDatums(c *C) { 95 tests := []struct { 96 a []interface{} 97 b []interface{} 98 same bool 99 }{ 100 // Positive cases 101 {[]interface{}{1}, []interface{}{1}, true}, 102 {[]interface{}{1, "aa"}, []interface{}{1, "aa"}, true}, 103 {[]interface{}{1, "aa", 1}, []interface{}{1, "aa", 1}, true}, 104 105 // negative cases 106 {[]interface{}{1}, []interface{}{2}, false}, 107 {[]interface{}{1, "a"}, []interface{}{1, "aaaaaa"}, false}, 108 {[]interface{}{1, "aa", 3}, []interface{}{1, "aa", 2}, false}, 109 110 // Corner cases 111 {[]interface{}{}, []interface{}{}, true}, 112 {[]interface{}{nil}, []interface{}{nil}, true}, 113 {[]interface{}{}, []interface{}{1}, false}, 114 {[]interface{}{1}, []interface{}{1, 1}, false}, 115 {[]interface{}{nil}, []interface{}{1}, false}, 116 } 117 for _, tt := range tests { 118 testEqualDatums(c, tt.a, tt.b, tt.same) 119 } 120 } 121 122 func testEqualDatums(c *C, a []interface{}, b []interface{}, same bool) { 123 sc := new(stmtctx.StatementContext) 124 sc.IgnoreTruncate = true 125 res, err := EqualDatums(sc, MakeDatums(a...), MakeDatums(b...)) 126 c.Assert(err, IsNil) 127 c.Assert(res, Equals, same, Commentf("a: %v, b: %v", a, b)) 128 } 129 130 func testDatumToInt64(c *C, val interface{}, expect int64) { 131 d := NewDatum(val) 132 sc := new(stmtctx.StatementContext) 133 sc.IgnoreTruncate = true 134 b, err := d.ToInt64(sc) 135 c.Assert(err, IsNil) 136 c.Assert(b, Equals, expect) 137 } 138 139 func (ts *testTypeConvertSuite) TestToInt64(c *C) { 140 testDatumToInt64(c, "0", int64(0)) 141 testDatumToInt64(c, int(0), int64(0)) 142 testDatumToInt64(c, int64(0), int64(0)) 143 testDatumToInt64(c, uint64(0), int64(0)) 144 testDatumToInt64(c, float32(3.1), int64(3)) 145 testDatumToInt64(c, float64(3.1), int64(3)) 146 testDatumToInt64(c, NewBinaryLiteralFromUint(100, -1), int64(100)) 147 testDatumToInt64(c, Enum{Name: "a", Value: 1}, int64(1)) 148 testDatumToInt64(c, Set{Name: "a", Value: 1}, int64(1)) 149 testDatumToInt64(c, json.CreateBinary(int64(3)), int64(3)) 150 151 t, err := ParseTime(&stmtctx.StatementContext{ 152 TimeZone: time.UTC, 153 }, "2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 0) 154 c.Assert(err, IsNil) 155 testDatumToInt64(c, t, int64(20111110111112)) 156 157 td, err := ParseDuration(nil, "11:11:11.999999", 6) 158 c.Assert(err, IsNil) 159 testDatumToInt64(c, td, int64(111112)) 160 161 ft := NewFieldType(mysql.TypeNewDecimal) 162 ft.Decimal = 5 163 v, err := Convert(3.1415926, ft) 164 c.Assert(err, IsNil) 165 testDatumToInt64(c, v, int64(3)) 166 167 binLit, err := ParseHexStr("0x9999999999999999999999999999999999999999999") 168 c.Assert(err, IsNil) 169 testDatumToInt64(c, binLit, -1) 170 } 171 172 func (ts *testTypeConvertSuite) TestToFloat32(c *C) { 173 ft := NewFieldType(mysql.TypeFloat) 174 var datum = NewFloat64Datum(281.37) 175 sc := new(stmtctx.StatementContext) 176 sc.IgnoreTruncate = true 177 converted, err := datum.ConvertTo(sc, ft) 178 c.Assert(err, IsNil) 179 c.Assert(converted.Kind(), Equals, KindFloat32) 180 c.Assert(converted.GetFloat32(), Equals, float32(281.37)) 181 182 datum.SetString("281.37") 183 converted, err = datum.ConvertTo(sc, ft) 184 c.Assert(err, IsNil) 185 c.Assert(converted.Kind(), Equals, KindFloat32) 186 c.Assert(converted.GetFloat32(), Equals, float32(281.37)) 187 188 ft = NewFieldType(mysql.TypeDouble) 189 datum = NewFloat32Datum(281.37) 190 converted, err = datum.ConvertTo(sc, ft) 191 c.Assert(err, IsNil) 192 c.Assert(converted.Kind(), Equals, KindFloat64) 193 // Convert to float32 and convert back to float64, we will get a different value. 194 c.Assert(converted.GetFloat64(), Not(Equals), 281.37) 195 c.Assert(converted.GetFloat64(), Equals, datum.GetFloat64()) 196 } 197 198 // mustParseTimeIntoDatum is similar to ParseTime but panic if any error occurs. 199 func mustParseTimeIntoDatum(s string, tp byte, fsp int) (d Datum) { 200 t, err := ParseTime(&stmtctx.StatementContext{TimeZone: time.UTC}, s, tp, fsp) 201 if err != nil { 202 panic("ParseTime fail") 203 } 204 d.SetMysqlTime(t) 205 return 206 } 207 208 func (ts *testDatumSuite) TestToJSON(c *C) { 209 ft := NewFieldType(mysql.TypeJSON) 210 sc := new(stmtctx.StatementContext) 211 tests := []struct { 212 datum Datum 213 expected string 214 success bool 215 }{ 216 {NewIntDatum(1), `1.0`, true}, 217 {NewFloat64Datum(2), `2`, true}, 218 {NewStringDatum("\"hello, 世界\""), `"hello, 世界"`, true}, 219 {NewStringDatum("[1, 2, 3]"), `[1, 2, 3]`, true}, 220 {NewStringDatum("{}"), `{}`, true}, 221 {mustParseTimeIntoDatum("2011-11-10 11:11:11.111111", mysql.TypeTimestamp, 6), `"2011-11-10 11:11:11.111111"`, true}, 222 223 // can not parse JSON from this string, so error occurs. 224 {NewStringDatum("hello, 世界"), "", false}, 225 } 226 for _, tt := range tests { 227 obtain, err := tt.datum.ConvertTo(sc, ft) 228 if tt.success { 229 c.Assert(err, IsNil) 230 231 sd := NewStringDatum(tt.expected) 232 var expected Datum 233 expected, err = sd.ConvertTo(sc, ft) 234 c.Assert(err, IsNil) 235 236 var cmp int 237 cmp, err = obtain.CompareDatum(sc, &expected) 238 c.Assert(err, IsNil) 239 c.Assert(cmp, Equals, 0) 240 } else { 241 c.Assert(err, NotNil) 242 } 243 } 244 } 245 246 func (ts *testDatumSuite) TestIsNull(c *C) { 247 tests := []struct { 248 data interface{} 249 isnull bool 250 }{ 251 {nil, true}, 252 {0, false}, 253 {1, false}, 254 {1.1, false}, 255 {"string", false}, 256 {"", false}, 257 } 258 for _, tt := range tests { 259 testIsNull(c, tt.data, tt.isnull) 260 } 261 } 262 263 func testIsNull(c *C, data interface{}, isnull bool) { 264 d := NewDatum(data) 265 c.Assert(d.IsNull(), Equals, isnull, Commentf("data: %v, isnull: %v", data, isnull)) 266 } 267 268 func (ts *testDatumSuite) TestToBytes(c *C) { 269 tests := []struct { 270 a Datum 271 out []byte 272 }{ 273 {NewIntDatum(1), []byte("1")}, 274 {NewDecimalDatum(NewDecFromInt(1)), []byte("1")}, 275 {NewFloat64Datum(1.23), []byte("1.23")}, 276 {NewStringDatum("abc"), []byte("abc")}, 277 } 278 sc := new(stmtctx.StatementContext) 279 sc.IgnoreTruncate = true 280 for _, tt := range tests { 281 bin, err := tt.a.ToBytes() 282 c.Assert(err, IsNil) 283 c.Assert(bin, BytesEquals, tt.out) 284 } 285 } 286 287 func mustParseDurationDatum(str string, fsp int) Datum { 288 dur, err := ParseDuration(nil, str, fsp) 289 if err != nil { 290 panic(err) 291 } 292 return NewDurationDatum(dur) 293 } 294 295 func (ts *testDatumSuite) TestComputePlusAndMinus(c *C) { 296 sc := &stmtctx.StatementContext{TimeZone: time.UTC} 297 tests := []struct { 298 a Datum 299 b Datum 300 plus Datum 301 minus Datum 302 hasErr bool 303 }{ 304 {NewIntDatum(72), NewIntDatum(28), NewIntDatum(100), NewIntDatum(44), false}, 305 {NewIntDatum(72), NewUintDatum(28), NewIntDatum(100), NewIntDatum(44), false}, 306 {NewUintDatum(72), NewUintDatum(28), NewUintDatum(100), NewUintDatum(44), false}, 307 {NewUintDatum(72), NewIntDatum(28), NewUintDatum(100), NewUintDatum(44), false}, 308 {NewFloat64Datum(72.0), NewFloat64Datum(28.0), NewFloat64Datum(100.0), NewFloat64Datum(44.0), false}, 309 {NewDecimalDatum(NewDecFromStringForTest("72.5")), NewDecimalDatum(NewDecFromInt(3)), NewDecimalDatum(NewDecFromStringForTest("75.5")), NewDecimalDatum(NewDecFromStringForTest("69.5")), false}, 310 {NewIntDatum(72), NewFloat64Datum(42), Datum{}, Datum{}, true}, 311 {NewStringDatum("abcd"), NewIntDatum(42), Datum{}, Datum{}, true}, 312 } 313 314 for ith, tt := range tests { 315 got, err := ComputePlus(tt.a, tt.b) 316 c.Assert(err != nil, Equals, tt.hasErr) 317 v, err := got.CompareDatum(sc, &tt.plus) 318 c.Assert(err, IsNil) 319 c.Assert(v, Equals, 0, Commentf("%dth got:%#v, expect:%#v", ith, got, tt.plus)) 320 } 321 } 322 323 func (ts *testDatumSuite) TestCopyDatum(c *C) { 324 var raw Datum 325 raw.b = []byte("raw") 326 raw.k = KindRaw 327 tests := []Datum{ 328 NewIntDatum(72), 329 NewUintDatum(72), 330 NewStringDatum("abcd"), 331 NewBytesDatum([]byte("abcd")), 332 raw, 333 } 334 335 sc := new(stmtctx.StatementContext) 336 sc.IgnoreTruncate = true 337 for _, tt := range tests { 338 tt1 := CopyDatum(tt) 339 res, err := tt.CompareDatum(sc, &tt1) 340 c.Assert(err, IsNil) 341 c.Assert(res, Equals, 0) 342 if tt.b != nil { 343 c.Assert(&tt.b[0], Not(Equals), &tt1.b[0]) 344 } 345 } 346 } 347 348 func prepareCompareDatums() ([]Datum, []Datum) { 349 vals := make([]Datum, 0, 5) 350 vals = append(vals, NewIntDatum(1)) 351 vals = append(vals, NewFloat64Datum(1.23)) 352 vals = append(vals, NewStringDatum("abcde")) 353 vals = append(vals, NewDecimalDatum(NewDecFromStringForTest("1.2345"))) 354 vals = append(vals, NewTimeDatum(Time{Time: FromGoTime(time.Date(2018, 3, 8, 16, 1, 0, 315313000, time.UTC)), Fsp: 6, Type: mysql.TypeTimestamp})) 355 356 vals1 := make([]Datum, 0, 5) 357 vals1 = append(vals1, NewIntDatum(1)) 358 vals1 = append(vals1, NewFloat64Datum(1.23)) 359 vals1 = append(vals1, NewStringDatum("abcde")) 360 vals1 = append(vals1, NewDecimalDatum(NewDecFromStringForTest("1.2345"))) 361 vals1 = append(vals1, NewTimeDatum(Time{Time: FromGoTime(time.Date(2018, 3, 8, 16, 1, 0, 315313000, time.UTC)), Fsp: 6, Type: mysql.TypeTimestamp})) 362 return vals, vals1 363 } 364 365 func BenchmarkCompareDatum(b *testing.B) { 366 vals, vals1 := prepareCompareDatums() 367 sc := new(stmtctx.StatementContext) 368 b.ResetTimer() 369 for i := 0; i < b.N; i++ { 370 for j, v := range vals { 371 v.CompareDatum(sc, &vals1[j]) 372 } 373 } 374 } 375 376 func BenchmarkCompareDatumByReflect(b *testing.B) { 377 vals, vals1 := prepareCompareDatums() 378 b.ResetTimer() 379 for i := 0; i < b.N; i++ { 380 reflect.DeepEqual(vals, vals1) 381 } 382 }