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