Skip to content

Commit b8936b7

Browse files
committed
Adding hook to vhook to allow vtables to be modified
This commit changes the vtable 'xUpdate' goModule field to a new 'cXUpdate' callback function which in turns calls a 'goVUpdate' callback. This new callback allows Go defined virtual table implementations satisfying the VTabUpdater interface (also newly defined) a way to delete/insert/update rows in a VTab. Additionally, an anonymous interface is used within the goVUpdate callback looking for 'TableName() string' which, when defined on a VTab is used to provide a better contextual error message to end users if the VTab is read only. Care was taken to follow existing code style/conventions for this addition, and for backwards-compatibility with existing VTab implementations (hence why a new interface was required).
1 parent b2e4645 commit b8936b7

File tree

1 file changed

+72
-2
lines changed

1 file changed

+72
-2
lines changed

sqlite3_vtable.go

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,19 @@ static int cXRowid(sqlite3_vtab_cursor *pCursor, sqlite3_int64 *pRowid) {
182182
return SQLITE_OK;
183183
}
184184
185+
char* goVUpdate(void *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid);
186+
187+
static int cXUpdate(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid) {
188+
char *pzErr = goVUpdate(((goVTab*)pVTab)->vTab, argc, argv, pRowid);
189+
if (pzErr) {
190+
if (pVTab->zErrMsg)
191+
sqlite3_free(pVTab->zErrMsg);
192+
pVTab->zErrMsg = pzErr;
193+
return SQLITE_ERROR;
194+
}
195+
return SQLITE_OK;
196+
}
197+
185198
static sqlite3_module goModule = {
186199
0, // iVersion
187200
cXCreate, // xCreate - create a table
@@ -196,8 +209,8 @@ static sqlite3_module goModule = {
196209
cXEof, // xEof
197210
cXColumn, // xColumn - read data
198211
cXRowid, // xRowid - read data
212+
cXUpdate, // xUpdate - write data
199213
// Not implemented
200-
0, // xUpdate - write data
201214
0, // xBegin - begin transaction
202215
0, // xSync - sync transaction
203216
0, // xCommit - commit transaction
@@ -218,6 +231,7 @@ static int _sqlite3_create_module(sqlite3 *db, const char *zName, uintptr_t pCli
218231
import "C"
219232

220233
import (
234+
"fmt"
221235
"math"
222236
"reflect"
223237
"unsafe"
@@ -306,7 +320,7 @@ func orderBys(info *C.sqlite3_index_info) []InfoOrderBy {
306320
return ob
307321
}
308322

309-
// IndexResult is a Go struct represetnation of what eventually ends up in the
323+
// IndexResult is a Go struct representation of what eventually ends up in the
310324
// output fields for `sqlite3_index_info`
311325
// See: https://www.sqlite.org/c3ref/index_info.html
312326
type IndexResult struct {
@@ -502,6 +516,53 @@ func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char {
502516
return nil
503517
}
504518

519+
//export goVUpdate
520+
func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid *C.sqlite3_int64) *C.char {
521+
vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab)
522+
523+
var tname string
524+
if n, ok := vt.vTab.(interface {
525+
TableName() string
526+
}); ok {
527+
tname = n.TableName() + " "
528+
}
529+
530+
err := fmt.Errorf("virtual %s table %sis read-only", vt.module.name, tname)
531+
if v, ok := vt.vTab.(VTabUpdater); ok {
532+
// convert argv
533+
args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
534+
vals := make([]interface{}, 0, argc)
535+
for _, v := range args {
536+
conv, err := callbackArgGeneric(v)
537+
if err != nil {
538+
return mPrintf("%s", err.Error())
539+
}
540+
vals = append(vals, conv.Interface())
541+
}
542+
543+
switch {
544+
case argc == 1:
545+
err = v.Delete(vals[0])
546+
547+
case argc > 1 && vals[0] == nil:
548+
var id int64
549+
id, err = v.Insert(vals[1], vals[2:])
550+
if err == nil {
551+
*pRowid = C.sqlite3_int64(id)
552+
}
553+
554+
case argc > 1:
555+
err = v.Update(vals[1], vals[2:])
556+
}
557+
}
558+
559+
if err != nil {
560+
return mPrintf("%s", err.Error())
561+
}
562+
563+
return nil
564+
}
565+
505566
// Module is a "virtual table module", it defines the implementation of a
506567
// virtual tables. See: http://sqlite.org/c3ref/module.html
507568
type Module interface {
@@ -526,6 +587,15 @@ type VTab interface {
526587
Open() (VTabCursor, error)
527588
}
528589

590+
// VTabUpdater is a type that allows a VTab to be inserted, updated, or
591+
// deleted.
592+
// See: https://sqlite.org/vtab.html#xupdate
593+
type VTabUpdater interface {
594+
Delete(interface{}) error
595+
Insert(interface{}, []interface{}) (int64, error)
596+
Update(interface{}, []interface{}) error
597+
}
598+
529599
// VTabCursor describes cursors that point into the virtual table and are used
530600
// to loop through the virtual table. See: http://sqlite.org/c3ref/vtab_cursor.html
531601
type VTabCursor interface {

0 commit comments

Comments
 (0)