Files
sqlite3/module.go

134 lines
2.4 KiB
Go
Raw Normal View History

2023-01-12 13:43:35 +00:00
package sqlite3
import (
"context"
2023-02-23 14:13:32 +00:00
"crypto/rand"
2023-03-06 17:51:25 +00:00
"math"
2023-01-12 13:43:35 +00:00
"os"
2023-02-23 14:13:32 +00:00
"runtime"
2023-01-28 12:47:39 +00:00
"strconv"
2023-01-12 13:43:35 +00:00
"sync"
"sync/atomic"
"github.com/tetratelabs/wazero"
2023-01-28 12:47:39 +00:00
"github.com/tetratelabs/wazero/api"
2023-01-12 13:43:35 +00:00
)
2023-03-01 10:34:08 +00:00
// Configure SQLite WASM.
//
// Importing package embed initializes these
// with an appropriate build of SQLite:
//
// import _ "github.com/ncruces/go-sqlite3/embed"
2023-01-12 13:43:35 +00:00
var (
2023-03-01 10:34:08 +00:00
Binary []byte // WASM binary to load.
2023-01-12 13:43:35 +00:00
Path string // Path to load the binary from.
)
2023-01-28 12:47:39 +00:00
var sqlite3 sqlite3Runtime
2023-01-12 13:43:35 +00:00
2023-01-28 12:47:39 +00:00
type sqlite3Runtime struct {
once sync.Once
runtime wazero.Runtime
compiled wazero.CompiledModule
instances atomic.Uint64
err error
}
2023-01-12 13:43:35 +00:00
2023-03-06 12:22:17 +00:00
func instantiateModule() (m *module, err error) {
ctx := context.Background()
sqlite3.once.Do(func() { sqlite3.compileModule(ctx) })
if sqlite3.err != nil {
return nil, sqlite3.err
2023-01-28 12:47:39 +00:00
}
2023-01-18 01:30:11 +00:00
2023-03-06 12:22:17 +00:00
name := "sqlite3-" + strconv.FormatUint(sqlite3.instances.Add(1), 10)
cfg := wazero.NewModuleConfig().WithName(name).
2023-02-23 14:13:32 +00:00
WithSysWalltime().WithSysNanotime().WithSysNanosleep().
WithOsyield(runtime.Gosched).
WithRandSource(rand.Reader)
2023-03-06 12:22:17 +00:00
mod, err := sqlite3.runtime.InstantiateModule(ctx, sqlite3.compiled, cfg)
if err != nil {
return nil, err
}
module := &module{
Module: mod,
ctx: ctx,
mem: memory{mod},
}
err = module.loadAPI()
if err != nil {
return nil, err
}
return module, nil
2023-01-28 12:47:39 +00:00
}
2023-02-15 16:24:34 +00:00
func (s *sqlite3Runtime) compileModule(ctx context.Context) {
s.runtime = wazero.NewRuntime(ctx)
2023-02-16 13:52:05 +00:00
vfsInstantiate(ctx, s.runtime)
2023-01-12 13:43:35 +00:00
2023-01-28 12:47:39 +00:00
bin := Binary
if bin == nil && Path != "" {
bin, s.err = os.ReadFile(Path)
if s.err != nil {
return
2023-01-12 13:43:35 +00:00
}
}
2023-02-20 13:38:03 +00:00
if bin == nil {
s.err = binaryErr
return
}
2023-01-12 13:43:35 +00:00
2023-02-15 16:24:34 +00:00
s.compiled, s.err = s.runtime.CompileModule(ctx, bin)
2023-01-12 13:43:35 +00:00
}
2023-03-06 12:22:17 +00:00
type module struct {
api.Module
ctx context.Context
mem memory
api sqliteAPI
}
2023-03-06 17:51:25 +00:00
func (c *module) error(rc uint64, handle uint32, sql ...string) error {
if rc == _OK {
return nil
}
err := Error{code: rc}
if err.Code() == NOMEM || err.ExtendedCode() == IOERR_NOMEM {
panic(oomErr)
}
var r []uint64
r, _ = c.api.errstr.Call(c.ctx, rc)
if r != nil {
err.str = c.mem.readString(uint32(r[0]), _MAX_STRING)
}
r, _ = c.api.errmsg.Call(c.ctx, uint64(handle))
if r != nil {
err.msg = c.mem.readString(uint32(r[0]), _MAX_STRING)
}
if sql != nil {
r, _ = c.api.erroff.Call(c.ctx, uint64(handle))
if r != nil && r[0] != math.MaxUint32 {
err.sql = sql[0][r[0]:]
}
}
switch err.msg {
case err.str, "not an error":
err.msg = ""
}
return &err
}