-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathnullable_field.go
More file actions
133 lines (121 loc) · 2.58 KB
/
Copy pathnullable_field.go
File metadata and controls
133 lines (121 loc) · 2.58 KB
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
package magicsql
import (
"database/sql"
"time"
)
// NullableField implements the sql Scanner interface to make null values suck
// a little less. When a null value is encountered, it's simply ignored, so
// the actual source can be set to a value that represents null or left at its
// default. Data loss can happen if the source fields aren't of proper size,
// and not all types are supported.
type NullableField struct {
Value interface{}
}
// Scan implements the Scanner interface. Always returns a nil error. Only
// works with primitive types or simple mappings of time.Time fields.
func (nf *NullableField) Scan(src interface{}) error {
// Create a nullable field based on the type of the destination data
switch nf.Value.(type) {
case *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64:
nf.storeInt(src)
case *float32, *float64:
nf.storeFloat(src)
case *bool:
nf.storeBool(src)
case *string:
nf.storeString(src)
case *time.Time:
nf.storeTime(src)
}
return nil
}
func (nf *NullableField) storeInt(src interface{}) {
var n sql.NullInt64
n.Scan(src)
if !n.Valid {
return
}
var i = n.Int64
switch d := nf.Value.(type) {
case *int:
*d = int(i)
case *int8:
*d = int8(i)
case *int16:
*d = int16(i)
case *int32:
*d = int32(i)
case *int64:
*d = int64(i)
case *uint:
*d = uint(i)
case *uint8:
*d = uint8(i)
case *uint16:
*d = uint16(i)
case *uint32:
*d = uint32(i)
case *uint64:
*d = uint64(i)
}
}
func (nf *NullableField) storeFloat(src interface{}) {
var n sql.NullFloat64
n.Scan(src)
if !n.Valid {
return
}
var f = n.Float64
switch d := nf.Value.(type) {
case *float32:
*d = float32(f)
case *float64:
*d = float64(f)
}
}
func (nf *NullableField) storeBool(src interface{}) {
var n sql.NullBool
n.Scan(src)
if !n.Valid {
return
}
d := nf.Value.(*bool)
*d = n.Bool
}
func (nf *NullableField) storeString(src interface{}) {
var n sql.NullString
n.Scan(src)
if !n.Valid {
return
}
d := nf.Value.(*string)
*d = n.String
}
func (nf *NullableField) storeTime(src interface{}) {
d := nf.Value.(*time.Time)
switch st := src.(type) {
case time.Time:
*d = st
case string:
*d = parseTime(st).Local()
case []byte:
*d = parseTime(string(st)).Local()
}
}
// parseTime attempts to parse a string into a time, using formats I've seen in
// mysql and sqlite
func parseTime(s string) time.Time {
var fmts = []string{
"2006-01-02 15:04:05",
"2006-01-02 15:04:05-07:00",
}
var t time.Time
var err error
for _, fmt := range fmts {
t, err = time.Parse(fmt, s)
if err == nil {
return t
}
}
return t
}