Compare commits

...

5 Commits

9 changed files with 2 additions and 178 deletions

2
.envrc
View File

@ -1,2 +0,0 @@
source_env ..
use flake syndicate#sqlite_actor

1
.gitignore vendored
View File

@ -1 +0,0 @@
/.direnv

View File

@ -1,32 +1,5 @@
# sqlite_actor
Syndicate actor for accessing SQLite databases.
Absorbed by the Syndesizer
## Build
Depends on the [SQLcipher](https://www.zetetic.net/sqlcipher/) library and pkg-config.
## Example configuration
```
? <example-dataspace ?ds> [
$ds <query example-row "SELECT id, name FROM stuff">
$ds ? <example-row ?id ?name> [
$log ! <log "-" { row: <example-row $id $name> }>
]
<require-service <daemon sqlite_actor>>
? <service-object <daemon sqlite_actor> ?cap> [
$cap {
dataspace: $ds
database: "/var/db/example.db"
}
]
<daemon sqlite_actor {
argv: [ "/usr/local/bin/sqlite_actor" ]
protocol: application/syndicate
}>
]
```
https://git.syndicate-lang.org/ehmry/syndicate_utils

View File

@ -1,2 +0,0 @@
include ../syndicate-nim/depends.tup
NIM_FLAGS += --path:$(TUP_CWD)/../syndicate-nim/src

View File

@ -1,6 +0,0 @@
version 1 .
; When asserted the actor reponds with
; rows as records of the given label and
; row columns as record fields.
Query = <query @label any @statement string> .

View File

@ -1,8 +0,0 @@
version = "20230527"
author = "Emery Hemingway"
description = "Syndicate Actor for accessing SQLite databases"
license = "Unlicense"
srcDir = "src"
bin = @["sqlite_actor"]
requires "nim >= 1.6.10", "syndicate >= 20230518"

View File

@ -1,4 +0,0 @@
include_rules
: foreach ../*.prs |> !preserves_schema_nim |> {schema}
: sqlite_actor.nim | $(SYNDICATE_PROTOCOL) {schema} |> !nim_bin |> {bin}
: {bin} |> !assert_built |>

View File

@ -1,14 +0,0 @@
import
preserves
type
Query* {.preservesRecord: "query".} = object
`label`*: Preserve[void]
`statement`*: string
proc `$`*(x: Query): string =
`$`(toPreserve(x))
proc encode*(x: Query): seq[byte] =
encode(toPreserve(x))

View File

@ -1,112 +0,0 @@
# 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
import ./sql
proc logError(db: Sqlite3; context: string) =
writeLine(stderr, errmsg(db), ": ", context)
type
Value = Preserve[void]
Args {.preservesDictionary.} = object
database: string
dataspace: Ref
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..<arity: result.record[col] = extractValue(stmt, col)
runActor("main") do (root: Ref; turn: var Turn):
connectStdio(root, turn)
during(turn, root, ?Args) do (path: string, ds: Ref):
var db: Sqlite3
if open_v2(path, addr db, SQLITE_OPEN_READONLY, nil) != SQLITE_OK:
logError(db, path)
else:
during(turn, ds, ?Query) do (label: Value, statement: string):
var stmt: Stmt
if prepare_v2(db, statement, statement.len.cint, addr stmt, nil) != SQLITE_OK:
logError(db, statement)
else:
try:
let arity = column_count(stmt)
var res = step(stmt)
while res == SQLITE_ROW:
var rec = extractRecord(stmt, label, arity)
discard publish(turn, ds, rec)
res = step(stmt)
assert res != 100
if res != SQLITE_DONE:
logError(db, statement)
finally:
if finalize(stmt) != SQLITE_OK: logError(db, statement)
do:
close(db)