Sakura Lang

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 --json

Manifest 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.

Edit on GitHub

On this page