Query SQLite with bound parameters
Keep SQL text literal and interpolate values as parameters, then decode typed rows.
Generated by cargo run -p agent_docs from compiler/examples/. Edit the example files, then regenerate.
Keep SQL text literal and interpolate values as parameters, then decode typed rows.
find_notes returns List<Note>, which determines the row schema for Sql.query.
Each sql"…" interpolation becomes a bound parameter; neither string concatenation
nor a trust bypass is needed. SqlError propagates from the database operations.
Tests install Sql.memory, a real fresh in-memory SQLite database, so no filesystem
permission or persistent database is needed. One test proves SQL-looking input
matches only its literal value. Another runs a transaction that writes and then
fails on an absent table, checks SqlError, and proves the first write rolled back.
The harness uses test mode. For a persistent database, query sakura doc std::sql::sqlite
and supply an appropriately confined filesystem handler and manifest permissions.
Source project · harness mode: test.
From the repository root, with this checkout's sakura on PATH:
cd compiler/examples/sqlite_query
sakura check --json
sakura test --jsonManifest and permissions
[package]
name = "sqlite_query"
version = "0.1.0"
edition = "2026"
[permissions]Source and tests
src/main.sakura includes all required imports and tests.
// milestone: M3
// mode: test
// spec: design §17.145
import std/sql.{Sql, SqlError}
type Note = { id: Int, title: String }
fn create_notes() -> Unit ! {Sql, Fail<SqlError>} {
Sql.migrate([sql"create table notes (id integer primary key, title text not null)"])
}
fn add_note(title: String) -> Unit ! {Sql, Fail<SqlError>} {
let _ = Sql.execute(sql"insert into notes (title) values ({title})")
}
fn find_notes(title: String) -> List<Note> ! {Sql, Fail<SqlError>} {
Sql.query(sql"select id, title from notes where title = {title} order by id")
}
test "SQLite binds interpolated values, including SQL-looking text" {
with Sql.memory() {
create_notes()
add_note("tea")
let literal = "x' OR 1=1 --"
expect_eq(find_notes(literal), [])
add_note(literal)
expect_eq(find_notes(literal), [Note { id: 2, title: literal }])
expect_eq(find_notes("tea"), [Note { id: 1, title: "tea" }])
}
}
test "a failing transaction rolls back earlier writes" {
with Sql.memory() {
create_notes()
expect_fails_with(
fn() {
Sql.transaction(fn() {
add_note("rolled back")
let _ = Sql.execute(sql"insert into absent_table (title) values ({"bad"})")
})
},
fn(e) {
match e {
SqlError { .. } => true,
}
},
)
expect_eq(find_notes("rolled back"), [])
}
}Compiler provenance and regeneration
These eight projects teach supported Sakura APIs on the JavaScript backend. Each
project is exercised offline by cargo test -p sakura_cli --test examples;
HTTP and LLM exchanges are synthetic committed cassettes, with no network or API keys.
Use Node 26 for execution, including its built-in SQLite implementation.
The initial API baseline is compiler revision
5cae349516c21714d90d36e87f1cfd6debe980c6.
CI reruns these projects against the compiler in the same checkout. Build that
checkout's CLI and run sakura --version and sakura prompt before adapting a recipe;
pre-alpha APIs can change. An installed skill's frontmatter records its compiler
version and build revision. These teaching examples are separate from the agent
evaluation tasks.
The source of each page is its example's recipe.md, sakura.toml,
src/main.sakura and fixture files. Run cargo run -p agent_docs from compiler/
to regenerate the cookbook and skill reference. Edit those sources, not the rendered pages.