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

Column names and raw rows from query_rows.

pub type Rows =
  @internal Rows

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

  • Immediate

    Take the write lock at BEGIN. Use this for any transaction that writes: it can’t fail halfway through because another connection wrote first.

  • Deferred

    Take locks as statements need them. Fine for read-only transactions.

  • Exclusive

    Take 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 bool(value: Bool) -> value.Value

A boolean, stored as the integer 0 or 1.

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.

✨ Search Document