Turso extension API
The turso_ext crate simplifies the creation and registration of libraries meant to extend the functionality of Turso, that can be loaded
like traditional sqlite3 extensions, but are able to be written in much more ergonomic Rust.
Attention
If you wish to link with extensions dynamically, you will need to coordinate the allocator with each extension you would like to load at runtime by either using MiMalloc (the default) or setting your global allocator of choice in macros/src/ext/mod.rs.
E.g
#[cfg(not(target_family = "wasm"))]
#[cfg(not(feature = "static"))]
#[global_allocator]
- static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
+ static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc
Then add the allocator to the extension you want to use
[]
= "0.5"
Then you just the compile the dynamic library and extract the necessary .so/.dylib/.dll files from .target folder.
Currently supported features
- [ x ] Scalar Functions: Create scalar functions using the
scalarmacro. - [ x ] Aggregate Functions: Define aggregate functions with
AggregateDerivemacro andAggFunctrait. - [ x ] Virtual tables: Create a module for a virtual table with the
VTabModuleDerivemacro andVTabCursortrait. - [ x ] VFS Modules: Extend Turso's OS interface by implementing
VfsExtensionandVfsFiletraits.
Installation
On the root of the workspace run:
Add the crate to your extensions/your_crate_name/Cargo.toml:
This modification in Cargo.toml is only needed if you are creating an extension as a separate Crate. If you are
creating an extension inside core, this step should be skipped.
[]
= ["turso_ext/static"]
[]
= { = "path/to/limbo/extensions/core", = ["static", "vfs"] } # temporary until crate is published
# mimalloc is required if you intend on linking dynamically. It is imported for you by the register_extension
# macro, so no configuration is needed. But it must be added to your Cargo.toml
[]
= { = "0.1", = false }
# NOTE: Crate must be of type `cdylib` if you wish to link dynamically
[]
= ["cdylib", "lib"]
cargo build will output a shared library that can be loaded by the following options:
CLI:
`.load target/debug/libyour_crate_name`
SQL:
SELECT load_extension('target/debug/libyour_crate_name')
Extensions can be registered with the register_extension! macro:
register_extension!
NOTE: Currently, any Derive macro used from this crate is required to be in the same
file as the register_extension macro.
Scalar Example:
use ;
/// Annotate each with the scalar macro, specifying the name you would like to call it with
/// and optionally, an alias.. e.g. SELECT double(4); or SELECT twice(4);
Aggregates Example:
use ;
/// annotate your struct with the AggregateDerive macro, and it must implement the below AggFunc trait
;
Virtual Table Example:
/// Example: A virtual table that operates on a CSV file as a database table.
/// This example assumes that the CSV file is located at "data.csv" in the current directory.
;
/// The cursor for iterating over CSV rows.
/// Implement the VTabCursor trait for your cursor type
Using the core Connection:
You can use the Rc<Connection> to query the same underlying connection that creates the VTable:
let mut stmt = self.connection.prepare;
stmt.bind_at;
/// use the connection similarly to the API of the core library
while let Row = stmt.step
stmt.close;
if let Ok = conn.execute
VFS Example
NOTE: Requires 'vfs' feature enabled.
use ;
/// Your struct must also impl Default
;
Cargo.toml Config
Edit the workspace Cargo.toml to include your extension as a workspace dependency, e.g:
[workspace.dependencies]
turso_core = { path = "core", version = "0.0.17" }
limbo_crypto = { path = "extensions/crypto", version = "0.0.17" }
turso_ext = { path = "extensions/core", version = "0.0.17" }
limbo_macros = { path = "macros", version = "0.0.17" }
...
+limbo_csv = { path = "extensions/csv", version = "0.0.17" }
And add your extension as a feature in core/Cargo.toml:
[features]
default = ["fs", "json", "uuid", "time"]
fs = []
json = ["dep:jsonb", "dep:pest", "dep:pest_derive", "dep:serde", "dep:indexmap"]
uuid = ["limbo_uuid/static"]
...
+csv = ["limbo_csv/static"]
Register Extension in Core
Lastly, you have to register your extension statically in the core crate:
pub fn register_builtins(&self) -> Result<(), String> {
#[allow(unused_variables)]
let ext_api = self.build_turso_ext();
#[cfg(feature = "uuid")]
if unsafe { !limbo_uuid::register_extension_static(&ext_api).is_ok() } {
return Err("Failed to register uuid extension".to_string());
}
+ #[cfg(feature = "csv")]
+ if unsafe { !limbo_csv::register_extension_static(&ext_api).is_ok() } {
+ return Err("Failed to register csv extension".to_string());
+ }
Ok(())
}