7.9.1. SQL-01 — Hello dasSQLITE

This tutorial introduces the dasSQLITE high-level API: opening a database with RAII, reading the linked SQLite version, and running a one-shot scalar query.

7.9.1.1. Setup

Two requires:

require daslib/sql
require sqlite/sqlite_boost

daslib/sql is the abstract layer — it re-exports Option<T> and Result<T, string> so user code only needs one import for those types. sqlite/sqlite_boost is the SQLite provider; it ships SqlRunner, with_sqlite, and the CRUD helpers used in every tutorial.

7.9.1.2. Library version

sqlite_version() reads the linked libsqlite3 version. It needs no database connection:

print("sqlite version: {sqlite_version()}\n")
// sqlite version: 3.45.0

7.9.1.3. Opening a database

The canonical form is with_sqlite(path) <| $(db) { ... }. It opens the database, passes a SqlRunner handle to the block, and closes the connection on block exit — including on panic and early return:

with_sqlite(":memory:") <| $(db) {
    // db is a SqlRunner
}

:memory: opens an in-memory database. Use a filesystem path to open or create a database file. Failure (path not writable, disk full, etc.) panics with the libsqlite3 errmsg text. with_sqlite is the strict form; under the hood it calls open_sqlite, which itself wraps try_open_sqlite(path) : Result<SqlRunner, string> (ok(runner) on success, err(errmsg) on failure). Tutorial 6 walks the full try_ / _opt rail; for now the strict form is the right default.

Each future SQL provider ships its own with_<provider> helper — with_postgres, with_mysql, etc. The block body is the same shape across providers.

7.9.1.4. Declaration form

If the database lifetime is the function body or a struct field, use var inscope directly — finalize on SqlRunner runs at scope exit:

var inscope db = open_sqlite(":memory:")
// ... use db ...
// sqlite3_close_v2 fires here

The with_sqlite form is preferred when the lifetime is a single block; it’s harder to forget the inscope.

7.9.1.5. Running a scalar query

query_scalar prepares a statement, steps once, reads column 0, finalizes, and returns the value:

with_sqlite(":memory:") <| $(db) {
    let v = db |> query_scalar("SELECT SQLITE_VERSION()", type<string>)
    print("server reports: {v}\n")
}

The result type is passed as a type<...> witness — daslang’s gen2 convention for type-parameterized functions. Chunk 1 ships the string overload; int, int64, and double overloads follow with the read-side rework.

query_scalar panics on prepare/step error and on zero rows. The try_query_scalar(db, sql, type<T>) : Result<T, string> sibling returns the libsqlite3 errmsg on any failure; tutorial 6 covers it alongside the rest of the try_ rail.