Transações
Nesta página
db.transaction(|tx| { ... }) envolve um bloco de operações em uma transação
ACID. O handle tx é intercambiável com db — você chama tx.execute,
tx.query, etc. exatamente como faria fora da transação.
Ao final do bloco o Zolo faz commit automaticamente. Se ocorrer um pânico ou um erro não tratado dentro do callback, a transação sofre rollback e o banco volta ao estado anterior.
Transferência bancária atômica: débito e crédito em duas contas dentro de uma única transação.
// Feature: Database — transactions with `db.transaction(fn)`
// Syntax: the callback receives the same db; auto-commit on return,
// auto-rollback on panic or error.
// When to use: multi-step operations that must be atomic (bank
// transfer, batch insert).
use std::database::Database
let db = Database.open("sqlite://:memory:").unwrap()
defer db.close()
db.execute("CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance REAL)").unwrap()
db.execute("INSERT INTO accounts VALUES (1, 100.0)").unwrap()
db.execute("INSERT INTO accounts VALUES (2, 50.0)").unwrap()
// Transfer: debit 1, credit 2 — atomic.
fn transfer(tx, from_id: int, to_id: int, amount: float) {
tx.execute("UPDATE accounts SET balance = balance - ? WHERE id = ?", [amount, from_id]).unwrap()
tx.execute("UPDATE accounts SET balance = balance + ? WHERE id = ?", [amount, to_id]).unwrap()
}
db.transaction(|tx| {
transfer(tx, 1, 2, 30.0)
})
let rows = db.query("SELECT id, balance FROM accounts ORDER BY id").unwrap()
for row in rows {
print("account {row.id}: {row.balance}")
}
// expected:
// account 1: 70
// account 2: 80
Requer a CLI/host do Zolo — abra no playground ou rode localmente.
A mesma transferência usando sql"..." com interpolação — os valores capturados
são vinculados, não concatenados.
// Feature: `db.transaction(|tx| { ... })` mixed with `sql"..."`
// Syntax: the callback receives a tx handle interchangeable with `db`
// for `sql"..."` execution. Auto-commits on return; auto-rollback on
// panic or error.
// When to use: multi-step writes that must be atomic — bank transfers,
// inventory updates, rename + foreign-key fix-ups. Compare with the
// older form in `05-transactions.zolo` (positional binding).
use std::database::Database
let db = Database.open("sqlite://:memory:").unwrap()
defer db.close()
db.execute(/* sql */ "CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance INTEGER)").unwrap()
db.execute(/* sql */ "INSERT INTO accounts (id, balance) VALUES (1, 100)").unwrap()
db.execute(/* sql */ "INSERT INTO accounts (id, balance) VALUES (2, 50)").unwrap()
let from_id = 1
let to_id = 2
let amount = 30
// Both UPDATEs use `sql"..."` interpolation — captured values are bound.
db.transaction(|tx| {
sql"UPDATE accounts SET balance = balance - {amount} WHERE id = {from_id}".execute(tx).unwrap()
sql"UPDATE accounts SET balance = balance + {amount} WHERE id = {to_id}".execute(tx).unwrap()
})
let rows = sql"SELECT id, balance FROM accounts ORDER BY id".query(db).unwrap()
for row in rows {
print("{row.id}: {row.balance}")
}
// expected:
// 1: 70
// 2: 80
Requer a CLI/host do Zolo — abra no playground ou rode localmente.
Desafio
Simule uma falha no meio da transação (ex.: error("saldo insuficiente")) e
verifique que os saldos permanecem inalterados após o rollback.