package sqlite3
import (
"errors"
"reflect"
)
func CreateModule [T VTab ](db *Conn , name string , create , connect VTabConstructor [T ]) error {
var flags int
const (
VTAB_CREATOR = 0x001
VTAB_DESTROYER = 0x002
VTAB_UPDATER = 0x004
VTAB_RENAMER = 0x008
VTAB_OVERLOADER = 0x010
VTAB_CHECKER = 0x020
VTAB_TXN = 0x040
VTAB_SAVEPOINTER = 0x080
VTAB_SHADOWTABS = 0x100
)
if create != nil {
flags |= VTAB_CREATOR
}
vtab := reflect .TypeOf (connect ).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 [VTabTxn ](vtab ) {
flags |= VTAB_TXN
}
if implements [VTabSavepointer ](vtab ) {
flags |= VTAB_SAVEPOINTER
}
if implements [VTabShadowTabler ](vtab ) {
flags |= VTAB_SHADOWTABS
}
var modulePtr ptr_t
defer db .arena .Mark ()()
namePtr := db .arena .String (name )
if connect != nil {
modulePtr = db .wrp .AddHandle (module [T ]{create , connect })
}
rc := res_t (db .wrp .Xsqlite3_create_module_go (int32 (db .handle ),
int32 (namePtr ), int32 (flags ), int32 (modulePtr )))
return db .error (rc )
}
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 {
if c .interrupt .Err () != nil {
return INTERRUPT
}
defer c .arena .Mark ()()
textPtr := c .arena .String (sql )
rc := res_t (c .wrp .Xsqlite3_declare_vtab (int32 (c .handle ), int32 (textPtr )))
return c .error (rc )
}
type VTabConflictMode uint8
const (
VTAB_ROLLBACK VTabConflictMode = 1
VTAB_IGNORE VTabConflictMode = 2
VTAB_FAIL VTabConflictMode = 3
VTAB_ABORT VTabConflictMode = 4
VTAB_REPLACE VTabConflictMode = 5
)
func (c *Conn ) VTabOnConflict () VTabConflictMode {
return VTabConflictMode (c .wrp .Xsqlite3_vtab_on_conflict (int32 (c .handle )))
}
type VTabConfigOption uint8
const (
VTAB_CONSTRAINT_SUPPORT VTabConfigOption = 1
VTAB_INNOCUOUS VTabConfigOption = 2
VTAB_DIRECTONLY VTabConfigOption = 3
VTAB_USES_ALL_SCHEMAS VTabConfigOption = 4
)
func (c *Conn ) VTabConfig (op VTabConfigOption , args ...any ) error {
var i int32
if op == VTAB_CONSTRAINT_SUPPORT && len (args ) > 0 {
if b , ok := args [0 ].(bool ); ok && b {
i = 1
}
}
rc := res_t (c .wrp .Xsqlite3_vtab_config_go (int32 (c .handle ), int32 (op ), i ))
return c .error (rc )
}
type VTabConstructor [T VTab ] func (db *Conn , module, schema, table string , arg ...string ) (T , error )
type module[T VTab ] [2 ]VTabConstructor [T ]
type vtabConstructor int
const (
xCreate vtabConstructor = 0
xConnect vtabConstructor = 1
)
type VTab interface {
BestIndex (*IndexInfo ) error
Open () (VTabCursor , error )
}
type VTabDestroyer interface {
VTab
Destroy () error
}
type VTabUpdater interface {
VTab
Update (arg ...Value ) (rowid int64 , err error )
}
type VTabRenamer interface {
VTab
Rename (new string ) error
}
type VTabOverloader interface {
VTab
FindFunction (arg int , name string ) (ScalarFunction , IndexConstraintOp )
}
type VTabShadowTabler interface {
VTab
ShadowTables ()
}
type VTabChecker interface {
VTab
Integrity (schema, table string , flags int ) error
}
type VTabTxn interface {
VTab
Begin () error
Sync () error
Commit () error
Rollback () error
}
type VTabSavepointer interface {
VTabTxn
Savepoint (id int ) error
Release (id int ) error
RollbackTo (id int ) error
}
type VTabCursor interface {
Filter (idxNum int , idxStr string , arg ...Value ) error
Next () error
EOF () bool
Column (ctx Context , n int ) error
RowID () (int64 , error )
}
type IndexInfo struct {
Constraint []IndexConstraint
OrderBy []IndexOrderBy
ColumnsUsed uint64
ConstraintUsage []IndexConstraintUsage
IdxNum int
IdxStr string
IdxFlags IndexScanFlag
OrderByConsumed bool
EstimatedCost float64
EstimatedRows int64
c *Conn
handle ptr_t
}
type IndexConstraint struct {
Column int
Op IndexConstraintOp
Usable bool
}
type IndexOrderBy struct {
Column int
Desc bool
}
type IndexConstraintUsage struct {
ArgvIndex int
Omit bool
}
func (idx *IndexInfo ) RHSValue (column int ) (Value , error ) {
defer idx .c .arena .Mark ()()
valPtr := idx .c .arena .New (ptrlen )
rc := res_t (idx .c .wrp .Xsqlite3_vtab_rhs_value (int32 (idx .handle ),
int32 (column ), int32 (valPtr )))
if err := idx .c .error (rc ); err != nil {
return Value {}, err
}
return Value {
c : idx .c ,
handle : ptr_t (idx .c .wrp .Read32 (valPtr )),
}, nil
}
func (idx *IndexInfo ) Collation (column int ) string {
ptr := ptr_t (idx .c .wrp .Xsqlite3_vtab_collation (int32 (idx .handle ),
int32 (column )))
return idx .c .wrp .ReadString (ptr , _MAX_NAME )
}
func (idx *IndexInfo ) Distinct () int {
i := int32 (idx .c .wrp .Xsqlite3_vtab_distinct (int32 (idx .handle )))
return int (i )
}
func (idx *IndexInfo ) In (column , handle int ) bool {
b := int32 (idx .c .wrp .Xsqlite3_vtab_in (int32 (idx .handle ),
int32 (column ), int32 (handle )))
return b != 0
}
func (idx *IndexInfo ) load () {
mem := idx .c .wrp .Memory
ptr := idx .handle
nConstraint := int32 (mem .Read32 (ptr + 0 ))
idx .Constraint = make ([]IndexConstraint , nConstraint )
idx .ConstraintUsage = make ([]IndexConstraintUsage , nConstraint )
idx .OrderBy = make ([]IndexOrderBy , int32 (mem .Read32 (ptr +8 )))
constraintPtr := ptr_t (mem .Read32 (ptr + 4 ))
constraint := idx .Constraint
for i := range idx .Constraint {
constraint [i ] = IndexConstraint {
Column : int (int32 (mem .Read32 (constraintPtr + 0 ))),
Op : IndexConstraintOp (mem .Read (constraintPtr + 4 )),
Usable : mem .Read (constraintPtr +5 ) != 0 ,
}
constraintPtr += 12
}
orderByPtr := ptr_t (mem .Read32 (ptr + 12 ))
orderBy := idx .OrderBy
for i := range orderBy {
orderBy [i ] = IndexOrderBy {
Column : int (int32 (mem .Read32 (orderByPtr + 0 ))),
Desc : mem .Read (orderByPtr +4 ) != 0 ,
}
orderByPtr += 8
}
idx .EstimatedCost = mem .ReadFloat64 (ptr + 40 )
idx .EstimatedRows = int64 (mem .Read64 (ptr + 48 ))
idx .ColumnsUsed = mem .Read64 (ptr + 64 )
}
func (idx *IndexInfo ) save () {
mem := idx .c .wrp .Memory
ptr := idx .handle
usagePtr := ptr_t (mem .Read32 (ptr + 16 ))
for _ , usage := range idx .ConstraintUsage {
mem .Write32 (usagePtr +0 , uint32 (usage .ArgvIndex ))
if usage .Omit {
mem .Write (usagePtr +4 , 1 )
}
usagePtr += 8
}
mem .Write32 (ptr +20 , uint32 (idx .IdxNum ))
if idx .IdxStr != "" {
mem .Write32 (ptr +24 , uint32 (idx .c .wrp .NewString (idx .IdxStr )))
mem .WriteBool (ptr +28 , true )
}
if idx .OrderByConsumed {
mem .WriteBool (ptr +32 , true )
}
mem .WriteFloat64 (ptr +40 , idx .EstimatedCost )
mem .Write64 (ptr +48 , uint64 (idx .EstimatedRows ))
mem .Write32 (ptr +56 , uint32 (idx .IdxFlags ))
}
type IndexConstraintOp uint8
const (
INDEX_CONSTRAINT_EQ IndexConstraintOp = 2
INDEX_CONSTRAINT_GT IndexConstraintOp = 4
INDEX_CONSTRAINT_LE IndexConstraintOp = 8
INDEX_CONSTRAINT_LT IndexConstraintOp = 16
INDEX_CONSTRAINT_GE IndexConstraintOp = 32
INDEX_CONSTRAINT_MATCH IndexConstraintOp = 64
INDEX_CONSTRAINT_LIKE IndexConstraintOp = 65
INDEX_CONSTRAINT_GLOB IndexConstraintOp = 66
INDEX_CONSTRAINT_REGEXP IndexConstraintOp = 67
INDEX_CONSTRAINT_NE IndexConstraintOp = 68
INDEX_CONSTRAINT_ISNOT IndexConstraintOp = 69
INDEX_CONSTRAINT_ISNOTNULL IndexConstraintOp = 70
INDEX_CONSTRAINT_ISNULL IndexConstraintOp = 71
INDEX_CONSTRAINT_IS IndexConstraintOp = 72
INDEX_CONSTRAINT_LIMIT IndexConstraintOp = 73
INDEX_CONSTRAINT_OFFSET IndexConstraintOp = 74
INDEX_CONSTRAINT_FUNCTION IndexConstraintOp = 150
)
type IndexScanFlag uint32
const (
INDEX_SCAN_UNIQUE IndexScanFlag = 0x00000001
INDEX_SCAN_HEX IndexScanFlag = 0x00000002
)
func (e *env ) vtabModuleCallback (kind vtabConstructor , pMod , nArg , pArg , ppVTab , pzErr int32 ) int32 {
arg := make ([]reflect .Value , 1 +nArg )
arg [0 ] = reflect .ValueOf (e .DB )
for i := range nArg {
ptr := ptr_t (e .Memory .Read32 (ptr_t (pArg + i *ptrlen )))
arg [i +1 ] = reflect .ValueOf (e .ReadString (ptr , _MAX_SQL_LENGTH ))
}
module := e .vtabGetHandle (pMod )
val := reflect .ValueOf (module ).Index (int (kind )).Call (arg )
err , _ := val [1 ].Interface ().(error )
if err == nil {
e .vtabPutHandle (ppVTab , val [0 ].Interface ())
}
return e .vtabError (pzErr , _PTR_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_create (pMod , nArg , pArg , ppVTab , pzErr int32 ) int32 {
return e .vtabModuleCallback (xCreate , pMod , nArg , pArg , ppVTab , pzErr )
}
func (e *env ) Xgo_vtab_connect (pMod , nArg , pArg , ppVTab , pzErr int32 ) int32 {
return e .vtabModuleCallback (xConnect , pMod , nArg , pArg , ppVTab , pzErr )
}
func (e *env ) Xgo_vtab_disconnect (pVTab int32 ) int32 {
err := e .vtabDelHandle (pVTab )
return e .vtabError (0 , _PTR_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_destroy (pVTab int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTabDestroyer )
err := errors .Join (vtab .Destroy (), e .vtabDelHandle (pVTab ))
return e .vtabError (0 , _PTR_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_best_index (pVTab , pIdxInfo int32 ) int32 {
var info IndexInfo
info .handle = ptr_t (pIdxInfo )
info .c = e .DB .(*Conn )
info .load ()
vtab := e .vtabGetHandle (pVTab ).(VTab )
err := vtab .BestIndex (&info )
info .save ()
return e .vtabError (pVTab , _VTAB_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_update (pVTab , nArg , pArg , pRowID int32 ) int32 {
db := e .DB .(*Conn )
args := callbackArgs (db , nArg , ptr_t (pArg ))
defer returnArgs (args )
vtab := e .vtabGetHandle (pVTab ).(VTabUpdater )
rowID , err := vtab .Update (*args ...)
if err == nil {
e .Memory .Write64 (ptr_t (pRowID ), uint64 (rowID ))
}
return e .vtabError (pVTab , _VTAB_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_rename (pVTab , zNew int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTabRenamer )
err := vtab .Rename (e .ReadString (ptr_t (zNew ), _MAX_NAME ))
return e .vtabError (pVTab , _VTAB_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_find_function (pVTab , nArg , zName , pxFunc int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTabOverloader )
f , op := vtab .FindFunction (int (nArg ), e .ReadString (ptr_t (zName ), _MAX_NAME ))
if op != 0 {
var wrapper ptr_t
wrapper = e .AddHandle (func (c Context , arg ...Value ) {
defer e .DelHandle (wrapper )
f (c , arg ...)
})
e .Memory .Write32 (ptr_t (pxFunc ), uint32 (wrapper ))
}
return int32 (op )
}
func (e *env ) Xgo_vtab_integrity (pVTab , zSchema , zTabName , mFlags , pzErr int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTabChecker )
schema := e .ReadString (ptr_t (zSchema ), _MAX_NAME )
table := e .ReadString (ptr_t (zTabName ), _MAX_NAME )
err := vtab .Integrity (schema , table , int (uint32 (mFlags )))
return e .vtabError (pzErr , _PTR_ERROR , err , _OK )
}
func (e *env ) Xgo_vtab_begin (pVTab int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTabTxn )
err := vtab .Begin ()
return e .vtabError (pVTab , _VTAB_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_sync (pVTab int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTabTxn )
err := vtab .Sync ()
return e .vtabError (pVTab , _VTAB_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_commit (pVTab int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTabTxn )
err := vtab .Commit ()
return e .vtabError (pVTab , _VTAB_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_rollback (pVTab int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTabTxn )
err := vtab .Rollback ()
return e .vtabError (pVTab , _VTAB_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_savepoint (pVTab , id int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTabSavepointer )
err := vtab .Savepoint (int (id ))
return e .vtabError (pVTab , _VTAB_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_release (pVTab , id int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTabSavepointer )
err := vtab .Release (int (id ))
return e .vtabError (pVTab , _VTAB_ERROR , err , ERROR )
}
func (e *env ) Xgo_vtab_rollback_to (pVTab , id int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTabSavepointer )
err := vtab .RollbackTo (int (id ))
return e .vtabError (pVTab , _VTAB_ERROR , err , ERROR )
}
func (e *env ) Xgo_cur_open (pVTab , ppCur int32 ) int32 {
vtab := e .vtabGetHandle (pVTab ).(VTab )
cursor , err := vtab .Open ()
if err == nil {
e .vtabPutHandle (ppCur , cursor )
}
return e .vtabError (pVTab , _VTAB_ERROR , err , ERROR )
}
func (e *env ) Xgo_cur_close (pCur int32 ) int32 {
err := e .vtabDelHandle (pCur )
return e .vtabError (0 , _PTR_ERROR , err , ERROR )
}
func (e *env ) Xgo_cur_filter (pCur , idxNum , idxStr , nArg , pArg int32 ) int32 {
db := e .DB .(*Conn )
args := callbackArgs (db , nArg , ptr_t (pArg ))
defer returnArgs (args )
var idxName string
if idxStr != 0 {
idxName = e .ReadString (ptr_t (idxStr ), _MAX_LENGTH )
}
cursor := e .vtabGetHandle (pCur ).(VTabCursor )
err := cursor .Filter (int (idxNum ), idxName , *args ...)
return e .vtabError (pCur , _CURSOR_ERROR , err , ERROR )
}
func (e *env ) Xgo_cur_eof (pCur int32 ) int32 {
cursor := e .vtabGetHandle (pCur ).(VTabCursor )
if cursor .EOF () {
return 1
}
return 0
}
func (e *env ) Xgo_cur_next (pCur int32 ) int32 {
cursor := e .vtabGetHandle (pCur ).(VTabCursor )
err := cursor .Next ()
return e .vtabError (pCur , _CURSOR_ERROR , err , ERROR )
}
func (e *env ) Xgo_cur_column (pCur , pCtx , n int32 ) int32 {
cursor := e .vtabGetHandle (pCur ).(VTabCursor )
db := e .DB .(*Conn )
err := cursor .Column (Context {db , ptr_t (pCtx )}, int (n ))
return e .vtabError (pCur , _CURSOR_ERROR , err , ERROR )
}
func (e *env ) Xgo_cur_rowid (pCur , pRowID int32 ) int32 {
cursor := e .vtabGetHandle (pCur ).(VTabCursor )
rowID , err := cursor .RowID ()
if err == nil {
e .Memory .Write64 (ptr_t (pRowID ), uint64 (rowID ))
}
return e .vtabError (pCur , _CURSOR_ERROR , err , ERROR )
}
const (
_PTR_ERROR = iota
_VTAB_ERROR
_CURSOR_ERROR
)
func (e *env ) vtabError (ptr int32 , kind uint32 , err error , def ErrorCode ) int32 {
const zErrMsgOffset = 8
msg , code := errorCode (err , def )
if ptr != 0 && msg != "" {
switch kind {
case _VTAB_ERROR :
ptr = ptr + zErrMsgOffset
case _CURSOR_ERROR :
ptr = int32 (e .Memory .Read32 (ptr_t (ptr ))) + zErrMsgOffset
}
db := e .DB .(*Conn )
if ptr := ptr_t (e .Memory .Read32 (ptr_t (ptr ))); ptr != 0 {
db .wrp .Free (ptr )
}
e .Memory .Write32 (ptr_t (ptr ), uint32 (db .wrp .NewString (msg )))
}
return int32 (code )
}
func (e *env ) vtabGetHandle (ptr int32 ) any {
const handleOffset = 4
handle := ptr_t (e .Memory .Read32 (ptr_t (ptr ) - handleOffset ))
return e .GetHandle (handle )
}
func (e *env ) vtabDelHandle (ptr int32 ) error {
const handleOffset = 4
handle := ptr_t (e .Memory .Read32 (ptr_t (ptr ) - handleOffset ))
return e .DelHandle (handle )
}
func (e *env ) vtabPutHandle (pptr int32 , val any ) {
const handleOffset = 4
handle := e .AddHandle (val )
ptr := ptr_t (e .Memory .Read32 (ptr_t (pptr )))
e .Memory .Write32 (ptr -handleOffset , uint32 (handle ))
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .