Reference · Database

db.exec

signature
db.exec(str, any...) -> map | error(str)

What it does

Runs a statement that changes the database (insert, update, delete, create table) and gives { changes, last_id }: how many rows it matched, and the id of the one row an insert made (null after anything else, and always on PostgreSQL, where insert ... returning id with db.one does that). The two mean the same on SQLite, PostgreSQL and MySQL. Values go in through ?, in order, or :name from one map, never into the SQL text itself. A SQL failure is an error value whose text starts db: , so ? passes it on and ?? gives a default.

Examples

db.open("app.db")print(db.exec("create table todos (id integer primary key, text text not null, done bool default false)"))print(db.exec("insert into todos (text) values (?)", "milk"))print(db.exec("insert into todos (text) values (:text)", { text: "eggs" }))print(db.exec("update todos set done = true where id = ?", 1))print(db.exec("delete from todos where done = ?", true))print(db.query("select * from todos"))
output
{ changes: 0, last_id: null }{ changes: 1, last_id: 1 }{ changes: 1, last_id: 2 }{ changes: 1, last_id: null }{ changes: 1, last_id: null }[{ id: 2, text: "eggs", done: false }]

Edge cases

db.open("app.db")db.exec("create table t (id integer primary key, a text)")print(db.exec("insert into t (a) values ('x')"), db.exec("update t set a = 'y' where id = 99"))print(db.exec("insert into t (a) values (?); insert into t (a) values ('skipped')", "one"))print(db.exec("insert into t (a) values ('two'); insert into t (a) values ('three')"))print(db.query("select a from t order by id") | map((r) => r.a))print(db.exec("insert into t (a) values (?)", "a", "b"))print(db.exec("insert into t (a) values (:a)", { b: 1 }))print(db.exec("insert into t (a) values (?) returning id", "r"))print(db.exec("insert into t (a) values (?)", [1, 2]), db.one("select a from t order by id desc limit 1"))print(db.exec(""))print(db.exec("insert into t (a) values ('p'), ('q')"), db.exec("update t set a = a where id = 1"))print(db.exec("insert into t (a) values (?)", math.nan))print(is_err(db.exec("insert into t (a) values (?)", int("4O"))), db.one("select count(*) as n from t where a = '4O'"))
output
{ changes: 1, last_id: 1 } { changes: 0, last_id: null }{ changes: 1, last_id: 2 }{ changes: 1, last_id: 4 }["x", "one", "two", "three"]err("db: the query has 1 parameter, got 2")err("db: no value for :a"){ changes: 1, last_id: 5 }{ changes: 1, last_id: 6 } { a: "[1,2]" }err("db: the SQL has no statement in it (it is empty, or only comments)"){ changes: 2, last_id: null } { changes: 1, last_id: null }err("db: value 1 is NaN, which cannot be stored: SQLite keeps NaN as null and MySQL refuses NaN and the infinities, so no database is given one")true { n: 0 }

Errors

Every failure the database reports is an error value: a missing table, a broken constraint, a syntax error. db.error_kind tells a unique or foreign key failure from the rest. SQL that is not a string is a runtime error.

db.open("app.db")db.exec("create table users (email text unique not null)")db.exec("insert into users values ('a@x.io')")let r = db.exec("insert into users values (?)", "a@x.io")print(is_err(r), r.error, db.error_kind(r))print(db.exec("drop table nope") ?? "failed")print(db.exec("insert into users values (?)", null))
output
true db: UNIQUE constraint failed: users.email uniquefailederr("db: NOT NULL constraint failed: users.email")
this one fails
print(db.exec(42))
output
line 1: `db.exec` expects SQL text as its first argument, got int

Chaining

The SQL comes first in a pipeline, the values in the call; ? after the call passes a failure on from a function:

db.open("app.db")db.exec("create table t (n integer check (n > 0))")fn add(n: int) = db.exec("insert into t values (?)", n)?print("insert into t values (?)" | db.exec(3))print(add(4), add(-1))print(db.query("select n from t") | map((r) => r.n))
output
{ changes: 1, last_id: 1 }{ changes: 1, last_id: 2 } err("db: CHECK constraint failed: n > 0")[3, 4]

See also

db.query, db.one, db.transaction, db.error_kind, db.open

Thread
7Useful? Like it, or add to its thread below.

Thread 0 posts

On db.exec(): questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.

    Edit this page on GitHub · every example's output was made by running it (tools/refcheck.mjs) · questions on Discord or r/quillo