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 "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
29
30
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
40 wanterr string
41 wantiface any
42 wantptr *int64
43 wantnil bool
44 wantusrdef userDefined
45 wantusrstr userDefinedString
46 }
47
48
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
66 return []conversionTest{
67
68 {s: "foo", d: &scanstr, wantstr: "foo"},
69 {s: 123, d: &scanint, wantint: 123},
70 {s: someTime, d: &scantime, wanttime: someTime},
71
72
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
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
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
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
118
119 {s: time.Unix(2, 5).UTC(), d: &scanraw, wantraw: RawBytes("1970-01-01T00:00:02.000000005Z")},
120
121
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
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
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
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
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
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
164 {s: any(nil), d: &scanptr, wantnil: true},
165 {s: int64(42), d: &scanptr, wantptr: &answer},
166
167
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
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
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
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 var buf RawBytes
358 rows := &Rows{}
359 test := func(name string, in any, want string) {
360 if err := convertAssignRows(&buf, in, rows); err != nil {
361 t.Fatalf("%s: convertAssign = %v", name, err)
362 }
363 match := len(buf) == len(want)
364 if match {
365 for i, b := range buf {
366 if want[i] != b {
367 match = false
368 break
369 }
370 }
371 }
372 if !match {
373 t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want))
374 }
375 }
376
377 n := testing.AllocsPerRun(100, func() {
378 for _, tt := range tests {
379 rows.raw = rows.raw[:0]
380 test(tt.name, tt.in, tt.want)
381 }
382 })
383
384
385
386
387
388 measureAllocs := false
389 switch runtime.GOARCH {
390 case "amd64", "arm64":
391 measureAllocs = runtime.Compiler == "gc"
392 }
393
394 if n > 0.5 && measureAllocs {
395 t.Fatalf("allocs = %v; want 0", n)
396 }
397
398
399 n = testing.AllocsPerRun(100, func() {
400 test("string", "foo", "foo")
401 })
402 if n > 1.5 && measureAllocs {
403 t.Fatalf("allocs = %v; want max 1", n)
404 }
405 }
406
407
408 func TestUserDefinedBytes(t *testing.T) {
409 type userDefinedBytes []byte
410 var u userDefinedBytes
411 v := []byte("foo")
412
413 convertAssign(&u, v)
414 if &u[0] == &v[0] {
415 t.Fatal("userDefinedBytes got potentially dirty driver memory")
416 }
417 }
418
419 type Valuer_V string
420
421 func (v Valuer_V) Value() (driver.Value, error) {
422 return strings.ToUpper(string(v)), nil
423 }
424
425 type Valuer_P string
426
427 func (p *Valuer_P) Value() (driver.Value, error) {
428 if p == nil {
429 return "nil-to-str", nil
430 }
431 return strings.ToUpper(string(*p)), nil
432 }
433
434 func TestDriverArgs(t *testing.T) {
435 var nilValuerVPtr *Valuer_V
436 var nilValuerPPtr *Valuer_P
437 var nilStrPtr *string
438 tests := []struct {
439 args []any
440 want []driver.NamedValue
441 }{
442 0: {
443 args: []any{Valuer_V("foo")},
444 want: []driver.NamedValue{
445 {
446 Ordinal: 1,
447 Value: "FOO",
448 },
449 },
450 },
451 1: {
452 args: []any{nilValuerVPtr},
453 want: []driver.NamedValue{
454 {
455 Ordinal: 1,
456 Value: nil,
457 },
458 },
459 },
460 2: {
461 args: []any{nilValuerPPtr},
462 want: []driver.NamedValue{
463 {
464 Ordinal: 1,
465 Value: "nil-to-str",
466 },
467 },
468 },
469 3: {
470 args: []any{"plain-str"},
471 want: []driver.NamedValue{
472 {
473 Ordinal: 1,
474 Value: "plain-str",
475 },
476 },
477 },
478 4: {
479 args: []any{nilStrPtr},
480 want: []driver.NamedValue{
481 {
482 Ordinal: 1,
483 Value: nil,
484 },
485 },
486 },
487 }
488 for i, tt := range tests {
489 ds := &driverStmt{Locker: &sync.Mutex{}, si: stubDriverStmt{nil}}
490 got, err := driverArgsConnLocked(nil, ds, tt.args)
491 if err != nil {
492 t.Errorf("test[%d]: %v", i, err)
493 continue
494 }
495 if !reflect.DeepEqual(got, tt.want) {
496 t.Errorf("test[%d]: got %v, want %v", i, got, tt.want)
497 }
498 }
499 }
500
501 type dec struct {
502 form byte
503 neg bool
504 coefficient [16]byte
505 exponent int32
506 }
507
508 func (d dec) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
509 coef := make([]byte, 16)
510 copy(coef, d.coefficient[:])
511 return d.form, d.neg, coef, d.exponent
512 }
513
514 func (d *dec) Compose(form byte, negative bool, coefficient []byte, exponent int32) error {
515 switch form {
516 default:
517 return fmt.Errorf("unknown form %d", form)
518 case 1, 2:
519 d.form = form
520 d.neg = negative
521 return nil
522 case 0:
523 }
524 d.form = form
525 d.neg = negative
526 d.exponent = exponent
527
528
529
530 if len(coefficient) > 16 {
531 return fmt.Errorf("coefficient too large")
532 }
533 copy(d.coefficient[:], coefficient)
534
535 return nil
536 }
537
538 type decFinite struct {
539 neg bool
540 coefficient [16]byte
541 exponent int32
542 }
543
544 func (d decFinite) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
545 coef := make([]byte, 16)
546 copy(coef, d.coefficient[:])
547 return 0, d.neg, coef, d.exponent
548 }
549
550 func (d *decFinite) Compose(form byte, negative bool, coefficient []byte, exponent int32) error {
551 switch form {
552 default:
553 return fmt.Errorf("unknown form %d", form)
554 case 1, 2:
555 return fmt.Errorf("unsupported form %d", form)
556 case 0:
557 }
558 d.neg = negative
559 d.exponent = exponent
560
561
562
563 if len(coefficient) > 16 {
564 return fmt.Errorf("coefficient too large")
565 }
566 copy(d.coefficient[:], coefficient)
567
568 return nil
569 }
570
571 func TestDecimal(t *testing.T) {
572 list := []struct {
573 name string
574 in decimalDecompose
575 out dec
576 err bool
577 }{
578 {name: "same", in: dec{exponent: -6}, out: dec{exponent: -6}},
579
580
581 {name: "diff", in: decFinite{exponent: -6}, out: dec{exponent: -6}},
582
583 {name: "bad-form", in: dec{form: 200}, err: true},
584 }
585 for _, item := range list {
586 t.Run(item.name, func(t *testing.T) {
587 out := dec{}
588 err := convertAssign(&out, item.in)
589 if item.err {
590 if err == nil {
591 t.Fatalf("unexpected nil error")
592 }
593 return
594 }
595 if err != nil {
596 t.Fatalf("unexpected error: %v", err)
597 }
598 if !reflect.DeepEqual(out, item.out) {
599 t.Fatalf("got %#v want %#v", out, item.out)
600 }
601 })
602 }
603 }
604
View as plain text