Database Transactions
BEGIN TRANSACTION groups statements atomically. COMMIT makes them durable; ROLLBACK discards them. This is essential for operations like money transfers where partial updates would corrupt data.
Code
// nyx-db transactions — BEGIN, COMMIT, ROLLBACK
import "std/sqlite"
fn main() -> int {
// Use embedded SQLite for the demo (recipe 55 shows basic usage)
let db: int = sqlite_open(":memory:")
if db < 0 { return 1 }
sqlite_exec(db, "CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance INTEGER)")
sqlite_exec(db, "INSERT INTO accounts VALUES (1, 1000)")
sqlite_exec(db, "INSERT INTO accounts VALUES (2, 500)")
// Atomic money transfer using a transaction
sqlite_exec(db, "BEGIN TRANSACTION")
// Debit account 1
sqlite_exec(db, "UPDATE accounts SET balance = balance - 100 WHERE id = 1")
// Credit account 2
sqlite_exec(db, "UPDATE accounts SET balance = balance + 100 WHERE id = 2")
// Commit atomically
sqlite_exec(db, "COMMIT")
print("transfer committed")
// Verify balances
let rows: Array = sqlite_query(db, "SELECT id, balance FROM accounts ORDER BY id")
var i: int = 0
while i < rows.length() {
let row: Array = rows[i]
let id: String = row[0]
let bal: String = row[1]
print(" account " + id + ": " + bal)
i = i + 1
}
// Rollback example
sqlite_exec(db, "BEGIN TRANSACTION")
sqlite_exec(db, "UPDATE accounts SET balance = 0 WHERE id = 1")
sqlite_exec(db, "ROLLBACK")
print("rolled back — balance unchanged")
sqlite_close(db)
return 0
}
Output
transfer committed account 1: 900 account 2: 600 rolled back — balance unchanged
Explanation
Without transactions, a crash between the debit and credit would leave $100 missing — the cardinal sin of financial software. BEGIN TRANSACTION opens an atomic scope; all writes inside are staged until COMMIT flushes them as a single durable operation. If anything goes wrong, ROLLBACK throws away every change since BEGIN. This gives you the A (atomicity) and C (consistency) of ACID for free.