Running scripts
To run a whole file of SQL — a migration, a seed, a data import — use execute_script. It splits
the string into statements, runs each in order, and (when no transaction is open) wraps the lot in one implicit transaction, so the script is all-or-nothing: any statement’s error rolls the whole
run back.
It discards result rows and returns a small ScriptSummary — statements run, total rows
affected, accrued cost. That summary is O(1), so even an import of millions of rows never buffers
results in memory.
use jed::{split_statements, CreateOptions, Database};
fn main() -> jed::Result<()> {
let mut db = Database::create(CreateOptions { path: Some("app.jed".into()), ..Default::default() })?;
// execute_script runs a whole migration as ONE implicit transaction: split it into statements,
// run each in order, and commit all-or-nothing (any error rolls the lot back). It DISCARDS
// result rows — you get back only an O(1) summary (statements run, rows affected, cost), so a
// huge import never buffers results.
let summary = db.execute_script(
"CREATE TABLE account (id i32 PRIMARY KEY, balance i64);
INSERT INTO account VALUES (1, 100), (2, 50);
CREATE INDEX account_balance ON account (balance);",
)?;
println!("ran {} statements", summary.statements_run);
// split_statements is the library-level primitive (no Database needed). When you DO want each
// statement's rows, loop it yourself and run the spans through the normal path — the host owns
// the policy (one transaction or autocommit, drain rows or drop them).
for stmt in split_statements("SELECT id FROM account; SELECT balance FROM account") {
let _rows = db.query(stmt.text(), &[])?;
}
Ok(())
}
package main
import (
"context"
"fmt"
"log"
jed "github.com/jackc/jed/impl/go"
)
func main() {
db, err := jed.CreateDatabase(jed.CreateOptions{Path: "app.jed"})
if err != nil {
log.Fatal(err)
}
defer db.Close()
ctx := context.Background()
// ExecuteScript runs a whole migration as ONE implicit transaction: split it into statements,
// run each in order, and commit all-or-nothing (any error rolls the lot back). It DISCARDS
// result rows — you get back only an O(1) summary (statements run, rows affected, cost), so a
// huge import never buffers results.
summary, err := db.ExecuteScript(
`CREATE TABLE account (id i32 PRIMARY KEY, balance i64);
INSERT INTO account VALUES (1, 100), (2, 50);
CREATE INDEX account_balance ON account (balance);`)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ran %d statements\n", summary.StatementsRun)
// SplitStatements is the library-level primitive (no Database needed). When you DO want each
// statement's rows, loop it yourself and run the spans through the normal path — the host owns
// the policy (one transaction or autocommit, drain rows or drop them).
for stmt := range jed.SplitStatements("SELECT id FROM account; SELECT balance FROM account") {
if _, err := db.Query(ctx, stmt.Text); err != nil {
log.Fatal(err)
}
}
}
import { createDatabase, splitStatements } from 'jed-ts';
const db = createDatabase({ path: 'app.jed' });
// executeScript runs a whole migration as ONE implicit transaction: split it into statements, run
// each in order, and commit all-or-nothing (any error rolls the lot back). It DISCARDS result rows —
// you get back only an O(1) summary (statements run, rows affected, cost), so a huge import never
// buffers results.
const summary = db.executeScript(
`CREATE TABLE account (id i32 PRIMARY KEY, balance i64);
INSERT INTO account VALUES (1, 100), (2, 50);
CREATE INDEX account_balance ON account (balance);`
);
console.log(`ran ${summary.statementsRun} statements`);
// splitStatements is the library-level primitive (no Database needed). When you DO want each
// statement's rows, loop it yourself and run the spans through the normal path — the host owns the
// policy (one transaction or autocommit, drain the rows or drop them).
for (const stmt of splitStatements('SELECT id FROM account; SELECT balance FROM account')) {
db.query(stmt.text);
}
db.close();
The splitter is a primitive too
split_statements is the library-level building block underneath execute_script — a pure,
streaming statement scanner that needs no open database. A ; inside a string literal, a
dollar-quoted string, or a comment is never treated as a boundary. When you do want each
statement’s rows (not just a success/fail summary), loop it yourself and run the spans through the
normal execute / query path — you own the policy (one transaction or autocommit, drain the rows
or drop them).
Transaction control inside a script
Because execute_script owns the implicit transaction boundary, an explicit BEGIN, COMMIT, or ROLLBACK inside the script is rejected (0A000). Run on a session that already has a
transaction open and the script simply joins it — no wrapper, no auto-commit, so the caller stays in
control. For a script that manages its own transactions, use the split_statements loop instead.