module sqlite

module lib/sqlite.mu

import "sqlite"

sqlite provides minimal SQLite bindings via the FFI.

Imports

Functions

close#

fn close(db)

close closes a SQLite database handle.

Source lib/sqlite.mu:112
fn close(db) {
    err := _require_handle(db, "sqlite.close")
    if is_error(err) {
        return err
    }
    rc := _sqlite3_close(db)
    if is_error(rc) {
        return rc
    }
    if rc != _SQLITE_OK {
        return error("sqlite.close failed with code " + inspect(rc))
    }
    return nil
}

exec#

fn exec(db, sql)

exec executes a SQL statement without returning rows.

Source lib/sqlite.mu:128
fn exec(db, sql) {
    err := _require_handle(db, "sqlite.exec")
    if is_error(err) {
        return err
    }
    if !typing.is_str(sql) {
        return error("sqlite.exec expects string sql, got " + type(sql))
    }
    rc := _sqlite3_exec(db, sql, nil, nil, nil)
    if is_error(rc) {
        return rc
    }
    if rc != _SQLITE_OK {
        return _error_with_db(db, rc, "sqlite.exec")
    }
    return nil
}

open#

fn open(path)

open opens a SQLite database and returns a handle.

Source lib/sqlite.mu:81
fn open(path) {
    if !typing.is_str(path) {
        return error("sqlite.open expects string path, got " + type(path))
    }
    out := _ptr_buffer()
    rc := _sqlite3_open(path, out)
    if is_error(rc) {
        return rc
    }
    db := _ptr_from_buffer(out)
    if is_error(db) {
        return db
    }
    if rc != _SQLITE_OK {
        if db != 0 {
            msg := _errmsg(db)
            _sqlite3_close(db)
            if is_error(msg) {
                return msg
            }
            return error("sqlite.open failed: " + msg)
        }
        return error("sqlite.open failed with code " + inspect(rc))
    }
    if db == 0 {
        return error("sqlite.open returned null handle")
    }
    return db
}

prepare#

fn prepare(db, sql)

prepare compiles a statement and returns the statement handle.

Source lib/sqlite.mu:197
fn prepare(db, sql) {
    err := _require_handle(db, "sqlite.prepare")
    if is_error(err) {
        return err
    }
    if !typing.is_str(sql) {
        return error("sqlite.prepare expects string sql, got " + type(sql))
    }
    out := _ptr_buffer()
    rc := _sqlite3_prepare_v2(db, sql, -1, out, nil)
    if is_error(rc) {
        return rc
    }
    if rc != _SQLITE_OK {
        return _error_with_db(db, rc, "sqlite.prepare")
    }
    stmt := _ptr_from_buffer(out)
    if is_error(stmt) {
        return stmt
    }
    if stmt == 0 {
        return error("sqlite.prepare returned null statement")
    }
    return stmt
}

query#

fn query(db, sql)

query executes a SQL statement and returns rows as lists of values.

Source lib/sqlite.mu:147
fn query(db, sql) {
    err := _require_handle(db, "sqlite.query")
    if is_error(err) {
        return err
    }
    if !typing.is_str(sql) {
        return error("sqlite.query expects string sql, got " + type(sql))
    }
    stmt := prepare(db, sql)
    if is_error(stmt) {
        return stmt
    }
    rows := []
    while true {
        rc := _sqlite3_step(stmt)
        if is_error(rc) {
            _sqlite3_finalize(stmt)
            return rc
        }
        if rc == _SQLITE_ROW {
            row := _read_row(stmt)
            if is_error(row) {
                _sqlite3_finalize(stmt)
                return row
            }
            rows = append(rows, row)
            if is_error(rows) {
                _sqlite3_finalize(stmt)
                return rows
            }
            continue
        }
        if rc == _SQLITE_DONE {
            break
        }
        query_err := _error_with_db(db, rc, "sqlite.query")
        _sqlite3_finalize(stmt)
        return query_err
    }
    rc := _sqlite3_finalize(stmt)
    if is_error(rc) {
        return rc
    }
    if rc != _SQLITE_OK {
        return _error_with_db(db, rc, "sqlite.query finalize")
    }
    return rows
}

Macros

sqlite_path#

macro sqlite_path()

sqlite_path() expands to the path of the SQLite shared library to bind against, decided at COMPILE time: MU_SQLITE_PATH if it is set, otherwise the platform's usual location.

It is exported because the choice belongs to whoever builds the program, and because a cross-compile has to override it -- platform() inside a macro reports the BUILD host, not the target.

Source lib/sqlite.mu:47
macro.define("sqlite_path", fn() {
  override := env("MU_SQLITE_PATH")
  if override != nil && type(override) == "STRING" {
    return quote { unquote override }
  }
  platformKey := platform()
  return quote { unquote _sqlite_default_path(platformKey) }
})

Internal helpers

Underscore-prefixed names are implementation detail. They are listed so the module's source reads without surprises, not as API — they may change at any time.

_SQLITE_BLOB
_SQLITE_DONE
_SQLITE_FLOAT
_SQLITE_INTEGER
_SQLITE_NULL
_SQLITE_OK
_SQLITE_ROW
_SQLITE_TEXT
_column_blob(stmt, index)
_column_text(stmt, index)
_column_value(stmt, index)
_copy_bytes(ptr, length)copy_bytes copies length bytes from ptr into a new buffer.
_copy_string_len(ptr, length)copy_string_len copies length bytes from ptr and returns a string.
_cstring(ptr)cstring copies a null-terminated string from ptr.
_decode_uint64_le(buffer)decode_uint64_le reads an unsigned 64-bit little-endian value from buffer.
_errmsg(db)
_error_with_db(db, rc, action)
_memcpy
_ptr_buffer()ptr_buffer allocates an 8-byte buffer suitable for pointer out-params.
_ptr_from_buffer(buffer)ptr_from_buffer decodes a pointer-sized value from an 8-byte buffer.
_read_row(stmt)
_require_handle(value, name)
_sqlite3_close
_sqlite3_column_blob
_sqlite3_column_bytes
_sqlite3_column_count
_sqlite3_column_int64
_sqlite3_column_text
_sqlite3_column_type
_sqlite3_errmsg
_sqlite3_exec
_sqlite3_finalize
_sqlite3_open
_sqlite3_prepare_v2
_sqlite3_step
_sqlite_default_path(platformKey)
_sqlite_default_paths()
_strlen