zig-sqlite
zig-sqlite copied to clipboard
Virtual tables
Initial work on virtual tables. The goal is to be able to easily create virtual tables using Zig and expose as little as possible of the C-ism of the raw sqlite interface.
Currently implementing a virtual table is done by providing a Table type which must implement the following:
const Cursor = <cursor type ...>
const InitError = error{...};
const BuildBestIndexErrror = error{...};
fn init(allocator: mem.Allocator, diags: *sqlite.vtab.VTabDiagnostics) InitError!*Table
fn deinit(table: *Table, allocator: mem.Allocator) void
fn buildBestIndex(table: *Table, diags: *sqlite.vtab.VTabDiagnostics, builder: *sqlite.vtab.BestIndexBuilder) BuildBestIndexError!void
The cursor type must implement the following:
const InitError = error{...};
const NextError = error{...};
const HasNextError = error{...};
const FilterError = error{...};
const ColumnError = error{...};
const RowIDError = error{...};
fn init(allocator: mem.Allocator, parent: *Table) InitError!*Cursor
fn deinit(cursor: *Cursor) void
fn next(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) NextError!void
fn hasNext(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) HasNextError!bool
fn filter(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics, sqlite.vtab.IndexIdentifier) FilterError!bool
fn column(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics, column_number: i32) ColumnError!Column
fn rowId(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics) RowIDError!i64
It's relatively complete and should largely abstract the raw sqlite interface but one thing is missing: passing the arguments to the filter function.
SQLite gives us an array of sqlite3_value and I'll have to think longer on how I want to convert this to idiomatic Zig types.
I'm building a demo here (not completely ready yet)
I've ended up with an API for filter that's not too bad:
pub const FilterArg = struct {
value: ?*c.sqlite3_value,
pub fn as(self: FilterArg, comptime Type: type) Type {
...
}
};
pub fn filter(cursor: *Cursor, diags: *sqlite.vtab.VTabDiagnostics, index: sqlite.vtab.IndexIdentifier, args: []sqlite.vtab.FilterArg) FilterError!void {
...
}
you would use the arg like this:
const arg1 = filter_arg1.as([]const u8);
const arg2 = filter_arg2.as(usize);
...
as uses helpers.setTypeFromValue which is also used for the arguments of a user function.
Still to do:
- improve the tests
- implement
xCreateandxDestroy - add
connect,disconnectcalled byxConnectandxDisconnectrespectively - add an argument parser for args received on
xCreate
I'm not going to implement all the "write" functions (xUpdate etc); this will come at a later point.
For tests I'm thinking I could pass a bunch of random parameters as arguments to the virtual table creation, then since I know these parameters I can check that the rows I fetched match the rows generated.
https://github.com/vrischmann/zig-sqlite-vtab-demo/blob/09f5fac452f3acdf52f35bef7340809b089e82ab/src/vtab_apida_ext.zig#L14
Yes, I think we need some C stuff to glue zig code, there is a go library use similar tricks.
-
https://github.com/riyaz-ali/sqlite/blob/master/extension.c In extension.c, it will call a go func
go_sqlite3_extension_init, andgo_sqlite3_extension_initis an exported FFI function which used to bridge C and Go code. -
https://github.com/riyaz-ali/sqlite/blob/master/_examples/series/series.go This is a demo to to write
gen_seriesmodule in Go
Hope this help you.
Thanks ! I'll take a look at your links once I get back to working on building an extension.
The abiilty to build loadable extensions is part of #112 now. This PR will focus only on virtual tables.
I'm still not 100% sure when/how to use xCreate vs xConnect and xDestroy vs xDisconnect, I think I'll implement in another PR. The arguments are now parsed into a more usable type.
I'll focus next on improving tests and that will be it for this PR I think.