-
Notifications
You must be signed in to change notification settings - Fork 6
/
sqlReceiver.go
347 lines (322 loc) · 7.93 KB
/
sqlReceiver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
package db
import (
"database/sql"
"fmt"
"github.com/goradd/goradd/pkg/datetime"
. "github.com/goradd/goradd/pkg/orm/query"
"log"
"strconv"
"time"
)
// SqlReceiver is an encapsulation of a way of receiving data from sql queries as interface{} pointers. This allows you
// to get data without knowing the type of data you are asking for ahead of time, and is easier for dealing with NULL fields.
// Some database drivers (MySql for one) return different results in fields depending on how you call the query (using
// a prepared statement can return different results than without one), or if the data does not quite fit (UInt64 in particular
// will return a string if the returned value is bigger than MaxInt64, but smaller than MaxUint64.)
//
// Pass the address of the R member to the sql.Scan method when using an object of this type,
// because there are some idiosyncracies with
// how Go treats return values that prevents returning an address of R from a function
type SqlReceiver struct {
R interface{}
}
func (r SqlReceiver) IntI() interface{} {
if r.R == nil {
return nil
}
switch r.R.(type) {
case int64:
return int(r.R.(int64))
case int:
return r.R
case string:
i, err := strconv.Atoi(r.R.(string))
if err != nil {
log.Panic(err)
}
return int(i)
case []byte:
i, err := strconv.Atoi(string(r.R.([]byte)[:]))
if err != nil {
log.Panic(err)
}
return int(i)
default:
log.Panicln("Unknown type returned from sql driver")
return nil
}
}
// Some drivers (like MySQL) return all integers as Int64. This converts a value to a GO uint. Its up to you to make sure
// you only use this on 32-bit uints or smaller
func (r SqlReceiver) UintI() interface{} {
if r.R == nil {
return nil
}
switch r.R.(type) {
case int64:
return uint(r.R.(int64))
case int:
return uint(r.R.(int))
case uint:
return r.R
case string:
i, err := strconv.ParseUint(r.R.(string), 10, 32)
if err != nil {
log.Panic(err)
}
return uint(i)
case []byte:
i, err := strconv.ParseUint(string(r.R.([]byte)[:]), 10, 32)
if err != nil {
log.Panic(err)
}
return uint(i)
default:
log.Panicln("Unknown type returned from sql driver")
return nil
}
}
// Returns the given value as an interface to an Int64
func (r SqlReceiver) Int64I() interface{} {
if r.R == nil {
return nil
}
switch r.R.(type) {
case int64:
return r.R
case int:
return int64(r.R.(int))
case string:
i, err := strconv.ParseInt(r.R.(string), 10, 64)
if err != nil {
log.Panic(err)
}
return i
case []byte:
i, err := strconv.ParseInt(string(r.R.([]byte)[:]), 10, 64)
if err != nil {
log.Panic(err)
}
return i
default:
log.Panicln("Unknown type returned from sql driver")
return nil
}
}
// Some drivers (like MySQL) return all integers as Int64. This converts to uint64. Its up to you to make sure
// you only use this on 64-bit uints or smaller.
func (r SqlReceiver) Uint64I() interface{} {
if r.R == nil {
return nil
}
switch r.R.(type) {
case int64:
return uint64(r.R.(int64))
case int:
return uint64(r.R.(int))
case string: // Mysql returns this if the detected value is greater than int64 size
i, err := strconv.ParseUint(r.R.(string), 10, 64)
if err != nil {
log.Panic(err)
}
return i
case []byte:
i, err := strconv.ParseUint(string(r.R.([]byte)[:]), 10, 64)
if err != nil {
log.Panic(err)
}
return i
default:
log.Panicln("Unknown type returned from sql driver")
return nil
}
}
// BoolI returns the value as an interface to a boolean
func (r SqlReceiver) BoolI() interface{} {
if r.R == nil {
return nil
}
switch r.R.(type) {
case bool:
return r.R
case int:
return (r.R.(int) != 0)
case int64:
return (r.R.(int64) != 0)
case string:
b, err := strconv.ParseBool(r.R.(string))
if err != nil {
log.Panic(err)
}
return b
case []byte:
b, err := strconv.ParseBool(string(r.R.([]byte)[:]))
if err != nil {
log.Panic(err)
}
return b
default:
log.Panicln("Unknown type returned from sql driver")
return nil
}
}
// StringI returns the value as an interface to a string
func (r SqlReceiver) StringI() interface{} {
if r.R == nil {
return nil
}
switch r.R.(type) {
case string:
return r.R
case []byte:
return string(r.R.([]byte)[:])
default:
return fmt.Sprint(r.R)
}
}
// FloatI returns the value as an interface to a float32 value.
func (r SqlReceiver) FloatI() interface{} {
if r.R == nil {
return nil
}
switch r.R.(type) {
case float32:
return r.R
case float64:
return float32(r.R.(float64))
case string:
f, err := strconv.ParseFloat(r.R.(string), 32)
if err != nil {
log.Panic(err)
}
return f
case []byte:
f, err := strconv.ParseFloat(string(r.R.([]byte)[:]), 32)
if err != nil {
log.Panic(err)
}
return float32(f)
default:
log.Panicln("Unknown type returned from sql driver")
return nil
}
}
// DoubleI returns the value as a float64 interface
func (r SqlReceiver) DoubleI() interface{} {
if r.R == nil {
return nil
}
switch r.R.(type) {
case float32:
return float64(r.R.(float32))
case float64:
return r.R
case string:
f, err := strconv.ParseFloat(r.R.(string), 64)
if err != nil {
log.Panic(err)
}
return f
case []byte:
f, err := strconv.ParseFloat(string(r.R.([]byte)[:]), 64)
if err != nil {
log.Panic(err)
}
return f
default:
log.Panicln("Unknown type returned from sql driver")
return nil
}
}
// TimeI returns the value as a datetime.DateTime value in the server's timezone.
func (r SqlReceiver) TimeI() interface{} {
if r.R == nil {
return nil
}
var date datetime.DateTime
var err error
switch v := r.R.(type) {
case time.Time:
date = datetime.NewDateTime(v)
case string:
date, err = datetime.FromSqlDateTime(v) // Note that this must always include timezone information if coming from a timestamp with timezone column
if err != nil {
return nil // This is likely caused by attempting to read a default value, and getting someting like "CURRENT_TIMESTAMP"
}
case []byte:
s := string(v)
date, err = datetime.FromSqlDateTime(s)
if err != nil {
if (s == "CURRENT_TIMESTAMP") { // Mysql version of now
return datetime.Current
}
return nil
}
// TODO: SQL Lite, may return an int or float. Not sure we can support these.
default:
log.Panicln("Unknown type returned from sql driver")
return nil
}
return date
}
// Unpack converts a SqlReceiver to a type corresponding to the given GoColumnType
func (r SqlReceiver) Unpack(typ GoColumnType) interface{} {
switch typ {
case ColTypeBytes:
return r.R
case ColTypeString:
return r.StringI()
case ColTypeInteger:
return r.IntI()
case ColTypeUnsigned:
return r.UintI()
case ColTypeInteger64:
return r.Int64I()
case ColTypeUnsigned64:
return r.Uint64I()
case ColTypeDateTime:
return r.TimeI()
case ColTypeFloat:
return r.FloatI()
case ColTypeDouble:
return r.DoubleI()
case ColTypeBool:
return r.BoolI()
default:
return r.R
}
}
// ReceiveRows gets data from a sql result set and returns it as a slice of maps. Each column is mapped to its column name.
// If you provide column names, those will be used in the map. Otherwise it will get the column names out of the
// result set provided
func ReceiveRows(rows *sql.Rows, columnTypes []GoColumnType, columnNames []string) (values []map[string]interface{}) {
var err error
values = []map[string]interface{}{}
columnReceivers := make([]SqlReceiver, len(columnTypes))
columnValueReceivers := make([]interface{}, len(columnTypes))
if columnNames == nil {
columnNames, err = rows.Columns()
if err != nil {
log.Panic(err)
}
}
for i, _ := range columnReceivers {
columnValueReceivers[i] = &(columnReceivers[i].R)
}
for rows.Next() {
err = rows.Scan(columnValueReceivers...)
if err != nil {
log.Panic(err)
}
v1 := make(map[string]interface{}, len(columnReceivers))
for j, vr := range columnReceivers {
v1[columnNames[j]] = vr.Unpack(columnTypes[j])
}
values = append(values, v1)
}
err = rows.Err()
if err != nil {
log.Panic(err)
}
return
}