github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/database/sql/convert_test.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package sql 6 7 import ( 8 "database/sql/driver" 9 "fmt" 10 "reflect" 11 "runtime" 12 "strings" 13 "sync" 14 "testing" 15 "time" 16 ) 17 18 var someTime = time.Unix(123, 0) 19 var answer int64 = 42 20 21 type ( 22 userDefined float64 23 userDefinedSlice []int 24 userDefinedString string 25 ) 26 27 type conversionTest struct { 28 s, d any // source and destination 29 30 // following are used if they're non-zero 31 wantint int64 32 wantuint uint64 33 wantstr string 34 wantbytes []byte 35 wantraw RawBytes 36 wantf32 float32 37 wantf64 float64 38 wanttime time.Time 39 wantbool bool // used if d is of type *bool 40 wanterr string 41 wantiface any 42 wantptr *int64 // if non-nil, *d's pointed value must be equal to *wantptr 43 wantnil bool // if true, *d must be *int64(nil) 44 wantusrdef userDefined 45 wantusrstr userDefinedString 46 } 47 48 // Target variables for scanning into. 49 var ( 50 scanstr string 51 scanbytes []byte 52 scanraw RawBytes 53 scanint int 54 scanuint8 uint8 55 scanuint16 uint16 56 scanbool bool 57 scanf32 float32 58 scanf64 float64 59 scantime time.Time 60 scanptr *int64 61 scaniface any 62 ) 63 64 func conversionTests() []conversionTest { 65 // Return a fresh instance to test so "go test -count 2" works correctly. 66 return []conversionTest{ 67 // Exact conversions (destination pointer type matches source type) 68 {s: "foo", d: &scanstr, wantstr: "foo"}, 69 {s: 123, d: &scanint, wantint: 123}, 70 {s: someTime, d: &scantime, wanttime: someTime}, 71 72 // To strings 73 {s: "string", d: &scanstr, wantstr: "string"}, 74 {s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"}, 75 {s: 123, d: &scanstr, wantstr: "123"}, 76 {s: int8(123), d: &scanstr, wantstr: "123"}, 77 {s: int64(123), d: &scanstr, wantstr: "123"}, 78 {s: uint8(123), d: &scanstr, wantstr: "123"}, 79 {s: uint16(123), d: &scanstr, wantstr: "123"}, 80 {s: uint32(123), d: &scanstr, wantstr: "123"}, 81 {s: uint64(123), d: &scanstr, wantstr: "123"}, 82 {s: 1.5, d: &scanstr, wantstr: "1.5"}, 83 84 // From time.Time: 85 {s: time.Unix(1, 0).UTC(), d: &scanstr, wantstr: "1970-01-01T00:00:01Z"}, 86 {s: time.Unix(1453874597, 0).In(time.FixedZone("here", -3600*8)), d: &scanstr, wantstr: "2016-01-26T22:03:17-08:00"}, 87 {s: time.Unix(1, 2).UTC(), d: &scanstr, wantstr: "1970-01-01T00:00:01.000000002Z"}, 88 {s: time.Time{}, d: &scanstr, wantstr: "0001-01-01T00:00:00Z"}, 89 {s: time.Unix(1, 2).UTC(), d: &scanbytes, wantbytes: []byte("1970-01-01T00:00:01.000000002Z")}, 90 {s: time.Unix(1, 2).UTC(), d: &scaniface, wantiface: time.Unix(1, 2).UTC()}, 91 92 // To []byte 93 {s: nil, d: &scanbytes, wantbytes: nil}, 94 {s: "string", d: &scanbytes, wantbytes: []byte("string")}, 95 {s: []byte("byteslice"), d: &scanbytes, wantbytes: []byte("byteslice")}, 96 {s: 123, d: &scanbytes, wantbytes: []byte("123")}, 97 {s: int8(123), d: &scanbytes, wantbytes: []byte("123")}, 98 {s: int64(123), d: &scanbytes, wantbytes: []byte("123")}, 99 {s: uint8(123), d: &scanbytes, wantbytes: []byte("123")}, 100 {s: uint16(123), d: &scanbytes, wantbytes: []byte("123")}, 101 {s: uint32(123), d: &scanbytes, wantbytes: []byte("123")}, 102 {s: uint64(123), d: &scanbytes, wantbytes: []byte("123")}, 103 {s: 1.5, d: &scanbytes, wantbytes: []byte("1.5")}, 104 105 // To RawBytes 106 {s: nil, d: &scanraw, wantraw: nil}, 107 {s: []byte("byteslice"), d: &scanraw, wantraw: RawBytes("byteslice")}, 108 {s: "string", d: &scanraw, wantraw: RawBytes("string")}, 109 {s: 123, d: &scanraw, wantraw: RawBytes("123")}, 110 {s: int8(123), d: &scanraw, wantraw: RawBytes("123")}, 111 {s: int64(123), d: &scanraw, wantraw: RawBytes("123")}, 112 {s: uint8(123), d: &scanraw, wantraw: RawBytes("123")}, 113 {s: uint16(123), d: &scanraw, wantraw: RawBytes("123")}, 114 {s: uint32(123), d: &scanraw, wantraw: RawBytes("123")}, 115 {s: uint64(123), d: &scanraw, wantraw: RawBytes("123")}, 116 {s: 1.5, d: &scanraw, wantraw: RawBytes("1.5")}, 117 // time.Time has been placed here to check that the RawBytes slice gets 118 // correctly reset when calling time.Time.AppendFormat. 119 {s: time.Unix(2, 5).UTC(), d: &scanraw, wantraw: RawBytes("1970-01-01T00:00:02.000000005Z")}, 120 121 // Strings to integers 122 {s: "255", d: &scanuint8, wantuint: 255}, 123 {s: "256", d: &scanuint8, wanterr: "converting driver.Value type string (\"256\") to a uint8: value out of range"}, 124 {s: "256", d: &scanuint16, wantuint: 256}, 125 {s: "-1", d: &scanint, wantint: -1}, 126 {s: "foo", d: &scanint, wanterr: "converting driver.Value type string (\"foo\") to a int: invalid syntax"}, 127 128 // int64 to smaller integers 129 {s: int64(5), d: &scanuint8, wantuint: 5}, 130 {s: int64(256), d: &scanuint8, wanterr: "converting driver.Value type int64 (\"256\") to a uint8: value out of range"}, 131 {s: int64(256), d: &scanuint16, wantuint: 256}, 132 {s: int64(65536), d: &scanuint16, wanterr: "converting driver.Value type int64 (\"65536\") to a uint16: value out of range"}, 133 134 // True bools 135 {s: true, d: &scanbool, wantbool: true}, 136 {s: "True", d: &scanbool, wantbool: true}, 137 {s: "TRUE", d: &scanbool, wantbool: true}, 138 {s: "1", d: &scanbool, wantbool: true}, 139 {s: 1, d: &scanbool, wantbool: true}, 140 {s: int64(1), d: &scanbool, wantbool: true}, 141 {s: uint16(1), d: &scanbool, wantbool: true}, 142 143 // False bools 144 {s: false, d: &scanbool, wantbool: false}, 145 {s: "false", d: &scanbool, wantbool: false}, 146 {s: "FALSE", d: &scanbool, wantbool: false}, 147 {s: "0", d: &scanbool, wantbool: false}, 148 {s: 0, d: &scanbool, wantbool: false}, 149 {s: int64(0), d: &scanbool, wantbool: false}, 150 {s: uint16(0), d: &scanbool, wantbool: false}, 151 152 // Not bools 153 {s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`}, 154 {s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`}, 155 156 // Floats 157 {s: float64(1.5), d: &scanf64, wantf64: float64(1.5)}, 158 {s: int64(1), d: &scanf64, wantf64: float64(1)}, 159 {s: float64(1.5), d: &scanf32, wantf32: float32(1.5)}, 160 {s: "1.5", d: &scanf32, wantf32: float32(1.5)}, 161 {s: "1.5", d: &scanf64, wantf64: float64(1.5)}, 162 163 // Pointers 164 {s: any(nil), d: &scanptr, wantnil: true}, 165 {s: int64(42), d: &scanptr, wantptr: &answer}, 166 167 // To interface{} 168 {s: float64(1.5), d: &scaniface, wantiface: float64(1.5)}, 169 {s: int64(1), d: &scaniface, wantiface: int64(1)}, 170 {s: "str", d: &scaniface, wantiface: "str"}, 171 {s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")}, 172 {s: true, d: &scaniface, wantiface: true}, 173 {s: nil, d: &scaniface}, 174 {s: []byte(nil), d: &scaniface, wantiface: []byte(nil)}, 175 176 // To a user-defined type 177 {s: 1.5, d: new(userDefined), wantusrdef: 1.5}, 178 {s: int64(123), d: new(userDefined), wantusrdef: 123}, 179 {s: "1.5", d: new(userDefined), wantusrdef: 1.5}, 180 {s: []byte{1, 2, 3}, d: new(userDefinedSlice), wanterr: `unsupported Scan, storing driver.Value type []uint8 into type *sql.userDefinedSlice`}, 181 {s: "str", d: new(userDefinedString), wantusrstr: "str"}, 182 183 // Other errors 184 {s: complex(1, 2), d: &scanstr, wanterr: `unsupported Scan, storing driver.Value type complex128 into type *string`}, 185 } 186 } 187 188 func intPtrValue(intptr any) any { 189 return reflect.Indirect(reflect.Indirect(reflect.ValueOf(intptr))).Int() 190 } 191 192 func intValue(intptr any) int64 { 193 return reflect.Indirect(reflect.ValueOf(intptr)).Int() 194 } 195 196 func uintValue(intptr any) uint64 { 197 return reflect.Indirect(reflect.ValueOf(intptr)).Uint() 198 } 199 200 func float64Value(ptr any) float64 { 201 return *(ptr.(*float64)) 202 } 203 204 func float32Value(ptr any) float32 { 205 return *(ptr.(*float32)) 206 } 207 208 func timeValue(ptr any) time.Time { 209 return *(ptr.(*time.Time)) 210 } 211 212 func TestConversions(t *testing.T) { 213 for n, ct := range conversionTests() { 214 err := convertAssign(ct.d, ct.s) 215 errstr := "" 216 if err != nil { 217 errstr = err.Error() 218 } 219 errf := func(format string, args ...any) { 220 base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d) 221 t.Errorf(base+format, args...) 222 } 223 if errstr != ct.wanterr { 224 errf("got error %q, want error %q", errstr, ct.wanterr) 225 } 226 if ct.wantstr != "" && ct.wantstr != scanstr { 227 errf("want string %q, got %q", ct.wantstr, scanstr) 228 } 229 if ct.wantbytes != nil && string(ct.wantbytes) != string(scanbytes) { 230 errf("want byte %q, got %q", ct.wantbytes, scanbytes) 231 } 232 if ct.wantraw != nil && string(ct.wantraw) != string(scanraw) { 233 errf("want RawBytes %q, got %q", ct.wantraw, scanraw) 234 } 235 if ct.wantint != 0 && ct.wantint != intValue(ct.d) { 236 errf("want int %d, got %d", ct.wantint, intValue(ct.d)) 237 } 238 if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) { 239 errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d)) 240 } 241 if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) { 242 errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d)) 243 } 244 if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) { 245 errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d)) 246 } 247 if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" { 248 errf("want bool %v, got %v", ct.wantbool, *bp) 249 } 250 if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) { 251 errf("want time %v, got %v", ct.wanttime, timeValue(ct.d)) 252 } 253 if ct.wantnil && *ct.d.(**int64) != nil { 254 errf("want nil, got %v", intPtrValue(ct.d)) 255 } 256 if ct.wantptr != nil { 257 if *ct.d.(**int64) == nil { 258 errf("want pointer to %v, got nil", *ct.wantptr) 259 } else if *ct.wantptr != intPtrValue(ct.d) { 260 errf("want pointer to %v, got %v", *ct.wantptr, intPtrValue(ct.d)) 261 } 262 } 263 if ifptr, ok := ct.d.(*any); ok { 264 if !reflect.DeepEqual(ct.wantiface, scaniface) { 265 errf("want interface %#v, got %#v", ct.wantiface, scaniface) 266 continue 267 } 268 if srcBytes, ok := ct.s.([]byte); ok { 269 dstBytes := (*ifptr).([]byte) 270 if len(srcBytes) > 0 && &dstBytes[0] == &srcBytes[0] { 271 errf("copy into interface{} didn't copy []byte data") 272 } 273 } 274 } 275 if ct.wantusrdef != 0 && ct.wantusrdef != *ct.d.(*userDefined) { 276 errf("want userDefined %f, got %f", ct.wantusrdef, *ct.d.(*userDefined)) 277 } 278 if len(ct.wantusrstr) != 0 && ct.wantusrstr != *ct.d.(*userDefinedString) { 279 errf("want userDefined %q, got %q", ct.wantusrstr, *ct.d.(*userDefinedString)) 280 } 281 } 282 } 283 284 func TestNullString(t *testing.T) { 285 var ns NullString 286 convertAssign(&ns, []byte("foo")) 287 if !ns.Valid { 288 t.Errorf("expecting not null") 289 } 290 if ns.String != "foo" { 291 t.Errorf("expecting foo; got %q", ns.String) 292 } 293 convertAssign(&ns, nil) 294 if ns.Valid { 295 t.Errorf("expecting null on nil") 296 } 297 if ns.String != "" { 298 t.Errorf("expecting blank on nil; got %q", ns.String) 299 } 300 } 301 302 type valueConverterTest struct { 303 c driver.ValueConverter 304 in, out any 305 err string 306 } 307 308 var valueConverterTests = []valueConverterTest{ 309 {driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""}, 310 {driver.DefaultParameterConverter, NullString{"", false}, nil, ""}, 311 } 312 313 func TestValueConverters(t *testing.T) { 314 for i, tt := range valueConverterTests { 315 out, err := tt.c.ConvertValue(tt.in) 316 goterr := "" 317 if err != nil { 318 goterr = err.Error() 319 } 320 if goterr != tt.err { 321 t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q", 322 i, tt.c, tt.in, tt.in, goterr, tt.err) 323 } 324 if tt.err != "" { 325 continue 326 } 327 if !reflect.DeepEqual(out, tt.out) { 328 t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)", 329 i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out) 330 } 331 } 332 } 333 334 // Tests that assigning to RawBytes doesn't allocate (and also works). 335 func TestRawBytesAllocs(t *testing.T) { 336 var tests = []struct { 337 name string 338 in any 339 want string 340 }{ 341 {"uint64", uint64(12345678), "12345678"}, 342 {"uint32", uint32(1234), "1234"}, 343 {"uint16", uint16(12), "12"}, 344 {"uint8", uint8(1), "1"}, 345 {"uint", uint(123), "123"}, 346 {"int", int(123), "123"}, 347 {"int8", int8(1), "1"}, 348 {"int16", int16(12), "12"}, 349 {"int32", int32(1234), "1234"}, 350 {"int64", int64(12345678), "12345678"}, 351 {"float32", float32(1.5), "1.5"}, 352 {"float64", float64(64), "64"}, 353 {"bool", false, "false"}, 354 {"time", time.Unix(2, 5).UTC(), "1970-01-01T00:00:02.000000005Z"}, 355 } 356 357 buf := make(RawBytes, 10) 358 test := func(name string, in any, want string) { 359 if err := convertAssign(&buf, in); err != nil { 360 t.Fatalf("%s: convertAssign = %v", name, err) 361 } 362 match := len(buf) == len(want) 363 if match { 364 for i, b := range buf { 365 if want[i] != b { 366 match = false 367 break 368 } 369 } 370 } 371 if !match { 372 t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want)) 373 } 374 } 375 376 n := testing.AllocsPerRun(100, func() { 377 for _, tt := range tests { 378 test(tt.name, tt.in, tt.want) 379 } 380 }) 381 382 // The numbers below are only valid for 64-bit interface word sizes, 383 // and gc. With 32-bit words there are more convT2E allocs, and 384 // with gccgo, only pointers currently go in interface data. 385 // So only care on amd64 gc for now. 386 measureAllocs := runtime.GOARCH == "amd64" && runtime.Compiler == "gc" 387 388 if n > 0.5 && measureAllocs { 389 t.Fatalf("allocs = %v; want 0", n) 390 } 391 392 // This one involves a convT2E allocation, string -> interface{} 393 n = testing.AllocsPerRun(100, func() { 394 test("string", "foo", "foo") 395 }) 396 if n > 1.5 && measureAllocs { 397 t.Fatalf("allocs = %v; want max 1", n) 398 } 399 } 400 401 // https://golang.org/issues/13905 402 func TestUserDefinedBytes(t *testing.T) { 403 type userDefinedBytes []byte 404 var u userDefinedBytes 405 v := []byte("foo") 406 407 convertAssign(&u, v) 408 if &u[0] == &v[0] { 409 t.Fatal("userDefinedBytes got potentially dirty driver memory") 410 } 411 } 412 413 type Valuer_V string 414 415 func (v Valuer_V) Value() (driver.Value, error) { 416 return strings.ToUpper(string(v)), nil 417 } 418 419 type Valuer_P string 420 421 func (p *Valuer_P) Value() (driver.Value, error) { 422 if p == nil { 423 return "nil-to-str", nil 424 } 425 return strings.ToUpper(string(*p)), nil 426 } 427 428 func TestDriverArgs(t *testing.T) { 429 var nilValuerVPtr *Valuer_V 430 var nilValuerPPtr *Valuer_P 431 var nilStrPtr *string 432 tests := []struct { 433 args []any 434 want []driver.NamedValue 435 }{ 436 0: { 437 args: []any{Valuer_V("foo")}, 438 want: []driver.NamedValue{ 439 { 440 Ordinal: 1, 441 Value: "FOO", 442 }, 443 }, 444 }, 445 1: { 446 args: []any{nilValuerVPtr}, 447 want: []driver.NamedValue{ 448 { 449 Ordinal: 1, 450 Value: nil, 451 }, 452 }, 453 }, 454 2: { 455 args: []any{nilValuerPPtr}, 456 want: []driver.NamedValue{ 457 { 458 Ordinal: 1, 459 Value: "nil-to-str", 460 }, 461 }, 462 }, 463 3: { 464 args: []any{"plain-str"}, 465 want: []driver.NamedValue{ 466 { 467 Ordinal: 1, 468 Value: "plain-str", 469 }, 470 }, 471 }, 472 4: { 473 args: []any{nilStrPtr}, 474 want: []driver.NamedValue{ 475 { 476 Ordinal: 1, 477 Value: nil, 478 }, 479 }, 480 }, 481 } 482 for i, tt := range tests { 483 ds := &driverStmt{Locker: &sync.Mutex{}, si: stubDriverStmt{nil}} 484 got, err := driverArgsConnLocked(nil, ds, tt.args) 485 if err != nil { 486 t.Errorf("test[%d]: %v", i, err) 487 continue 488 } 489 if !reflect.DeepEqual(got, tt.want) { 490 t.Errorf("test[%d]: got %v, want %v", i, got, tt.want) 491 } 492 } 493 } 494 495 type dec struct { 496 form byte 497 neg bool 498 coefficient [16]byte 499 exponent int32 500 } 501 502 func (d dec) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) { 503 coef := make([]byte, 16) 504 copy(coef, d.coefficient[:]) 505 return d.form, d.neg, coef, d.exponent 506 } 507 508 func (d *dec) Compose(form byte, negative bool, coefficient []byte, exponent int32) error { 509 switch form { 510 default: 511 return fmt.Errorf("unknown form %d", form) 512 case 1, 2: 513 d.form = form 514 d.neg = negative 515 return nil 516 case 0: 517 } 518 d.form = form 519 d.neg = negative 520 d.exponent = exponent 521 522 // This isn't strictly correct, as the extra bytes could be all zero, 523 // ignore this for this test. 524 if len(coefficient) > 16 { 525 return fmt.Errorf("coefficient too large") 526 } 527 copy(d.coefficient[:], coefficient) 528 529 return nil 530 } 531 532 type decFinite struct { 533 neg bool 534 coefficient [16]byte 535 exponent int32 536 } 537 538 func (d decFinite) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) { 539 coef := make([]byte, 16) 540 copy(coef, d.coefficient[:]) 541 return 0, d.neg, coef, d.exponent 542 } 543 544 func (d *decFinite) Compose(form byte, negative bool, coefficient []byte, exponent int32) error { 545 switch form { 546 default: 547 return fmt.Errorf("unknown form %d", form) 548 case 1, 2: 549 return fmt.Errorf("unsupported form %d", form) 550 case 0: 551 } 552 d.neg = negative 553 d.exponent = exponent 554 555 // This isn't strictly correct, as the extra bytes could be all zero, 556 // ignore this for this test. 557 if len(coefficient) > 16 { 558 return fmt.Errorf("coefficient too large") 559 } 560 copy(d.coefficient[:], coefficient) 561 562 return nil 563 } 564 565 func TestDecimal(t *testing.T) { 566 list := []struct { 567 name string 568 in decimalDecompose 569 out dec 570 err bool 571 }{ 572 {name: "same", in: dec{exponent: -6}, out: dec{exponent: -6}}, 573 574 // Ensure reflection is not used to assign the value by using different types. 575 {name: "diff", in: decFinite{exponent: -6}, out: dec{exponent: -6}}, 576 577 {name: "bad-form", in: dec{form: 200}, err: true}, 578 } 579 for _, item := range list { 580 t.Run(item.name, func(t *testing.T) { 581 out := dec{} 582 err := convertAssign(&out, item.in) 583 if item.err { 584 if err == nil { 585 t.Fatalf("unexpected nil error") 586 } 587 return 588 } 589 if err != nil { 590 t.Fatalf("unexpected error: %v", err) 591 } 592 if !reflect.DeepEqual(out, item.out) { 593 t.Fatalf("got %#v want %#v", out, item.out) 594 } 595 }) 596 } 597 }