batch row column fetches in Next

This commit is contained in:
Yasuhiro Matsumoto
2026-04-03 12:45:26 +09:00
parent cc39db7160
commit e1557be6ce

View File

@@ -78,6 +78,42 @@ _sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT); return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
} }
typedef struct {
int typ;
sqlite3_int64 i64;
double f64;
const void *ptr;
int n;
} sqlite3_go_col;
static void
_sqlite3_column_values(sqlite3_stmt *stmt, int ncol, sqlite3_go_col *cols) {
for (int i = 0; i < ncol; i++) {
sqlite3_go_col *col = &cols[i];
col->typ = sqlite3_column_type(stmt, i);
col->ptr = 0;
col->n = 0;
switch (col->typ) {
case SQLITE_INTEGER:
col->i64 = sqlite3_column_int64(stmt, i);
break;
case SQLITE_FLOAT:
col->f64 = sqlite3_column_double(stmt, i);
break;
case SQLITE_BLOB:
col->ptr = sqlite3_column_blob(stmt, i);
col->n = sqlite3_column_bytes(stmt, i);
break;
case SQLITE_TEXT:
col->ptr = sqlite3_column_text(stmt, i);
col->n = sqlite3_column_bytes(stmt, i);
break;
default:
break;
}
}
}
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
@@ -210,6 +246,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math"
"net/url" "net/url"
"reflect" "reflect"
"runtime" "runtime"
@@ -397,6 +434,7 @@ type SQLiteRows struct {
cls bool // True if we need to close the parent statement in Close cls bool // True if we need to close the parent statement in Close
cols []string cols []string
decltype []string decltype []string
colvals *C.sqlite3_go_col
ctx context.Context // no better alternative to pass context into Next() method ctx context.Context // no better alternative to pass context into Next() method
closemu sync.Mutex closemu sync.Mutex
} }
@@ -2107,8 +2145,15 @@ func (s *SQLiteStmt) query(ctx context.Context, args []driver.NamedValue) (drive
cls: s.cls, cls: s.cls,
cols: nil, cols: nil,
decltype: nil, decltype: nil,
colvals: nil,
ctx: ctx, ctx: ctx,
} }
if rows.nc > 0 {
rows.colvals = (*C.sqlite3_go_col)(C.malloc(C.size_t(rows.nc) * C.size_t(unsafe.Sizeof(C.sqlite3_go_col{}))))
if rows.colvals == nil {
return nil, errors.New("sqlite3: failed to allocate row buffer")
}
}
return rows, nil return rows, nil
} }
@@ -2209,9 +2254,17 @@ func (rc *SQLiteRows) Close() error {
defer rc.closemu.Unlock() defer rc.closemu.Unlock()
s := rc.s s := rc.s
if s == nil { if s == nil {
if rc.colvals != nil {
C.free(unsafe.Pointer(rc.colvals))
rc.colvals = nil
}
return nil return nil
} }
rc.s = nil // remove reference to SQLiteStmt rc.s = nil // remove reference to SQLiteStmt
if rc.colvals != nil {
C.free(unsafe.Pointer(rc.colvals))
rc.colvals = nil
}
s.mu.Lock() s.mu.Lock()
if s.closed { if s.closed {
s.mu.Unlock() s.mu.Unlock()
@@ -2307,13 +2360,19 @@ func (rc *SQLiteRows) nextSyncLocked(dest []driver.Value) error {
} }
rc.declTypes() rc.declTypes()
if len(dest) == 0 {
return nil
}
C._sqlite3_column_values(rc.s.s, C.int(len(dest)), rc.colvals)
colvals := (*[(math.MaxInt32 - 1) / unsafe.Sizeof(C.sqlite3_go_col{})]C.sqlite3_go_col)(unsafe.Pointer(rc.colvals))[:len(dest):len(dest)]
decltype := rc.decltype decltype := rc.decltype
_ = decltype[len(dest)-1] _ = decltype[len(dest)-1]
for i := range dest { for i := range dest {
switch C.sqlite3_column_type(rc.s.s, C.int(i)) { col := &colvals[i]
switch col.typ {
case C.SQLITE_INTEGER: case C.SQLITE_INTEGER:
val := int64(C.sqlite3_column_int64(rc.s.s, C.int(i))) val := int64(col.i64)
switch decltype[i] { switch decltype[i] {
case columnTimestamp, columnDatetime, columnDate: case columnTimestamp, columnDatetime, columnDate:
var t time.Time var t time.Time
@@ -2336,14 +2395,14 @@ func (rc *SQLiteRows) nextSyncLocked(dest []driver.Value) error {
dest[i] = val dest[i] = val
} }
case C.SQLITE_FLOAT: case C.SQLITE_FLOAT:
dest[i] = float64(C.sqlite3_column_double(rc.s.s, C.int(i))) dest[i] = float64(col.f64)
case C.SQLITE_BLOB: case C.SQLITE_BLOB:
p := C.sqlite3_column_blob(rc.s.s, C.int(i)) p := col.ptr
if p == nil { if p == nil {
dest[i] = []byte{} dest[i] = []byte{}
continue continue
} }
n := C.sqlite3_column_bytes(rc.s.s, C.int(i)) n := col.n
dest[i] = C.GoBytes(p, n) dest[i] = C.GoBytes(p, n)
case C.SQLITE_NULL: case C.SQLITE_NULL:
dest[i] = nil dest[i] = nil
@@ -2351,8 +2410,8 @@ func (rc *SQLiteRows) nextSyncLocked(dest []driver.Value) error {
var err error var err error
var timeVal time.Time var timeVal time.Time
n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i))) n := int(col.n)
s := C.GoStringN((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))), C.int(n)) s := C.GoStringN((*C.char)(unsafe.Pointer(col.ptr)), C.int(n))
switch decltype[i] { switch decltype[i] {
case columnTimestamp, columnDatetime, columnDate: case columnTimestamp, columnDatetime, columnDate: