Source file
src/database/sql/convert_test.go
1
2
3
4
5 package sql
6
7 import (
8 "database/sql/driver"
9 "fmt"
10 "internal/asan"
11 "reflect"
12 "runtime"
13 "strings"
14 "sync"
15 "testing"
16 "time"
17 "uuid"
18 )
19
20 var someTime = time.Unix(123, 0)
21 var answer int64 = 42
22
23 type (
24 userDefined float64
25 userDefinedSlice []int
26 userDefinedString string
27 )
28
29 type conversionTest struct {
30 s, d any
31
32
33 wantint int64
34 wantuint uint64
35 wantstr string
36 wantbytes []byte
37 wantraw RawBytes
38 wantf32 float32
39 wantf64 float64
40 wanttime time.Time
41 wantbool bool
42 wantuuid uuid.UUID
43 wanterr string
44 wantiface any
45 wantptr *int64
46 wantnil bool
47 wantusrdef userDefined
48 wantusrstr userDefinedString
49 }
50
51
52 var (
53 scanstr string
54 scanbytes []byte
55 scanraw RawBytes
56 scanint int
57 scanint64 int64
58 scanuint8 uint8
59 scanuint16 uint16
60 scanbool bool
61 scanf32 float32
62 scanf64 float64
63 scantime time.Time
64 scanuuid uuid.UUID
65 scanptr *int64
66 scaniface any
67 )
68
69 func conversionTests() []conversionTest {
70
71 return []conversionTest{
72
73 {s: "foo", d: &scanstr, wantstr: "foo"},
74 {s: 123, d: &scanint, wantint: 123},
75 {s: int64(123), d: &scanint64, wantint: 123},
76 {s: int64(123), d: &scanint, wantint: 123},
77 {s: int64(-123), d: &scanint, wantint: -123},
78 {s: someTime, d: &scantime, wanttime: someTime},
79
80
81 {s: "string", d: &scanstr, wantstr: "string"},
82 {s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"},
83 {s: 123, d: &scanstr, wantstr: "123"},
84 {s: int8(123), d: &scanstr, wantstr: "123"},
85 {s: int64(123), d: &scanstr, wantstr: "123"},
86 {s: uint8(123), d: &scanstr, wantstr: "123"},
87 {s: uint16(123), d: &scanstr, wantstr: "123"},
88 {s: uint32(123), d: &scanstr, wantstr: "123"},
89 {s: uint64(123), d: &scanstr, wantstr: "123"},
90 {s: 1.5, d: &scanstr, wantstr: "1.5"},
91
92
93 {s: time.Unix(1, 0).UTC(), d: &scanstr, wantstr: "1970-01-01T00:00:01Z"},
94 {s: time.Unix(1453874597, 0).In(time.FixedZone("here", -3600*8)), d: &scanstr, wantstr: "2016-01-26T22:03:17-08:00"},
95 {s: time.Unix(1, 2).UTC(), d: &scanstr, wantstr: "1970-01-01T00:00:01.000000002Z"},
96 {s: time.Time{}, d: &scanstr, wantstr: "0001-01-01T00:00:00Z"},
97 {s: time.Unix(1, 2).UTC(), d: &scanbytes, wantbytes: []byte("1970-01-01T00:00:01.000000002Z")},
98 {s: time.Unix(1, 2).UTC(), d: &scaniface, wantiface: time.Unix(1, 2).UTC()},
99
100
101 {s: nil, d: &scanbytes, wantbytes: nil},
102 {s: "string", d: &scanbytes, wantbytes: []byte("string")},
103 {s: []byte("byteslice"), d: &scanbytes, wantbytes: []byte("byteslice")},
104 {s: 123, d: &scanbytes, wantbytes: []byte("123")},
105 {s: int8(123), d: &scanbytes, wantbytes: []byte("123")},
106 {s: int64(123), d: &scanbytes, wantbytes: []byte("123")},
107 {s: uint8(123), d: &scanbytes, wantbytes: []byte("123")},
108 {s: uint16(123), d: &scanbytes, wantbytes: []byte("123")},
109 {s: uint32(123), d: &scanbytes, wantbytes: []byte("123")},
110 {s: uint64(123), d: &scanbytes, wantbytes: []byte("123")},
111 {s: 1.5, d: &scanbytes, wantbytes: []byte("1.5")},
112
113
114 {s: nil, d: &scanraw, wantraw: nil},
115 {s: []byte("byteslice"), d: &scanraw, wantraw: RawBytes("byteslice")},
116 {s: "string", d: &scanraw, wantraw: RawBytes("string")},
117 {s: 123, d: &scanraw, wantraw: RawBytes("123")},
118 {s: int8(123), d: &scanraw, wantraw: RawBytes("123")},
119 {s: int64(123), d: &scanraw, wantraw: RawBytes("123")},
120 {s: uint8(123), d: &scanraw, wantraw: RawBytes("123")},
121 {s: uint16(123), d: &scanraw, wantraw: RawBytes("123")},
122 {s: uint32(123), d: &scanraw, wantraw: RawBytes("123")},
123 {s: uint64(123), d: &scanraw, wantraw: RawBytes("123")},
124 {s: 1.5, d: &scanraw, wantraw: RawBytes("1.5")},
125
126
127 {s: time.Unix(2, 5).UTC(), d: &scanraw, wantraw: RawBytes("1970-01-01T00:00:02.000000005Z")},
128
129
130 {s: "255", d: &scanuint8, wantuint: 255},
131 {s: "256", d: &scanuint8, wanterr: "converting driver.Value type string (\"256\") to a uint8: value out of range"},
132 {s: "256", d: &scanuint16, wantuint: 256},
133 {s: "-1", d: &scanint, wantint: -1},
134 {s: "foo", d: &scanint, wanterr: "converting driver.Value type string (\"foo\") to a int: invalid syntax"},
135
136
137 {s: int64(5), d: &scanuint8, wantuint: 5},
138 {s: int64(256), d: &scanuint8, wanterr: "converting driver.Value type int64 (\"256\") to a uint8: value out of range"},
139 {s: int64(256), d: &scanuint16, wantuint: 256},
140 {s: int64(65536), d: &scanuint16, wanterr: "converting driver.Value type int64 (\"65536\") to a uint16: value out of range"},
141
142
143 {s: true, d: &scanbool, wantbool: true},
144 {s: "True", d: &scanbool, wantbool: true},
145 {s: "TRUE", d: &scanbool, wantbool: true},
146 {s: "1", d: &scanbool, wantbool: true},
147 {s: 1, d: &scanbool, wantbool: true},
148 {s: int64(1), d: &scanbool, wantbool: true},
149 {s: uint16(1), d: &scanbool, wantbool: true},
150
151
152 {s: false, d: &scanbool, wantbool: false},
153 {s: "false", d: &scanbool, wantbool: false},
154 {s: "FALSE", d: &scanbool, wantbool: false},
155 {s: "0", d: &scanbool, wantbool: false},
156 {s: 0, d: &scanbool, wantbool: false},
157 {s: int64(0), d: &scanbool, wantbool: false},
158 {s: uint16(0), d: &scanbool, wantbool: false},
159
160
161 {s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`},
162 {s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`},
163
164
165 {s: float64(1.5), d: &scanf64, wantf64: float64(1.5)},
166 {s: int64(1), d: &scanf64, wantf64: float64(1)},
167 {s: float64(1.5), d: &scanf32, wantf32: float32(1.5)},
168 {s: "1.5", d: &scanf32, wantf32: float32(1.5)},
169 {s: "1.5", d: &scanf64, wantf64: float64(1.5)},
170
171
172 {s: "97b6a4e0-5323-43e9-82c8-88110d6686d6", d: &scanuuid, wantuuid: uuid.MustParse("97b6a4e0-5323-43e9-82c8-88110d6686d6")},
173 {s: "97B6A4E0-5323-43E9-82C8-88110D6686D6", d: &scanuuid, wantuuid: uuid.MustParse("97b6a4e0-5323-43e9-82c8-88110d6686d6")},
174 {s: "x", d: &scanuuid, wanterr: `converting driver.Value type string ("x") to a UUID: invalid uuid`},
175 {s: []byte("97b6a4e0-5323-43e9-82c8-88110d6686d6"), d: &scanuuid, wantuuid: uuid.MustParse("97b6a4e0-5323-43e9-82c8-88110d6686d6")},
176 {s: []byte("97B6A4E0-5323-43E9-82C8-88110D6686D6"), d: &scanuuid, wantuuid: uuid.MustParse("97b6a4e0-5323-43e9-82c8-88110d6686d6")},
177 {s: []byte{0x97, 0xb6, 0xa4, 0xe0, 0x53, 0x23, 0x43, 0xe9, 0x82, 0xc8, 0x88, 0x11, 0x0d, 0x66, 0x86, 0xd6}, d: &scanuuid, wantuuid: uuid.MustParse("97b6a4e0-5323-43e9-82c8-88110d6686d6")},
178 {s: []byte("x"), d: &scanuuid, wanterr: `converting driver.Value type []byte ("x") to a UUID: invalid uuid`},
179
180
181 {s: any(nil), d: &scanptr, wantnil: true},
182 {s: int64(42), d: &scanptr, wantptr: &answer},
183
184
185 {s: float64(1.5), d: &scaniface, wantiface: float64(1.5)},
186 {s: int64(1), d: &scaniface, wantiface: int64(1)},
187 {s: "str", d: &scaniface, wantiface: "str"},
188 {s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")},
189 {s: true, d: &scaniface, wantiface: true},
190 {s: nil, d: &scaniface},
191 {s: []byte(nil), d: &scaniface, wantiface: []byte(nil)},
192
193
194 {s: 1.5, d: new(userDefined), wantusrdef: 1.5},
195 {s: int64(123), d: new(userDefined), wantusrdef: 123},
196 {s: "1.5", d: new(userDefined), wantusrdef: 1.5},
197 {s: []byte{1, 2, 3}, d: new(userDefinedSlice), wanterr: `unsupported Scan, storing driver.Value type []uint8 into type *sql.userDefinedSlice`},
198 {s: "str", d: new(userDefinedString), wantusrstr: "str"},
199
200
201 {s: complex(1, 2), d: &scanstr, wanterr: `unsupported Scan, storing driver.Value type complex128 into type *string`},
202 }
203 }
204
205 func intPtrValue(intptr any) any {
206 return reflect.Indirect(reflect.Indirect(reflect.ValueOf(intptr))).Int()
207 }
208
209 func intValue(intptr any) int64 {
210 return reflect.Indirect(reflect.ValueOf(intptr)).Int()
211 }
212
213 func uintValue(intptr any) uint64 {
214 return reflect.Indirect(reflect.ValueOf(intptr)).Uint()
215 }
216
217 func float64Value(ptr any) float64 {
218 return *(ptr.(*float64))
219 }
220
221 func float32Value(ptr any) float32 {
222 return *(ptr.(*float32))
223 }
224
225 func timeValue(ptr any) time.Time {
226 return *(ptr.(*time.Time))
227 }
228
229 func uuidValue(ptr any) uuid.UUID {
230 return *(ptr.(*uuid.UUID))
231 }
232
233 func TestConversions(t *testing.T) {
234 for n, ct := range conversionTests() {
235 err := convertAssign(ct.d, ct.s)
236 errstr := ""
237 if err != nil {
238 errstr = err.Error()
239 }
240 errf := func(format string, args ...any) {
241 base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d)
242 t.Errorf(base+format, args...)
243 }
244 if errstr != ct.wanterr {
245 errf("got error %q, want error %q", errstr, ct.wanterr)
246 }
247 if ct.wantstr != "" && ct.wantstr != scanstr {
248 errf("want string %q, got %q", ct.wantstr, scanstr)
249 }
250 if ct.wantbytes != nil && string(ct.wantbytes) != string(scanbytes) {
251 errf("want byte %q, got %q", ct.wantbytes, scanbytes)
252 }
253 if ct.wantraw != nil && string(ct.wantraw) != string(scanraw) {
254 errf("want RawBytes %q, got %q", ct.wantraw, scanraw)
255 }
256 if ct.wantint != 0 && ct.wantint != intValue(ct.d) {
257 errf("want int %d, got %d", ct.wantint, intValue(ct.d))
258 }
259 if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) {
260 errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d))
261 }
262 if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) {
263 errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d))
264 }
265 if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) {
266 errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d))
267 }
268 if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" {
269 errf("want bool %v, got %v", ct.wantbool, *bp)
270 }
271 if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) {
272 errf("want time %v, got %v", ct.wanttime, timeValue(ct.d))
273 }
274 if ct.wantuuid != (uuid.UUID{}) && ct.wantuuid != uuidValue(ct.d) {
275 errf("want uuid %v, got %v", ct.wantuuid, uuidValue(ct.d))
276 }
277 if ct.wantnil && *ct.d.(**int64) != nil {
278 errf("want nil, got %v", intPtrValue(ct.d))
279 }
280 if ct.wantptr != nil {
281 if *ct.d.(**int64) == nil {
282 errf("want pointer to %v, got nil", *ct.wantptr)
283 } else if *ct.wantptr != intPtrValue(ct.d) {
284 errf("want pointer to %v, got %v", *ct.wantptr, intPtrValue(ct.d))
285 }
286 }
287 if ifptr, ok := ct.d.(*any); ok {
288 if !reflect.DeepEqual(ct.wantiface, scaniface) {
289 errf("want interface %#v, got %#v", ct.wantiface, scaniface)
290 continue
291 }
292 if srcBytes, ok := ct.s.([]byte); ok {
293 dstBytes := (*ifptr).([]byte)
294 if len(srcBytes) > 0 && &dstBytes[0] == &srcBytes[0] {
295 errf("copy into interface{} didn't copy []byte data")
296 }
297 }
298 }
299 if ct.wantusrdef != 0 && ct.wantusrdef != *ct.d.(*userDefined) {
300 errf("want userDefined %f, got %f", ct.wantusrdef, *ct.d.(*userDefined))
301 }
302 if len(ct.wantusrstr) != 0 && ct.wantusrstr != *ct.d.(*userDefinedString) {
303 errf("want userDefined %q, got %q", ct.wantusrstr, *ct.d.(*userDefinedString))
304 }
305 }
306 }
307
308 func TestNullString(t *testing.T) {
309 var ns NullString
310 convertAssign(&ns, []byte("foo"))
311 if !ns.Valid {
312 t.Errorf("expecting not null")
313 }
314 if ns.String != "foo" {
315 t.Errorf("expecting foo; got %q", ns.String)
316 }
317 convertAssign(&ns, nil)
318 if ns.Valid {
319 t.Errorf("expecting null on nil")
320 }
321 if ns.String != "" {
322 t.Errorf("expecting blank on nil; got %q", ns.String)
323 }
324 }
325
326 type valueConverterTest struct {
327 c driver.ValueConverter
328 in, out any
329 err string
330 }
331
332 var valueConverterTests = []valueConverterTest{
333 {driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""},
334 {driver.DefaultParameterConverter, NullString{"", false}, nil, ""},
335 }
336
337 func TestValueConverters(t *testing.T) {
338 for i, tt := range valueConverterTests {
339 out, err := tt.c.ConvertValue(tt.in)
340 goterr := ""
341 if err != nil {
342 goterr = err.Error()
343 }
344 if goterr != tt.err {
345 t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q",
346 i, tt.c, tt.in, tt.in, goterr, tt.err)
347 }
348 if tt.err != "" {
349 continue
350 }
351 if !reflect.DeepEqual(out, tt.out) {
352 t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)",
353 i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
354 }
355 }
356 }
357
358
359 func TestRawBytesAllocs(t *testing.T) {
360 var tests = []struct {
361 name string
362 in any
363 want string
364 }{
365 {"uint64", uint64(12345678), "12345678"},
366 {"uint32", uint32(1234), "1234"},
367 {"uint16", uint16(12), "12"},
368 {"uint8", uint8(1), "1"},
369 {"uint", uint(123), "123"},
370 {"int", int(123), "123"},
371 {"int8", int8(1), "1"},
372 {"int16", int16(12), "12"},
373 {"int32", int32(1234), "1234"},
374 {"int64", int64(12345678), "12345678"},
375 {"float32", float32(1.5), "1.5"},
376 {"float64", float64(64), "64"},
377 {"bool", false, "false"},
378 {"time", time.Unix(2, 5).UTC(), "1970-01-01T00:00:02.000000005Z"},
379 }
380 if asan.Enabled {
381 t.Skip("test allocates more with -asan; see #70079")
382 }
383
384 var buf RawBytes
385 rows := &Rows{}
386 test := func(name string, in any, want string) {
387 if err := convertAssignRows(&buf, in, rows); err != nil {
388 t.Fatalf("%s: convertAssign = %v", name, err)
389 }
390 match := len(buf) == len(want)
391 if match {
392 for i, b := range buf {
393 if want[i] != b {
394 match = false
395 break
396 }
397 }
398 }
399 if !match {
400 t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want))
401 }
402 }
403
404 n := testing.AllocsPerRun(100, func() {
405 for _, tt := range tests {
406 rows.raw = rows.raw[:0]
407 test(tt.name, tt.in, tt.want)
408 }
409 })
410
411
412
413
414
415 measureAllocs := false
416 switch runtime.GOARCH {
417 case "amd64", "arm64":
418 measureAllocs = runtime.Compiler == "gc"
419 }
420
421 if n > 0.5 && measureAllocs {
422 t.Fatalf("allocs = %v; want 0", n)
423 }
424
425
426 n = testing.AllocsPerRun(100, func() {
427 test("string", "foo", "foo")
428 })
429 if n > 1.5 && measureAllocs {
430 t.Fatalf("allocs = %v; want max 1", n)
431 }
432 }
433
434
435 func TestUserDefinedBytes(t *testing.T) {
436 type userDefinedBytes []byte
437 var u userDefinedBytes
438 v := []byte("foo")
439
440 convertAssign(&u, v)
441 if &u[0] == &v[0] {
442 t.Fatal("userDefinedBytes got potentially dirty driver memory")
443 }
444 }
445
446 type Valuer_V string
447
448 func (v Valuer_V) Value() (driver.Value, error) {
449 return strings.ToUpper(string(v)), nil
450 }
451
452 type Valuer_P string
453
454 func (p *Valuer_P) Value() (driver.Value, error) {
455 if p == nil {
456 return "nil-to-str", nil
457 }
458 return strings.ToUpper(string(*p)), nil
459 }
460
461 func TestDriverArgs(t *testing.T) {
462 var nilValuerVPtr *Valuer_V
463 var nilValuerPPtr *Valuer_P
464 var nilStrPtr *string
465 tests := []struct {
466 args []any
467 want []driver.NamedValue
468 }{
469 0: {
470 args: []any{Valuer_V("foo")},
471 want: []driver.NamedValue{
472 {
473 Ordinal: 1,
474 Value: "FOO",
475 },
476 },
477 },
478 1: {
479 args: []any{nilValuerVPtr},
480 want: []driver.NamedValue{
481 {
482 Ordinal: 1,
483 Value: nil,
484 },
485 },
486 },
487 2: {
488 args: []any{nilValuerPPtr},
489 want: []driver.NamedValue{
490 {
491 Ordinal: 1,
492 Value: "nil-to-str",
493 },
494 },
495 },
496 3: {
497 args: []any{"plain-str"},
498 want: []driver.NamedValue{
499 {
500 Ordinal: 1,
501 Value: "plain-str",
502 },
503 },
504 },
505 4: {
506 args: []any{nilStrPtr},
507 want: []driver.NamedValue{
508 {
509 Ordinal: 1,
510 Value: nil,
511 },
512 },
513 },
514 }
515 for i, tt := range tests {
516 ds := &driverStmt{Locker: &sync.Mutex{}, si: stubDriverStmt{nil}}
517 got, err := driverArgsConnLocked(nil, ds, tt.args)
518 if err != nil {
519 t.Errorf("test[%d]: %v", i, err)
520 continue
521 }
522 if !reflect.DeepEqual(got, tt.want) {
523 t.Errorf("test[%d]: got %v, want %v", i, got, tt.want)
524 }
525 }
526 }
527
528 type dec struct {
529 form byte
530 neg bool
531 coefficient [16]byte
532 exponent int32
533 }
534
535 func (d dec) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
536 coef := make([]byte, 16)
537 copy(coef, d.coefficient[:])
538 return d.form, d.neg, coef, d.exponent
539 }
540
541 func (d *dec) Compose(form byte, negative bool, coefficient []byte, exponent int32) error {
542 switch form {
543 default:
544 return fmt.Errorf("unknown form %d", form)
545 case 1, 2:
546 d.form = form
547 d.neg = negative
548 return nil
549 case 0:
550 }
551 d.form = form
552 d.neg = negative
553 d.exponent = exponent
554
555
556
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 type decFinite struct {
566 neg bool
567 coefficient [16]byte
568 exponent int32
569 }
570
571 func (d decFinite) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
572 coef := make([]byte, 16)
573 copy(coef, d.coefficient[:])
574 return 0, d.neg, coef, d.exponent
575 }
576
577 func (d *decFinite) Compose(form byte, negative bool, coefficient []byte, exponent int32) error {
578 switch form {
579 default:
580 return fmt.Errorf("unknown form %d", form)
581 case 1, 2:
582 return fmt.Errorf("unsupported form %d", form)
583 case 0:
584 }
585 d.neg = negative
586 d.exponent = exponent
587
588
589
590 if len(coefficient) > 16 {
591 return fmt.Errorf("coefficient too large")
592 }
593 copy(d.coefficient[:], coefficient)
594
595 return nil
596 }
597
598 func TestDecimal(t *testing.T) {
599 list := []struct {
600 name string
601 in decimalDecompose
602 out dec
603 err bool
604 }{
605 {name: "same", in: dec{exponent: -6}, out: dec{exponent: -6}},
606
607
608 {name: "diff", in: decFinite{exponent: -6}, out: dec{exponent: -6}},
609
610 {name: "bad-form", in: dec{form: 200}, err: true},
611 }
612 for _, item := range list {
613 t.Run(item.name, func(t *testing.T) {
614 out := dec{}
615 err := convertAssign(&out, item.in)
616 if item.err {
617 if err == nil {
618 t.Fatalf("unexpected nil error")
619 }
620 return
621 }
622 if err != nil {
623 t.Fatalf("unexpected error: %v", err)
624 }
625 if !reflect.DeepEqual(out, item.out) {
626 t.Fatalf("got %#v want %#v", out, item.out)
627 }
628 })
629 }
630 }
631
632 func TestConvertAssignNoContext(t *testing.T) {
633 const want = 42
634 var got int64
635 if err := ConvertAssign(driver.ScanContext{}, &got, want); err != nil {
636 t.Fatalf("ConvertAssign: %v", err)
637 }
638 if got != int64(want) {
639 t.Errorf("after ConvertAssign: got %v, want %v", got, want)
640 }
641 }
642
View as plain text