github.com/XiaoMi/Gaea@v1.2.5/parser/tidb-types/convert_test.go (about) 1 // Copyright 2015 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 "fmt" 18 "math" 19 "strconv" 20 "time" 21 22 . "github.com/pingcap/check" 23 "github.com/pingcap/errors" 24 25 "github.com/XiaoMi/Gaea/mysql" 26 "github.com/XiaoMi/Gaea/parser/stmtctx" 27 "github.com/XiaoMi/Gaea/parser/terror" 28 "github.com/XiaoMi/Gaea/parser/tidb-types/json" 29 "github.com/XiaoMi/Gaea/util/testleak" 30 ) 31 32 var _ = Suite(&testTypeConvertSuite{}) 33 34 type testTypeConvertSuite struct { 35 } 36 37 type invalidMockType struct { 38 } 39 40 // Convert converts the val with type tp. 41 func Convert(val interface{}, target *FieldType) (v interface{}, err error) { 42 d := NewDatum(val) 43 sc := new(stmtctx.StatementContext) 44 sc.TimeZone = time.UTC 45 ret, err := d.ConvertTo(sc, target) 46 if err != nil { 47 return ret.GetValue(), errors.Trace(err) 48 } 49 return ret.GetValue(), nil 50 } 51 52 func (s *testTypeConvertSuite) TestConvertType(c *C) { 53 defer testleak.AfterTest(c)() 54 ft := NewFieldType(mysql.TypeBlob) 55 ft.Flen = 4 56 ft.Charset = "utf8" 57 v, err := Convert("123456", ft) 58 c.Assert(ErrDataTooLong.Equal(err), IsTrue) 59 c.Assert(v, Equals, "1234") 60 ft = NewFieldType(mysql.TypeString) 61 ft.Flen = 4 62 ft.Charset = mysql.CharsetBin 63 v, err = Convert("12345", ft) 64 c.Assert(ErrDataTooLong.Equal(err), IsTrue) 65 c.Assert(v, DeepEquals, []byte("1234")) 66 67 ft = NewFieldType(mysql.TypeFloat) 68 ft.Flen = 5 69 ft.Decimal = 2 70 v, err = Convert(111.114, ft) 71 c.Assert(err, IsNil) 72 c.Assert(v, Equals, float32(111.11)) 73 74 ft = NewFieldType(mysql.TypeFloat) 75 ft.Flen = 5 76 ft.Decimal = 2 77 v, err = Convert(999.999, ft) 78 c.Assert(err, NotNil) 79 c.Assert(v, Equals, float32(999.99)) 80 81 ft = NewFieldType(mysql.TypeFloat) 82 ft.Flen = 5 83 ft.Decimal = 2 84 v, err = Convert(-999.999, ft) 85 c.Assert(err, NotNil) 86 c.Assert(v, Equals, float32(-999.99)) 87 88 ft = NewFieldType(mysql.TypeFloat) 89 ft.Flen = 5 90 ft.Decimal = 2 91 v, err = Convert(1111.11, ft) 92 c.Assert(err, NotNil) 93 c.Assert(v, Equals, float32(999.99)) 94 95 ft = NewFieldType(mysql.TypeFloat) 96 ft.Flen = 5 97 ft.Decimal = 2 98 v, err = Convert(999.916, ft) 99 c.Assert(err, IsNil) 100 c.Assert(v, Equals, float32(999.92)) 101 102 ft = NewFieldType(mysql.TypeFloat) 103 ft.Flen = 5 104 ft.Decimal = 2 105 v, err = Convert(999.914, ft) 106 c.Assert(err, IsNil) 107 c.Assert(v, Equals, float32(999.91)) 108 109 ft = NewFieldType(mysql.TypeFloat) 110 ft.Flen = 5 111 ft.Decimal = 2 112 v, err = Convert(999.9155, ft) 113 c.Assert(err, IsNil) 114 c.Assert(v, Equals, float32(999.92)) 115 116 // For TypeBlob 117 ft = NewFieldType(mysql.TypeBlob) 118 _, err = Convert(&invalidMockType{}, ft) 119 c.Assert(err, NotNil) 120 121 // Nil 122 ft = NewFieldType(mysql.TypeBlob) 123 v, err = Convert(nil, ft) 124 c.Assert(err, IsNil) 125 c.Assert(v, IsNil) 126 127 // TypeDouble 128 ft = NewFieldType(mysql.TypeDouble) 129 ft.Flen = 5 130 ft.Decimal = 2 131 v, err = Convert(999.9155, ft) 132 c.Assert(err, IsNil) 133 c.Assert(v, Equals, float64(999.92)) 134 135 // For TypeString 136 ft = NewFieldType(mysql.TypeString) 137 ft.Flen = 3 138 v, err = Convert("12345", ft) 139 c.Assert(ErrDataTooLong.Equal(err), IsTrue) 140 c.Assert(v, Equals, "123") 141 ft = NewFieldType(mysql.TypeString) 142 ft.Flen = 3 143 ft.Charset = mysql.CharsetBin 144 v, err = Convert("12345", ft) 145 c.Assert(ErrDataTooLong.Equal(err), IsTrue) 146 c.Assert(v, DeepEquals, []byte("123")) 147 148 // For TypeDuration 149 ft = NewFieldType(mysql.TypeDuration) 150 ft.Decimal = 3 151 v, err = Convert("10:11:12.123456", ft) 152 c.Assert(err, IsNil) 153 c.Assert(v.(Duration).String(), Equals, "10:11:12.123") 154 ft.Decimal = 1 155 vv, err := Convert(v, ft) 156 c.Assert(err, IsNil) 157 c.Assert(vv.(Duration).String(), Equals, "10:11:12.1") 158 159 vd, err := ParseTime(nil, "2010-10-10 10:11:11.12345", mysql.TypeDatetime, 2) 160 c.Assert(vd.String(), Equals, "2010-10-10 10:11:11.12") 161 c.Assert(err, IsNil) 162 v, err = Convert(vd, ft) 163 c.Assert(err, IsNil) 164 c.Assert(v.(Duration).String(), Equals, "10:11:11.1") 165 166 vt, err := ParseTime(&stmtctx.StatementContext{TimeZone: time.UTC}, "2010-10-10 10:11:11.12345", mysql.TypeTimestamp, 2) 167 c.Assert(vt.String(), Equals, "2010-10-10 10:11:11.12") 168 c.Assert(err, IsNil) 169 v, err = Convert(vt, ft) 170 c.Assert(err, IsNil) 171 c.Assert(v.(Duration).String(), Equals, "10:11:11.1") 172 173 // For mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDate 174 ft = NewFieldType(mysql.TypeTimestamp) 175 ft.Decimal = 3 176 v, err = Convert("2010-10-10 10:11:11.12345", ft) 177 c.Assert(err, IsNil) 178 c.Assert(v.(Time).String(), Equals, "2010-10-10 10:11:11.123") 179 ft.Decimal = 1 180 vv, err = Convert(v, ft) 181 c.Assert(err, IsNil) 182 c.Assert(vv.(Time).String(), Equals, "2010-10-10 10:11:11.1") 183 184 // For TypeLonglong 185 ft = NewFieldType(mysql.TypeLonglong) 186 v, err = Convert("100", ft) 187 c.Assert(err, IsNil) 188 c.Assert(v, Equals, int64(100)) 189 // issue 4287. 190 v, err = Convert(math.Pow(2, 63)-1, ft) 191 c.Assert(err, IsNil) 192 c.Assert(v, Equals, int64(math.MaxInt64)) 193 ft = NewFieldType(mysql.TypeLonglong) 194 ft.Flag |= mysql.UnsignedFlag 195 v, err = Convert("100", ft) 196 c.Assert(err, IsNil) 197 c.Assert(v, Equals, uint64(100)) 198 // issue 3470 199 ft = NewFieldType(mysql.TypeLonglong) 200 v, err = Convert(Duration{Duration: time.Duration(12*time.Hour + 59*time.Minute + 59*time.Second + 555*time.Millisecond), Fsp: 3}, ft) 201 c.Assert(err, IsNil) 202 c.Assert(v, Equals, int64(130000)) 203 v, err = Convert(Time{ 204 Time: FromDate(2017, 1, 1, 12, 59, 59, 555000), 205 Type: mysql.TypeDatetime, 206 Fsp: MaxFsp}, ft) 207 c.Assert(err, IsNil) 208 c.Assert(v, Equals, int64(20170101130000)) 209 210 // For TypeBit 211 ft = NewFieldType(mysql.TypeBit) 212 ft.Flen = 24 // 3 bytes. 213 v, err = Convert("100", ft) 214 c.Assert(err, IsNil) 215 c.Assert(v, DeepEquals, NewBinaryLiteralFromUint(3223600, 3)) 216 217 v, err = Convert(NewBinaryLiteralFromUint(100, -1), ft) 218 c.Assert(err, IsNil) 219 c.Assert(v, DeepEquals, NewBinaryLiteralFromUint(100, 3)) 220 221 ft.Flen = 1 222 v, err = Convert(1, ft) 223 c.Assert(err, IsNil) 224 c.Assert(v, DeepEquals, NewBinaryLiteralFromUint(1, 1)) 225 226 _, err = Convert(2, ft) 227 c.Assert(err, NotNil) 228 229 ft.Flen = 0 230 _, err = Convert(2, ft) 231 c.Assert(err, NotNil) 232 233 // For TypeNewDecimal 234 ft = NewFieldType(mysql.TypeNewDecimal) 235 ft.Flen = 8 236 ft.Decimal = 4 237 v, err = Convert(3.1416, ft) 238 c.Assert(err, IsNil, Commentf(errors.ErrorStack(err))) 239 c.Assert(v.(*MyDecimal).String(), Equals, "3.1416") 240 v, err = Convert("3.1415926", ft) 241 c.Assert(terror.ErrorEqual(err, ErrTruncated), IsTrue, Commentf("err %v", err)) 242 c.Assert(v.(*MyDecimal).String(), Equals, "3.1416") 243 v, err = Convert("99999", ft) 244 c.Assert(terror.ErrorEqual(err, ErrOverflow), IsTrue, Commentf("err %v", err)) 245 c.Assert(v.(*MyDecimal).String(), Equals, "9999.9999") 246 v, err = Convert("-10000", ft) 247 c.Assert(terror.ErrorEqual(err, ErrOverflow), IsTrue, Commentf("err %v", err)) 248 c.Assert(v.(*MyDecimal).String(), Equals, "-9999.9999") 249 250 // Test Datum.ToDecimal with bad number. 251 d := NewDatum("hello") 252 sc := new(stmtctx.StatementContext) 253 _, err = d.ToDecimal(sc) 254 c.Assert(terror.ErrorEqual(err, ErrBadNumber), IsTrue, Commentf("err %v", err)) 255 256 sc.IgnoreTruncate = true 257 v, err = d.ToDecimal(sc) 258 c.Assert(err, IsNil) 259 c.Assert(v.(*MyDecimal).String(), Equals, "0") 260 261 // For TypeYear 262 ft = NewFieldType(mysql.TypeYear) 263 v, err = Convert("2015", ft) 264 c.Assert(err, IsNil) 265 c.Assert(v, Equals, int64(2015)) 266 v, err = Convert(2015, ft) 267 c.Assert(err, IsNil) 268 c.Assert(v, Equals, int64(2015)) 269 _, err = Convert(1800, ft) 270 c.Assert(err, NotNil) 271 dt, err := ParseDate(nil, "2015-11-11") 272 c.Assert(err, IsNil) 273 v, err = Convert(dt, ft) 274 c.Assert(err, IsNil) 275 c.Assert(v, Equals, int64(2015)) 276 v, err = Convert(ZeroDuration, ft) 277 c.Assert(err, IsNil) 278 c.Assert(v, Equals, int64(time.Now().Year())) 279 280 // For enum 281 ft = NewFieldType(mysql.TypeEnum) 282 ft.Elems = []string{"a", "b", "c"} 283 v, err = Convert("a", ft) 284 c.Assert(err, IsNil) 285 c.Assert(v, DeepEquals, Enum{Name: "a", Value: 1}) 286 v, err = Convert(2, ft) 287 c.Log(errors.ErrorStack(err)) 288 c.Assert(err, IsNil) 289 c.Assert(v, DeepEquals, Enum{Name: "b", Value: 2}) 290 _, err = Convert("d", ft) 291 c.Assert(err, NotNil) 292 v, err = Convert(4, ft) 293 c.Assert(terror.ErrorEqual(err, ErrTruncated), IsTrue, Commentf("err %v", err)) 294 c.Assert(v, DeepEquals, Enum{}) 295 296 ft = NewFieldType(mysql.TypeSet) 297 ft.Elems = []string{"a", "b", "c"} 298 v, err = Convert("a", ft) 299 c.Assert(err, IsNil) 300 c.Assert(v, DeepEquals, Set{Name: "a", Value: 1}) 301 v, err = Convert(2, ft) 302 c.Assert(err, IsNil) 303 c.Assert(v, DeepEquals, Set{Name: "b", Value: 2}) 304 v, err = Convert(3, ft) 305 c.Assert(err, IsNil) 306 c.Assert(v, DeepEquals, Set{Name: "a,b", Value: 3}) 307 _, err = Convert("d", ft) 308 c.Assert(err, NotNil) 309 _, err = Convert(9, ft) 310 c.Assert(err, NotNil) 311 } 312 313 func testToString(c *C, val interface{}, expect string) { 314 b, err := ToString(val) 315 c.Assert(err, IsNil) 316 c.Assert(b, Equals, expect) 317 } 318 319 func (s *testTypeConvertSuite) TestConvertToString(c *C) { 320 defer testleak.AfterTest(c)() 321 testToString(c, "0", "0") 322 testToString(c, true, "1") 323 testToString(c, "false", "false") 324 testToString(c, int(0), "0") 325 testToString(c, int64(0), "0") 326 testToString(c, uint64(0), "0") 327 testToString(c, float32(1.6), "1.6") 328 testToString(c, float64(-0.6), "-0.6") 329 testToString(c, []byte{1}, "\x01") 330 testToString(c, NewBinaryLiteralFromUint(0x4D7953514C, -1), "MySQL") 331 testToString(c, NewBinaryLiteralFromUint(0x41, -1), "A") 332 testToString(c, Enum{Name: "a", Value: 1}, "a") 333 testToString(c, Set{Name: "a", Value: 1}, "a") 334 335 t, err := ParseTime(&stmtctx.StatementContext{TimeZone: time.UTC}, 336 "2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 6) 337 c.Assert(err, IsNil) 338 testToString(c, t, "2011-11-10 11:11:11.999999") 339 340 td, err := ParseDuration(nil, "11:11:11.999999", 6) 341 c.Assert(err, IsNil) 342 testToString(c, td, "11:11:11.999999") 343 344 ft := NewFieldType(mysql.TypeNewDecimal) 345 ft.Flen = 10 346 ft.Decimal = 5 347 v, err := Convert(3.1415926, ft) 348 c.Assert(terror.ErrorEqual(err, ErrTruncated), IsTrue, Commentf("err %v", err)) 349 testToString(c, v, "3.14159") 350 351 _, err = ToString(&invalidMockType{}) 352 c.Assert(err, NotNil) 353 354 // test truncate 355 tests := []struct { 356 flen int 357 charset string 358 input string 359 output string 360 }{ 361 {5, "utf8", "你好,世界", "你好,世界"}, 362 {5, "utf8mb4", "你好,世界", "你好,世界"}, 363 {4, "utf8", "你好,世界", "你好,世"}, 364 {4, "utf8mb4", "你好,世界", "你好,世"}, 365 {15, "binary", "你好,世界", "你好,世界"}, 366 {12, "binary", "你好,世界", "你好,世"}, 367 {0, "binary", "你好,世界", ""}, 368 } 369 for _, tt := range tests { 370 ft = NewFieldType(mysql.TypeVarchar) 371 ft.Flen = tt.flen 372 ft.Charset = tt.charset 373 inputDatum := NewStringDatum(tt.input) 374 sc := new(stmtctx.StatementContext) 375 outputDatum, err := inputDatum.ConvertTo(sc, ft) 376 if tt.input != tt.output { 377 c.Assert(ErrDataTooLong.Equal(err), IsTrue) 378 } else { 379 c.Assert(err, IsNil) 380 } 381 c.Assert(outputDatum.GetString(), Equals, tt.output) 382 } 383 } 384 385 func testStrToInt(c *C, str string, expect int64, truncateAsErr bool, expectErr error) { 386 sc := new(stmtctx.StatementContext) 387 sc.IgnoreTruncate = !truncateAsErr 388 val, err := StrToInt(sc, str) 389 if expectErr != nil { 390 c.Assert(terror.ErrorEqual(err, expectErr), IsTrue, Commentf("err %v", err)) 391 } else { 392 c.Assert(err, IsNil) 393 c.Assert(val, Equals, expect) 394 } 395 } 396 397 func testStrToUint(c *C, str string, expect uint64, truncateAsErr bool, expectErr error) { 398 sc := new(stmtctx.StatementContext) 399 sc.IgnoreTruncate = !truncateAsErr 400 val, err := StrToUint(sc, str) 401 if expectErr != nil { 402 c.Assert(terror.ErrorEqual(err, expectErr), IsTrue, Commentf("err %v", err)) 403 } else { 404 c.Assert(err, IsNil) 405 c.Assert(val, Equals, expect) 406 } 407 } 408 409 func testStrToFloat(c *C, str string, expect float64, truncateAsErr bool, expectErr error) { 410 sc := new(stmtctx.StatementContext) 411 sc.IgnoreTruncate = !truncateAsErr 412 val, err := StrToFloat(sc, str) 413 if expectErr != nil { 414 c.Assert(terror.ErrorEqual(err, expectErr), IsTrue, Commentf("err %v", err)) 415 } else { 416 c.Assert(err, IsNil) 417 c.Assert(val, Equals, expect) 418 } 419 } 420 421 func (s *testTypeConvertSuite) TestStrToNum(c *C) { 422 defer testleak.AfterTest(c)() 423 testStrToInt(c, "0", 0, true, nil) 424 testStrToInt(c, "-1", -1, true, nil) 425 testStrToInt(c, "100", 100, true, nil) 426 testStrToInt(c, "65.0", 65, false, nil) 427 testStrToInt(c, "65.0", 65, true, nil) 428 testStrToInt(c, "", 0, false, nil) 429 testStrToInt(c, "", 0, true, ErrTruncated) 430 testStrToInt(c, "xx", 0, true, ErrTruncated) 431 testStrToInt(c, "xx", 0, false, nil) 432 testStrToInt(c, "11xx", 11, true, ErrTruncated) 433 testStrToInt(c, "11xx", 11, false, nil) 434 testStrToInt(c, "xx11", 0, false, nil) 435 436 testStrToUint(c, "0", 0, true, nil) 437 testStrToUint(c, "", 0, false, nil) 438 testStrToUint(c, "", 0, false, nil) 439 testStrToUint(c, "-1", 0xffffffffffffffff, false, ErrOverflow) 440 testStrToUint(c, "100", 100, true, nil) 441 testStrToUint(c, "+100", 100, true, nil) 442 testStrToUint(c, "65.0", 65, true, nil) 443 testStrToUint(c, "xx", 0, true, ErrTruncated) 444 testStrToUint(c, "11xx", 11, true, ErrTruncated) 445 testStrToUint(c, "xx11", 0, true, ErrTruncated) 446 447 // TODO: makes StrToFloat return truncated value instead of zero to make it pass. 448 testStrToFloat(c, "", 0, true, ErrTruncated) 449 testStrToFloat(c, "-1", -1.0, true, nil) 450 testStrToFloat(c, "1.11", 1.11, true, nil) 451 testStrToFloat(c, "1.11.00", 1.11, false, nil) 452 testStrToFloat(c, "1.11.00", 1.11, true, ErrTruncated) 453 testStrToFloat(c, "xx", 0.0, false, nil) 454 testStrToFloat(c, "0x00", 0.0, false, nil) 455 testStrToFloat(c, "11.xx", 11.0, false, nil) 456 testStrToFloat(c, "11.xx", 11.0, true, ErrTruncated) 457 testStrToFloat(c, "xx.11", 0.0, false, nil) 458 459 // for issue #5111 460 testStrToFloat(c, "1e649", math.MaxFloat64, true, ErrTruncatedWrongVal) 461 testStrToFloat(c, "1e649", math.MaxFloat64, false, nil) 462 testStrToFloat(c, "-1e649", -math.MaxFloat64, true, ErrTruncatedWrongVal) 463 testStrToFloat(c, "-1e649", -math.MaxFloat64, false, nil) 464 } 465 466 func (s *testTypeConvertSuite) TestFieldTypeToStr(c *C) { 467 defer testleak.AfterTest(c)() 468 v := TypeToStr(mysql.TypeUnspecified, "not binary") 469 c.Assert(v, Equals, TypeStr(mysql.TypeUnspecified)) 470 v = TypeToStr(mysql.TypeBlob, mysql.CharsetBin) 471 c.Assert(v, Equals, "blob") 472 v = TypeToStr(mysql.TypeString, mysql.CharsetBin) 473 c.Assert(v, Equals, "binary") 474 } 475 476 func accept(c *C, tp byte, value interface{}, unsigned bool, expected string) { 477 ft := NewFieldType(tp) 478 if unsigned { 479 ft.Flag |= mysql.UnsignedFlag 480 } 481 d := NewDatum(value) 482 sc := new(stmtctx.StatementContext) 483 sc.TimeZone = time.UTC 484 sc.IgnoreTruncate = true 485 casted, err := d.ConvertTo(sc, ft) 486 c.Assert(err, IsNil, Commentf("%v", ft)) 487 if casted.IsNull() { 488 c.Assert(expected, Equals, "<nil>") 489 } else { 490 str, err := casted.ToString() 491 c.Assert(err, IsNil) 492 c.Assert(str, Equals, expected) 493 } 494 } 495 496 func unsignedAccept(c *C, tp byte, value interface{}, expected string) { 497 accept(c, tp, value, true, expected) 498 } 499 500 func signedAccept(c *C, tp byte, value interface{}, expected string) { 501 accept(c, tp, value, false, expected) 502 } 503 504 func deny(c *C, tp byte, value interface{}, unsigned bool, expected string) { 505 ft := NewFieldType(tp) 506 if unsigned { 507 ft.Flag |= mysql.UnsignedFlag 508 } 509 d := NewDatum(value) 510 sc := new(stmtctx.StatementContext) 511 casted, err := d.ConvertTo(sc, ft) 512 c.Assert(err, NotNil) 513 if casted.IsNull() { 514 c.Assert(expected, Equals, "<nil>") 515 } else { 516 str, err := casted.ToString() 517 c.Assert(err, IsNil) 518 c.Assert(str, Equals, expected) 519 } 520 } 521 522 func unsignedDeny(c *C, tp byte, value interface{}, expected string) { 523 deny(c, tp, value, true, expected) 524 } 525 526 func signedDeny(c *C, tp byte, value interface{}, expected string) { 527 deny(c, tp, value, false, expected) 528 } 529 530 func strvalue(v interface{}) string { 531 return fmt.Sprintf("%v", v) 532 } 533 534 func (s *testTypeConvertSuite) TestConvert(c *C) { 535 defer testleak.AfterTest(c)() 536 // integer ranges 537 signedDeny(c, mysql.TypeTiny, -129, "-128") 538 signedAccept(c, mysql.TypeTiny, -128, "-128") 539 signedAccept(c, mysql.TypeTiny, 127, "127") 540 signedDeny(c, mysql.TypeTiny, 128, "127") 541 unsignedDeny(c, mysql.TypeTiny, -1, "255") 542 unsignedAccept(c, mysql.TypeTiny, 0, "0") 543 unsignedAccept(c, mysql.TypeTiny, 255, "255") 544 unsignedDeny(c, mysql.TypeTiny, 256, "255") 545 546 signedDeny(c, mysql.TypeShort, int64(math.MinInt16)-1, strvalue(int64(math.MinInt16))) 547 signedAccept(c, mysql.TypeShort, int64(math.MinInt16), strvalue(int64(math.MinInt16))) 548 signedAccept(c, mysql.TypeShort, int64(math.MaxInt16), strvalue(int64(math.MaxInt16))) 549 signedDeny(c, mysql.TypeShort, int64(math.MaxInt16)+1, strvalue(int64(math.MaxInt16))) 550 unsignedDeny(c, mysql.TypeShort, -1, "65535") 551 unsignedAccept(c, mysql.TypeShort, 0, "0") 552 unsignedAccept(c, mysql.TypeShort, uint64(math.MaxUint16), strvalue(uint64(math.MaxUint16))) 553 unsignedDeny(c, mysql.TypeShort, uint64(math.MaxUint16)+1, strvalue(uint64(math.MaxUint16))) 554 555 signedDeny(c, mysql.TypeInt24, -1<<23-1, strvalue(-1<<23)) 556 signedAccept(c, mysql.TypeInt24, -1<<23, strvalue(-1<<23)) 557 signedAccept(c, mysql.TypeInt24, 1<<23-1, strvalue(1<<23-1)) 558 signedDeny(c, mysql.TypeInt24, 1<<23, strvalue(1<<23-1)) 559 unsignedDeny(c, mysql.TypeInt24, -1, "16777215") 560 unsignedAccept(c, mysql.TypeInt24, 0, "0") 561 unsignedAccept(c, mysql.TypeInt24, 1<<24-1, strvalue(1<<24-1)) 562 unsignedDeny(c, mysql.TypeInt24, 1<<24, strvalue(1<<24-1)) 563 564 signedDeny(c, mysql.TypeLong, int64(math.MinInt32)-1, strvalue(int64(math.MinInt32))) 565 signedAccept(c, mysql.TypeLong, int64(math.MinInt32), strvalue(int64(math.MinInt32))) 566 signedAccept(c, mysql.TypeLong, int64(math.MaxInt32), strvalue(int64(math.MaxInt32))) 567 signedDeny(c, mysql.TypeLong, uint64(math.MaxUint64), strvalue(uint64(math.MaxInt32))) 568 signedDeny(c, mysql.TypeLong, int64(math.MaxInt32)+1, strvalue(int64(math.MaxInt32))) 569 signedDeny(c, mysql.TypeLong, "1343545435346432587475", strvalue(int64(math.MaxInt32))) 570 unsignedDeny(c, mysql.TypeLong, -1, "4294967295") 571 unsignedAccept(c, mysql.TypeLong, 0, "0") 572 unsignedAccept(c, mysql.TypeLong, uint64(math.MaxUint32), strvalue(uint64(math.MaxUint32))) 573 unsignedDeny(c, mysql.TypeLong, uint64(math.MaxUint32)+1, strvalue(uint64(math.MaxUint32))) 574 575 signedDeny(c, mysql.TypeLonglong, math.MinInt64*1.1, strvalue(int64(math.MinInt64))) 576 signedAccept(c, mysql.TypeLonglong, int64(math.MinInt64), strvalue(int64(math.MinInt64))) 577 signedAccept(c, mysql.TypeLonglong, int64(math.MaxInt64), strvalue(int64(math.MaxInt64))) 578 signedDeny(c, mysql.TypeLonglong, math.MaxInt64*1.1, strvalue(int64(math.MaxInt64))) 579 unsignedAccept(c, mysql.TypeLonglong, -1, "18446744073709551615") 580 unsignedAccept(c, mysql.TypeLonglong, 0, "0") 581 unsignedAccept(c, mysql.TypeLonglong, uint64(math.MaxUint64), strvalue(uint64(math.MaxUint64))) 582 unsignedDeny(c, mysql.TypeLonglong, math.MaxUint64*1.1, strvalue(uint64(math.MaxUint64))) 583 584 // integer from string 585 signedAccept(c, mysql.TypeLong, " 234 ", "234") 586 signedAccept(c, mysql.TypeLong, " 2.35e3 ", "2350") 587 signedAccept(c, mysql.TypeLong, " 2.e3 ", "2000") 588 signedAccept(c, mysql.TypeLong, " -2.e3 ", "-2000") 589 signedAccept(c, mysql.TypeLong, " 2e2 ", "200") 590 signedAccept(c, mysql.TypeLong, " 0.002e3 ", "2") 591 signedAccept(c, mysql.TypeLong, " .002e3 ", "2") 592 signedAccept(c, mysql.TypeLong, " 20e-2 ", "0") 593 signedAccept(c, mysql.TypeLong, " -20e-2 ", "0") 594 signedAccept(c, mysql.TypeLong, " +2.51 ", "3") 595 signedAccept(c, mysql.TypeLong, " -9999.5 ", "-10000") 596 signedAccept(c, mysql.TypeLong, " 999.4", "999") 597 signedAccept(c, mysql.TypeLong, " -3.58", "-4") 598 signedDeny(c, mysql.TypeLong, " 1a ", "1") 599 signedDeny(c, mysql.TypeLong, " +1+ ", "1") 600 601 // integer from float 602 signedAccept(c, mysql.TypeLong, 234.5456, "235") 603 signedAccept(c, mysql.TypeLong, -23.45, "-23") 604 unsignedAccept(c, mysql.TypeLonglong, 234.5456, "235") 605 unsignedDeny(c, mysql.TypeLonglong, -23.45, "18446744073709551593") 606 607 // float from string 608 signedAccept(c, mysql.TypeFloat, "23.523", "23.523") 609 signedAccept(c, mysql.TypeFloat, int64(123), "123") 610 signedAccept(c, mysql.TypeFloat, uint64(123), "123") 611 signedAccept(c, mysql.TypeFloat, int(123), "123") 612 signedAccept(c, mysql.TypeFloat, float32(123), "123") 613 signedAccept(c, mysql.TypeFloat, float64(123), "123") 614 signedAccept(c, mysql.TypeDouble, " -23.54", "-23.54") 615 signedDeny(c, mysql.TypeDouble, "-23.54a", "-23.54") 616 signedDeny(c, mysql.TypeDouble, "-23.54e2e", "-2354") 617 signedDeny(c, mysql.TypeDouble, "+.e", "0") 618 signedAccept(c, mysql.TypeDouble, "1e+1", "10") 619 620 // year 621 signedDeny(c, mysql.TypeYear, 123, "<nil>") 622 signedDeny(c, mysql.TypeYear, 3000, "<nil>") 623 signedAccept(c, mysql.TypeYear, "2000", "2000") 624 625 // time from string 626 signedAccept(c, mysql.TypeDate, "2012-08-23", "2012-08-23") 627 signedAccept(c, mysql.TypeDatetime, "2012-08-23 12:34:03.123456", "2012-08-23 12:34:03") 628 signedAccept(c, mysql.TypeDatetime, ZeroDatetime, "0000-00-00 00:00:00") 629 signedAccept(c, mysql.TypeDatetime, int64(0), "0000-00-00 00:00:00") 630 signedAccept(c, mysql.TypeTimestamp, "2012-08-23 12:34:03.123456", "2012-08-23 12:34:03") 631 signedAccept(c, mysql.TypeDuration, "10:11:12", "10:11:12") 632 signedAccept(c, mysql.TypeDuration, ZeroDatetime, "00:00:00") 633 signedAccept(c, mysql.TypeDuration, ZeroDuration, "00:00:00") 634 signedAccept(c, mysql.TypeDuration, 0, "00:00:00") 635 636 signedDeny(c, mysql.TypeDate, "2012-08-x", "0000-00-00") 637 signedDeny(c, mysql.TypeDatetime, "2012-08-x", "0000-00-00 00:00:00") 638 signedDeny(c, mysql.TypeTimestamp, "2012-08-x", "0000-00-00 00:00:00") 639 signedDeny(c, mysql.TypeDuration, "2012-08-x", "00:00:00") 640 641 // string from string 642 signedAccept(c, mysql.TypeString, "abc", "abc") 643 644 // string from integer 645 signedAccept(c, mysql.TypeString, 5678, "5678") 646 signedAccept(c, mysql.TypeString, ZeroDuration, "00:00:00") 647 signedAccept(c, mysql.TypeString, ZeroDatetime, "0000-00-00 00:00:00") 648 signedAccept(c, mysql.TypeString, []byte("123"), "123") 649 650 //TODO add more tests 651 signedAccept(c, mysql.TypeNewDecimal, 123, "123") 652 signedAccept(c, mysql.TypeNewDecimal, int64(123), "123") 653 signedAccept(c, mysql.TypeNewDecimal, uint64(123), "123") 654 signedAccept(c, mysql.TypeNewDecimal, float32(123), "123") 655 signedAccept(c, mysql.TypeNewDecimal, 123.456, "123.456") 656 signedAccept(c, mysql.TypeNewDecimal, "-123.456", "-123.456") 657 signedAccept(c, mysql.TypeNewDecimal, NewDecFromInt(12300000), "12300000") 658 dec := NewDecFromInt(-123) 659 dec.Shift(-5) 660 dec.Round(dec, 5, ModeHalfEven) 661 signedAccept(c, mysql.TypeNewDecimal, dec, "-0.00123") 662 } 663 664 func (s *testTypeConvertSuite) TestGetValidFloat(c *C) { 665 tests := []struct { 666 origin string 667 valid string 668 }{ 669 {"-100", "-100"}, 670 {"1abc", "1"}, 671 {"-1-1", "-1"}, 672 {"+1+1", "+1"}, 673 {"123..34", "123."}, 674 {"123.23E-10", "123.23E-10"}, 675 {"1.1e1.3", "1.1e1"}, 676 {"11e1.3", "11e1"}, 677 {"1.1e-13a", "1.1e-13"}, 678 {"1.", "1."}, 679 {".1", ".1"}, 680 {"", "0"}, 681 {"123e+", "123"}, 682 {"123.e", "123."}, 683 } 684 sc := new(stmtctx.StatementContext) 685 for _, tt := range tests { 686 prefix, _ := getValidFloatPrefix(sc, tt.origin) 687 c.Assert(prefix, Equals, tt.valid) 688 _, err := strconv.ParseFloat(prefix, 64) 689 c.Assert(err, IsNil) 690 } 691 floatStr, err := floatStrToIntStr(sc, "1e9223372036854775807", "1e9223372036854775807") 692 c.Assert(err, IsNil) 693 c.Assert(floatStr, Equals, "1") 694 floatStr, err = floatStrToIntStr(sc, "125e342", "125e342.83") 695 c.Assert(err, IsNil) 696 c.Assert(floatStr, Equals, "125") 697 floatStr, err = floatStrToIntStr(sc, "1e21", "1e21") 698 c.Assert(err, IsNil) 699 c.Assert(floatStr, Equals, "1") 700 } 701 702 // TestConvertTime tests time related conversion. 703 // time conversion is complicated including Date/Datetime/Time/Timestamp etc, 704 // Timestamp may involving timezone. 705 func (s *testTypeConvertSuite) TestConvertTime(c *C) { 706 timezones := []*time.Location{ 707 time.UTC, 708 time.FixedZone("", 3*3600), 709 time.Local, 710 } 711 712 for _, timezone := range timezones { 713 sc := &stmtctx.StatementContext{ 714 TimeZone: timezone, 715 } 716 testConvertTimeTimeZone(c, sc) 717 } 718 } 719 720 func testConvertTimeTimeZone(c *C, sc *stmtctx.StatementContext) { 721 raw := FromDate(2002, 3, 4, 4, 6, 7, 8) 722 tests := []struct { 723 input Time 724 target *FieldType 725 expect Time 726 }{ 727 { 728 input: Time{Type: mysql.TypeDatetime, Time: raw}, 729 target: NewFieldType(mysql.TypeTimestamp), 730 expect: Time{Type: mysql.TypeTimestamp, Time: raw}, 731 }, 732 { 733 input: Time{Type: mysql.TypeDatetime, Time: raw}, 734 target: NewFieldType(mysql.TypeTimestamp), 735 expect: Time{Type: mysql.TypeTimestamp, Time: raw}, 736 }, 737 { 738 input: Time{Type: mysql.TypeDatetime, Time: raw}, 739 target: NewFieldType(mysql.TypeTimestamp), 740 expect: Time{Type: mysql.TypeTimestamp, Time: raw}, 741 }, 742 { 743 input: Time{Type: mysql.TypeTimestamp, Time: raw}, 744 target: NewFieldType(mysql.TypeDatetime), 745 expect: Time{Type: mysql.TypeDatetime, Time: raw}, 746 }, 747 } 748 749 for _, test := range tests { 750 var d Datum 751 d.SetMysqlTime(test.input) 752 nd, err := d.ConvertTo(sc, test.target) 753 c.Assert(err, IsNil) 754 t := nd.GetMysqlTime() 755 c.Assert(t.Type, Equals, test.expect.Type) 756 c.Assert(t.Time, Equals, test.expect.Time) 757 } 758 } 759 760 func (s *testTypeConvertSuite) TestConvertJSONToInt(c *C) { 761 var tests = []struct { 762 In string 763 Out int64 764 }{ 765 {`{}`, 0}, 766 {`[]`, 0}, 767 {`3`, 3}, 768 {`-3`, -3}, 769 {`4.5`, 5}, 770 {`true`, 1}, 771 {`false`, 0}, 772 {`null`, 0}, 773 {`"hello"`, 0}, 774 {`"123hello"`, 123}, 775 {`"1234"`, 1234}, 776 } 777 for _, tt := range tests { 778 j, err := json.ParseBinaryFromString(tt.In) 779 c.Assert(err, IsNil) 780 781 casted, _ := ConvertJSONToInt(new(stmtctx.StatementContext), j, false) 782 c.Assert(casted, Equals, tt.Out) 783 } 784 } 785 786 func (s *testTypeConvertSuite) TestConvertJSONToFloat(c *C) { 787 var tests = []struct { 788 In string 789 Out float64 790 }{ 791 {`{}`, 0}, 792 {`[]`, 0}, 793 {`3`, 3}, 794 {`-3`, -3}, 795 {`4.5`, 4.5}, 796 {`true`, 1}, 797 {`false`, 0}, 798 {`null`, 0}, 799 {`"hello"`, 0}, 800 {`"123.456hello"`, 123.456}, 801 {`"1234"`, 1234}, 802 } 803 for _, tt := range tests { 804 j, err := json.ParseBinaryFromString(tt.In) 805 c.Assert(err, IsNil) 806 casted, _ := ConvertJSONToFloat(new(stmtctx.StatementContext), j) 807 c.Assert(casted, Equals, tt.Out) 808 } 809 } 810 811 func (s *testTypeConvertSuite) TestConvertJSONToDecimal(c *C) { 812 var tests = []struct { 813 In string 814 Out *MyDecimal 815 }{ 816 {`{}`, NewDecFromStringForTest("0")}, 817 {`[]`, NewDecFromStringForTest("0")}, 818 {`3`, NewDecFromStringForTest("3")}, 819 {`-3`, NewDecFromStringForTest("-3")}, 820 {`4.5`, NewDecFromStringForTest("4.5")}, 821 {`"1234"`, NewDecFromStringForTest("1234")}, 822 {`"1234567890123456789012345678901234567890123456789012345"`, NewDecFromStringForTest("1234567890123456789012345678901234567890123456789012345")}, 823 } 824 for _, tt := range tests { 825 j, err := json.ParseBinaryFromString(tt.In) 826 c.Assert(err, IsNil) 827 casted, _ := ConvertJSONToDecimal(new(stmtctx.StatementContext), j) 828 c.Assert(casted.Compare(tt.Out), Equals, 0) 829 } 830 } 831 832 func (s *testTypeConvertSuite) TestNumberToDuration(c *C) { 833 var testCases = []struct { 834 number int64 835 fsp int 836 hasErr bool 837 year int 838 month int 839 day int 840 hour int 841 minute int 842 second int 843 }{ 844 {20171222, 0, true, 0, 0, 0, 0, 0, 0}, 845 {171222, 0, false, 0, 0, 0, 17, 12, 22}, 846 {20171222020005, 0, false, 2017, 12, 22, 02, 00, 05}, 847 {10000000000, 0, true, 0, 0, 0, 0, 0, 0}, 848 {171222, 1, false, 0, 0, 0, 17, 12, 22}, 849 {176022, 1, true, 0, 0, 0, 0, 0, 0}, 850 {8391222, 1, true, 0, 0, 0, 0, 0, 0}, 851 {8381222, 0, false, 0, 0, 0, 838, 12, 22}, 852 {1001222, 0, false, 0, 0, 0, 100, 12, 22}, 853 {171260, 1, true, 0, 0, 0, 0, 0, 0}, 854 } 855 856 for _, tc := range testCases { 857 dur, err := NumberToDuration(tc.number, tc.fsp) 858 if tc.hasErr { 859 c.Assert(err, NotNil) 860 continue 861 } 862 c.Assert(err, IsNil) 863 c.Assert(dur.Hour(), Equals, tc.hour) 864 c.Assert(dur.Minute(), Equals, tc.minute) 865 c.Assert(dur.Second(), Equals, tc.second) 866 } 867 868 var testCases1 = []struct { 869 number int64 870 dur time.Duration 871 }{ 872 {171222, 17*time.Hour + 12*time.Minute + 22*time.Second}, 873 {-171222, -(17*time.Hour + 12*time.Minute + 22*time.Second)}, 874 } 875 876 for _, tc := range testCases1 { 877 dur, err := NumberToDuration(tc.number, 0) 878 c.Assert(err, IsNil) 879 c.Assert(dur.Duration, Equals, tc.dur) 880 } 881 }