Skip to content

Commit 0b47c1f

Browse files
committed
gosqlite: replace uintptr handles with Go pointers
Removed the uintptr indirection for sqlite3_stmt, replacing the uintptr with a plain Go pointer. Since 34bef2d was authored the toolchain has changed such that the indirection is no longer necessary to avoid allocations. Restoring regular pointers will enable regular tooling to function again and simplifies the code. ``` │ /tmp/uintptr-bench.txt │ /tmp/no-cstmt-bench.txt │ │ allocs/op │ allocs/op vs base │ WALHookAndExec-16 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹ Persist-16 11.00 ± 0% 11.00 ± 0% ~ (p=1.000 n=10) ¹ QueryRows100MixedTypes-16 107.0 ± 0% 107.0 ± 0% ~ (p=1.000 n=10) ¹ EmptyExec-16 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹ BeginTxNoop-16 13.00 ± 0% 13.00 ± 0% ~ (p=1.000 n=10) ¹ geomean 6.870 6.870 +0.00% ¹ all samples are equal ``` Updates tailscale/corp#9919
1 parent 3ded194 commit 0b47c1f

File tree

2 files changed

+58
-99
lines changed

2 files changed

+58
-99
lines changed

cgosqlite/cgosqlite.go

Lines changed: 35 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -68,31 +68,10 @@ type DB struct {
6868
declTypes map[string]string
6969
}
7070

71-
// cStmt is a wrapper around an sqlite3 *sqlite3_stmt. Except rather than
72-
// storing it as a pointer, it's stored as uintptr to avoid allocations due to
73-
// poor interactions between cgo's pointer checker and Go's escape analysis.
74-
//
75-
// The ptr method returns the value as a pointer, for call sites that haven't
76-
// yet been optimized or don't need the optimization. This lets us migrate
77-
// incrementally.
78-
//
79-
// See http://go/corp/9919.
80-
type cStmt struct {
81-
v C.handle_sqlite3_stmt
82-
}
83-
84-
// cStmtFromPtr returns a cStmt from a C pointer.
85-
func cStmtFromPtr(p *C.sqlite3_stmt) cStmt {
86-
return cStmt{v: C.handle_sqlite3_stmt(uintptr(unsafe.Pointer(p)))}
87-
}
88-
89-
func (h cStmt) int() C.handle_sqlite3_stmt { return h.v }
90-
func (h cStmt) ptr() *C.sqlite3_stmt { return (*C.sqlite3_stmt)(unsafe.Pointer(uintptr(h.v))) }
91-
9271
// Stmt implements sqliteh.Stmt.
9372
type Stmt struct {
9473
db *DB
95-
stmt cStmt
74+
stmt *C.sqlite3_stmt
9675
start C.struct_timespec
9776

9877
// used as scratch space when calling into cgo
@@ -202,7 +181,7 @@ func (db *DB) Prepare(query string, prepFlags sqliteh.PrepareFlags) (stmt sqlite
202181
return nil, "", err
203182
}
204183
remainingQuery = query[len(query)-int(C.strlen(csqlTail)):]
205-
return &Stmt{db: db, stmt: cStmtFromPtr(cstmt)}, remainingQuery, nil
184+
return &Stmt{db: db, stmt: cstmt}, remainingQuery, nil
206185
}
207186

208187
func (db *DB) DisableFunction(name string, numArgs int) error {
@@ -212,45 +191,45 @@ func (db *DB) DisableFunction(name string, numArgs int) error {
212191
}
213192

214193
func (stmt *Stmt) DBHandle() sqliteh.DB {
215-
cdb := C.sqlite3_db_handle(stmt.stmt.ptr())
194+
cdb := C.sqlite3_db_handle(stmt.stmt)
216195
if cdb != nil {
217196
return &DB{db: cdb}
218197
}
219198
return nil
220199
}
221200

222201
func (stmt *Stmt) SQL() string {
223-
return C.GoString(C.sqlite3_sql(stmt.stmt.ptr()))
202+
return C.GoString(C.sqlite3_sql(stmt.stmt))
224203
}
225204

226205
func (stmt *Stmt) ExpandedSQL() string {
227206
// sqlite3_expanded_sql returns a string obtained by sqlite3_malloc, which
228207
// must be freed after use.
229-
cstr := C.sqlite3_expanded_sql(stmt.stmt.ptr())
208+
cstr := C.sqlite3_expanded_sql(stmt.stmt)
230209
defer C.sqlite3_free(unsafe.Pointer(cstr))
231210
return C.GoString(cstr)
232211
}
233212

234213
func (stmt *Stmt) Reset() error {
235-
return errCode(C.sqlite3_reset(stmt.stmt.ptr()))
214+
return errCode(C.sqlite3_reset(stmt.stmt))
236215
}
237216

238217
func (stmt *Stmt) Finalize() error {
239-
return errCode(C.sqlite3_finalize(stmt.stmt.ptr()))
218+
return errCode(C.sqlite3_finalize(stmt.stmt))
240219
}
241220

242221
func (stmt *Stmt) ClearBindings() error {
243-
return errCode(C.sqlite3_clear_bindings(stmt.stmt.ptr()))
222+
return errCode(C.sqlite3_clear_bindings(stmt.stmt))
244223
}
245224

246225
func (stmt *Stmt) ResetAndClear() (time.Duration, error) {
247226
if stmt.start != (C.struct_timespec{}) {
248227
stmt.duration = 0
249-
err := errCode(C.reset_and_clear(stmt.stmt.int(), &stmt.start, &stmt.duration))
228+
err := errCode(C.reset_and_clear(stmt.stmt, &stmt.start, &stmt.duration))
250229
return time.Duration(stmt.duration), err
251230
}
252-
if sp := stmt.stmt.int(); sp != 0 {
253-
return 0, errCode(C.reset_and_clear(stmt.stmt.int(), nil, nil))
231+
if stmt.stmt != nil {
232+
return 0, errCode(C.reset_and_clear(stmt.stmt, nil, nil))
254233
}
255234
// The statement was never initialized. This can happen if, for example, the
256235
// parser found only comments (so the statement was not empty, but did not
@@ -263,19 +242,19 @@ func (stmt *Stmt) StartTimer() {
263242
}
264243

265244
func (stmt *Stmt) ColumnDatabaseName(col int) string {
266-
return C.GoString((*C.char)(unsafe.Pointer(C.sqlite3_column_database_name(stmt.stmt.ptr(), C.int(col)))))
245+
return C.GoString((*C.char)(unsafe.Pointer(C.sqlite3_column_database_name(stmt.stmt, C.int(col)))))
267246
}
268247

269248
func (stmt *Stmt) ColumnTableName(col int) string {
270-
return C.GoString((*C.char)(unsafe.Pointer(C.sqlite3_column_table_name(stmt.stmt.ptr(), C.int(col)))))
249+
return C.GoString((*C.char)(unsafe.Pointer(C.sqlite3_column_table_name(stmt.stmt, C.int(col)))))
271250
}
272251

273252
func (stmt *Stmt) Step(colType []sqliteh.ColumnType) (row bool, err error) {
274253
var ptr *C.char
275254
if len(colType) > 0 {
276255
ptr = (*C.char)(unsafe.Pointer(&colType[0]))
277256
}
278-
res := C.ts_sqlite3_step(stmt.stmt.int(), ptr, C.int(len(colType)))
257+
res := C.ts_sqlite3_step(stmt.stmt, ptr, C.int(len(colType)))
279258
switch res {
280259
case C.SQLITE_ROW:
281260
return true, nil
@@ -288,7 +267,7 @@ func (stmt *Stmt) Step(colType []sqliteh.ColumnType) (row bool, err error) {
288267

289268
func (stmt *Stmt) StepResult() (row bool, lastInsertRowID, changes int64, d time.Duration, err error) {
290269
stmt.rowid, stmt.changes, stmt.duration = 0, 0, 0
291-
res := C.step_result(stmt.stmt.int(), &stmt.rowid, &stmt.changes, &stmt.duration)
270+
res := C.step_result(stmt.stmt, &stmt.rowid, &stmt.changes, &stmt.duration)
292271
lastInsertRowID = int64(stmt.rowid)
293272
changes = int64(stmt.changes)
294273
d = time.Duration(stmt.duration)
@@ -304,51 +283,51 @@ func (stmt *Stmt) StepResult() (row bool, lastInsertRowID, changes int64, d time
304283
}
305284

306285
func (stmt *Stmt) BindDouble(col int, val float64) error {
307-
return errCode(C.ts_sqlite3_bind_double(stmt.stmt.int(), C.int(col), C.double(val)))
286+
return errCode(C.sqlite3_bind_double(stmt.stmt, C.int(col), C.double(val)))
308287
}
309288

310289
func (stmt *Stmt) BindInt64(col int, val int64) error {
311-
return errCode(C.ts_sqlite3_bind_int64(stmt.stmt.int(), C.int(col), C.sqlite3_int64(val)))
290+
return errCode(C.sqlite3_bind_int64(stmt.stmt, C.int(col), C.sqlite3_int64(val)))
312291
}
313292

314293
func (stmt *Stmt) BindNull(col int) error {
315-
return errCode(C.ts_sqlite3_bind_null(stmt.stmt.int(), C.int(col)))
294+
return errCode(C.sqlite3_bind_null(stmt.stmt, C.int(col)))
316295
}
317296

318297
func (stmt *Stmt) BindText64(col int, val string) error {
319298
if len(val) == 0 {
320-
return errCode(C.bind_text64_empty(stmt.stmt.int(), C.int(col)))
299+
return errCode(C.bind_text64_empty(stmt.stmt, C.int(col)))
321300
}
322301
v := C.CString(val) // freed by sqlite
323-
return errCode(C.bind_text64(stmt.stmt.int(), C.int(col), v, C.sqlite3_uint64(len(val))))
302+
return errCode(C.bind_text64(stmt.stmt, C.int(col), v, C.sqlite3_uint64(len(val))))
324303
}
325304

326305
func (stmt *Stmt) BindZeroBlob64(col int, n uint64) error {
327-
return errCode(C.sqlite3_bind_zeroblob64(stmt.stmt.ptr(), C.int(col), C.sqlite3_uint64(n)))
306+
return errCode(C.sqlite3_bind_zeroblob64(stmt.stmt, C.int(col), C.sqlite3_uint64(n)))
328307
}
329308

330309
func (stmt *Stmt) BindBlob64(col int, val []byte) error {
331310
var str *C.char
332311
if len(val) > 0 {
333312
str = (*C.char)(unsafe.Pointer(&val[0]))
334313
}
335-
return errCode(C.bind_blob64(stmt.stmt.int(), C.int(col), str, C.sqlite3_uint64(len(val))))
314+
return errCode(C.bind_blob64(stmt.stmt, C.int(col), str, C.sqlite3_uint64(len(val))))
336315
}
337316

338317
func (stmt *Stmt) BindParameterCount() int {
339-
return int(C.sqlite3_bind_parameter_count(stmt.stmt.ptr()))
318+
return int(C.sqlite3_bind_parameter_count(stmt.stmt))
340319
}
341320

342321
func (stmt *Stmt) BindParameterName(col int) string {
343-
cstr := C.sqlite3_bind_parameter_name(stmt.stmt.ptr(), C.int(col))
322+
cstr := C.sqlite3_bind_parameter_name(stmt.stmt, C.int(col))
344323
if cstr == nil {
345324
return ""
346325
}
347326
return C.GoString(cstr)
348327
}
349328

350329
func (stmt *Stmt) BindParameterIndex(name string) int {
351-
return int(C.bind_parameter_index(stmt.stmt.int(), name))
330+
return int(C.bind_parameter_index(stmt.stmt, name))
352331
}
353332

354333
func (stmt *Stmt) BindParameterIndexSearch(name string) int {
@@ -363,45 +342,45 @@ func (stmt *Stmt) BindParameterIndexSearch(name string) int {
363342
}
364343

365344
func (stmt *Stmt) ColumnCount() int {
366-
return int(C.sqlite3_column_count(stmt.stmt.ptr()))
345+
return int(C.sqlite3_column_count(stmt.stmt))
367346
}
368347

369348
func (stmt *Stmt) ColumnName(col int) string {
370-
return C.GoString(C.sqlite3_column_name(stmt.stmt.ptr(), C.int(col)))
349+
return C.GoString(C.sqlite3_column_name(stmt.stmt, C.int(col)))
371350
}
372351

373352
func (stmt *Stmt) ColumnText(col int) string {
374-
str := (*C.char)(unsafe.Pointer(C.ts_sqlite3_column_text(stmt.stmt.int(), C.int(col))))
375-
n := C.ts_sqlite3_column_bytes(stmt.stmt.int(), C.int(col))
353+
str := (*C.char)(unsafe.Pointer(C.ts_sqlite3_column_text(stmt.stmt, C.int(col))))
354+
n := C.ts_sqlite3_column_bytes(stmt.stmt, C.int(col))
376355
if str == nil || n == 0 {
377356
return ""
378357
}
379358
return C.GoStringN(str, n)
380359
}
381360

382361
func (stmt *Stmt) ColumnBlob(col int) []byte {
383-
res := C.ts_sqlite3_column_blob(stmt.stmt.int(), C.int(col))
362+
res := C.ts_sqlite3_column_blob(stmt.stmt, C.int(col))
384363
if res == nil {
385364
return nil
386365
}
387-
n := int(C.ts_sqlite3_column_bytes(stmt.stmt.int(), C.int(col)))
366+
n := int(C.ts_sqlite3_column_bytes(stmt.stmt, C.int(col)))
388367
return unsafe.Slice((*byte)(unsafe.Pointer(res)), n)
389368
}
390369

391370
func (stmt *Stmt) ColumnDouble(col int) float64 {
392-
return float64(C.ts_sqlite3_column_double(stmt.stmt.int(), C.int(col)))
371+
return float64(C.ts_sqlite3_column_double(stmt.stmt, C.int(col)))
393372
}
394373

395374
func (stmt *Stmt) ColumnInt64(col int) int64 {
396-
return int64(C.ts_sqlite3_column_int64(stmt.stmt.int(), C.int(col)))
375+
return int64(C.ts_sqlite3_column_int64(stmt.stmt, C.int(col)))
397376
}
398377

399378
func (stmt *Stmt) ColumnType(col int) sqliteh.ColumnType {
400-
return sqliteh.ColumnType(C.ts_sqlite3_column_type(stmt.stmt.int(), C.int(col)))
379+
return sqliteh.ColumnType(C.ts_sqlite3_column_type(stmt.stmt, C.int(col)))
401380
}
402381

403382
func (stmt *Stmt) ColumnDeclType(col int) string {
404-
cstr := C.sqlite3_column_decltype(stmt.stmt.ptr(), C.int(col))
383+
cstr := C.sqlite3_column_decltype(stmt.stmt, C.int(col))
405384
if cstr == nil {
406385
return ""
407386
}

cgosqlite/cgosqlite.h

Lines changed: 23 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,24 @@
55
#include <string.h>
66
#include <time.h>
77

8-
// uintptr versions of sqlite3 pointer types, to avoid allocations
9-
// in cgo code. (go/corp/9919)
10-
typedef uintptr_t handle_sqlite3_stmt; // a *sqlite3_stmt
11-
typedef uintptr_t handle_sqlite3; // a *sqlite3 (DB conn)
12-
138
// Helper methods to deal with int <-> pointer pain.
149

15-
static int bind_text64(handle_sqlite3_stmt stmt, int col, const char* str, sqlite3_uint64 len) {
16-
return sqlite3_bind_text64((sqlite3_stmt*)(stmt), col, str, len, free, SQLITE_UTF8);
17-
}
18-
19-
static int bind_text64_empty(handle_sqlite3_stmt stmt, int col) {
20-
return sqlite3_bind_text64((sqlite3_stmt*)(stmt), col, "", 0, SQLITE_STATIC, SQLITE_UTF8);
21-
}
22-
23-
static int bind_blob64(handle_sqlite3_stmt stmt, int col, char* str, sqlite3_uint64 n) {
24-
return sqlite3_bind_blob64((sqlite3_stmt*)(stmt), col, str, n, SQLITE_TRANSIENT);
25-
}
26-
27-
static int ts_sqlite3_bind_double(handle_sqlite3_stmt stmt, int col, double v) {
28-
return sqlite3_bind_double((sqlite3_stmt*)(stmt), col, v);
10+
static int bind_text64(sqlite3_stmt* stmt, int col, const char* str, sqlite3_uint64 len) {
11+
return sqlite3_bind_text64(stmt, col, str, len, free, SQLITE_UTF8);
2912
}
3013

31-
static int ts_sqlite3_bind_int64(handle_sqlite3_stmt stmt, int col, sqlite3_int64 v) {
32-
return sqlite3_bind_int64((sqlite3_stmt*)(stmt), col, v);
14+
static int bind_text64_empty(sqlite3_stmt* stmt, int col) {
15+
return sqlite3_bind_text64(stmt, col, "", 0, SQLITE_STATIC, SQLITE_UTF8);
3316
}
3417

35-
static int ts_sqlite3_bind_null(handle_sqlite3_stmt stmt, int col) {
36-
return sqlite3_bind_null((sqlite3_stmt*)(stmt), col);
18+
static int bind_blob64(sqlite3_stmt* stmt, int col, char* str, sqlite3_uint64 n) {
19+
return sqlite3_bind_blob64(stmt, col, str, n, SQLITE_TRANSIENT);
3720
}
3821

3922
// We only need the Go string's memory for the duration of the call,
4023
// and the GC pins it for us if we pass the gostring_t to C, so we
4124
// do the conversion here instead of with C.CString.
42-
static int bind_parameter_index(handle_sqlite3_stmt stmt, _GoString_ s) {
25+
static int bind_parameter_index(sqlite3_stmt* stmt, _GoString_ s) {
4326
size_t n = _GoStringLen(s);
4427
const char *p = (const char *)_GoStringPtr(s);
4528

@@ -49,7 +32,7 @@ static int bind_parameter_index(handle_sqlite3_stmt stmt, _GoString_ s) {
4932
return 0;
5033
}
5134
memmove(zName, p, n);
52-
return sqlite3_bind_parameter_index((sqlite3_stmt*)(stmt), zName);
35+
return sqlite3_bind_parameter_index(stmt, zName);
5336
}
5437

5538
static void monotonic_clock_gettime(struct timespec* t) {
@@ -65,8 +48,7 @@ static int64_t ns_since(const struct timespec t1)
6548
}
6649

6750
// step_result combines several cgo calls to save overhead.
68-
static int step_result(handle_sqlite3_stmt stmth, sqlite3_int64* rowid, sqlite3_int64* changes, int64_t* duration_ns) {
69-
sqlite3_stmt* stmt = (sqlite3_stmt*)(stmth);
51+
static int step_result(sqlite3_stmt* stmt, sqlite3_int64* rowid, sqlite3_int64* changes, int64_t* duration_ns) {
7052
struct timespec t1;
7153
if (duration_ns) {
7254
monotonic_clock_gettime(&t1);
@@ -84,8 +66,7 @@ static int step_result(handle_sqlite3_stmt stmth, sqlite3_int64* rowid, sqlite3_
8466
}
8567

8668
// reset_and_clear combines two cgo calls to save overhead.
87-
static int reset_and_clear(handle_sqlite3_stmt stmth, struct timespec* start, int64_t* duration_ns) {
88-
sqlite3_stmt* stmt = (sqlite3_stmt*)(stmth);
69+
static int reset_and_clear(sqlite3_stmt* stmt, struct timespec* start, int64_t* duration_ns) {
8970
int ret = sqlite3_reset(stmt);
9071
int ret2 = sqlite3_clear_bindings(stmt);
9172
if (duration_ns) {
@@ -111,8 +92,7 @@ static void ts_sqlite3_wal_hook_go(sqlite3* db) {
11192
sqlite3_wal_hook(db, wal_callback_into_go, 0);
11293
}
11394

114-
static int ts_sqlite3_step(handle_sqlite3_stmt stmth, char* outType , int outTypeLen) {
115-
sqlite3_stmt* stmt = (sqlite3_stmt*)(stmth);
95+
static int ts_sqlite3_step(sqlite3_stmt* stmt, char* outType , int outTypeLen) {
11696
int res = sqlite3_step(stmt);
11797
if (res == SQLITE_ROW && outTypeLen > 0) {
11898
int cols = sqlite3_column_count(stmt);
@@ -123,28 +103,28 @@ static int ts_sqlite3_step(handle_sqlite3_stmt stmth, char* outType , int outTyp
123103
return res;
124104
}
125105

126-
static const unsigned char *ts_sqlite3_column_text(handle_sqlite3_stmt stmt, int iCol) {
127-
return sqlite3_column_text((sqlite3_stmt*)(stmt), iCol);
106+
static const unsigned char *ts_sqlite3_column_text(sqlite3_stmt* stmt, int iCol) {
107+
return sqlite3_column_text(stmt, iCol);
128108
}
129109

130-
static const unsigned char *ts_sqlite3_column_blob(handle_sqlite3_stmt stmt, int iCol) {
131-
return sqlite3_column_blob((sqlite3_stmt*)(stmt), iCol);
110+
static const unsigned char *ts_sqlite3_column_blob(sqlite3_stmt* stmt, int iCol) {
111+
return sqlite3_column_blob(stmt, iCol);
132112
}
133113

134-
static int ts_sqlite3_column_type(handle_sqlite3_stmt stmt, int iCol) {
135-
return sqlite3_column_type((sqlite3_stmt*)(stmt), iCol);
114+
static int ts_sqlite3_column_type(sqlite3_stmt* stmt, int iCol) {
115+
return sqlite3_column_type(stmt, iCol);
136116
}
137117

138-
static int ts_sqlite3_column_bytes(handle_sqlite3_stmt stmt, int iCol) {
139-
return sqlite3_column_bytes((sqlite3_stmt*)(stmt), iCol);
118+
static int ts_sqlite3_column_bytes(sqlite3_stmt* stmt, int iCol) {
119+
return sqlite3_column_bytes(stmt, iCol);
140120
}
141121

142-
static double ts_sqlite3_column_double(handle_sqlite3_stmt stmt, int iCol) {
143-
return sqlite3_column_double((sqlite3_stmt*)(stmt), iCol);
122+
static double ts_sqlite3_column_double(sqlite3_stmt* stmt, int iCol) {
123+
return sqlite3_column_double(stmt, iCol);
144124
}
145125

146-
static sqlite3_int64 ts_sqlite3_column_int64(handle_sqlite3_stmt stmt, int iCol) {
147-
return sqlite3_column_int64((sqlite3_stmt*)(stmt), iCol);
126+
static sqlite3_int64 ts_sqlite3_column_int64(sqlite3_stmt* stmt, int iCol) {
127+
return sqlite3_column_int64(stmt, iCol);
148128
}
149129

150130
static int ts_sqlite3_disable_function(sqlite3 *db, const char *zFunctionName, int nArg) {

0 commit comments

Comments
 (0)