blob: 98af9fb64c58ba39dcadfe9ae2b1e7e6eeb37ea1 [file] [log] [blame]
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -07001// 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
5package sql
6
7import (
Brad Fitzpatrick7fc4c072012-01-19 16:04:26 -08008 "database/sql/driver"
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -07009 "fmt"
10 "reflect"
Brad Fitzpatrick5f341df2014-01-10 12:30:23 -080011 "runtime"
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070012 "testing"
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -080013 "time"
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070014)
15
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -080016var someTime = time.Unix(123, 0)
Andrew Pritchardcc39bb92012-02-08 17:14:15 +110017var answer int64 = 42
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -080018
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070019type conversionTest struct {
20 s, d interface{} // source and destination
21
22 // following are used if they're non-zero
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -080023 wantint int64
24 wantuint uint64
25 wantstr string
Julien Schmidt81d26e32013-03-22 12:19:21 -070026 wantbytes []byte
27 wantraw RawBytes
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -080028 wantf32 float32
29 wantf64 float64
30 wanttime time.Time
31 wantbool bool // used if d is of type *bool
32 wanterr string
33 wantiface interface{}
Andrew Pritchardcc39bb92012-02-08 17:14:15 +110034 wantptr *int64 // if non-nil, *d's pointed value must be equal to *wantptr
35 wantnil bool // if true, *d must be *int64(nil)
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070036}
37
38// Target variables for scanning into.
39var (
40 scanstr string
Julien Schmidt81d26e32013-03-22 12:19:21 -070041 scanbytes []byte
42 scanraw RawBytes
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070043 scanint int
44 scanint8 int8
45 scanint16 int16
46 scanint32 int32
47 scanuint8 uint8
48 scanuint16 uint16
Brad Fitzpatrick8089e572011-11-02 11:46:04 -070049 scanbool bool
50 scanf32 float32
51 scanf64 float64
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -080052 scantime time.Time
Andrew Pritchardcc39bb92012-02-08 17:14:15 +110053 scanptr *int64
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -080054 scaniface interface{}
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070055)
56
57var conversionTests = []conversionTest{
58 // Exact conversions (destination pointer type matches source type)
59 {s: "foo", d: &scanstr, wantstr: "foo"},
60 {s: 123, d: &scanint, wantint: 123},
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -080061 {s: someTime, d: &scantime, wanttime: someTime},
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070062
63 // To strings
Julien Schmidt81d26e32013-03-22 12:19:21 -070064 {s: "string", d: &scanstr, wantstr: "string"},
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070065 {s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"},
66 {s: 123, d: &scanstr, wantstr: "123"},
67 {s: int8(123), d: &scanstr, wantstr: "123"},
68 {s: int64(123), d: &scanstr, wantstr: "123"},
69 {s: uint8(123), d: &scanstr, wantstr: "123"},
70 {s: uint16(123), d: &scanstr, wantstr: "123"},
71 {s: uint32(123), d: &scanstr, wantstr: "123"},
72 {s: uint64(123), d: &scanstr, wantstr: "123"},
73 {s: 1.5, d: &scanstr, wantstr: "1.5"},
74
Julien Schmidt81d26e32013-03-22 12:19:21 -070075 // To []byte
76 {s: nil, d: &scanbytes, wantbytes: nil},
77 {s: "string", d: &scanbytes, wantbytes: []byte("string")},
78 {s: []byte("byteslice"), d: &scanbytes, wantbytes: []byte("byteslice")},
79 {s: 123, d: &scanbytes, wantbytes: []byte("123")},
80 {s: int8(123), d: &scanbytes, wantbytes: []byte("123")},
81 {s: int64(123), d: &scanbytes, wantbytes: []byte("123")},
82 {s: uint8(123), d: &scanbytes, wantbytes: []byte("123")},
83 {s: uint16(123), d: &scanbytes, wantbytes: []byte("123")},
84 {s: uint32(123), d: &scanbytes, wantbytes: []byte("123")},
85 {s: uint64(123), d: &scanbytes, wantbytes: []byte("123")},
86 {s: 1.5, d: &scanbytes, wantbytes: []byte("1.5")},
87
88 // To RawBytes
89 {s: nil, d: &scanraw, wantraw: nil},
90 {s: []byte("byteslice"), d: &scanraw, wantraw: RawBytes("byteslice")},
91 {s: 123, d: &scanraw, wantraw: RawBytes("123")},
92 {s: int8(123), d: &scanraw, wantraw: RawBytes("123")},
93 {s: int64(123), d: &scanraw, wantraw: RawBytes("123")},
94 {s: uint8(123), d: &scanraw, wantraw: RawBytes("123")},
95 {s: uint16(123), d: &scanraw, wantraw: RawBytes("123")},
96 {s: uint32(123), d: &scanraw, wantraw: RawBytes("123")},
97 {s: uint64(123), d: &scanraw, wantraw: RawBytes("123")},
98 {s: 1.5, d: &scanraw, wantraw: RawBytes("1.5")},
99
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700100 // Strings to integers
101 {s: "255", d: &scanuint8, wantuint: 255},
Rob Pike02f67192011-12-13 10:42:05 -0800102 {s: "256", d: &scanuint8, wanterr: `converting string "256" to a uint8: strconv.ParseUint: parsing "256": value out of range`},
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700103 {s: "256", d: &scanuint16, wantuint: 256},
104 {s: "-1", d: &scanint, wantint: -1},
Rob Pike02f67192011-12-13 10:42:05 -0800105 {s: "foo", d: &scanint, wanterr: `converting string "foo" to a int: strconv.ParseInt: parsing "foo": invalid syntax`},
Brad Fitzpatrick8089e572011-11-02 11:46:04 -0700106
107 // True bools
108 {s: true, d: &scanbool, wantbool: true},
109 {s: "True", d: &scanbool, wantbool: true},
110 {s: "TRUE", d: &scanbool, wantbool: true},
111 {s: "1", d: &scanbool, wantbool: true},
112 {s: 1, d: &scanbool, wantbool: true},
113 {s: int64(1), d: &scanbool, wantbool: true},
114 {s: uint16(1), d: &scanbool, wantbool: true},
115
116 // False bools
117 {s: false, d: &scanbool, wantbool: false},
118 {s: "false", d: &scanbool, wantbool: false},
119 {s: "FALSE", d: &scanbool, wantbool: false},
120 {s: "0", d: &scanbool, wantbool: false},
121 {s: 0, d: &scanbool, wantbool: false},
122 {s: int64(0), d: &scanbool, wantbool: false},
123 {s: uint16(0), d: &scanbool, wantbool: false},
124
125 // Not bools
126 {s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`},
127 {s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`},
128
129 // Floats
130 {s: float64(1.5), d: &scanf64, wantf64: float64(1.5)},
131 {s: int64(1), d: &scanf64, wantf64: float64(1)},
132 {s: float64(1.5), d: &scanf32, wantf32: float32(1.5)},
133 {s: "1.5", d: &scanf32, wantf32: float32(1.5)},
134 {s: "1.5", d: &scanf64, wantf64: float64(1.5)},
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -0800135
Andrew Pritchardcc39bb92012-02-08 17:14:15 +1100136 // Pointers
137 {s: interface{}(nil), d: &scanptr, wantnil: true},
138 {s: int64(42), d: &scanptr, wantptr: &answer},
139
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -0800140 // To interface{}
141 {s: float64(1.5), d: &scaniface, wantiface: float64(1.5)},
142 {s: int64(1), d: &scaniface, wantiface: int64(1)},
143 {s: "str", d: &scaniface, wantiface: "str"},
144 {s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")},
145 {s: true, d: &scaniface, wantiface: true},
146 {s: nil, d: &scaniface},
Julien Schmidt5e74f502013-03-25 17:43:30 -0700147 {s: []byte(nil), d: &scaniface, wantiface: []byte(nil)},
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700148}
149
Andrew Pritchardcc39bb92012-02-08 17:14:15 +1100150func intPtrValue(intptr interface{}) interface{} {
151 return reflect.Indirect(reflect.Indirect(reflect.ValueOf(intptr))).Int()
152}
153
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700154func intValue(intptr interface{}) int64 {
155 return reflect.Indirect(reflect.ValueOf(intptr)).Int()
156}
157
158func uintValue(intptr interface{}) uint64 {
159 return reflect.Indirect(reflect.ValueOf(intptr)).Uint()
160}
161
Brad Fitzpatrick8089e572011-11-02 11:46:04 -0700162func float64Value(ptr interface{}) float64 {
163 return *(ptr.(*float64))
164}
165
166func float32Value(ptr interface{}) float32 {
167 return *(ptr.(*float32))
168}
169
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -0800170func timeValue(ptr interface{}) time.Time {
171 return *(ptr.(*time.Time))
172}
173
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700174func TestConversions(t *testing.T) {
175 for n, ct := range conversionTests {
176 err := convertAssign(ct.d, ct.s)
177 errstr := ""
178 if err != nil {
Russ Coxc2049d22011-11-01 22:04:37 -0400179 errstr = err.Error()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700180 }
181 errf := func(format string, args ...interface{}) {
182 base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d)
183 t.Errorf(base+format, args...)
184 }
185 if errstr != ct.wanterr {
186 errf("got error %q, want error %q", errstr, ct.wanterr)
187 }
188 if ct.wantstr != "" && ct.wantstr != scanstr {
189 errf("want string %q, got %q", ct.wantstr, scanstr)
190 }
191 if ct.wantint != 0 && ct.wantint != intValue(ct.d) {
192 errf("want int %d, got %d", ct.wantint, intValue(ct.d))
193 }
194 if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) {
195 errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d))
196 }
Brad Fitzpatrick8089e572011-11-02 11:46:04 -0700197 if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) {
198 errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d))
199 }
200 if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) {
201 errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d))
202 }
203 if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" {
204 errf("want bool %v, got %v", ct.wantbool, *bp)
205 }
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -0800206 if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) {
207 errf("want time %v, got %v", ct.wanttime, timeValue(ct.d))
208 }
Andrew Pritchardcc39bb92012-02-08 17:14:15 +1100209 if ct.wantnil && *ct.d.(**int64) != nil {
210 errf("want nil, got %v", intPtrValue(ct.d))
211 }
212 if ct.wantptr != nil {
213 if *ct.d.(**int64) == nil {
214 errf("want pointer to %v, got nil", *ct.wantptr)
215 } else if *ct.wantptr != intPtrValue(ct.d) {
216 errf("want pointer to %v, got %v", *ct.wantptr, intPtrValue(ct.d))
217 }
218 }
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -0800219 if ifptr, ok := ct.d.(*interface{}); ok {
220 if !reflect.DeepEqual(ct.wantiface, scaniface) {
221 errf("want interface %#v, got %#v", ct.wantiface, scaniface)
222 continue
223 }
224 if srcBytes, ok := ct.s.([]byte); ok {
225 dstBytes := (*ifptr).([]byte)
Julien Schmidt5e74f502013-03-25 17:43:30 -0700226 if len(srcBytes) > 0 && &dstBytes[0] == &srcBytes[0] {
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -0800227 errf("copy into interface{} didn't copy []byte data")
228 }
229 }
230 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700231 }
232}
233
Brad Fitzpatrickbc0139b2012-01-19 09:27:45 -0800234func TestNullString(t *testing.T) {
235 var ns NullString
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700236 convertAssign(&ns, []byte("foo"))
237 if !ns.Valid {
238 t.Errorf("expecting not null")
239 }
240 if ns.String != "foo" {
241 t.Errorf("expecting foo; got %q", ns.String)
242 }
243 convertAssign(&ns, nil)
244 if ns.Valid {
245 t.Errorf("expecting null on nil")
246 }
247 if ns.String != "" {
248 t.Errorf("expecting blank on nil; got %q", ns.String)
249 }
250}
Brad Fitzpatrickbc0139b2012-01-19 09:27:45 -0800251
252type valueConverterTest struct {
253 c driver.ValueConverter
254 in, out interface{}
255 err string
256}
257
258var valueConverterTests = []valueConverterTest{
259 {driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""},
260 {driver.DefaultParameterConverter, NullString{"", false}, nil, ""},
261}
262
263func TestValueConverters(t *testing.T) {
264 for i, tt := range valueConverterTests {
265 out, err := tt.c.ConvertValue(tt.in)
266 goterr := ""
267 if err != nil {
268 goterr = err.Error()
269 }
270 if goterr != tt.err {
Brad Fitzpatricke5babef2013-10-16 16:30:39 -0700271 t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q",
Brad Fitzpatrickbc0139b2012-01-19 09:27:45 -0800272 i, tt.c, tt.in, tt.in, goterr, tt.err)
273 }
274 if tt.err != "" {
275 continue
276 }
277 if !reflect.DeepEqual(out, tt.out) {
Brad Fitzpatricke5babef2013-10-16 16:30:39 -0700278 t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)",
Brad Fitzpatrickbc0139b2012-01-19 09:27:45 -0800279 i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
280 }
281 }
282}
Brad Fitzpatrick258ed3f2014-01-10 12:19:36 -0800283
284// Tests that assigning to RawBytes doesn't allocate (and also works).
285func TestRawBytesAllocs(t *testing.T) {
Russ Cox1806a572014-08-18 21:13:11 -0400286 var tests = []struct {
287 name string
288 in interface{}
289 want string
290 }{
291 {"uint64", uint64(12345678), "12345678"},
292 {"uint32", uint32(1234), "1234"},
293 {"uint16", uint16(12), "12"},
294 {"uint8", uint8(1), "1"},
295 {"uint", uint(123), "123"},
296 {"int", int(123), "123"},
297 {"int8", int8(1), "1"},
298 {"int16", int16(12), "12"},
299 {"int32", int32(1234), "1234"},
300 {"int64", int64(12345678), "12345678"},
301 {"float32", float32(1.5), "1.5"},
302 {"float64", float64(64), "64"},
303 {"bool", false, "false"},
304 }
305
Brad Fitzpatrick258ed3f2014-01-10 12:19:36 -0800306 buf := make(RawBytes, 10)
307 test := func(name string, in interface{}, want string) {
308 if err := convertAssign(&buf, in); err != nil {
309 t.Fatalf("%s: convertAssign = %v", name, err)
310 }
311 match := len(buf) == len(want)
312 if match {
313 for i, b := range buf {
314 if want[i] != b {
315 match = false
316 break
317 }
318 }
319 }
320 if !match {
321 t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want))
322 }
323 }
Russ Cox1806a572014-08-18 21:13:11 -0400324
Brad Fitzpatrick258ed3f2014-01-10 12:19:36 -0800325 n := testing.AllocsPerRun(100, func() {
Russ Cox1806a572014-08-18 21:13:11 -0400326 for _, tt := range tests {
327 test(tt.name, tt.in, tt.want)
328 }
Brad Fitzpatrick258ed3f2014-01-10 12:19:36 -0800329 })
Brad Fitzpatrick5f341df2014-01-10 12:30:23 -0800330
331 // The numbers below are only valid for 64-bit interface word sizes,
332 // and gc. With 32-bit words there are more convT2E allocs, and
333 // with gccgo, only pointers currently go in interface data.
334 // So only care on amd64 gc for now.
335 measureAllocs := runtime.GOARCH == "amd64" && runtime.Compiler == "gc"
336
337 if n > 0.5 && measureAllocs {
Brad Fitzpatrick258ed3f2014-01-10 12:19:36 -0800338 t.Fatalf("allocs = %v; want 0", n)
339 }
340
341 // This one involves a convT2E allocation, string -> interface{}
342 n = testing.AllocsPerRun(100, func() {
343 test("string", "foo", "foo")
344 })
Brad Fitzpatrick5f341df2014-01-10 12:30:23 -0800345 if n > 1.5 && measureAllocs {
Brad Fitzpatrick258ed3f2014-01-10 12:19:36 -0800346 t.Fatalf("allocs = %v; want max 1", n)
347 }
348}