Skip to content

Commit d115715

Browse files
committed
🎨 Create func signatures for the SQLite VTab
plugin/module.go now has the functions signatures to create a virtual table in SQLite. The remaining work is now to implement the function body. **Explanation of the code** As specified in the SQLite documentation (https://www.sqlite.org/vtab.html), a VTab (Virtual Table) is implemented with three structs: - the module struct => Infer the schema and create the connection - the table struct => Find the best querying method and open the cursor struct - the cursor struct => query and update the rows Each of these structs have several methods attached to them. You can refer to the source code to find their exact "raison d'être".
1 parent edd7028 commit d115715

File tree

4 files changed

+132
-2
lines changed

4 files changed

+132
-2
lines changed

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ go 1.22.1
44

55
require (
66
github.com/fatih/color v1.16.0 // indirect
7+
github.com/gammazero/deque v0.2.1 // indirect
78
github.com/golang/protobuf v1.5.4 // indirect
89
github.com/hashicorp/go-hclog v1.6.3 // indirect
910
github.com/hashicorp/go-plugin v1.6.0 // indirect
1011
github.com/hashicorp/yamux v0.1.1 // indirect
1112
github.com/mattn/go-colorable v0.1.13 // indirect
1213
github.com/mattn/go-isatty v0.0.20 // indirect
14+
github.com/mattn/go-sqlite3 v1.14.22 // indirect
1315
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
1416
github.com/oklog/run v1.1.0 // indirect
1517
golang.org/x/net v0.24.0 // indirect

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
33
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
44
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
55
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
6+
github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0=
7+
github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU=
68
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
79
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
810
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
@@ -20,6 +22,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
2022
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
2123
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
2224
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
25+
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
26+
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
2327
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
2428
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
2529
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=

plugin/module.go

+111
Original file line numberDiff line numberDiff line change
@@ -1 +1,112 @@
11
package plugin
2+
3+
import (
4+
"github.com/gammazero/deque"
5+
"github.com/mattn/go-sqlite3"
6+
)
7+
8+
// This file links the plugin to the SQLite Virtual Table interface
9+
10+
// SQLiteModule is a struct that holds the information about the SQLite module
11+
//
12+
// For each table that the plugin provides and for each profile, a new SQLiteModule
13+
// should be created and registered in the main program
14+
type SQLiteModule struct {
15+
PluginPath string
16+
PluginManifest PluginManifest
17+
TableIndex int
18+
client *InternalClient
19+
}
20+
21+
// SQLiteTable that holds the information needed for the BestIndex and Open methods
22+
type SQLiteTable struct {
23+
nextCursor int
24+
tableIndex int
25+
schema DatabaseSchema
26+
client *InternalClient
27+
}
28+
29+
// SQLiteCursor holds the information needed for the Column, Filter, EOF and Next methods
30+
type SQLiteCursor struct {
31+
tableIndex int
32+
cursorIndex int
33+
schema DatabaseSchema
34+
client *InternalClient
35+
noMoreRows bool
36+
rows *deque.Deque[[]interface{}] // A ring buffer to store the rows before sending them to SQLite
37+
nextCursor *int
38+
}
39+
40+
// EponymousOnlyModule is a method that is used to mark the table as eponymous-only
41+
//
42+
// See https://www.sqlite.org/vtab.html#eponymous_virtual_tables for more information
43+
func (m *SQLiteModule) EponymousOnlyModule() {}
44+
45+
// Create is called when the virtual table is created e.g. CREATE VIRTUAL TABLE or SELECT...FROM(epo_table)
46+
//
47+
// Its main job is to create a new RPC client and return the needed information
48+
// for the SQLite virtual table methods
49+
func (m *SQLiteModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
50+
return nil, nil
51+
}
52+
53+
// Connect is called when the virtual table is connected
54+
//
55+
// Because it's an eponymous-only module, the method must be identical to Create
56+
func (m *SQLiteModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
57+
return m.Create(c, args)
58+
}
59+
60+
// BestIndex is called when the virtual table is queried
61+
// to figure out the best way to query the table
62+
//
63+
// However, we don't use it that way but only to serialize the constraints
64+
// for the Filter method
65+
func (t *SQLiteTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
66+
return nil, nil
67+
}
68+
69+
// Open is called when a new cursor is opened
70+
//
71+
// It should return a new cursor
72+
func (t *SQLiteTable) Open() (sqlite3.VTabCursor, error) {
73+
return nil, nil
74+
}
75+
76+
// Close is called when the cursor is no longer needed
77+
func (c *SQLiteCursor) Close() error { return nil }
78+
79+
// These methods are not used in this plugin
80+
func (v *SQLiteTable) Disconnect() error { return nil }
81+
func (v *SQLiteTable) Destroy() error { return nil }
82+
func (v *SQLiteModule) DestroyModule() {}
83+
84+
// Column is called when a column is queried
85+
//
86+
// It should return the value of the column
87+
func (c *SQLiteCursor) Column(cst int) (interface{}, error) { return nil, nil }
88+
89+
// EOF is called after each row is queried to check if there are more rows
90+
func (c *SQLiteCursor) EOF() bool { return false }
91+
92+
// Next is called to move the cursor to the next row
93+
//
94+
// If noMoreRows is set to false, and the cursor is at the end of the rows,
95+
// Next will ask the plugin for more rows
96+
//
97+
// If noMoreRows is set to true, Next will set EOF to true
98+
func (c *SQLiteCursor) Next() error { return nil }
99+
100+
// RowID is called to get the row ID of the current row
101+
func (c *SQLiteCursor) RowID() (int64, error) { return 0, nil }
102+
103+
func (c *SQLiteCursor) Filter(idxNum int, idxStr string, vals []interface{}) error {
104+
// Filter can be called several times with the same cursor
105+
// Each time, it is supposed to reset the cursor to the beginning
106+
// Therefore, it should wipe out all the cursor fields
107+
//
108+
// Moreover, for the sake of simplicity, we will create a new cursor on the plugin side,
109+
// which means the cursorIndex must be incremented while not yelding any conflict
110+
// How to fix this? We must have access to the parent struct (SQLiteTable).
111+
return nil
112+
}

plugin/structs.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ type PluginManifest struct {
4343
Version string
4444
Author string
4545
Description string
46+
// A list of tables that the plugin will provide
47+
Tables []string
4648

4749
UserConfig []PluginConfigField
4850
}
@@ -61,8 +63,19 @@ type PluginConfigField struct {
6163
// you can generate a unique key. The primary key must be unique for each row.
6264
// It is used to update and delete rows.
6365
type DatabaseSchema struct {
64-
// ... other fields
65-
HandleLimit bool
66+
// The columns of the table
67+
Columns []DatabaseSchemaColumn
68+
// The primary key is the index of the column that is the primary key (starting from 0)
69+
PrimaryKey int
70+
71+
// The following fields are used to optimize the queries
72+
73+
// HandleLimit is a boolean that specifies whether the plugin can handle the LIMIT clause.
74+
// If so, the plugin should return only the specified number of rows.
75+
HandleLimit bool
76+
77+
// HandleOffset is a boolean that specifies whether the plugin can handle the OFFSET clause.
78+
// If not, the main program will skip the n offseted rows.
6679
HandleOffset bool
6780
}
6881

0 commit comments

Comments
 (0)