# SPDX-FileCopyrightText: ☭ Emery Hemingway # SPDX-License-Identifier: Unlicense # Avoid Sqlite3 from the standard library because it is # only held together by wishful thinking and dlload. {.passC: staticExec("pkg-config --cflags sqlcipher").} {.passL: staticExec("pkg-config --libs sqlcipher").} {.pragma: sqlite3h, header: "sqlite3.h".} var SQLITE_VERSION_NUMBER {.importc, sqlite3h.}: cint SQLITE_OK {.importc, sqlite3h.}: cint SQLITE_ROW {.importc, sqlite3h.}: cint SQLITE_DONE {.importc, sqlite3h.}: cint SQLITE_OPEN_READONLY {.importc, sqlite3h.}: cint const SQLITE_INTEGER = 1 SQLITE_FLOAT = 2 SQLITE_TEXT = 3 SQLITE_BLOB = 4 # SQLITE_NULL = 5 type Sqlite3 {.importc: "sqlite3", sqlite3h.} = distinct pointer Stmt {.importc: "sqlite3_stmt", sqlite3h.} = distinct pointer {.pragma: importSqlite3, importc: "sqlite3_$1", sqlite3h.} proc libversion_number: cint {.importSqlite3.} proc open_v2(filename: cstring; ppDb: ptr Sqlite3; flags: cint; zVfs: cstring): cint {.importSqlite3.} proc close(ds: Sqlite3): int32 {.discardable, importSqlite3.} proc errmsg(db: Sqlite3): cstring {.importSqlite3.} proc prepare_v2(db: Sqlite3; zSql: cstring, nByte: cint; ppStmt: ptr Stmt; pzTail: ptr cstring): cint {.importSqlite3.} proc step(para1: Stmt): cint {.importSqlite3.} proc column_count(stmt: Stmt): int32 {.importSqlite3.} proc column_blob(stmt: Stmt; col: cint): pointer {.importSqlite3.} proc column_bytes(stmt: Stmt; col: cint): cint {.importSqlite3.} proc column_double(stmt: Stmt; col: cint): float64 {.importSqlite3.} proc column_int64(stmt: Stmt; col: cint): int64 {.importSqlite3.} proc column_text(stmt: Stmt; col: cint): cstring {.importSqlite3.} proc column_type(stmt: Stmt; col: cint): cint {.importSqlite3.} proc finalize(stmt: Stmt): cint {.importSqlite3.} doAssert libversion_number() == SQLITE_VERSION_NUMBER import preserves, syndicate, syndicate/relays import ./sql proc logError(db: Sqlite3; context: string) = writeLine(stderr, errmsg(db), ": ", context) type Value = Preserve[void] Args {.preservesDictionary.} = object database: string dataspace: Cap proc extractValue(stmt: Stmt; col: cint): Value = case column_type(stmt, col) of SQLITE_INTEGER: result = toPreserve(column_int64(stmt, col)) of SQLITE_FLOAT: result = toPreserve(column_double(stmt, col)) of SQLITE_TEXT: result = Value(kind: pkString, string: newString(column_bytes(stmt, col))) if result.string.len > 0: copyMem(addr result.string[0], column_text(stmt, col), result.string.len) of SQLITE_BLOB: result = Value(kind: pkByteString, bytes: newSeq[byte](column_bytes(stmt, col))) if result.bytes.len > 0: copyMem(addr result.bytes[0], column_blob(stmt, col), result.bytes.len) else: result = initRecord[void]("null") proc extractRecord(stmt: Stmt; label: Value, arity: cint): Value = result = initRecord(label, arity) for col in 0..