blob: aa0b6f116a00c84d6670c2fb9d8de8be22dd3a37 [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"
11 "testing"
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -080012 "time"
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070013)
14
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -080015var someTime = time.Unix(123, 0)
Andrew Pritchardcc39bb92012-02-08 17:14:15 +110016var answer int64 = 42
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -080017
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070018type conversionTest struct {
19 s, d interface{} // source and destination
20
21 // following are used if they're non-zero
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -080022 wantint int64
23 wantuint uint64
24 wantstr string
Julien Schmidt81d26e32013-03-22 12:19:21 -070025 wantbytes []byte
26 wantraw RawBytes
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -080027 wantf32 float32
28 wantf64 float64
29 wanttime time.Time
30 wantbool bool // used if d is of type *bool
31 wanterr string
32 wantiface interface{}
Andrew Pritchardcc39bb92012-02-08 17:14:15 +110033 wantptr *int64 // if non-nil, *d's pointed value must be equal to *wantptr
34 wantnil bool // if true, *d must be *int64(nil)
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070035}
36
37// Target variables for scanning into.
38var (
39 scanstr string
Julien Schmidt81d26e32013-03-22 12:19:21 -070040 scanbytes []byte
41 scanraw RawBytes
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070042 scanint int
43 scanint8 int8
44 scanint16 int16
45 scanint32 int32
46 scanuint8 uint8
47 scanuint16 uint16
Brad Fitzpatrick8089e572011-11-02 11:46:04 -070048 scanbool bool
49 scanf32 float32
50 scanf64 float64
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -080051 scantime time.Time
Andrew Pritchardcc39bb92012-02-08 17:14:15 +110052 scanptr *int64
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -080053 scaniface interface{}
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070054)
55
56var conversionTests = []conversionTest{
57 // Exact conversions (destination pointer type matches source type)
58 {s: "foo", d: &scanstr, wantstr: "foo"},
59 {s: 123, d: &scanint, wantint: 123},
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -080060 {s: someTime, d: &scantime, wanttime: someTime},
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070061
62 // To strings
Julien Schmidt81d26e32013-03-22 12:19:21 -070063 {s: "string", d: &scanstr, wantstr: "string"},
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070064 {s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"},
65 {s: 123, d: &scanstr, wantstr: "123"},
66 {s: int8(123), d: &scanstr, wantstr: "123"},
67 {s: int64(123), d: &scanstr, wantstr: "123"},
68 {s: uint8(123), d: &scanstr, wantstr: "123"},
69 {s: uint16(123), d: &scanstr, wantstr: "123"},
70 {s: uint32(123), d: &scanstr, wantstr: "123"},
71 {s: uint64(123), d: &scanstr, wantstr: "123"},
72 {s: 1.5, d: &scanstr, wantstr: "1.5"},
73
Julien Schmidt81d26e32013-03-22 12:19:21 -070074 // To []byte
75 {s: nil, d: &scanbytes, wantbytes: nil},
76 {s: "string", d: &scanbytes, wantbytes: []byte("string")},
77 {s: []byte("byteslice"), d: &scanbytes, wantbytes: []byte("byteslice")},
78 {s: 123, d: &scanbytes, wantbytes: []byte("123")},
79 {s: int8(123), d: &scanbytes, wantbytes: []byte("123")},
80 {s: int64(123), d: &scanbytes, wantbytes: []byte("123")},
81 {s: uint8(123), d: &scanbytes, wantbytes: []byte("123")},
82 {s: uint16(123), d: &scanbytes, wantbytes: []byte("123")},
83 {s: uint32(123), d: &scanbytes, wantbytes: []byte("123")},
84 {s: uint64(123), d: &scanbytes, wantbytes: []byte("123")},
85 {s: 1.5, d: &scanbytes, wantbytes: []byte("1.5")},
86
87 // To RawBytes
88 {s: nil, d: &scanraw, wantraw: nil},
89 {s: []byte("byteslice"), d: &scanraw, wantraw: RawBytes("byteslice")},
90 {s: 123, d: &scanraw, wantraw: RawBytes("123")},
91 {s: int8(123), d: &scanraw, wantraw: RawBytes("123")},
92 {s: int64(123), d: &scanraw, wantraw: RawBytes("123")},
93 {s: uint8(123), d: &scanraw, wantraw: RawBytes("123")},
94 {s: uint16(123), d: &scanraw, wantraw: RawBytes("123")},
95 {s: uint32(123), d: &scanraw, wantraw: RawBytes("123")},
96 {s: uint64(123), d: &scanraw, wantraw: RawBytes("123")},
97 {s: 1.5, d: &scanraw, wantraw: RawBytes("1.5")},
98
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070099 // Strings to integers
100 {s: "255", d: &scanuint8, wantuint: 255},
Rob Pike02f67192011-12-13 10:42:05 -0800101 {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 -0700102 {s: "256", d: &scanuint16, wantuint: 256},
103 {s: "-1", d: &scanint, wantint: -1},
Rob Pike02f67192011-12-13 10:42:05 -0800104 {s: "foo", d: &scanint, wanterr: `converting string "foo" to a int: strconv.ParseInt: parsing "foo": invalid syntax`},
Brad Fitzpatrick8089e572011-11-02 11:46:04 -0700105
106 // True bools
107 {s: true, d: &scanbool, wantbool: true},
108 {s: "True", d: &scanbool, wantbool: true},
109 {s: "TRUE", d: &scanbool, wantbool: true},
110 {s: "1", d: &scanbool, wantbool: true},
111 {s: 1, d: &scanbool, wantbool: true},
112 {s: int64(1), d: &scanbool, wantbool: true},
113 {s: uint16(1), d: &scanbool, wantbool: true},
114
115 // False bools
116 {s: false, d: &scanbool, wantbool: false},
117 {s: "false", d: &scanbool, wantbool: false},
118 {s: "FALSE", d: &scanbool, wantbool: false},
119 {s: "0", d: &scanbool, wantbool: false},
120 {s: 0, d: &scanbool, wantbool: false},
121 {s: int64(0), d: &scanbool, wantbool: false},
122 {s: uint16(0), d: &scanbool, wantbool: false},
123
124 // Not bools
125 {s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`},
126 {s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`},
127
128 // Floats
129 {s: float64(1.5), d: &scanf64, wantf64: float64(1.5)},
130 {s: int64(1), d: &scanf64, wantf64: float64(1)},
131 {s: float64(1.5), d: &scanf32, wantf32: float32(1.5)},
132 {s: "1.5", d: &scanf32, wantf32: float32(1.5)},
133 {s: "1.5", d: &scanf64, wantf64: float64(1.5)},
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -0800134
Andrew Pritchardcc39bb92012-02-08 17:14:15 +1100135 // Pointers
136 {s: interface{}(nil), d: &scanptr, wantnil: true},
137 {s: int64(42), d: &scanptr, wantptr: &answer},
138
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -0800139 // To interface{}
140 {s: float64(1.5), d: &scaniface, wantiface: float64(1.5)},
141 {s: int64(1), d: &scaniface, wantiface: int64(1)},
142 {s: "str", d: &scaniface, wantiface: "str"},
143 {s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")},
144 {s: true, d: &scaniface, wantiface: true},
145 {s: nil, d: &scaniface},
Julien Schmidt5e74f502013-03-25 17:43:30 -0700146 {s: []byte(nil), d: &scaniface, wantiface: []byte(nil)},
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700147}
148
Andrew Pritchardcc39bb92012-02-08 17:14:15 +1100149func intPtrValue(intptr interface{}) interface{} {
150 return reflect.Indirect(reflect.Indirect(reflect.ValueOf(intptr))).Int()
151}
152
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700153func intValue(intptr interface{}) int64 {
154 return reflect.Indirect(reflect.ValueOf(intptr)).Int()
155}
156
157func uintValue(intptr interface{}) uint64 {
158 return reflect.Indirect(reflect.ValueOf(intptr)).Uint()
159}
160
Brad Fitzpatrick8089e572011-11-02 11:46:04 -0700161func float64Value(ptr interface{}) float64 {
162 return *(ptr.(*float64))
163}
164
165func float32Value(ptr interface{}) float32 {
166 return *(ptr.(*float32))
167}
168
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -0800169func timeValue(ptr interface{}) time.Time {
170 return *(ptr.(*time.Time))
171}
172
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700173func TestConversions(t *testing.T) {
174 for n, ct := range conversionTests {
175 err := convertAssign(ct.d, ct.s)
176 errstr := ""
177 if err != nil {
Russ Coxc2049d22011-11-01 22:04:37 -0400178 errstr = err.Error()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700179 }
180 errf := func(format string, args ...interface{}) {
181 base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d)
182 t.Errorf(base+format, args...)
183 }
184 if errstr != ct.wanterr {
185 errf("got error %q, want error %q", errstr, ct.wanterr)
186 }
187 if ct.wantstr != "" && ct.wantstr != scanstr {
188 errf("want string %q, got %q", ct.wantstr, scanstr)
189 }
190 if ct.wantint != 0 && ct.wantint != intValue(ct.d) {
191 errf("want int %d, got %d", ct.wantint, intValue(ct.d))
192 }
193 if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) {
194 errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d))
195 }
Brad Fitzpatrick8089e572011-11-02 11:46:04 -0700196 if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) {
197 errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d))
198 }
199 if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) {
200 errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d))
201 }
202 if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" {
203 errf("want bool %v, got %v", ct.wantbool, *bp)
204 }
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -0800205 if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) {
206 errf("want time %v, got %v", ct.wanttime, timeValue(ct.d))
207 }
Andrew Pritchardcc39bb92012-02-08 17:14:15 +1100208 if ct.wantnil && *ct.d.(**int64) != nil {
209 errf("want nil, got %v", intPtrValue(ct.d))
210 }
211 if ct.wantptr != nil {
212 if *ct.d.(**int64) == nil {
213 errf("want pointer to %v, got nil", *ct.wantptr)
214 } else if *ct.wantptr != intPtrValue(ct.d) {
215 errf("want pointer to %v, got %v", *ct.wantptr, intPtrValue(ct.d))
216 }
217 }
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -0800218 if ifptr, ok := ct.d.(*interface{}); ok {
219 if !reflect.DeepEqual(ct.wantiface, scaniface) {
220 errf("want interface %#v, got %#v", ct.wantiface, scaniface)
221 continue
222 }
223 if srcBytes, ok := ct.s.([]byte); ok {
224 dstBytes := (*ifptr).([]byte)
Julien Schmidt5e74f502013-03-25 17:43:30 -0700225 if len(srcBytes) > 0 && &dstBytes[0] == &srcBytes[0] {
Brad Fitzpatrick9c060b82012-02-06 10:06:22 -0800226 errf("copy into interface{} didn't copy []byte data")
227 }
228 }
229 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700230 }
231}
232
Brad Fitzpatrickbc0139b2012-01-19 09:27:45 -0800233func TestNullString(t *testing.T) {
234 var ns NullString
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700235 convertAssign(&ns, []byte("foo"))
236 if !ns.Valid {
237 t.Errorf("expecting not null")
238 }
239 if ns.String != "foo" {
240 t.Errorf("expecting foo; got %q", ns.String)
241 }
242 convertAssign(&ns, nil)
243 if ns.Valid {
244 t.Errorf("expecting null on nil")
245 }
246 if ns.String != "" {
247 t.Errorf("expecting blank on nil; got %q", ns.String)
248 }
249}
Brad Fitzpatrickbc0139b2012-01-19 09:27:45 -0800250
251type valueConverterTest struct {
252 c driver.ValueConverter
253 in, out interface{}
254 err string
255}
256
257var valueConverterTests = []valueConverterTest{
258 {driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""},
259 {driver.DefaultParameterConverter, NullString{"", false}, nil, ""},
260}
261
262func TestValueConverters(t *testing.T) {
263 for i, tt := range valueConverterTests {
264 out, err := tt.c.ConvertValue(tt.in)
265 goterr := ""
266 if err != nil {
267 goterr = err.Error()
268 }
269 if goterr != tt.err {
Brad Fitzpatricke5babef2013-10-16 16:30:39 -0700270 t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q",
Brad Fitzpatrickbc0139b2012-01-19 09:27:45 -0800271 i, tt.c, tt.in, tt.in, goterr, tt.err)
272 }
273 if tt.err != "" {
274 continue
275 }
276 if !reflect.DeepEqual(out, tt.out) {
Brad Fitzpatricke5babef2013-10-16 16:30:39 -0700277 t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)",
Brad Fitzpatrickbc0139b2012-01-19 09:27:45 -0800278 i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
279 }
280 }
281}
Brad Fitzpatrick258ed3f2014-01-10 12:19:36 -0800282
283// Tests that assigning to RawBytes doesn't allocate (and also works).
284func TestRawBytesAllocs(t *testing.T) {
285 buf := make(RawBytes, 10)
286 test := func(name string, in interface{}, want string) {
287 if err := convertAssign(&buf, in); err != nil {
288 t.Fatalf("%s: convertAssign = %v", name, err)
289 }
290 match := len(buf) == len(want)
291 if match {
292 for i, b := range buf {
293 if want[i] != b {
294 match = false
295 break
296 }
297 }
298 }
299 if !match {
300 t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want))
301 }
302 }
303 n := testing.AllocsPerRun(100, func() {
304 test("uint64", uint64(12345678), "12345678")
305 test("uint32", uint32(1234), "1234")
306 test("uint16", uint16(12), "12")
307 test("uint8", uint8(1), "1")
308 test("uint", uint(123), "123")
309 test("int", int(123), "123")
310 test("int8", int8(1), "1")
311 test("int16", int16(12), "12")
312 test("int32", int32(1234), "1234")
313 test("int64", int64(12345678), "12345678")
314 test("float32", float32(1.5), "1.5")
315 test("float64", float64(64), "64")
316 test("bool", false, "false")
317 })
318 if n > 0.5 {
319 t.Fatalf("allocs = %v; want 0", n)
320 }
321
322 // This one involves a convT2E allocation, string -> interface{}
323 n = testing.AllocsPerRun(100, func() {
324 test("string", "foo", "foo")
325 })
326 if n > 1.5 {
327 t.Fatalf("allocs = %v; want max 1", n)
328 }
329}