Skip to content

Rust

The crate compiles the bundled amalgamation itself (via build.rs and the cc crate), so there are no system dependencies:

Terminal window
cargo add sqlite-predict
cargo add rusqlite --features bundled

Register it as an auto-extension once at startup; every connection you open afterward has the functions:

fn main() -> rusqlite::Result<()> {
// Registers sqlite-predict process-wide. Call once, before opening
// connections. `register` is unsafe: it registers a C entry point globally.
unsafe { sqlite_predict::register().expect("register sqlite-predict") };
let db = rusqlite::Connection::open_in_memory()?;
let version: String = db.query_row("select predict_version()", [], |r| r.get(0))?;
println!("{version}");
db.execute_batch(
"create table readings(ts text, value real);
insert into readings
select printf('2024-01-01T%02d:00:00', n), 50 + n
from (with recursive c(n) as (select 0 union all select n+1 from c where n < 23)
select n from c);",
)?;
// forecast() is an aggregate like sum(): plain SQL supplies the rows
// (WHERE, joins, bound params compose, GROUP BY splits the series),
// and each group returns one JSON document
let doc: String = db.query_row(
"select forecast(ts, value, 6) from readings", [], |r| r.get(0))?;
println!("{doc}");
Ok(())
}

Because register() uses sqlite3_auto_extension, every connection any rusqlite-based stack opens afterward has the functions, Diesel and sqlx included, with no per-connection hook:

unsafe { sqlite_predict::register()?; } // once, before connecting
// then through any rusqlite-based stack:
let doc: String = conn.query_row(
"SELECT forecast(ts, value, 24) FROM readings WHERE city = ?1",
["SF"], |r| r.get(0))?;

If a migration tool manages your schema, exclude sqlite-predict’s model registry (_predict_* tables) from diffs so it doesn’t get dropped.

Next: Operations.