Towards virtual tables.

This commit is contained in:
Nuno Cruces
2023-11-16 01:16:38 +00:00
parent 314098addb
commit 787086b8c1
17 changed files with 317 additions and 97 deletions

165
vtab.go
View File

@@ -1,21 +1,93 @@
package sqlite3
import (
"context"
"reflect"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/tetratelabs/wazero/api"
)
// CreateModule register a new virtual table module name.
func CreateModule[T VTab](conn *Conn, name string, module Module[T]) error {
var flags int
const (
VTAB_CREATOR = 0x01
VTAB_DESTROYER = 0x02
VTAB_UPDATER = 0x04
VTAB_RENAMER = 0x08
VTAB_OVERLOADER = 0x10
VTAB_CHECKER = 0x20
VTAB_TX = 0x40
VTAB_SAVEPOINTER = 0x80
)
create, ok := reflect.TypeOf(module).MethodByName("Create")
connect, _ := reflect.TypeOf(module).MethodByName("Connect")
if ok && create.Type == connect.Type {
flags |= VTAB_CREATOR
}
vtab := connect.Type.Out(0)
if implements[VTabDestroyer](vtab) {
flags |= VTAB_DESTROYER
}
if implements[VTabUpdater](vtab) {
flags |= VTAB_UPDATER
}
if implements[VTabRenamer](vtab) {
flags |= VTAB_RENAMER
}
if implements[VTabOverloader](vtab) {
flags |= VTAB_OVERLOADER
}
if implements[VTabChecker](vtab) {
flags |= VTAB_CHECKER
}
if implements[VTabTx](vtab) {
flags |= VTAB_TX
}
if implements[VTabSavepointer](vtab) {
flags |= VTAB_SAVEPOINTER
}
defer conn.arena.reset()
namePtr := conn.arena.string(name)
modulePtr := util.AddHandle(conn.ctx, module)
r := conn.call(conn.api.createModule, uint64(conn.handle),
uint64(namePtr), uint64(flags), uint64(modulePtr))
return conn.error(r)
}
func implements[T any](typ reflect.Type) bool {
var ptr *T
return typ.Implements(reflect.TypeOf(ptr).Elem())
}
func (c *Conn) DeclareVtab(sql string) error {
defer c.arena.reset()
sqlPtr := c.arena.string(sql)
r := c.call(c.api.declareVTab, uint64(c.handle), uint64(sqlPtr))
return c.error(r)
}
// A Module defines the implementation of a virtual table.
// Modules that don't also implement [ModuleCreator] provide
// A Module that doesn't implement [ModuleCreator] provides
// eponymous-only virtual tables or table-valued functions.
//
// https://sqlite.org/c3ref/module.html
type Module interface {
type Module[T VTab] interface {
// https://sqlite.org/vtab.html#xconnect
Connect(db *Conn, arg ...string) (VTab, error)
Connect(c *Conn, arg ...string) (T, error)
}
// A ModuleCreator extends Module for
// non-eponymous virtual tables.
type ModuleCreator interface {
Module
// A ModuleCreator allows virtual tables to be created.
// A persistent virtual table must implement [VTabDestroyer].
type ModuleCreator[T VTab] interface {
Module[T]
// https://sqlite.org/vtab.html#xcreate
Create(db *Conn, arg ...string) (VTabDestroyer, error)
Create(c *Conn, arg ...string) (T, error)
}
// A VTab describes a particular instance of the virtual table.
@@ -30,7 +102,7 @@ type VTab interface {
Open() (VTabCursor, error)
}
// A VTabDestroyer allows a virtual table to be destroyed.
// A VTabDestroyer allows a persistent virtual table to be destroyed.
type VTabDestroyer interface {
VTab
// https://sqlite.org/vtab.html#sqlite3_module.xDestroy
@@ -173,3 +245,78 @@ type IndexScanFlag uint8
const (
Unique IndexScanFlag = 1
)
func vtabConnectCallback(ctx context.Context, mod api.Module, pMod, argc, argv, ppVTab, pzErr uint32) uint32 {
const handleOffset = 4
handle := util.ReadUint32(mod, pMod-handleOffset)
module := util.GetHandle(ctx, handle)
db := ctx.Value(connKey{}).(*Conn)
arg := make([]reflect.Value, 1+argc)
arg[0] = reflect.ValueOf(db)
for i := uint32(0); i < argc; i++ {
ptr := util.ReadUint32(mod, argv+i*ptrlen)
arg[i+1] = reflect.ValueOf(util.ReadString(mod, ptr, _MAX_STRING))
}
res := reflect.ValueOf(module).MethodByName("Connect").Call(arg)
err, _ := res[1].Interface().(error)
if err == nil {
handle := util.AddHandle(ctx, res[0].Interface())
ptr := util.ReadUint32(mod, ppVTab)
util.WriteUint32(mod, ptr-handleOffset, handle)
return _OK
}
// TODO: error message
return errorCode(err, ERROR)
}
func vtabBestIndexCallback(ctx context.Context, mod api.Module, pVTab, pIdxInfo uint32) uint32 {
const handleOffset = 4
handle := util.ReadUint32(mod, pVTab-handleOffset)
vtab := util.GetHandle(ctx, handle).(VTab)
_ = vtab
return 1
}
func vtabDisconnectCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
return 1
}
func vtabIntegrityCallback(ctx context.Context, mod api.Module, pVTab, zSchema, zTabName, mFlags, pzErr uint32) uint32 {
return 1
}
func vtabCallbackI(ctx context.Context, mod api.Module, _ uint32) uint32 {
return 1
}
func vtabCallbackII(ctx context.Context, mod api.Module, _, _ uint32) uint32 {
return 1
}
func vtabCallbackIIII(ctx context.Context, mod api.Module, _, _, _, _ uint32) uint32 {
return 1
}
func cursorOpenCallback(ctx context.Context, mod api.Module, pVTab, ppCur uint32) uint32 {
return 1
}
func cursorFilterCallback(ctx context.Context, mod api.Module, pCur, idxNum, idxStr, argc, argv uint32) uint32 {
return 1
}
func cursorColumnCallback(ctx context.Context, mod api.Module, pCur, pCtx, n uint32) uint32 {
return 1
}
func cursorRowidCallback(ctx context.Context, mod api.Module, pCur, pRowid uint32) uint32 {
return 1
}
func cursorCallbackI(ctx context.Context, mod api.Module, _ uint32) uint32 {
return 1
}