2023-01-12 05:57:09 +00:00
|
|
|
package sqlite3
|
2023-01-11 14:58:20 +00:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"context"
|
2023-01-12 13:43:35 +00:00
|
|
|
"io/fs"
|
|
|
|
|
"path/filepath"
|
2023-01-12 05:57:09 +00:00
|
|
|
"strconv"
|
2023-01-11 14:58:20 +00:00
|
|
|
|
|
|
|
|
"github.com/tetratelabs/wazero"
|
|
|
|
|
"github.com/tetratelabs/wazero/api"
|
2023-01-12 13:43:35 +00:00
|
|
|
"github.com/tetratelabs/wazero/experimental/writefs"
|
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-16 12:54:24 +00:00
|
|
|
func Open(name string) (conn *Conn, err error) {
|
|
|
|
|
return OpenFlags(name, OPEN_READWRITE|OPEN_CREATE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func OpenFlags(name string, flags OpenFlag) (conn *Conn, err error) {
|
2023-01-12 05:57:09 +00:00
|
|
|
once.Do(compile)
|
|
|
|
|
|
2023-01-12 13:43:35 +00:00
|
|
|
var fs fs.FS
|
|
|
|
|
if name != ":memory:" {
|
|
|
|
|
dir := filepath.Dir(name)
|
|
|
|
|
name = filepath.Base(name)
|
|
|
|
|
fs, err = writefs.NewDirFS(dir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 05:57:09 +00:00
|
|
|
ctx := context.TODO()
|
|
|
|
|
|
|
|
|
|
cfg := wazero.NewModuleConfig().
|
|
|
|
|
WithName("sqlite3-" + strconv.FormatUint(counter.Add(1), 10))
|
2023-01-12 13:43:35 +00:00
|
|
|
if fs != nil {
|
|
|
|
|
cfg = cfg.WithFS(fs)
|
|
|
|
|
}
|
2023-01-12 05:57:09 +00:00
|
|
|
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"),
|
2023-01-12 13:43:35 +00:00
|
|
|
errstr: module.ExportedFunction("sqlite3_errstr"),
|
2023-01-12 05:57:09 +00:00
|
|
|
errmsg: module.ExportedFunction("sqlite3_errmsg"),
|
2023-01-12 13:43:35 +00:00
|
|
|
erroff: module.ExportedFunction("sqlite3_error_offset"),
|
2023-01-12 05:57:09 +00:00
|
|
|
open: module.ExportedFunction("sqlite3_open_v2"),
|
|
|
|
|
close: module.ExportedFunction("sqlite3_close"),
|
2023-01-15 04:35:37 +00:00
|
|
|
prepare: module.ExportedFunction("sqlite3_prepare_v3"),
|
|
|
|
|
finalize: module.ExportedFunction("sqlite3_finalize"),
|
2023-01-12 05:57:09 +00:00
|
|
|
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 13:43:35 +00:00
|
|
|
defer func() {
|
|
|
|
|
if conn == nil {
|
|
|
|
|
c.Close()
|
|
|
|
|
}
|
|
|
|
|
}()
|
2023-01-12 05:57:09 +00:00
|
|
|
|
2023-01-12 13:43:35 +00:00
|
|
|
namePtr := c.newString(name)
|
2023-01-15 04:35:37 +00:00
|
|
|
connPtr := c.newBytes(ptrSize)
|
2023-01-12 13:43:35 +00:00
|
|
|
|
2023-01-16 12:54:24 +00:00
|
|
|
r, err := c.api.open.Call(ctx, uint64(namePtr), uint64(connPtr), uint64(flags), 0)
|
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-15 04:35:37 +00:00
|
|
|
c.handle, _ = c.memory.ReadUint32Le(connPtr)
|
|
|
|
|
c.free(connPtr)
|
2023-01-12 13:43:35 +00:00
|
|
|
c.free(namePtr)
|
2023-01-11 14:58:20 +00:00
|
|
|
|
2023-01-16 12:54:24 +00:00
|
|
|
if r[0] != _OK {
|
2023-01-12 13:43:35 +00:00
|
|
|
return nil, c.error(r[0])
|
2023-01-12 05:57:09 +00:00
|
|
|
}
|
|
|
|
|
return &c, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:43:35 +00:00
|
|
|
func (c *Conn) Close() error {
|
|
|
|
|
r, err := c.api.close.Call(context.TODO(), uint64(c.handle))
|
2023-01-12 05:57:09 +00:00
|
|
|
if err != nil {
|
2023-01-11 14:58:20 +00:00
|
|
|
return err
|
|
|
|
|
}
|
2023-01-12 13:43:35 +00:00
|
|
|
|
2023-01-16 12:54:24 +00:00
|
|
|
if r[0] != _OK {
|
2023-01-12 13:43:35 +00:00
|
|
|
return c.error(r[0])
|
|
|
|
|
}
|
|
|
|
|
return c.module.Close(context.TODO())
|
2023-01-11 14:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:43:35 +00:00
|
|
|
func (c *Conn) Exec(sql string) error {
|
|
|
|
|
sqlPtr := c.newString(sql)
|
|
|
|
|
|
|
|
|
|
r, err := c.api.exec.Call(context.TODO(), uint64(c.handle), uint64(sqlPtr), 0, 0, 0)
|
2023-01-11 14:58:20 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:43:35 +00:00
|
|
|
c.free(sqlPtr)
|
|
|
|
|
|
2023-01-16 12:54:24 +00:00
|
|
|
if r[0] != _OK {
|
2023-01-12 13:43:35 +00:00
|
|
|
return c.error(r[0])
|
2023-01-11 14:58:20 +00:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 12:54:24 +00:00
|
|
|
func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) {
|
|
|
|
|
return c.PrepareFlags(sql, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) {
|
2023-01-15 04:35:37 +00:00
|
|
|
sqlPtr := c.newString(sql)
|
|
|
|
|
stmtPtr := c.newBytes(ptrSize)
|
|
|
|
|
tailPtr := c.newBytes(ptrSize)
|
|
|
|
|
|
|
|
|
|
r, err := c.api.prepare.Call(context.TODO(), uint64(c.handle),
|
2023-01-16 12:54:24 +00:00
|
|
|
uint64(sqlPtr), uint64(len(sql)+1), uint64(flags),
|
2023-01-15 04:35:37 +00:00
|
|
|
uint64(stmtPtr), uint64(tailPtr))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stmt = &Stmt{c: c}
|
|
|
|
|
stmt.handle, _ = c.memory.ReadUint32Le(stmtPtr)
|
|
|
|
|
i, _ := c.memory.ReadUint32Le(tailPtr)
|
|
|
|
|
tail = sql[i-sqlPtr:]
|
|
|
|
|
|
|
|
|
|
c.free(tailPtr)
|
|
|
|
|
c.free(stmtPtr)
|
|
|
|
|
c.free(sqlPtr)
|
|
|
|
|
|
2023-01-16 12:54:24 +00:00
|
|
|
if r[0] != _OK {
|
2023-01-15 04:35:37 +00:00
|
|
|
return nil, "", c.error(r[0])
|
|
|
|
|
}
|
|
|
|
|
if stmt.handle == 0 {
|
|
|
|
|
return nil, "", nil
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:43:35 +00:00
|
|
|
func (c *Conn) error(rc uint64) *Error {
|
2023-01-16 12:54:24 +00:00
|
|
|
serr := Error{
|
|
|
|
|
Code: ErrorCode(rc & 0xFF),
|
|
|
|
|
ExtendedCode: ExtendedErrorCode(rc),
|
|
|
|
|
}
|
2023-01-12 13:43:35 +00:00
|
|
|
|
|
|
|
|
var r []uint64
|
|
|
|
|
|
|
|
|
|
// string
|
2023-01-16 12:54:24 +00:00
|
|
|
r, _ = c.api.errstr.Call(context.TODO(), rc)
|
2023-01-12 13:43:35 +00:00
|
|
|
if r != nil {
|
|
|
|
|
serr.str = c.getString(uint32(r[0]), 512)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// message
|
|
|
|
|
r, _ = c.api.errmsg.Call(context.TODO(), uint64(c.handle))
|
|
|
|
|
if r != nil {
|
|
|
|
|
serr.msg = c.getString(uint32(r[0]), 512)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if serr.str == serr.msg {
|
|
|
|
|
serr.msg = ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &serr
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 05:57:09 +00:00
|
|
|
func (c *Conn) free(ptr uint32) {
|
2023-01-12 13:43:35 +00:00
|
|
|
if ptr == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-01-12 05:57:09 +00:00
|
|
|
_, 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])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-15 04:35:37 +00:00
|
|
|
const ptrSize = 4
|
|
|
|
|
|
2023-01-12 05:57:09 +00:00
|
|
|
type sqliteAPI struct {
|
|
|
|
|
malloc api.Function
|
|
|
|
|
free api.Function
|
2023-01-12 13:43:35 +00:00
|
|
|
errstr api.Function
|
2023-01-12 05:57:09 +00:00
|
|
|
errmsg api.Function
|
2023-01-12 13:43:35 +00:00
|
|
|
erroff api.Function
|
2023-01-12 05:57:09 +00:00
|
|
|
open api.Function
|
|
|
|
|
close api.Function
|
|
|
|
|
prepare api.Function
|
2023-01-15 04:35:37 +00:00
|
|
|
finalize api.Function
|
2023-01-12 05:57:09 +00:00
|
|
|
exec api.Function
|
|
|
|
|
step api.Function
|
|
|
|
|
columnInt api.Function
|
|
|
|
|
columnText api.Function
|
|
|
|
|
columnFloat api.Function
|
|
|
|
|
}
|