podman
721 строка · 19.9 Кб
1// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
2//
3// Use of this source code is governed by an MIT-style
4// license that can be found in the LICENSE file.
5
6//go:build sqlite_vtable || vtable
7// +build sqlite_vtable vtable
8
9package sqlite3
10
11/*
12#cgo CFLAGS: -std=gnu99
13#cgo CFLAGS: -DSQLITE_ENABLE_RTREE
14#cgo CFLAGS: -DSQLITE_THREADSAFE
15#cgo CFLAGS: -DSQLITE_ENABLE_FTS3
16#cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS
17#cgo CFLAGS: -DSQLITE_ENABLE_FTS4_UNICODE61
18#cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15
19#cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA=1
20#cgo CFLAGS: -Wno-deprecated-declarations
21
22#ifndef USE_LIBSQLITE3
23#include "sqlite3-binding.h"
24#else
25#include <sqlite3.h>
26#endif
27#include <stdlib.h>
28#include <stdint.h>
29#include <memory.h>
30
31static inline char *_sqlite3_mprintf(char *zFormat, char *arg) {
32return sqlite3_mprintf(zFormat, arg);
33}
34
35typedef struct goVTab goVTab;
36
37struct goVTab {
38sqlite3_vtab base;
39void *vTab;
40};
41
42uintptr_t goMInit(void *db, void *pAux, int argc, char **argv, char **pzErr, int isCreate);
43
44static int cXInit(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr, int isCreate) {
45void *vTab = (void *)goMInit(db, pAux, argc, (char**)argv, pzErr, isCreate);
46if (!vTab || *pzErr) {
47return SQLITE_ERROR;
48}
49goVTab *pvTab = (goVTab *)sqlite3_malloc(sizeof(goVTab));
50if (!pvTab) {
51*pzErr = sqlite3_mprintf("%s", "Out of memory");
52return SQLITE_NOMEM;
53}
54memset(pvTab, 0, sizeof(goVTab));
55pvTab->vTab = vTab;
56
57*ppVTab = (sqlite3_vtab *)pvTab;
58*pzErr = 0;
59return SQLITE_OK;
60}
61
62static inline int cXCreate(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) {
63return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 1);
64}
65static inline int cXConnect(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) {
66return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 0);
67}
68
69char* goVBestIndex(void *pVTab, void *icp);
70
71static inline int cXBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *info) {
72char *pzErr = goVBestIndex(((goVTab*)pVTab)->vTab, info);
73if (pzErr) {
74if (pVTab->zErrMsg)
75sqlite3_free(pVTab->zErrMsg);
76pVTab->zErrMsg = pzErr;
77return SQLITE_ERROR;
78}
79return SQLITE_OK;
80}
81
82char* goVRelease(void *pVTab, int isDestroy);
83
84static int cXRelease(sqlite3_vtab *pVTab, int isDestroy) {
85char *pzErr = goVRelease(((goVTab*)pVTab)->vTab, isDestroy);
86if (pzErr) {
87if (pVTab->zErrMsg)
88sqlite3_free(pVTab->zErrMsg);
89pVTab->zErrMsg = pzErr;
90return SQLITE_ERROR;
91}
92if (pVTab->zErrMsg)
93sqlite3_free(pVTab->zErrMsg);
94sqlite3_free(pVTab);
95return SQLITE_OK;
96}
97
98static inline int cXDisconnect(sqlite3_vtab *pVTab) {
99return cXRelease(pVTab, 0);
100}
101static inline int cXDestroy(sqlite3_vtab *pVTab) {
102return cXRelease(pVTab, 1);
103}
104
105typedef struct goVTabCursor goVTabCursor;
106
107struct goVTabCursor {
108sqlite3_vtab_cursor base;
109void *vTabCursor;
110};
111
112uintptr_t goVOpen(void *pVTab, char **pzErr);
113
114static int cXOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
115void *vTabCursor = (void *)goVOpen(((goVTab*)pVTab)->vTab, &(pVTab->zErrMsg));
116goVTabCursor *pCursor = (goVTabCursor *)sqlite3_malloc(sizeof(goVTabCursor));
117if (!pCursor) {
118return SQLITE_NOMEM;
119}
120memset(pCursor, 0, sizeof(goVTabCursor));
121pCursor->vTabCursor = vTabCursor;
122*ppCursor = (sqlite3_vtab_cursor *)pCursor;
123return SQLITE_OK;
124}
125
126static int setErrMsg(sqlite3_vtab_cursor *pCursor, char *pzErr) {
127if (pCursor->pVtab->zErrMsg)
128sqlite3_free(pCursor->pVtab->zErrMsg);
129pCursor->pVtab->zErrMsg = pzErr;
130return SQLITE_ERROR;
131}
132
133char* goVClose(void *pCursor);
134
135static int cXClose(sqlite3_vtab_cursor *pCursor) {
136char *pzErr = goVClose(((goVTabCursor*)pCursor)->vTabCursor);
137if (pzErr) {
138return setErrMsg(pCursor, pzErr);
139}
140sqlite3_free(pCursor);
141return SQLITE_OK;
142}
143
144char* goVFilter(void *pCursor, int idxNum, char* idxName, int argc, sqlite3_value **argv);
145
146static int cXFilter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) {
147char *pzErr = goVFilter(((goVTabCursor*)pCursor)->vTabCursor, idxNum, (char*)idxStr, argc, argv);
148if (pzErr) {
149return setErrMsg(pCursor, pzErr);
150}
151return SQLITE_OK;
152}
153
154char* goVNext(void *pCursor);
155
156static int cXNext(sqlite3_vtab_cursor *pCursor) {
157char *pzErr = goVNext(((goVTabCursor*)pCursor)->vTabCursor);
158if (pzErr) {
159return setErrMsg(pCursor, pzErr);
160}
161return SQLITE_OK;
162}
163
164int goVEof(void *pCursor);
165
166static inline int cXEof(sqlite3_vtab_cursor *pCursor) {
167return goVEof(((goVTabCursor*)pCursor)->vTabCursor);
168}
169
170char* goVColumn(void *pCursor, void *cp, int col);
171
172static int cXColumn(sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i) {
173char *pzErr = goVColumn(((goVTabCursor*)pCursor)->vTabCursor, ctx, i);
174if (pzErr) {
175return setErrMsg(pCursor, pzErr);
176}
177return SQLITE_OK;
178}
179
180char* goVRowid(void *pCursor, sqlite3_int64 *pRowid);
181
182static int cXRowid(sqlite3_vtab_cursor *pCursor, sqlite3_int64 *pRowid) {
183char *pzErr = goVRowid(((goVTabCursor*)pCursor)->vTabCursor, pRowid);
184if (pzErr) {
185return setErrMsg(pCursor, pzErr);
186}
187return SQLITE_OK;
188}
189
190char* goVUpdate(void *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid);
191
192static int cXUpdate(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid) {
193char *pzErr = goVUpdate(((goVTab*)pVTab)->vTab, argc, argv, pRowid);
194if (pzErr) {
195if (pVTab->zErrMsg)
196sqlite3_free(pVTab->zErrMsg);
197pVTab->zErrMsg = pzErr;
198return SQLITE_ERROR;
199}
200return SQLITE_OK;
201}
202
203static sqlite3_module goModule = {
2040, // iVersion
205cXCreate, // xCreate - create a table
206cXConnect, // xConnect - connect to an existing table
207cXBestIndex, // xBestIndex - Determine search strategy
208cXDisconnect, // xDisconnect - Disconnect from a table
209cXDestroy, // xDestroy - Drop a table
210cXOpen, // xOpen - open a cursor
211cXClose, // xClose - close a cursor
212cXFilter, // xFilter - configure scan constraints
213cXNext, // xNext - advance a cursor
214cXEof, // xEof
215cXColumn, // xColumn - read data
216cXRowid, // xRowid - read data
217cXUpdate, // xUpdate - write data
218// Not implemented
2190, // xBegin - begin transaction
2200, // xSync - sync transaction
2210, // xCommit - commit transaction
2220, // xRollback - rollback transaction
2230, // xFindFunction - function overloading
2240, // xRename - rename the table
2250, // xSavepoint
2260, // xRelease
2270 // xRollbackTo
228};
229
230// See https://sqlite.org/vtab.html#eponymous_only_virtual_tables
231static sqlite3_module goModuleEponymousOnly = {
2320, // iVersion
2330, // xCreate - create a table, which here is null
234cXConnect, // xConnect - connect to an existing table
235cXBestIndex, // xBestIndex - Determine search strategy
236cXDisconnect, // xDisconnect - Disconnect from a table
237cXDestroy, // xDestroy - Drop a table
238cXOpen, // xOpen - open a cursor
239cXClose, // xClose - close a cursor
240cXFilter, // xFilter - configure scan constraints
241cXNext, // xNext - advance a cursor
242cXEof, // xEof
243cXColumn, // xColumn - read data
244cXRowid, // xRowid - read data
245cXUpdate, // xUpdate - write data
246// Not implemented
2470, // xBegin - begin transaction
2480, // xSync - sync transaction
2490, // xCommit - commit transaction
2500, // xRollback - rollback transaction
2510, // xFindFunction - function overloading
2520, // xRename - rename the table
2530, // xSavepoint
2540, // xRelease
2550 // xRollbackTo
256};
257
258void goMDestroy(void*);
259
260static int _sqlite3_create_module(sqlite3 *db, const char *zName, uintptr_t pClientData) {
261return sqlite3_create_module_v2(db, zName, &goModule, (void*) pClientData, goMDestroy);
262}
263
264static int _sqlite3_create_module_eponymous_only(sqlite3 *db, const char *zName, uintptr_t pClientData) {
265return sqlite3_create_module_v2(db, zName, &goModuleEponymousOnly, (void*) pClientData, goMDestroy);
266}
267*/
268import "C"
269
270import (
271"fmt"
272"math"
273"reflect"
274"unsafe"
275)
276
277type sqliteModule struct {
278c *SQLiteConn
279name string
280module Module
281}
282
283type sqliteVTab struct {
284module *sqliteModule
285vTab VTab
286}
287
288type sqliteVTabCursor struct {
289vTab *sqliteVTab
290vTabCursor VTabCursor
291}
292
293// Op is type of operations.
294type Op uint8
295
296// Op mean identity of operations.
297const (
298OpEQ Op = 2
299OpGT = 4
300OpLE = 8
301OpLT = 16
302OpGE = 32
303OpMATCH = 64
304OpLIKE = 65 /* 3.10.0 and later only */
305OpGLOB = 66 /* 3.10.0 and later only */
306OpREGEXP = 67 /* 3.10.0 and later only */
307OpScanUnique = 1 /* Scan visits at most 1 row */
308)
309
310// InfoConstraint give information of constraint.
311type InfoConstraint struct {
312Column int
313Op Op
314Usable bool
315}
316
317// InfoOrderBy give information of order-by.
318type InfoOrderBy struct {
319Column int
320Desc bool
321}
322
323func constraints(info *C.sqlite3_index_info) []InfoConstraint {
324slice := *(*[]C.struct_sqlite3_index_constraint)(unsafe.Pointer(&reflect.SliceHeader{
325Data: uintptr(unsafe.Pointer(info.aConstraint)),
326Len: int(info.nConstraint),
327Cap: int(info.nConstraint),
328}))
329
330cst := make([]InfoConstraint, 0, len(slice))
331for _, c := range slice {
332var usable bool
333if c.usable > 0 {
334usable = true
335}
336cst = append(cst, InfoConstraint{
337Column: int(c.iColumn),
338Op: Op(c.op),
339Usable: usable,
340})
341}
342return cst
343}
344
345func orderBys(info *C.sqlite3_index_info) []InfoOrderBy {
346slice := *(*[]C.struct_sqlite3_index_orderby)(unsafe.Pointer(&reflect.SliceHeader{
347Data: uintptr(unsafe.Pointer(info.aOrderBy)),
348Len: int(info.nOrderBy),
349Cap: int(info.nOrderBy),
350}))
351
352ob := make([]InfoOrderBy, 0, len(slice))
353for _, c := range slice {
354var desc bool
355if c.desc > 0 {
356desc = true
357}
358ob = append(ob, InfoOrderBy{
359Column: int(c.iColumn),
360Desc: desc,
361})
362}
363return ob
364}
365
366// IndexResult is a Go struct representation of what eventually ends up in the
367// output fields for `sqlite3_index_info`
368// See: https://www.sqlite.org/c3ref/index_info.html
369type IndexResult struct {
370Used []bool // aConstraintUsage
371IdxNum int
372IdxStr string
373AlreadyOrdered bool // orderByConsumed
374EstimatedCost float64
375EstimatedRows float64
376}
377
378// mPrintf is a utility wrapper around sqlite3_mprintf
379func mPrintf(format, arg string) *C.char {
380cf := C.CString(format)
381defer C.free(unsafe.Pointer(cf))
382ca := C.CString(arg)
383defer C.free(unsafe.Pointer(ca))
384return C._sqlite3_mprintf(cf, ca)
385}
386
387//export goMInit
388func goMInit(db, pClientData unsafe.Pointer, argc C.int, argv **C.char, pzErr **C.char, isCreate C.int) C.uintptr_t {
389m := lookupHandle(pClientData).(*sqliteModule)
390if m.c.db != (*C.sqlite3)(db) {
391*pzErr = mPrintf("%s", "Inconsistent db handles")
392return 0
393}
394args := make([]string, argc)
395var A []*C.char
396slice := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(argv)), Len: int(argc), Cap: int(argc)}
397a := reflect.NewAt(reflect.TypeOf(A), unsafe.Pointer(&slice)).Elem().Interface()
398for i, s := range a.([]*C.char) {
399args[i] = C.GoString(s)
400}
401var vTab VTab
402var err error
403if isCreate == 1 {
404vTab, err = m.module.Create(m.c, args)
405} else {
406vTab, err = m.module.Connect(m.c, args)
407}
408
409if err != nil {
410*pzErr = mPrintf("%s", err.Error())
411return 0
412}
413vt := sqliteVTab{m, vTab}
414*pzErr = nil
415return C.uintptr_t(uintptr(newHandle(m.c, &vt)))
416}
417
418//export goVRelease
419func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char {
420vt := lookupHandle(pVTab).(*sqliteVTab)
421var err error
422if isDestroy == 1 {
423err = vt.vTab.Destroy()
424} else {
425err = vt.vTab.Disconnect()
426}
427if err != nil {
428return mPrintf("%s", err.Error())
429}
430return nil
431}
432
433//export goVOpen
434func goVOpen(pVTab unsafe.Pointer, pzErr **C.char) C.uintptr_t {
435vt := lookupHandle(pVTab).(*sqliteVTab)
436vTabCursor, err := vt.vTab.Open()
437if err != nil {
438*pzErr = mPrintf("%s", err.Error())
439return 0
440}
441vtc := sqliteVTabCursor{vt, vTabCursor}
442*pzErr = nil
443return C.uintptr_t(uintptr(newHandle(vt.module.c, &vtc)))
444}
445
446//export goVBestIndex
447func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char {
448vt := lookupHandle(pVTab).(*sqliteVTab)
449info := (*C.sqlite3_index_info)(icp)
450csts := constraints(info)
451res, err := vt.vTab.BestIndex(csts, orderBys(info))
452if err != nil {
453return mPrintf("%s", err.Error())
454}
455if len(res.Used) != len(csts) {
456return mPrintf("Result.Used != expected value", "")
457}
458
459// Get a pointer to constraint_usage struct so we can update in place.
460
461slice := *(*[]C.struct_sqlite3_index_constraint_usage)(unsafe.Pointer(&reflect.SliceHeader{
462Data: uintptr(unsafe.Pointer(info.aConstraintUsage)),
463Len: int(info.nConstraint),
464Cap: int(info.nConstraint),
465}))
466index := 1
467for i := range slice {
468if res.Used[i] {
469slice[i].argvIndex = C.int(index)
470slice[i].omit = C.uchar(1)
471index++
472}
473}
474
475info.idxNum = C.int(res.IdxNum)
476info.idxStr = (*C.char)(C.sqlite3_malloc(C.int(len(res.IdxStr) + 1)))
477if info.idxStr == nil {
478// C.malloc and C.CString ordinarily do this for you. See https://golang.org/cmd/cgo/
479panic("out of memory")
480}
481info.needToFreeIdxStr = C.int(1)
482
483idxStr := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
484Data: uintptr(unsafe.Pointer(info.idxStr)),
485Len: len(res.IdxStr) + 1,
486Cap: len(res.IdxStr) + 1,
487}))
488copy(idxStr, res.IdxStr)
489idxStr[len(idxStr)-1] = 0 // null-terminated string
490
491if res.AlreadyOrdered {
492info.orderByConsumed = C.int(1)
493}
494info.estimatedCost = C.double(res.EstimatedCost)
495info.estimatedRows = C.sqlite3_int64(res.EstimatedRows)
496
497return nil
498}
499
500//export goVClose
501func goVClose(pCursor unsafe.Pointer) *C.char {
502vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
503err := vtc.vTabCursor.Close()
504if err != nil {
505return mPrintf("%s", err.Error())
506}
507return nil
508}
509
510//export goMDestroy
511func goMDestroy(pClientData unsafe.Pointer) {
512m := lookupHandle(pClientData).(*sqliteModule)
513m.module.DestroyModule()
514}
515
516//export goVFilter
517func goVFilter(pCursor unsafe.Pointer, idxNum C.int, idxName *C.char, argc C.int, argv **C.sqlite3_value) *C.char {
518vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
519args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
520vals := make([]any, 0, argc)
521for _, v := range args {
522conv, err := callbackArgGeneric(v)
523if err != nil {
524return mPrintf("%s", err.Error())
525}
526vals = append(vals, conv.Interface())
527}
528err := vtc.vTabCursor.Filter(int(idxNum), C.GoString(idxName), vals)
529if err != nil {
530return mPrintf("%s", err.Error())
531}
532return nil
533}
534
535//export goVNext
536func goVNext(pCursor unsafe.Pointer) *C.char {
537vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
538err := vtc.vTabCursor.Next()
539if err != nil {
540return mPrintf("%s", err.Error())
541}
542return nil
543}
544
545//export goVEof
546func goVEof(pCursor unsafe.Pointer) C.int {
547vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
548err := vtc.vTabCursor.EOF()
549if err {
550return 1
551}
552return 0
553}
554
555//export goVColumn
556func goVColumn(pCursor, cp unsafe.Pointer, col C.int) *C.char {
557vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
558c := (*SQLiteContext)(cp)
559err := vtc.vTabCursor.Column(c, int(col))
560if err != nil {
561return mPrintf("%s", err.Error())
562}
563return nil
564}
565
566//export goVRowid
567func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char {
568vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
569rowid, err := vtc.vTabCursor.Rowid()
570if err != nil {
571return mPrintf("%s", err.Error())
572}
573*pRowid = C.sqlite3_int64(rowid)
574return nil
575}
576
577//export goVUpdate
578func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid *C.sqlite3_int64) *C.char {
579vt := lookupHandle(pVTab).(*sqliteVTab)
580
581var tname string
582if n, ok := vt.vTab.(interface {
583TableName() string
584}); ok {
585tname = n.TableName() + " "
586}
587
588err := fmt.Errorf("virtual %s table %sis read-only", vt.module.name, tname)
589if v, ok := vt.vTab.(VTabUpdater); ok {
590// convert argv
591args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
592vals := make([]any, 0, argc)
593for _, v := range args {
594conv, err := callbackArgGeneric(v)
595if err != nil {
596return mPrintf("%s", err.Error())
597}
598
599// work around for SQLITE_NULL
600x := conv.Interface()
601if z, ok := x.([]byte); ok && z == nil {
602x = nil
603}
604
605vals = append(vals, x)
606}
607
608switch {
609case argc == 1:
610err = v.Delete(vals[0])
611
612case argc > 1 && vals[0] == nil:
613var id int64
614id, err = v.Insert(vals[1], vals[2:])
615if err == nil {
616*pRowid = C.sqlite3_int64(id)
617}
618
619case argc > 1:
620err = v.Update(vals[1], vals[2:])
621}
622}
623
624if err != nil {
625return mPrintf("%s", err.Error())
626}
627
628return nil
629}
630
631// Module is a "virtual table module", it defines the implementation of a
632// virtual tables. See: http://sqlite.org/c3ref/module.html
633type Module interface {
634// http://sqlite.org/vtab.html#xcreate
635Create(c *SQLiteConn, args []string) (VTab, error)
636// http://sqlite.org/vtab.html#xconnect
637Connect(c *SQLiteConn, args []string) (VTab, error)
638// http://sqlite.org/c3ref/create_module.html
639DestroyModule()
640}
641
642// EponymousOnlyModule is a "virtual table module" (as above), but
643// for defining "eponymous only" virtual tables See: https://sqlite.org/vtab.html#eponymous_only_virtual_tables
644type EponymousOnlyModule interface {
645Module
646EponymousOnlyModule()
647}
648
649// VTab describes a particular instance of the virtual table.
650// See: http://sqlite.org/c3ref/vtab.html
651type VTab interface {
652// http://sqlite.org/vtab.html#xbestindex
653BestIndex([]InfoConstraint, []InfoOrderBy) (*IndexResult, error)
654// http://sqlite.org/vtab.html#xdisconnect
655Disconnect() error
656// http://sqlite.org/vtab.html#sqlite3_module.xDestroy
657Destroy() error
658// http://sqlite.org/vtab.html#xopen
659Open() (VTabCursor, error)
660}
661
662// VTabUpdater is a type that allows a VTab to be inserted, updated, or
663// deleted.
664// See: https://sqlite.org/vtab.html#xupdate
665type VTabUpdater interface {
666Delete(any) error
667Insert(any, []any) (int64, error)
668Update(any, []any) error
669}
670
671// VTabCursor describes cursors that point into the virtual table and are used
672// to loop through the virtual table. See: http://sqlite.org/c3ref/vtab_cursor.html
673type VTabCursor interface {
674// http://sqlite.org/vtab.html#xclose
675Close() error
676// http://sqlite.org/vtab.html#xfilter
677Filter(idxNum int, idxStr string, vals []any) error
678// http://sqlite.org/vtab.html#xnext
679Next() error
680// http://sqlite.org/vtab.html#xeof
681EOF() bool
682// http://sqlite.org/vtab.html#xcolumn
683Column(c *SQLiteContext, col int) error
684// http://sqlite.org/vtab.html#xrowid
685Rowid() (int64, error)
686}
687
688// DeclareVTab declares the Schema of a virtual table.
689// See: http://sqlite.org/c3ref/declare_vtab.html
690func (c *SQLiteConn) DeclareVTab(sql string) error {
691zSQL := C.CString(sql)
692defer C.free(unsafe.Pointer(zSQL))
693rv := C.sqlite3_declare_vtab(c.db, zSQL)
694if rv != C.SQLITE_OK {
695return c.lastError()
696}
697return nil
698}
699
700// CreateModule registers a virtual table implementation.
701// See: http://sqlite.org/c3ref/create_module.html
702func (c *SQLiteConn) CreateModule(moduleName string, module Module) error {
703mname := C.CString(moduleName)
704defer C.free(unsafe.Pointer(mname))
705udm := sqliteModule{c, moduleName, module}
706switch module.(type) {
707case EponymousOnlyModule:
708rv := C._sqlite3_create_module_eponymous_only(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm))))
709if rv != C.SQLITE_OK {
710return c.lastError()
711}
712return nil
713case Module:
714rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm))))
715if rv != C.SQLITE_OK {
716return c.lastError()
717}
718return nil
719}
720return nil
721}
722