nim_sqlite

Search:
Group by:

Opening a database connection.

A database connection is opened by calling the openDatabase procedure with the path to the database file as an argument. If the file doesn't exist, it will be created. An in-memory database can be created by using the special path ":memory:" as an argument. Once the database connection is no longer needed, close must be called to prevent memory leaks. Closing a connection finalizes its internally cached statements. Explicit statements created with stmt own their handles and must be finalized separately. Closing is rejected with SqliteUsageError while a connection operation is active, preventing callbacks and user-defined conversions from invalidating a statement that is being bound or executed. Database and extension paths containing embedded NUL bytes raise SqliteError before they are passed to SQLite.

let db = openDatabase("path/to/file.db")
# ... (do something with `db`)
db.close()

The compatibility overload opens or creates the database, uses a 100-entry statement cache, and keeps SQLite's normal connection settings. For explicit opening behavior, copy defaultOpenOptions and pass the resulting OpenOptions value:

var options = defaultOpenOptions
options.mode = OpenMode.readWriteExisting
options.cacheSize = 50
options.busyTimeoutMs = 5_000
options.noFollow = true
options.securityProfile = SecurityProfile.hardened
let db = openDatabase("path/to/existing.db", options)

OpenMode.readOnly and OpenMode.readWriteExisting never create the main database file; OpenMode.readWriteCreate creates it when missing. SQLite may fall back from read-write-existing to read-only access when operating-system permissions require it, so use isReadonly when writable access is mandatory. busyTimeoutMs gives ordinary lock contention a bounded retry period. uriFilename enables intentional SQLite file: URI interpretation, including URI parameters that can affect access and locking. noFollow rejects database paths containing symbolic links.

SecurityProfile.hardened enables SQLite defensive mode and disables trusted-schema behavior before initialization SQL runs. This can reject legitimate schemas that rely on application-defined functions or virtual tables. It is an additional defense rather than a sandbox, and it does not change WAL or durability policy. See SAFETY.md for the complete connection-opening and security contract.

Executing SQL

The exec procedure can be used to execute a single SQL statement. The execScript procedure is used to execute several statements, but it doesn't support parameter substitution.

Single-statement operations validate the complete input before execution:

  • Trailing whitespace, extra semicolons, and complete SQLite comments are allowed. This includes a final -- comment without a newline.
  • A second statement, malformed trailing SQL, or an incomplete block comment or quoted token raises SqliteError before anything executes.
  • Empty, whitespace-only, semicolon-only, and comment-only input raises SqliteError.

Use execScript for multiple statements. Unlike single-statement operations, it treats empty and comment-only scripts as no-ops. Every SQL operation rejects embedded NUL bytes rather than letting SQLite silently truncate the input. execScript rejects explicit BEGIN, COMMIT, END, ROLLBACK, SAVEPOINT, and RELEASE statements before they execute, preventing a script from invalidating the transaction that protects its work. Use exec when transaction control must be managed manually.

If a statement produces rows, exec and execScript discard them but continue stepping until the statement completes. A runtime error on any row raises SqliteError. When execScript starts its transaction, such an error rolls back the script.

db.execScript("""
    CREATE TABLE Person(
        name TEXT,
        age INTEGER
    );

    CREATE TABLE Log(
        message TEXT
    );
""")

db.exec("""
    INSERT INTO Person(name, age)
    VALUES(?, ?);
""", "John Doe", 37)

Named parameters

For order-independent binding, use SQLite :name parameters and pass a named tuple. A tuple field named name binds :name regardless of the order of the fields or parameters:

db.exec("""
    UPDATE Person
    SET age = :age
    WHERE name = :name
""", (name: "John Doe", age: 38))

Named tuples can be used with exec, iterate, all, one, and value, including their prepared-statement versions. execMany accepts an array or sequence of named tuples. Repeated occurrences of a parameter share one tuple field. Missing parameters, unknown tuple fields, and positional ? parameters used with a named tuple raise SqliteError.

Reading data

Four different procedures for reading data are available:

  • all: procedure returning all result rows
  • iterate: iterator yielding each result row one by one
  • one: procedure returning the first result row, or none if no result row exists
  • value: procedure returning the first column of the first result row, or none if no result row exists

Note that the procedures one and value returns the result wrapped in an Option. See the standard library options module for documentation on how to deal with Option values. For convenience the nim_sqlite module exports the options.get, options.isSome, and options.isNone procedures so the options module doesn't need to be explicitly imported for typical usage.

for row in db.iterate("SELECT name, age FROM Person"):
    # The 'row' variable is of type ResultRow.
    # The column values can be accesed by both index and column name:
    echo row[0].strVal      # Prints the name
    echo row["name"].strVal # Prints the name
    echo row[1].intVal      # Prints the age
    # Above we're using the raw DbValue's directly. Instead, we can unpack the
    # DbValue using the fromDb procedure:
    echo fromDb(row[0], string) # Prints the name
    echo fromDb(row[1], int)    # Prints the age
    # Alternatively, the entire row can be unpacked at once:
    let (name, age) = row.unpack((string, int))
    # Unpacking the value is preferable as it makes it possible to handle
    # bools, enums, distinct types, nullable types and more. For example, nullable
    # types are handled using Option[T]:
    echo fromDb(row[0], Option[string]) # Will work even if the db value is NULL

# Example of reading a single value. In this case, 'value' will be of type `Option[DbValue]`.
let value = db.value("SELECT age FROM Person WHERE name = ?", "John Doe")
if value.isSome:
    echo fromDb(value.get, int) # Prints age of John Doe

Scoped resources, deadlines, and online backups

withDatabase injects a db variable and closes it on every exit path. withStatement similarly injects a statement variable and finalizes it. Use withDeadline(db, timeoutMs) to interrupt long-running SQL after a monotonic deadline. SQLite can roll back an interrupted write transaction; inspect SqliteError.primaryCode for SQLITE_INTERRUPT.

backupDatabase(destination, source) copies the source main database into the destination. For incremental transfers, withBackup injects a backup handle; call step with a positive page count and inspect remainingPages and totalPages after each step. The destination cannot be used for other operations until the scope exits.

One connection and its statements must not be used concurrently across threads. Coordinate interrupt with connection close when requesting cancellation from another thread. Extensions are trusted native code and require OpenOptions.allowExtensions to be enabled explicitly.

Inserting data in bulk

The exec procedure works fine for inserting single rows, but it gets awkward when inserting many rows. For this purpose the execMany procedure can be used instead. It executes the same SQL repeatedly, but with different parameters each time.

let parameters = [
    (name: "Person 1", age: 17),
    (name: "Person 2", age: 55)
]
# Will insert two rows
db.execMany("""
    INSERT INTO Person(name, age)
    VALUES(:name, :age);
""", parameters)

Transactions

The procedures that can execute multiple SQL statements (execScript and execMany) are wrapped in a transaction by nim_sqlite. Transactions can also be controlled manually by using one of these two options:

db.transaction:
    # Anything inside here is executed inside a transaction which
    # will be rolled back in case of an error
    db.exec("DELETE FROM Person")
    db.exec("""INSERT INTO Person(name, age) VALUES("Jane Doe", 35)""")

Nested transaction blocks use SQLite savepoints. If an inner exception is caught by the outer block, only the inner block's work is rolled back. If an exception escapes the outer block, all nested work is rolled back.

The default mode is TransactionMode.deferred. Pass TransactionMode.immediate or TransactionMode.exclusive to select the corresponding SQLite BEGIN mode for an outermost transaction:

db.transaction(TransactionMode.immediate):
    db.exec("DELETE FROM Person")

Nested scopes inherit the outer mode. When SQL issued by the application has already started a transaction or savepoint, the template creates its own savepoint and leaves the manually managed outer scope open. Its caller remains responsible for the final COMMIT or ROLLBACK. Manually committing, rolling back, or releasing that outer scope from inside the template is unsupported because it invalidates the template's cleanup boundary.

Commit and savepoint-release failures trigger rollback cleanup. The original failure remains primary; every failure during scoped and fallback cleanup is retained in order through Nim's exception parent chain. See SAFETY.md for the complete failure contract.

  • Option 2: using the exec procedure manually
db.exec("BEGIN")
try:
    db.exec("DELETE FROM Person")
    db.exec("""INSERT INTO Person(name, age) VALUES("Jane Doe", 35)""")
    db.exec("COMMIT")
except:
    db.exec("ROLLBACK")

Prepared statements

All the procedures for executing SQL described above create and execute prepared statements internally. In addition to those procedures, nim_sqlite also offers an API for preparing SQL statements explicitly. Prepared statements are created with the stmt procedure, and the same procedures for executing SQL that are available directly on the connection object are also available for the prepared statement:

let stmt = db.stmt("INSERT INTO Person(name, age) VALUES (?, ?)")
stmt.exec("John Doe", 21)
# Once the statement is no longer needed it must be finalized
# to prevent memory leaks.
stmt.finalize()

An explicit statement owns its SQLite statement handle. Closing the database makes the statement unusable for further execution, but does not finalize its handle. It remains safe and necessary to finalize the statement afterward:

let stmt = db.stmt("SELECT name FROM Person")
db.close()
doAssert not stmt.isAlive
stmt.finalize()

The underlying SQLite connection is released after all explicit statements have been finalized. Prefer finalizing statements before closing the connection when practical; post-close finalization makes cleanup ordering safe.

There are performance benefits of reusing prepared statements, since the preparation only needs to be done once. However, nim_sqlite keeps an internal cache of prepared statements, so it's typically not necesarry to manage prepared statements manually. If you prefer if nim_sqlite doesn't perform this caching, you can disable it by setting the cacheSize parameter when opening the database:

let db = openDatabase(":memory:", cacheSize = 0)

Cached statements are leased to one connection-level operation at a time. If nested or reentrant code requests SQL whose cached statement is already leased or busy, nim_sqlite prepares a temporary statement and finalizes it afterward. Cache eviction skips leased and busy statements. This keeps nested queries independent, including when the same SQL is used with different parameters.

Explicit statements created with stmt are single-use for their complete binding and execution lifecycle. Reusing or finalizing the same statement, or closing its connection, from an active iterator or a user-defined named-parameter toDb conversion raises SqliteUsageError. The guard is released after successful execution, binding failures, exceptions, and iterator early exits, so the statement remains reusable afterward.

Error handling

SqliteError provides primaryCode, extendedCode, operation, and sqliteMessage fields for handling failures without parsing exception text. The operation is one of the stable SqliteOperation categories. SQLite result codes are captured before statement cleanup can replace the connection's current error. Library validation and conversion failures have zero result codes and an empty sqliteMessage.

SqliteUsageError is the catchable error for invalid public handle state, including use of a closed connection, use of a finalized statement, reentrant use of an active explicit statement, and close or finalize during an active operation. Internal invariant failures remain defects.

Neither exception type attaches SQL text or bound parameter values. SQLite's own message can identify schema objects or SQL tokens, so applications should apply their normal protection policy to logs and should bind secrets rather than placing them in SQL literals.

Supported types

For a type to be supported when using unpacking and parameter substitution the procedures toDb and fromDb must be implemented for the type. Below is table describing which types are supported by default and to which SQLite type they are mapped to:

Nim typeSQLite type
Ordinal

INTEGER

SomeFloat

REAL

string

TEXT

seq[byte]

BLOB

Option[T]

NULL if value is none(T), otherwise the type that T would use

Embedded NUL bytes remain valid in bound string (TEXT) and seq[byte] (BLOB) values; the SQL-input restriction does not apply to bound values.

SQLite INTEGER values are signed 64-bit integers. Binding an unsigned ordinal above high(int64), or decoding an integer into a narrower integer, range, boolean, character, or enum that cannot represent it, raises SqliteError instead of wrapping or depending on compiler range checks. Floating-point decoding returns the requested Nim floating-point type.

This can be extended by implementing toDb and fromDb for other types. Below is an example how support for times.Time can be added:

import times

proc toDb(t: Time): DbValue =
    DbValue(kind: sqliteInteger, intVal: toUnix(t))

proc fromDb(value: DbValue, T: typedesc[Time]): Time =
    fromUnix(value.fromDb(int))

Types

Backup = distinct BackupImpl
A scoped SQLite online backup handle.
BackupStep {.pure.} = enum
  more, done, busy, locked
DbConn = distinct DbConnImpl
Encapsulates a database connection.
DbMode = enum
  dbRead, dbReadWrite
DbValue = object
  case kind*: DbValueKind
  of sqliteInteger:
    intVal*: int64
  of sqliteReal:
    floatVal*: float64
  of sqliteText:
    strVal*: string
  of sqliteBlob:
    blobVal*: seq[byte]
  of sqliteNull:
    nil
Can represent any value in a SQLite database.
DbValueKind = enum
  sqliteNull, sqliteInteger, sqliteReal, sqliteText, sqliteBlob
Enum of all possible value types in a SQLite database.
OpenMode {.pure.} = enum
  readWriteCreate, readWriteExisting, readOnly
Controls whether opening a database may create or modify its main database file.
OpenOptions = object
  mode*: OpenMode            ## File access and creation behavior.
  cacheSize*: Natural        ## Maximum number of connection-cached statements.
  busyTimeoutMs*: int64      ## Lock wait budget in milliseconds; zero
                             ## disables the busy timeout.
  uriFilename*: bool         ## Interpret a ``file:`` path as an SQLite URI.
  noFollow*: bool            ## Reject database paths containing symbolic links.
  securityProfile*: SecurityProfile ## SQLite security configuration.
  allowExtensions*: bool     ## Permit explicit loading of trusted native extensions.
  maxSqlBytes*: int64        ## Optional SQL text byte limit; zero keeps SQLite's default.
  maxVmOps*: int64           ## Optional virtual-machine operation limit per statement.
Options for opening a database connection. Start with defaultOpenOptions when changing only selected fields.
ResultRow = object
SecurityProfile {.pure.} = enum
  normal, hardened
Selects connection-level SQLite security settings. normal keeps SQLite's compatibility defaults. hardened enables defensive mode and disables trusted-schema behavior, which can reject databases that rely on non-innocuous functions or virtual tables in their schema.
SqliteError = object of CatchableError
  primaryCode*: int32        ## SQLite's primary result code, or zero for a
                             ## library-side error.
  extendedCode*: int32       ## SQLite's extended result code, or zero for a
                             ## library-side error.
  operation*: SqliteOperation ## The operation category that failed.
  sqliteMessage*: string     ## SQLite's diagnostic message, or an empty
                             ## string for a library-side error.
Raised for SQLite failures and library validation errors. primaryCode and extendedCode are zero when the failure was produced by library-side validation rather than SQLite.
SqliteOperation {.pure.} = enum
  validation, conversion, openDatabase, closeDatabase, prepare, binding,
  execute, transaction, loadExtension, backup
Stable categories describing which library operation failed.
SqliteUsageError = object of CatchableError
Raised when a closed, finalized, or active handle is used in an invalid lifecycle state.
SqlStatement = distinct SqlStatementImpl
A prepared SQL statement.
TransactionMode {.pure.} = enum
  deferred, immediate, exclusive
Controls how an outermost transaction acquires SQLite locks. Nested transactions use savepoints, so their mode is inherited from the surrounding transaction.

Consts

defaultOpenOptions = (mode: OpenMode.readWriteCreate, cacheSize: 100,
                      busyTimeoutMs: 0, uriFilename: false, noFollow: false,
                      securityProfile: SecurityProfile.normal,
                      allowExtensions: false, maxSqlBytes: 0, maxVmOps: 0)
Compatibility defaults used by the convenience openDatabase overload. Copy this value before changing selected options.

Procs

proc `$`(dbVal: DbValue): string {....raises: [], tags: [], forbids: [].}
proc `==`(a, b: DbValue): bool {....raises: [], tags: [], forbids: [].}
Returns true if a and b represents the same value.
proc `[]`(row: ResultRow; column: string): DbValue {....raises: [], tags: [],
    forbids: [].}
Access a column in the result row based on column name. The column name must be unambiguous.
proc `[]`(row: ResultRow; idx: Natural): DbValue {....raises: [], tags: [],
    forbids: [].}
Access a column in the result row based on index.
proc all(db: DbConn; sql: string; params: varargs[DbValue, toDb]): seq[ResultRow] {.
    ...raises: [SqliteUsageError, SqliteError, Exception], tags: [RootEffect],
    forbids: [].}
Executes sql, which must be a single SQL statement, and returns all result rows.
proc all(statement: SqlStatement; params: varargs[DbValue, toDb]): seq[ResultRow] {.
    ...raises: [SqliteUsageError, Exception, SqliteError], tags: [RootEffect],
    forbids: [].}
Executes statement and returns all result rows.
proc all[T: tuple](db: DbConn; sql: string; params: T): seq[ResultRow]
Executes sql using named :name parameters and returns all rows.
proc all[T: tuple](statement: SqlStatement; params: T): seq[ResultRow]
Executes statement using named :name parameters and returns all rows.
proc backupDatabase(destination, source: DbConn) {.
    ...raises: [SqliteError, SqliteUsageError, Exception, Exception], tags: [],
    forbids: [].}
Copy the source main database into the destination main database. Busy and locked results are raised with structured SQLite error codes.
proc changes(db: DbConn): int64 {....raises: [SqliteUsageError], tags: [],
                                  forbids: [].}

Get the number of changes triggered by the most recent INSERT, UPDATE or DELETE statement as a signed 64-bit value.

For more information, refer to the SQLite documentation (https://www.sqlite.org/c3ref/changes.html).

proc close(db: DbConn) {....raises: [Exception, SqliteUsageError, SqliteError],
                         tags: [RootEffect], forbids: [].}

Logically closes the database connection. Cached statements are finalized immediately. Explicit statements created with stmt retain ownership of their handles and must still be finalized, even after the connection has been closed. SQLite releases the underlying connection after the last such statement is finalized.

Closing an already closed database is a harmless no-op. Closing while a connection or explicit-statement operation is active raises SqliteUsageError.

proc columns(row: ResultRow): seq[string] {....raises: [], tags: [], forbids: [].}
Returns all column names in the result row.
proc exec(db: DbConn; sql: string; params: varargs[DbValue, toDb]) {.
    ...raises: [SqliteUsageError, SqliteError, Exception], tags: [RootEffect],
    forbids: [].}
Executes sql, which must be a single SQL statement. Result rows are discarded, but the statement is stepped until it completes. Input without a statement or containing an embedded NUL byte raises SqliteError.

Example:

let db = openDatabase(":memory:")
db.exec("CREATE TABLE Person(name, age)")
db.exec("INSERT INTO Person(name, age) VALUES(?, ?)",
    "John Doe", 23)
proc exec(statement: SqlStatement; params: varargs[DbValue, toDb]) {.
    ...raises: [SqliteUsageError, Exception, SqliteError], tags: [RootEffect],
    forbids: [].}
Executes statement with params as parameters. Result rows are discarded, but the statement is stepped until it completes.
proc exec[T: tuple](db: DbConn; sql: string; params: T)
Executes sql using a named tuple whose field names correspond to :name parameters. Tuple field order does not affect binding. Result rows are discarded, but the statement is stepped until it completes. Input without a statement or containing an embedded NUL byte raises SqliteError.

Example:

let db = openDatabase(":memory:")
db.exec("CREATE TABLE Person(name, age)")
db.exec("INSERT INTO Person(name, age) VALUES(:name, :age)",
    (age: 23, name: "John Doe"))
proc exec[T: tuple](statement: SqlStatement; params: T)
Executes statement using named :name parameters. Tuple field order does not affect binding. Result rows are discarded, but the statement is stepped until it completes.
proc execMany(db: DbConn; sql: string; params: seq[seq[DbValue]]) {.
    ...raises: [SqliteUsageError, Exception, SqliteError, Exception, Exception],
    tags: [RootEffect], forbids: [].}
Executes sql, which must be a single SQL statement, repeatedly using each element of params as parameters. The statements are executed inside a transaction.
proc execMany(statement: SqlStatement; params: seq[seq[DbValue]]) {.
    ...raises: [SqliteUsageError, Exception, SqliteError, Exception, Exception],
    tags: [RootEffect], forbids: [].}
Executes statement repeatedly using each element of params as parameters. The statements are executed inside a transaction.
proc execMany[T: tuple](db: DbConn; sql: string; params: openArray[T])
Executes sql repeatedly using named tuples as parameters. Tuple field names correspond to :name parameters.
proc execMany[T: tuple](statement: SqlStatement; params: openArray[T])
Executes statement repeatedly using named tuples as parameters.
proc execScript(db: DbConn; sql: string) {....raises: [SqliteUsageError,
    SqliteError, Exception, SqliteUsageError, Exception, Exception],
    tags: [RootEffect], forbids: [].}
Executes sql, which can consist of multiple SQL statements. Each statement is stepped until completion, with result rows discarded. The statements are executed inside a transaction. Empty, semicolon-only, and comment-only scripts are no-ops; incomplete or invalid input raises SqliteError. Embedded NUL bytes and transaction-control statements also raise SqliteError. Rejecting transaction control prevents a script from committing or rolling back the transaction that protects it.
proc finalize(statement: SqlStatement): void {....raises: [SqliteUsageError],
    tags: [], forbids: [].}
Finalizes the statement and releases its SQLite handle. This must be called once the statement is no longer used, including if its database connection has already been closed. Finalizing an already finalized statement is a harmless no-op. Finalizing while the statement is active raises SqliteUsageError.
proc fromDb(value: DbValue; T: typedesc[DbValue]): T:type
Special overload that simply return value. The purpose of this overload is to do partial unpacking. For example, if the type of one column in a result row is unknown, the DbValue type can be kept just for that column.
for row in db.iterate("SELECT name, extra FROM Person"):
    # Type of 'extra' is unknown, so we don't unpack it.
    # The 'extra' variable will be of type 'DbValue'
    let (name, extra) = row.unpack((string, DbValue))
proc fromDb(value: DbValue; T: typedesc[Ordinal]): T:type
Convert a DbValue to an ordinal. Raises SqliteError unless value has the sqliteInteger kind and its value is representable by T.
proc fromDb(value: DbValue; T: typedesc[seq[byte]]): seq[byte]
Convert a DbValue to a sequence of bytes. Raises SqliteError unless value has the sqliteBlob kind.
proc fromDb(value: DbValue; T: typedesc[string]): string
Convert a DbValue to a string. Raises SqliteError unless value has the sqliteText kind.
proc fromDb[T: SomeFloat](value: DbValue; _: typedesc[T]): T
Convert a DbValue to the requested floating-point type. Raises SqliteError unless value has the sqliteReal kind.
proc fromDb[T](value: DbValue; _: typedesc[Option[T]]): Option[T]
Convert a DbValue to an optional value. Non-NULL values retain the storage-class validation for T.
proc interrupt(db: DbConn) {....raises: [SqliteUsageError], tags: [], forbids: [].}
Request interruption of currently running statements. The caller must synchronize this call with close; all other connection operations remain single-threaded. SQLite reports interrupted execution with SQLITE_INTERRUPT in SqliteError.primaryCode.
proc isAlive(statement: SqlStatement): bool {....raises: [SqliteUsageError],
    tags: [], forbids: [].}
proc isInTransaction(db: DbConn): bool {....raises: [SqliteUsageError], tags: [],
    forbids: [].}
proc isOpen(db: DbConn): bool {.inline, ...raises: [SqliteUsageError], tags: [],
                                forbids: [].}
proc isReadonly(db: DbConn): bool {....raises: [SqliteUsageError], tags: [],
                                    forbids: [].}
Returns true if db is in readonly mode.

Example:

let db = openDatabase(":memory:")
doAssert not db.isReadonly
let db2 = openDatabase(":memory:", dbRead)
doAssert db2.isReadonly
proc isReadonly(statement: SqlStatement): bool {....raises: [SqliteUsageError],
    tags: [], forbids: [].}
Whether SQLite considers this prepared statement read-only.
proc lastInsertRowId(db: DbConn): int64 {....raises: [SqliteUsageError], tags: [],
    forbids: [].}

Get the row id of the last inserted row. For tables with an integer primary key, the row id will be the primary key.

For more information, refer to the SQLite documentation (https://www.sqlite.org/c3ref/last_insert_rowid.html).

proc len(row: ResultRow): int {....raises: [], tags: [], forbids: [].}
Returns the number of columns in the result row.
proc loadExtension(db: DbConn; path: string) {.
    ...raises: [SqliteUsageError, SqliteError, SqliteError, Exception], tags: [],
    forbids: [].}
Load a trusted native extension when allowExtensions was enabled at open time. The C loading capability is disabled after each attempt.
proc one(db: DbConn; sql: string; params: varargs[DbValue, toDb]): Option[
    ResultRow] {....raises: [SqliteUsageError, SqliteError, Exception],
                 tags: [RootEffect], forbids: [].}
Executes sql, which must be a single SQL statement, and returns the first result row. Returns none(seq[DbValue]) if the result was empty.
proc one(statement: SqlStatement; params: varargs[DbValue, toDb]): Option[
    ResultRow] {....raises: [SqliteUsageError, Exception, SqliteError],
                 tags: [RootEffect], forbids: [].}
Executes statement and returns the first row found. Returns none(seq[DbValue]) if no result was found.
proc one[T: tuple](db: DbConn; sql: string; params: T): Option[ResultRow]
Executes sql using named :name parameters and returns the first row.
proc one[T: tuple](statement: SqlStatement; params: T): Option[ResultRow]
Executes statement using named :name parameters and returns the first row.
proc openDatabase(path: string; mode = dbReadWrite; cacheSize: Natural = 100): DbConn {.
    ...raises: [SqliteError, SqliteUsageError, Exception], tags: [RootEffect],
    forbids: [].}

Open a new database connection to a database file. To create an in-memory database the special path ":memory:" can be used. If the database doesn't already exist and mode is dbReadWrite, the database will be created. If the database doesn't exist and mode is dbRead, a SqliteError exception will be raised. Paths containing embedded NUL bytes also raise SqliteError.

NOTE: To avoid memory leaks, db.close must be called when the database connection is no longer needed.

Connection-level operations lease cached statements exclusively. If a cached statement is already leased or busy during nested or reentrant execution, a temporary statement is used and finalized after that operation. Leased and busy statements are not evicted from the cache.

Example:

let memDb = openDatabase(":memory:")
proc openDatabase(path: string; options: OpenOptions): DbConn {.
    ...raises: [SqliteError, SqliteError, SqliteUsageError, Exception],
    tags: [RootEffect], forbids: [].}

Open a database connection using explicit options. readOnly and readWriteExisting never create the main database file; readWriteCreate creates it when necessary. SQLite can fall back from readWriteExisting to read-only access when operating-system permissions prevent writing; use isReadonly to inspect the result.

busyTimeoutMs controls how long SQLite's busy handler may sleep while waiting on a lock. uriFilename enables SQLite URI filename parsing, whose query parameters can make the requested mode more restrictive. noFollow requests SQLITE_OPEN_NOFOLLOW. The hardened security profile enables SQLITE_DBCONFIG_DEFENSIVE and disables SQLITE_DBCONFIG_TRUSTED_SCHEMA. It can reject otherwise valid schemas and is not a sandbox for hostile SQL or database files.

Paths containing embedded NUL bytes raise SqliteError. Opening also fails if the busy timeout cannot be represented by SQLite's cint API.

Example:

var options = defaultOpenOptions
options.mode = OpenMode.readWriteExisting
options.busyTimeoutMs = 1_000
let db = openDatabase(":memory:", options)
db.close()
proc quickCheck(db: DbConn): seq[string] {.
    ...raises: [SqliteUsageError, SqliteError, Exception], tags: [RootEffect],
    forbids: [].}
Run SQLite's quick integrity check. An empty result means SQLite reported ok; otherwise each entry is a diagnostic line.
proc remainingPages(backup: Backup): int32 {....raises: [SqliteUsageError],
    tags: [], forbids: [].}
Pages still to copy after the most recent backup step.
proc rows(db: DbConn; sql: string; params: varargs[DbValue, toDb]): seq[
    seq[DbValue]] {....deprecated: "use \'all\' instead",
                    raises: [SqliteUsageError, SqliteError, Exception],
                    tags: [RootEffect], forbids: [].}
Deprecated: use 'all' instead
proc sqliteCompileOptions(): seq[string] {....raises: [], tags: [], forbids: [].}
Compile-time options reported by the bundled SQLite library.
proc sqliteVersion(): string {....raises: [], tags: [], forbids: [].}
Version of the SQLite library compiled into the application.
proc step(backup: Backup; pages: int32 = -1): BackupStep {.
    ...raises: [SqliteUsageError, SqliteError], tags: [], forbids: [].}
Copy at most pages pages, or all pages when pages is -1. busy and locked can be retried after the caller resolves lock contention. Other SQLite errors raise SqliteError.
proc stmt(db: DbConn; sql: string): SqlStatement {.
    ...raises: [SqliteUsageError, SqliteError], tags: [], forbids: [].}
Constructs a prepared statement from sql. The returned statement owns its SQLite handle and must be finalized independently, including when the database connection is closed first. Input without a statement or containing an embedded NUL byte raises SqliteError.
proc toDb[T: Option](val: T): DbValue
Convert an optional value to a DbValue.
proc toDb[T: Ordinal](val: T): DbValue
Convert an ordinal value to a DbValue. Raises SqliteError if val cannot be represented by SQLite's signed 64-bit INTEGER storage class.
proc toDb[T: seq[byte]](val: T): DbValue
Convert a sequence of bytes to a DbValue.
proc toDb[T: SomeFloat](val: T): DbValue
Convert a float to a DbValue.
proc toDb[T: string](val: T): DbValue
Convert a string to a DbValue.
proc toDb[T: type(nil)](val: T): DbValue
Convert a nil literal to a DbValue.
proc totalChanges(db: DbConn): int64 {....raises: [SqliteUsageError], tags: [],
                                       forbids: [].}
Number of rows changed by this connection since it was opened.
proc totalPages(backup: Backup): int32 {....raises: [SqliteUsageError], tags: [],
    forbids: [].}
Source page count observed by the most recent backup step.
proc unpack[T: tuple](row: ResultRow; _: typedesc[T]): T
Calls fromDb on each element of row and returns it as a tuple.
proc unpack[T: tuple](row: seq[DbValue]; _: typedesc[T]): T {....deprecated.}
Deprecated
proc unsafeHandle(db: DbConn): ptr abi.sqlite3 {.inline,
    ...raises: [SqliteUsageError], tags: [], forbids: [].}
Returns the raw SQLite3 handle. This can be used to interact directly with the SQLite C API with the sqlite3_abi package. Note that the handle should not be used after db.close has been called as doing so would break memory safety.
proc value(db: DbConn; sql: string; params: varargs[DbValue, toDb]): Option[
    DbValue] {....raises: [SqliteUsageError, SqliteError, Exception],
               tags: [RootEffect], forbids: [].}
Executes sql, which must be a single SQL statement, and returns the first column of the first result row. Returns none(DbValue) if the result was empty.
proc value(statement: SqlStatement; params: varargs[DbValue, toDb]): Option[
    DbValue] {....raises: [SqliteUsageError, Exception, SqliteError],
               tags: [RootEffect], forbids: [].}
Executes statement and returns the first column of the first row found. Returns none(DbValue) if no result was found.
proc value[T: tuple](db: DbConn; sql: string; params: T): Option[DbValue]
Executes sql using named :name parameters and returns the first column of the first row.
proc value[T: tuple](statement: SqlStatement; params: T): Option[DbValue]
Executes statement using named :name parameters and returns the first column of the first row.
proc values(row: ResultRow): seq[DbValue] {....raises: [], tags: [], forbids: [].}
Returns all column values in the result row.

Iterators

iterator iterate(db: DbConn; sql: string; params: varargs[DbValue, toDb]): ResultRow {.
    ...raises: [SqliteUsageError, SqliteError, SqliteUsageError, Exception],
    tags: [RootEffect], forbids: [].}
Executes sql, which must be a single SQL statement, and yields each result row one by one. Input without a statement or containing an embedded NUL byte raises SqliteError.
iterator iterate(statement: SqlStatement; params: varargs[DbValue, toDb]): ResultRow {.
    ...raises: [SqliteUsageError, Exception, SqliteError, SqliteUsageError],
    tags: [RootEffect], forbids: [].}
Executes statement and yields each result row one by one.
iterator iterate[T: tuple](db: DbConn; sql: string; params: T): ResultRow
Executes sql using named :name parameters and yields each result row. Tuple field order does not affect binding. Input without a statement or containing an embedded NUL byte raises SqliteError.
iterator iterate[T: tuple](statement: SqlStatement; params: T): ResultRow
Executes statement using named :name parameters and yields each row.
iterator rows(db: DbConn; sql: string; params: varargs[DbValue, toDb]): seq[
    DbValue] {....deprecated: "use \'iterate\' instead",
               raises: [SqliteUsageError, SqliteError, Exception],
               tags: [RootEffect], forbids: [].}
Deprecated: use 'iterate' instead

Templates

template transaction(db: DbConn; body: untyped)
Runs body in a deferred transaction. See the overload accepting TransactionMode for immediate and exclusive transactions.
template transaction(db: DbConn; mode: TransactionMode; body: untyped)
Runs body in a transaction using mode for an outermost scope. Nested scopes use unique SQLite savepoints and inherit the outer mode. If a transaction was started manually, this template creates a savepoint and leaves the manual transaction open.
template withBackup(destination, source: untyped; body: untyped)
Keep both connections open and finish the backup on every exit path. The backup variable is available inside body.
template withDatabase(path: untyped; body: untyped)
Open a database and close it on normal exit, exception, or early return.
template withDatabase(path: untyped; options: untyped; body: untyped)
Open a database with options and close it on every exit path.
template withDeadline(db: DbConn; timeoutMs: Natural; body: untyped)
Run body with a monotonic execution deadline. The progress handler interrupts long-running SQLite work after the deadline. Its granularity is roughly 1000 SQLite virtual-machine instructions. Do not re-enter the same connection from a SQLite progress callback.
template withStatement(connection: untyped; sql: untyped; body: untyped)
Prepare a statement and finalize it on every exit path.