glite
glite: a SQLite driver for Gleam on the Erlang VM.
Every call runs in the calling process, holding the connection for as long
as it uses SQLite, so callers use it one at a time. Share a Connection
between processes freely: when it is busy, calls queue up in order, and a
transaction holds it from BEGIN to COMMIT, so nothing else can mix into
it.
import glite
import glite/config
import glite/decode
pub fn main() {
let assert Ok(conn) = glite.connect(config.new("app.db"))
let assert Ok(Nil) =
glite.exec_script(conn, "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")
let assert Ok(id) =
glite.query_one(
conn,
"INSERT INTO users (name) VALUES (?) RETURNING id",
[glite.text("Alice")],
{
use id <- decode.element(0, decode.int)
decode.success(id)
},
)
let assert Ok(Nil) = glite.disconnect(conn)
}
Types
What backup wrote.
pub type Backup {
Backup(path: String, bytes: Int)
}
Constructors
-
Backup(path: String, bytes: Int)
A connection to one SQLite database. Share it between processes freely.
pub opaque type Connection
A query parameter.
pub type Param =
value.Value
Why a transaction did not commit.
pub type TransactionError(e) {
RolledBack(e)
Database(error.Error)
}
Constructors
-
RolledBack(e)The callback returned
Error(e); everything it did was rolled back. -
Database(error.Error)The database or the connection failed; nothing was committed.
How a transaction takes its locks.
pub type TransactionMode {
Immediate
Deferred
Exclusive
}
Constructors
-
ImmediateTake the write lock at BEGIN. Use this for any transaction that writes: it can’t fail halfway through because another connection wrote first.
-
DeferredTake locks as statements need them. Fine for read-only transactions.
-
ExclusiveTake the write lock at BEGIN and keep readers out (outside WAL mode).
Values
pub fn backup(
conn: Connection,
to path: String,
) -> Result(Backup, error.Error)
Write a consistent copy of the database to path with VACUUM INTO,
check the copy with PRAGMA integrity_check, and fsync it. path must
not exist yet.
Only the copy runs on the connection, within call_timeout_ms; writers
wait while it is made. The check and the fsync run in the calling process
afterwards, so they never hold up the connection, and they take as long as
the database’s size needs. A copy that fails, is interrupted, or doesn’t
pass the check is deleted, so the same path can be tried again.
Never pass a path that came from user input: the copy is written wherever the VM can write.
pub fn blob(value: BitArray) -> value.Value
pub fn connect(
config: config.Config,
) -> Result(Connection, error.Error)
Open a database. Every setting in the config is checked after opening; if
SQLite didn’t apply one, such as WAL mode, this returns PragmaMismatch.
The connection stays open until disconnect, or until the process that
called connect crashes. A process that exits normally leaves it open.
pub fn disconnect(conn: Connection) -> Result(Nil, error.Error)
Close the database, after the call or transaction running on it, if any,
finishes. Closing a connection that is already closed returns Ok(Nil);
calls on a closed connection return ConnectionClosed.
pub fn exec(
conn: Connection,
sql: String,
params: List(value.Value),
) -> Result(Int, error.Error)
Run one statement and return the number of rows it changed: always 0 for statements other than INSERT, UPDATE, DELETE and REPLACE.
The SQL must be one statement, with exactly as many parameters as it has placeholders.
pub fn exec_many(
conn: Connection,
sql: String,
params: List(List(value.Value)),
) -> Result(Int, error.Error)
Run one statement once per parameter list, all or nothing: if one run fails, the runs before it are rolled back. Returns the rows changed in total.
pub fn exec_script(
conn: Connection,
sql: String,
) -> Result(Nil, error.Error)
Run a script of several statements, without parameters, such as a schema migration.
pub fn float(value: Float) -> value.Value
pub fn int(value: Int) -> value.Value
pub fn null() -> value.Value
pub fn nullable(
value: option.Option(a),
to_param: fn(a) -> value.Value,
) -> value.Value
None becomes NULL; Some(x) becomes to_param(x).
pub fn query(
conn: Connection,
sql: String,
params: List(value.Value),
decoder: decode.RowDecoder(a),
) -> Result(List(a), error.Error)
Run a query and decode every row. More than max_rows rows is an error.
pub fn query_one(
conn: Connection,
sql: String,
params: List(value.Value),
decoder: decode.RowDecoder(a),
) -> Result(a, error.Error)
Run a query that returns exactly one row, and decode it. No rows, or more
than one, is a Decode error. It stops reading at the second row, so it
is cheap even on a query that matches many.
pub fn query_rows(
conn: Connection,
sql: String,
params: List(value.Value),
) -> Result(Rows, error.Error)
Run a query and return its column names and raw rows.
pub fn set_user_version(
conn: Connection,
version: Int,
) -> Result(Nil, error.Error)
Set the database’s user_version.
pub fn text(value: String) -> value.Value
pub fn transaction(
conn: Connection,
mode: TransactionMode,
body: fn(tx.Tx) -> Result(a, e),
) -> Result(a, TransactionError(e))
Run body in a transaction: commit if it returns Ok, roll back if it
returns Error or panics. A panic is rolled back and then re-raised in
the calling process.
The body runs in the calling process while it holds the connection, so no
other caller’s statements can mix into the transaction. Use the Tx it
gets, not the Connection: calling the connection from inside the body
panics.
The whole transaction must finish within call_timeout_ms. If it
doesn’t, its running statement is interrupted, it is rolled back, and the
result is Database(Timeout(_)).
A timeout can interrupt SQL but nothing else. A body that waits for a
message, loops, or calls a slow service keeps the connection busy, and
the caller waiting, until it returns, so keep the body to statements on
the Tx and quick Gleam code, and never call another glite connection
from it.
pub fn user_version(conn: Connection) -> Result(Int, error.Error)
The database’s user_version, for schema migrations.