mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
Towards virtual tables.
This commit is contained in:
165
vtab.go
165
vtab.go
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user