Skip to content

Commit e64d7a2

Browse files
committed
Better null/nil handling.
Drop custom Option[T] and use nil pointers. Not sure how to reduce the awful bind duplication without having to do a 2nd type switch for any pointer value, so just a lot of copy and paste for now.
1 parent c4ff3ad commit e64d7a2

File tree

2 files changed

+125
-48
lines changed

2 files changed

+125
-48
lines changed

sqlite3_test.go

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ import (
1515
type TestRow struct {
1616
Id int
1717
Int int
18-
Intn sqlite.Option[int]
18+
Intn *int
1919
Real float64
20-
Realn sqlite.Option[float64]
20+
Realn *float64
2121
Text string
22-
Textn sqlite.Option[string]
22+
Textn *string
2323
Blob []byte
2424
Blobn []byte
2525
Time time.Time
26-
Timen sqlite.Option[time.Time]
26+
Timen *time.Time
2727
}
2828

2929
func Test_Conn_NotExistWhenNotCreate(t *testing.T) {
@@ -50,11 +50,11 @@ func Test_Conn_ExecAndScan(t *testing.T) {
5050
assert.Equal(t, row.Text, "three")
5151
assert.Equal(t, string(row.Blob), "four")
5252
assert.Equal(t, row.Time, now.Truncate(time.Second))
53-
assert.Equal(t, row.Intn.Valid, false)
54-
assert.Equal(t, row.Realn.Valid, false)
55-
assert.Equal(t, row.Textn.Valid, false)
53+
assert.True(t, row.Intn == nil)
54+
assert.True(t, row.Realn == nil)
55+
assert.True(t, row.Textn == nil)
5656
assert.True(t, row.Blobn == nil)
57-
assert.Equal(t, row.Timen.Valid, false)
57+
assert.True(t, row.Timen == nil)
5858

5959
mustExec(db, "delete from test where id = ?", lastId)
6060
assert.Equal(t, db.Changes(), 1)
@@ -65,6 +65,38 @@ func Test_Conn_ExecAndScan(t *testing.T) {
6565
assert.Nil(t, queryLast(db))
6666
}
6767

68+
func Test_Conn_BindNil(t *testing.T) {
69+
db := testDB()
70+
defer db.Close()
71+
72+
mustExec(db, `
73+
insert into test (cintn, crealn, ctextn, cblobn, ctimen)
74+
values (?1, ?2, ?3, ?4, ?5)
75+
`, nil, nil, nil, nil, nil)
76+
assert.Equal(t, db.Changes(), 1)
77+
78+
row := queryLast(db)
79+
assert.True(t, row.Intn == nil)
80+
assert.True(t, row.Realn == nil)
81+
assert.True(t, row.Textn == nil)
82+
assert.True(t, row.Blobn == nil)
83+
assert.True(t, row.Timen == nil)
84+
85+
var data TestRow
86+
mustExec(db, `
87+
insert into test (cintn, crealn, ctextn, cblobn, ctimen)
88+
values (?1, ?2, ?3, ?4, ?5)
89+
`, data.Intn, data.Realn, data.Textn, data.Blobn, data.Timen)
90+
assert.Equal(t, db.Changes(), 1)
91+
92+
row = queryLast(db)
93+
assert.True(t, row.Intn == nil)
94+
assert.True(t, row.Realn == nil)
95+
assert.True(t, row.Textn == nil)
96+
assert.True(t, row.Blobn == nil)
97+
assert.True(t, row.Timen == nil)
98+
}
99+
68100
func Test_Conn_Scan_RawBytes(t *testing.T) {
69101
db := testDB()
70102
defer db.Close()
@@ -188,7 +220,7 @@ func Test_Transaction_Commit(t *testing.T) {
188220
})
189221

190222
assert.Equal(t, queryId(db, id1).Text, "hello")
191-
assert.Equal(t, queryId(db, id2).Textn.Value, "world")
223+
assert.Equal(t, *queryId(db, id2).Textn, "world")
192224
}
193225

194226
func Test_Transaction_Rollback(t *testing.T) {

stmt.go

Lines changed: 84 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -89,39 +89,90 @@ func (s *Stmt) Bind(args ...interface{}) error {
8989
var rc C.int
9090
bindIndex := C.int(i + 1)
9191

92-
if v == nil {
93-
rc = C.sqlite3_bind_null(stmt, bindIndex)
94-
if rc != C.SQLITE_OK {
95-
return errorFromCode(s.db, rc)
96-
}
97-
continue
98-
}
9992
switch v := v.(type) {
93+
case nil:
94+
rc = C.sqlite3_bind_null(stmt, bindIndex)
10095
case int:
10196
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(v))
97+
case *int:
98+
if v == nil {
99+
C.sqlite3_bind_null(stmt, bindIndex)
100+
} else {
101+
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(*v))
102+
}
102103
case string:
103104
if v == "" {
104105
rc = C.empty_string(stmt, bindIndex)
105106
} else {
106107
rc = C.sqlite3_bind_text(stmt, bindIndex, cStr(v), C.int(len(v)), C.SQLITE_TRANSIENT)
107108
}
109+
case *string:
110+
if v == nil {
111+
C.sqlite3_bind_null(stmt, bindIndex)
112+
} else {
113+
if v := *v; v == "" {
114+
rc = C.empty_string(stmt, bindIndex)
115+
} else {
116+
rc = C.sqlite3_bind_text(stmt, bindIndex, cStr(v), C.int(len(v)), C.SQLITE_TRANSIENT)
117+
}
118+
}
108119
case uint16:
109120
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(int64(v)))
121+
case *uint16:
122+
if v == nil {
123+
C.sqlite3_bind_null(stmt, bindIndex)
124+
} else {
125+
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(int64(*v)))
126+
}
110127
case uint32:
111128
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(int64(v)))
129+
case *uint32:
130+
if v == nil {
131+
C.sqlite3_bind_null(stmt, bindIndex)
132+
} else {
133+
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(int64(*v)))
134+
}
112135
case uint64:
113136
// OMG!!
114137
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(int64(v)))
138+
case *uint64:
139+
if v == nil {
140+
C.sqlite3_bind_null(stmt, bindIndex)
141+
} else {
142+
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(int64(*v)))
143+
}
115144
case int64:
116145
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(v))
146+
case *int64:
147+
if v == nil {
148+
C.sqlite3_bind_null(stmt, bindIndex)
149+
} else {
150+
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(*v))
151+
}
117152
case float64:
118153
rc = C.sqlite3_bind_double(stmt, bindIndex, C.double(v))
154+
case *float64:
155+
if v == nil {
156+
C.sqlite3_bind_null(stmt, bindIndex)
157+
} else {
158+
rc = C.sqlite3_bind_double(stmt, bindIndex, C.double(*v))
159+
}
119160
case bool:
120161
var sqliteBool int64
121162
if v {
122163
sqliteBool = 1
123164
}
124165
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(sqliteBool))
166+
case *bool:
167+
if v == nil {
168+
C.sqlite3_bind_null(stmt, bindIndex)
169+
} else {
170+
var sqliteBool int64
171+
if *v {
172+
sqliteBool = 1
173+
}
174+
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(sqliteBool))
175+
}
125176
case []byte:
126177
if len(v) == 0 {
127178
rc = C.sqlite3_bind_zeroblob(stmt, bindIndex, 0)
@@ -130,6 +181,12 @@ func (s *Stmt) Bind(args ...interface{}) error {
130181
}
131182
case time.Time:
132183
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64(v.Unix()))
184+
case *time.Time:
185+
if v == nil {
186+
C.sqlite3_bind_null(stmt, bindIndex)
187+
} else {
188+
rc = C.sqlite3_bind_int64(stmt, bindIndex, C.sqlite3_int64((*v).Unix()))
189+
}
133190
default:
134191
return Error{Code: C.SQLITE_MISUSE, Message: fmt.Sprintf("unsupported type %T (index: %d)", v, i)}
135192
}
@@ -258,62 +315,50 @@ func (s *Stmt) scan(i int, v interface{}) error {
258315
switch v := v.(type) {
259316
case *string:
260317
*v, err = s.ColumnText(i)
261-
case *Option[string]:
262-
if s.columnTypes[i] == C.SQLITE_NULL {
263-
*v = Option[string]{}
264-
} else {
318+
case **string:
319+
if s.columnTypes[i] != C.SQLITE_NULL {
265320
var n string
266321
n, err = s.ColumnText(i)
267-
*v = Option[string]{Value: n, Valid: true}
322+
*v = &n
268323
}
269324
case *int:
270325
*v = s.ColumnInt(i)
271-
case *Option[int]:
272-
if s.columnTypes[i] == C.SQLITE_NULL {
273-
*v = Option[int]{}
274-
} else {
275-
n := s.ColumnInt64(i)
276-
*v = Option[int]{Value: int(n), Valid: true}
326+
case **int:
327+
if s.columnTypes[i] != C.SQLITE_NULL {
328+
n := int(s.ColumnInt64(i))
329+
*v = &n
277330
}
278331
case *int64:
279332
*v = s.ColumnInt64(i)
280-
case *Option[int64]:
281-
if s.columnTypes[i] == C.SQLITE_NULL {
282-
*v = Option[int64]{}
283-
} else {
333+
case **int64:
334+
if s.columnTypes[i] != C.SQLITE_NULL {
284335
n := s.ColumnInt64(i)
285-
*v = Option[int64]{Value: n, Valid: true}
336+
*v = &n
286337
}
287338
case *float64:
288339
*v = s.ColumnDouble(i)
289-
case *Option[float64]:
290-
if s.columnTypes[i] == C.SQLITE_NULL {
291-
*v = Option[float64]{}
292-
} else {
340+
case **float64:
341+
if s.columnTypes[i] != C.SQLITE_NULL {
293342
n := s.ColumnDouble(i)
294-
*v = Option[float64]{Value: n, Valid: true}
343+
*v = &n
295344
}
296345
case *bool:
297346
*v = s.ColumnInt64(i) != 0
298-
case *Option[bool]:
299-
if s.columnTypes[i] == C.SQLITE_NULL {
300-
*v = Option[bool]{}
301-
} else {
347+
case **bool:
348+
if s.columnTypes[i] != C.SQLITE_NULL {
302349
n := s.ColumnInt64(i) != 0
303-
*v = Option[bool]{Value: n, Valid: true}
350+
*v = &n
304351
}
305352
case *[]byte:
306353
*v, err = s.ColumnBytes(i)
307354
case *RawBytes:
308355
*v, err = s.ColumnRawBytes(i)
309356
case *time.Time:
310357
*v = time.Unix(s.ColumnInt64(i), 0)
311-
case *Option[time.Time]:
312-
if s.columnTypes[i] == C.SQLITE_NULL {
313-
*v = Option[time.Time]{}
314-
} else {
315-
n := s.ColumnInt64(i)
316-
*v = Option[time.Time]{Value: time.Unix(n, 0), Valid: true}
358+
case **time.Time:
359+
if s.columnTypes[i] != C.SQLITE_NULL {
360+
n := time.Unix(s.ColumnInt64(i), 0)
361+
*v = &n
317362
}
318363
default:
319364
return Error{Code: C.SQLITE_MISUSE, Message: fmt.Sprintf("cannot scan into %T (index: %d)", v, i)}

0 commit comments

Comments
 (0)