mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-11 21:49:13 +00:00
Batch column scans. (#52)
This commit is contained in:
@@ -50,6 +50,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
@@ -532,46 +533,34 @@ func (r *rows) Next(dest []driver.Value) error {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
data := unsafe.Slice((*any)(unsafe.SliceData(dest)), len(dest))
|
||||
err := r.Stmt.Columns(data)
|
||||
for i := range dest {
|
||||
t := r.Stmt.ColumnType(i)
|
||||
if tm, ok := r.decodeTime(i, t); ok {
|
||||
dest[i] = tm
|
||||
continue
|
||||
if t, ok := r.decodeTime(i, dest[i]); ok {
|
||||
dest[i] = t
|
||||
} else if s, ok := dest[i].(string); ok {
|
||||
dest[i] = stringOrTime(s)
|
||||
}
|
||||
switch t {
|
||||
case sqlite3.INTEGER:
|
||||
dest[i] = r.Stmt.ColumnInt64(i)
|
||||
case sqlite3.FLOAT:
|
||||
dest[i] = r.Stmt.ColumnFloat(i)
|
||||
case sqlite3.BLOB:
|
||||
dest[i] = r.Stmt.ColumnRawBlob(i)
|
||||
case sqlite3.TEXT:
|
||||
dest[i] = stringOrTime(r.Stmt.ColumnText(i))
|
||||
case sqlite3.NULL:
|
||||
dest[i] = nil
|
||||
default:
|
||||
panic(util.AssertErr())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return r.Stmt.Err()
|
||||
}
|
||||
|
||||
func (r *rows) decodeTime(i int, typ sqlite3.Datatype) (_ time.Time, _ bool) {
|
||||
func (r *rows) decodeTime(i int, v any) (_ time.Time, _ bool) {
|
||||
if r.tmRead == sqlite3.TimeFormatDefault {
|
||||
return
|
||||
}
|
||||
switch typ {
|
||||
case sqlite3.INTEGER, sqlite3.FLOAT, sqlite3.TEXT:
|
||||
// maybe
|
||||
default:
|
||||
return
|
||||
}
|
||||
switch r.declType(i) {
|
||||
case "DATE", "TIME", "DATETIME", "TIMESTAMP":
|
||||
// maybe
|
||||
default:
|
||||
return
|
||||
}
|
||||
return r.Stmt.ColumnTime(i, r.tmRead), r.Stmt.Err() == nil
|
||||
switch v.(type) {
|
||||
case int64, float64, string:
|
||||
// maybe
|
||||
default:
|
||||
return
|
||||
}
|
||||
t, err := r.tmRead.Decode(v)
|
||||
return t, err == nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ ROOT=../
|
||||
BINARYEN="$ROOT/tools/binaryen-version_116/bin"
|
||||
WASI_SDK="$ROOT/tools/wasi-sdk-21.0/bin"
|
||||
|
||||
"$WASI_SDK/clang" --target=wasm32-wasi -flto -g0 -O2 \
|
||||
"$WASI_SDK/clang" --target=wasm32-wasi -std=c17 -flto -g0 -O2 \
|
||||
-Wall -Wextra -Wno-unused-parameter \
|
||||
-o sqlite3.wasm "$ROOT/sqlite3/main.c" \
|
||||
-I"$ROOT/sqlite3" \
|
||||
-mexec-model=reactor \
|
||||
|
||||
@@ -39,6 +39,7 @@ sqlite3_column_name
|
||||
sqlite3_column_text
|
||||
sqlite3_column_type
|
||||
sqlite3_column_value
|
||||
sqlite3_columns_go
|
||||
sqlite3_config_log_go
|
||||
sqlite3_create_aggregate_function_go
|
||||
sqlite3_create_collation_go
|
||||
|
||||
Binary file not shown.
51
sqlite3/column.c
Normal file
51
sqlite3/column.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
union sqlite3_data {
|
||||
sqlite3_int64 i;
|
||||
double d;
|
||||
struct {
|
||||
const void *ptr;
|
||||
int len;
|
||||
};
|
||||
};
|
||||
|
||||
int sqlite3_columns_go(sqlite3_stmt *stmt, int nCol, char *aType,
|
||||
union sqlite3_data *aData) {
|
||||
if (nCol != sqlite3_column_count(stmt)) {
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
int rc = SQLITE_OK;
|
||||
for (int i = 0; i < nCol; ++i) {
|
||||
const void *ptr = NULL;
|
||||
switch (aType[i] = sqlite3_column_type(stmt, i)) {
|
||||
default: // SQLITE_NULL
|
||||
aData[i] = (union sqlite3_data){};
|
||||
case SQLITE_INTEGER:
|
||||
aData[i].i = sqlite3_column_int64(stmt, i);
|
||||
continue;
|
||||
case SQLITE_FLOAT:
|
||||
aData[i].d = sqlite3_column_double(stmt, i);
|
||||
continue;
|
||||
case SQLITE_TEXT:
|
||||
ptr = sqlite3_column_text(stmt, i);
|
||||
break;
|
||||
case SQLITE_BLOB:
|
||||
ptr = sqlite3_column_blob(stmt, i);
|
||||
break;
|
||||
}
|
||||
if (ptr == NULL && rc == SQLITE_OK) {
|
||||
rc = sqlite3_errcode(sqlite3_db_handle(stmt));
|
||||
}
|
||||
aData[i].ptr = ptr;
|
||||
aData[i].len = sqlite3_column_bytes(stmt, i);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static_assert(offsetof(union sqlite3_data, i) == 0, "Unexpected offset");
|
||||
static_assert(offsetof(union sqlite3_data, d) == 0, "Unexpected offset");
|
||||
static_assert(offsetof(union sqlite3_data, ptr) == 0, "Unexpected offset");
|
||||
static_assert(offsetof(union sqlite3_data, len) == 4, "Unexpected offset");
|
||||
static_assert(sizeof(union sqlite3_data) == 8, "Unexpected size");
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "ext/uint.c"
|
||||
#include "ext/uuid.c"
|
||||
// Bindings
|
||||
#include "column.c"
|
||||
#include "func.c"
|
||||
#include "log.c"
|
||||
#include "pointer.c"
|
||||
|
||||
@@ -136,8 +136,6 @@ static int go_cur_close_wrapper(sqlite3_vtab_cursor *pCursor) {
|
||||
static int go_vtab_find_function_wrapper(
|
||||
sqlite3_vtab *pVTab, int nArg, const char *zName,
|
||||
void (**pxFunc)(sqlite3_context *, int, sqlite3_value **), void **ppArg) {
|
||||
struct go_vtab *vtab = container_of(pVTab, struct go_vtab, base);
|
||||
|
||||
go_handle handle;
|
||||
int rc = go_vtab_find_function(pVTab, nArg, zName, &handle);
|
||||
if (rc) {
|
||||
|
||||
37
stmt.go
37
stmt.go
@@ -549,3 +549,40 @@ func (s *Stmt) ColumnValue(col int) Value {
|
||||
handle: uint32(r),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stmt) Columns(dest []any) error {
|
||||
defer s.c.arena.mark()()
|
||||
count := uint64(len(dest))
|
||||
typePtr := s.c.arena.new(count)
|
||||
dataPtr := s.c.arena.new(8 * count)
|
||||
|
||||
r := s.c.call("sqlite3_columns_go",
|
||||
uint64(s.handle), count, uint64(typePtr), uint64(dataPtr))
|
||||
if err := s.c.error(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
types := util.View(s.c.mod, typePtr, count)
|
||||
for i := range dest {
|
||||
switch types[i] {
|
||||
case byte(INTEGER):
|
||||
dest[i] = int64(util.ReadUint64(s.c.mod, dataPtr+8*uint32(i)))
|
||||
continue
|
||||
case byte(FLOAT):
|
||||
dest[i] = util.ReadFloat64(s.c.mod, dataPtr+8*uint32(i))
|
||||
continue
|
||||
case byte(NULL):
|
||||
dest[i] = nil
|
||||
continue
|
||||
}
|
||||
ptr := util.ReadUint32(s.c.mod, dataPtr+8*uint32(i)+0)
|
||||
len := util.ReadUint32(s.c.mod, dataPtr+8*uint32(i)+4)
|
||||
buf := util.View(s.c.mod, ptr, uint64(len))
|
||||
if types[i] == byte(TEXT) {
|
||||
dest[i] = string(buf)
|
||||
} else {
|
||||
dest[i] = buf
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
2
vfs/tests/mptest/testdata/build.sh
vendored
2
vfs/tests/mptest/testdata/build.sh
vendored
@@ -7,7 +7,7 @@ ROOT=../../../../
|
||||
BINARYEN="$ROOT/tools/binaryen-version_116/bin"
|
||||
WASI_SDK="$ROOT/tools/wasi-sdk-21.0/bin"
|
||||
|
||||
"$WASI_SDK/clang" --target=wasm32-wasi -flto -g0 -O2 \
|
||||
"$WASI_SDK/clang" --target=wasm32-wasi -std=c17 -flto -g0 -O2 \
|
||||
-o mptest.wasm main.c \
|
||||
-I"$ROOT/sqlite3" \
|
||||
-msimd128 -mmutable-globals \
|
||||
|
||||
2
vfs/tests/speedtest1/testdata/build.sh
vendored
2
vfs/tests/speedtest1/testdata/build.sh
vendored
@@ -7,7 +7,7 @@ ROOT=../../../../
|
||||
BINARYEN="$ROOT/tools/binaryen-version_116/bin"
|
||||
WASI_SDK="$ROOT/tools/wasi-sdk-21.0/bin"
|
||||
|
||||
"$WASI_SDK/clang" --target=wasm32-wasi -flto -g0 -O2 \
|
||||
"$WASI_SDK/clang" --target=wasm32-wasi -std=c17 -flto -g0 -O2 \
|
||||
-o speedtest1.wasm main.c \
|
||||
-I"$ROOT/sqlite3" \
|
||||
-msimd128 -mmutable-globals \
|
||||
|
||||
Reference in New Issue
Block a user