1 |
747 |
jeremybenn |
// 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 |
|
|
"testing"
|
12 |
|
|
"time"
|
13 |
|
|
)
|
14 |
|
|
|
15 |
|
|
var someTime = time.Unix(123, 0)
|
16 |
|
|
|
17 |
|
|
type conversionTest struct {
|
18 |
|
|
s, d interface{} // source and destination
|
19 |
|
|
|
20 |
|
|
// following are used if they're non-zero
|
21 |
|
|
wantint int64
|
22 |
|
|
wantuint uint64
|
23 |
|
|
wantstr string
|
24 |
|
|
wantf32 float32
|
25 |
|
|
wantf64 float64
|
26 |
|
|
wanttime time.Time
|
27 |
|
|
wantbool bool // used if d is of type *bool
|
28 |
|
|
wanterr string
|
29 |
|
|
wantiface interface{}
|
30 |
|
|
}
|
31 |
|
|
|
32 |
|
|
// Target variables for scanning into.
|
33 |
|
|
var (
|
34 |
|
|
scanstr string
|
35 |
|
|
scanint int
|
36 |
|
|
scanint8 int8
|
37 |
|
|
scanint16 int16
|
38 |
|
|
scanint32 int32
|
39 |
|
|
scanuint8 uint8
|
40 |
|
|
scanuint16 uint16
|
41 |
|
|
scanbool bool
|
42 |
|
|
scanf32 float32
|
43 |
|
|
scanf64 float64
|
44 |
|
|
scantime time.Time
|
45 |
|
|
scaniface interface{}
|
46 |
|
|
)
|
47 |
|
|
|
48 |
|
|
var conversionTests = []conversionTest{
|
49 |
|
|
// Exact conversions (destination pointer type matches source type)
|
50 |
|
|
{s: "foo", d: &scanstr, wantstr: "foo"},
|
51 |
|
|
{s: 123, d: &scanint, wantint: 123},
|
52 |
|
|
{s: someTime, d: &scantime, wanttime: someTime},
|
53 |
|
|
|
54 |
|
|
// To strings
|
55 |
|
|
{s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"},
|
56 |
|
|
{s: 123, d: &scanstr, wantstr: "123"},
|
57 |
|
|
{s: int8(123), d: &scanstr, wantstr: "123"},
|
58 |
|
|
{s: int64(123), d: &scanstr, wantstr: "123"},
|
59 |
|
|
{s: uint8(123), d: &scanstr, wantstr: "123"},
|
60 |
|
|
{s: uint16(123), d: &scanstr, wantstr: "123"},
|
61 |
|
|
{s: uint32(123), d: &scanstr, wantstr: "123"},
|
62 |
|
|
{s: uint64(123), d: &scanstr, wantstr: "123"},
|
63 |
|
|
{s: 1.5, d: &scanstr, wantstr: "1.5"},
|
64 |
|
|
|
65 |
|
|
// Strings to integers
|
66 |
|
|
{s: "255", d: &scanuint8, wantuint: 255},
|
67 |
|
|
{s: "256", d: &scanuint8, wanterr: `converting string "256" to a uint8: strconv.ParseUint: parsing "256": value out of range`},
|
68 |
|
|
{s: "256", d: &scanuint16, wantuint: 256},
|
69 |
|
|
{s: "-1", d: &scanint, wantint: -1},
|
70 |
|
|
{s: "foo", d: &scanint, wanterr: `converting string "foo" to a int: strconv.ParseInt: parsing "foo": invalid syntax`},
|
71 |
|
|
|
72 |
|
|
// True bools
|
73 |
|
|
{s: true, d: &scanbool, wantbool: true},
|
74 |
|
|
{s: "True", d: &scanbool, wantbool: true},
|
75 |
|
|
{s: "TRUE", d: &scanbool, wantbool: true},
|
76 |
|
|
{s: "1", d: &scanbool, wantbool: true},
|
77 |
|
|
{s: 1, d: &scanbool, wantbool: true},
|
78 |
|
|
{s: int64(1), d: &scanbool, wantbool: true},
|
79 |
|
|
{s: uint16(1), d: &scanbool, wantbool: true},
|
80 |
|
|
|
81 |
|
|
// False bools
|
82 |
|
|
{s: false, d: &scanbool, wantbool: false},
|
83 |
|
|
{s: "false", d: &scanbool, wantbool: false},
|
84 |
|
|
{s: "FALSE", d: &scanbool, wantbool: false},
|
85 |
|
|
{s: "0", d: &scanbool, wantbool: false},
|
86 |
|
|
{s: 0, d: &scanbool, wantbool: false},
|
87 |
|
|
{s: int64(0), d: &scanbool, wantbool: false},
|
88 |
|
|
{s: uint16(0), d: &scanbool, wantbool: false},
|
89 |
|
|
|
90 |
|
|
// Not bools
|
91 |
|
|
{s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`},
|
92 |
|
|
{s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`},
|
93 |
|
|
|
94 |
|
|
// Floats
|
95 |
|
|
{s: float64(1.5), d: &scanf64, wantf64: float64(1.5)},
|
96 |
|
|
{s: int64(1), d: &scanf64, wantf64: float64(1)},
|
97 |
|
|
{s: float64(1.5), d: &scanf32, wantf32: float32(1.5)},
|
98 |
|
|
{s: "1.5", d: &scanf32, wantf32: float32(1.5)},
|
99 |
|
|
{s: "1.5", d: &scanf64, wantf64: float64(1.5)},
|
100 |
|
|
|
101 |
|
|
// To interface{}
|
102 |
|
|
{s: float64(1.5), d: &scaniface, wantiface: float64(1.5)},
|
103 |
|
|
{s: int64(1), d: &scaniface, wantiface: int64(1)},
|
104 |
|
|
{s: "str", d: &scaniface, wantiface: "str"},
|
105 |
|
|
{s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")},
|
106 |
|
|
{s: true, d: &scaniface, wantiface: true},
|
107 |
|
|
{s: nil, d: &scaniface},
|
108 |
|
|
}
|
109 |
|
|
|
110 |
|
|
func intValue(intptr interface{}) int64 {
|
111 |
|
|
return reflect.Indirect(reflect.ValueOf(intptr)).Int()
|
112 |
|
|
}
|
113 |
|
|
|
114 |
|
|
func uintValue(intptr interface{}) uint64 {
|
115 |
|
|
return reflect.Indirect(reflect.ValueOf(intptr)).Uint()
|
116 |
|
|
}
|
117 |
|
|
|
118 |
|
|
func float64Value(ptr interface{}) float64 {
|
119 |
|
|
return *(ptr.(*float64))
|
120 |
|
|
}
|
121 |
|
|
|
122 |
|
|
func float32Value(ptr interface{}) float32 {
|
123 |
|
|
return *(ptr.(*float32))
|
124 |
|
|
}
|
125 |
|
|
|
126 |
|
|
func timeValue(ptr interface{}) time.Time {
|
127 |
|
|
return *(ptr.(*time.Time))
|
128 |
|
|
}
|
129 |
|
|
|
130 |
|
|
func TestConversions(t *testing.T) {
|
131 |
|
|
for n, ct := range conversionTests {
|
132 |
|
|
err := convertAssign(ct.d, ct.s)
|
133 |
|
|
errstr := ""
|
134 |
|
|
if err != nil {
|
135 |
|
|
errstr = err.Error()
|
136 |
|
|
}
|
137 |
|
|
errf := func(format string, args ...interface{}) {
|
138 |
|
|
base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d)
|
139 |
|
|
t.Errorf(base+format, args...)
|
140 |
|
|
}
|
141 |
|
|
if errstr != ct.wanterr {
|
142 |
|
|
errf("got error %q, want error %q", errstr, ct.wanterr)
|
143 |
|
|
}
|
144 |
|
|
if ct.wantstr != "" && ct.wantstr != scanstr {
|
145 |
|
|
errf("want string %q, got %q", ct.wantstr, scanstr)
|
146 |
|
|
}
|
147 |
|
|
if ct.wantint != 0 && ct.wantint != intValue(ct.d) {
|
148 |
|
|
errf("want int %d, got %d", ct.wantint, intValue(ct.d))
|
149 |
|
|
}
|
150 |
|
|
if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) {
|
151 |
|
|
errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d))
|
152 |
|
|
}
|
153 |
|
|
if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) {
|
154 |
|
|
errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d))
|
155 |
|
|
}
|
156 |
|
|
if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) {
|
157 |
|
|
errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d))
|
158 |
|
|
}
|
159 |
|
|
if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" {
|
160 |
|
|
errf("want bool %v, got %v", ct.wantbool, *bp)
|
161 |
|
|
}
|
162 |
|
|
if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) {
|
163 |
|
|
errf("want time %v, got %v", ct.wanttime, timeValue(ct.d))
|
164 |
|
|
}
|
165 |
|
|
if ifptr, ok := ct.d.(*interface{}); ok {
|
166 |
|
|
if !reflect.DeepEqual(ct.wantiface, scaniface) {
|
167 |
|
|
errf("want interface %#v, got %#v", ct.wantiface, scaniface)
|
168 |
|
|
continue
|
169 |
|
|
}
|
170 |
|
|
if srcBytes, ok := ct.s.([]byte); ok {
|
171 |
|
|
dstBytes := (*ifptr).([]byte)
|
172 |
|
|
if &dstBytes[0] == &srcBytes[0] {
|
173 |
|
|
errf("copy into interface{} didn't copy []byte data")
|
174 |
|
|
}
|
175 |
|
|
}
|
176 |
|
|
}
|
177 |
|
|
}
|
178 |
|
|
}
|
179 |
|
|
|
180 |
|
|
func TestNullString(t *testing.T) {
|
181 |
|
|
var ns NullString
|
182 |
|
|
convertAssign(&ns, []byte("foo"))
|
183 |
|
|
if !ns.Valid {
|
184 |
|
|
t.Errorf("expecting not null")
|
185 |
|
|
}
|
186 |
|
|
if ns.String != "foo" {
|
187 |
|
|
t.Errorf("expecting foo; got %q", ns.String)
|
188 |
|
|
}
|
189 |
|
|
convertAssign(&ns, nil)
|
190 |
|
|
if ns.Valid {
|
191 |
|
|
t.Errorf("expecting null on nil")
|
192 |
|
|
}
|
193 |
|
|
if ns.String != "" {
|
194 |
|
|
t.Errorf("expecting blank on nil; got %q", ns.String)
|
195 |
|
|
}
|
196 |
|
|
}
|
197 |
|
|
|
198 |
|
|
type valueConverterTest struct {
|
199 |
|
|
c driver.ValueConverter
|
200 |
|
|
in, out interface{}
|
201 |
|
|
err string
|
202 |
|
|
}
|
203 |
|
|
|
204 |
|
|
var valueConverterTests = []valueConverterTest{
|
205 |
|
|
{driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""},
|
206 |
|
|
{driver.DefaultParameterConverter, NullString{"", false}, nil, ""},
|
207 |
|
|
}
|
208 |
|
|
|
209 |
|
|
func TestValueConverters(t *testing.T) {
|
210 |
|
|
for i, tt := range valueConverterTests {
|
211 |
|
|
out, err := tt.c.ConvertValue(tt.in)
|
212 |
|
|
goterr := ""
|
213 |
|
|
if err != nil {
|
214 |
|
|
goterr = err.Error()
|
215 |
|
|
}
|
216 |
|
|
if goterr != tt.err {
|
217 |
|
|
t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q",
|
218 |
|
|
i, tt.c, tt.in, tt.in, goterr, tt.err)
|
219 |
|
|
}
|
220 |
|
|
if tt.err != "" {
|
221 |
|
|
continue
|
222 |
|
|
}
|
223 |
|
|
if !reflect.DeepEqual(out, tt.out) {
|
224 |
|
|
t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)",
|
225 |
|
|
i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
|
226 |
|
|
}
|
227 |
|
|
}
|
228 |
|
|
}
|