Files
sqlite3/conn.go

208 lines
4.3 KiB
Go
Raw Normal View History

2023-01-12 05:57:09 +00:00
package sqlite3
2023-01-11 14:58:20 +00:00
import (
"bytes"
"context"
"errors"
"fmt"
2023-01-12 05:57:09 +00:00
"os"
"strconv"
"sync"
"sync/atomic"
2023-01-11 14:58:20 +00:00
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
)
2023-01-12 05:57:09 +00:00
// Configure SQLite.
var (
Binary []byte // Binary to load.
Path string // Path to load the binary from.
)
var (
once sync.Once
wasm wazero.Runtime
module wazero.CompiledModule
counter atomic.Uint64
)
2023-01-11 14:58:20 +00:00
2023-01-12 05:57:09 +00:00
func compile() {
ctx := context.Background()
2023-01-11 14:58:20 +00:00
2023-01-12 05:57:09 +00:00
wasm = wazero.NewRuntime(ctx)
2023-01-11 14:58:20 +00:00
wasi_snapshot_preview1.MustInstantiate(ctx, wasm)
2023-01-12 05:57:09 +00:00
if Binary == nil && Path != "" {
if bin, err := os.ReadFile(Path); err != nil {
panic(err)
} else {
Binary = bin
}
2023-01-11 14:58:20 +00:00
}
2023-01-12 05:57:09 +00:00
if m, err := wasm.CompileModule(ctx, Binary); err != nil {
2023-01-11 14:58:20 +00:00
panic(err)
2023-01-12 05:57:09 +00:00
} else {
module = m
2023-01-11 14:58:20 +00:00
}
}
2023-01-12 05:57:09 +00:00
type Conn struct {
handle uint32
module api.Module
memory api.Memory
api sqliteAPI
2023-01-11 14:58:20 +00:00
}
2023-01-12 05:57:09 +00:00
func Open(name string, flags uint64, vfs string) (*Conn, error) {
once.Do(compile)
ctx := context.TODO()
cfg := wazero.NewModuleConfig().
WithName("sqlite3-" + strconv.FormatUint(counter.Add(1), 10))
module, err := wasm.InstantiateModule(ctx, module, cfg)
2023-01-11 14:58:20 +00:00
if err != nil {
2023-01-12 05:57:09 +00:00
return nil, err
2023-01-11 14:58:20 +00:00
}
2023-01-12 05:57:09 +00:00
c := Conn{
module: module,
memory: module.Memory(),
api: sqliteAPI{
malloc: module.ExportedFunction("malloc"),
free: module.ExportedFunction("free"),
errmsg: module.ExportedFunction("sqlite3_errmsg"),
open: module.ExportedFunction("sqlite3_open_v2"),
close: module.ExportedFunction("sqlite3_close"),
prepare: module.ExportedFunction("sqlite3_prepare_v2"),
exec: module.ExportedFunction("sqlite3_exec"),
step: module.ExportedFunction("sqlite3_step"),
columnText: module.ExportedFunction("sqlite3_column_text"),
columnInt: module.ExportedFunction("sqlite3_column_int64"),
columnFloat: module.ExportedFunction("sqlite3_column_double"),
},
}
2023-01-11 14:58:20 +00:00
2023-01-12 05:57:09 +00:00
namePtr := c.newString(name)
defer c.free(namePtr)
2023-01-12 11:06:17 +00:00
handlePtr := c.newBytes(4)
2023-01-12 05:57:09 +00:00
defer c.free(handlePtr)
2023-01-11 14:58:20 +00:00
var vfsPtr uint32
if vfs != "" {
2023-01-12 05:57:09 +00:00
vfsPtr = c.newString(vfs)
defer c.free(vfsPtr)
2023-01-11 14:58:20 +00:00
}
2023-01-12 05:57:09 +00:00
r, err := c.api.open.Call(ctx, uint64(namePtr), uint64(handlePtr), flags, uint64(vfsPtr))
2023-01-11 14:58:20 +00:00
if err != nil {
2023-01-12 05:57:09 +00:00
_ = c.Close()
return nil, err
2023-01-11 14:58:20 +00:00
}
2023-01-12 05:57:09 +00:00
c.handle, _ = c.memory.ReadUint32Le(handlePtr)
2023-01-11 14:58:20 +00:00
if r[0] != SQLITE_OK {
2023-01-12 05:57:09 +00:00
err := fmt.Errorf("sqlite error (%d): %s", r[0], c.Errmsg())
_ = c.Close()
return nil, err
}
return &c, nil
}
func (c *Conn) Errmsg() error {
r, err := c.api.errmsg.Call(context.TODO(), uint64(c.handle))
if err != nil {
2023-01-11 14:58:20 +00:00
return err
}
2023-01-12 11:06:17 +00:00
return errors.New(c.getString(uint32(r[0]), 64))
2023-01-11 14:58:20 +00:00
}
2023-01-12 05:57:09 +00:00
func (c *Conn) Close() error {
r, err := c.api.close.Call(context.TODO(), uint64(c.handle))
2023-01-11 14:58:20 +00:00
if err != nil {
return err
}
if r[0] != SQLITE_OK {
2023-01-12 05:57:09 +00:00
return fmt.Errorf("sqlite error (%d): %s", r[0], c.Errmsg())
2023-01-11 14:58:20 +00:00
}
return nil
}
2023-01-12 05:57:09 +00:00
func (c *Conn) free(ptr uint32) {
_, err := c.api.free.Call(context.TODO(), uint64(ptr))
2023-01-11 14:58:20 +00:00
if err != nil {
panic(err)
}
}
2023-01-12 11:06:17 +00:00
func (c *Conn) newBytes(len uint32) uint32 {
r, err := c.api.malloc.Call(context.TODO(), uint64(len))
2023-01-11 14:58:20 +00:00
if err != nil {
panic(err)
}
2023-01-12 11:06:17 +00:00
if r[0] == 0 {
panic("sqlite3: out of memory")
}
2023-01-11 14:58:20 +00:00
return uint32(r[0])
}
2023-01-12 05:57:09 +00:00
func (c *Conn) newString(str string) uint32 {
2023-01-12 11:06:17 +00:00
ptr := c.newBytes(uint32(len(str) + 1))
2023-01-11 14:58:20 +00:00
2023-01-12 11:06:17 +00:00
buf, ok := c.memory.Read(ptr, uint32(len(str)+1))
if !ok {
c.api.free.Call(context.TODO(), uint64(ptr))
panic("sqlite3: failed to init string")
2023-01-11 14:58:20 +00:00
}
2023-01-12 11:06:17 +00:00
buf[len(str)] = 0
copy(buf, str)
2023-01-11 14:58:20 +00:00
return ptr
}
2023-01-12 11:06:17 +00:00
func (c *Conn) getString(ptr, maxlen uint32) string {
buf, ok := c.memory.Read(ptr, maxlen)
2023-01-11 14:58:20 +00:00
if !ok {
2023-01-12 11:06:17 +00:00
if size := c.memory.Size(); ptr < size {
buf, ok = c.memory.Read(ptr, size-ptr)
}
if !ok {
panic("sqlite3: invalid pointer")
}
2023-01-11 14:58:20 +00:00
}
if i := bytes.IndexByte(buf, 0); i < 0 {
2023-01-12 11:06:17 +00:00
panic("sqlite3: missing NUL terminator")
2023-01-11 14:58:20 +00:00
} else {
return string(buf[:i])
}
}
const (
SQLITE_OK = 0
SQLITE_ROW = 100
SQLITE_DONE = 101
SQLITE_OPEN_READWRITE = 0x00000002
SQLITE_OPEN_CREATE = 0x00000004
)
2023-01-12 05:57:09 +00:00
type sqliteAPI struct {
malloc api.Function
free api.Function
errmsg api.Function
open api.Function
close api.Function
prepare api.Function
exec api.Function
step api.Function
columnInt api.Function
columnText api.Function
columnFloat api.Function
}