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