diff --git a/driver/driver.go b/driver/driver.go index 6408e68..1d22bc8 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -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 - } - 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()) + if t, ok := r.decodeTime(i, dest[i]); ok { + dest[i] = t + } else if s, ok := dest[i].(string); ok { + dest[i] = stringOrTime(s) } } - - return r.Stmt.Err() + return 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 } diff --git a/embed/build.sh b/embed/build.sh index 615a2d0..638aca9 100755 --- a/embed/build.sh +++ b/embed/build.sh @@ -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 \ diff --git a/embed/exports.txt b/embed/exports.txt index 67c0306..ed7b81d 100644 --- a/embed/exports.txt +++ b/embed/exports.txt @@ -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 diff --git a/embed/sqlite3.wasm b/embed/sqlite3.wasm index 2e67f81..825b2dc 100755 Binary files a/embed/sqlite3.wasm and b/embed/sqlite3.wasm differ diff --git a/sqlite3/column.c b/sqlite3/column.c new file mode 100644 index 0000000..b8fceda --- /dev/null +++ b/sqlite3/column.c @@ -0,0 +1,51 @@ +#include + +#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"); diff --git a/sqlite3/main.c b/sqlite3/main.c index da2d37f..eb5074a 100644 --- a/sqlite3/main.c +++ b/sqlite3/main.c @@ -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" diff --git a/sqlite3/vtab.c b/sqlite3/vtab.c index 073402b..cb33864 100644 --- a/sqlite3/vtab.c +++ b/sqlite3/vtab.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) { diff --git a/stmt.go b/stmt.go index 48c2ad5..4837760 100644 --- a/stmt.go +++ b/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 +} diff --git a/vfs/tests/mptest/testdata/build.sh b/vfs/tests/mptest/testdata/build.sh index 34150e3..570fa12 100755 --- a/vfs/tests/mptest/testdata/build.sh +++ b/vfs/tests/mptest/testdata/build.sh @@ -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 \ diff --git a/vfs/tests/speedtest1/testdata/build.sh b/vfs/tests/speedtest1/testdata/build.sh index ad24c78..3436ec3 100755 --- a/vfs/tests/speedtest1/testdata/build.sh +++ b/vfs/tests/speedtest1/testdata/build.sh @@ -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 \