convenience api ft.2

This commit is contained in:
Richard Ramos 2020-05-12 23:07:06 -04:00
parent 910e4d4883
commit 6fbfdc7a5c
No known key found for this signature in database
GPG Key ID: 80D4B01265FDFE8F
3 changed files with 33 additions and 18 deletions

View File

@ -1,6 +1,8 @@
import std / [strutils, options, macros, typetraits]
import std / [options, macros, typetraits]
from sqlcipher/sqlite as sqlite import nil
# Adapted from https://github.com/GULPF/tiny_sqlite
type
DbConn* = ptr sqlite.sqlite3
@ -41,6 +43,12 @@ type
of sqliteNull:
discard
Tbind_destructor_func* = proc (para1: pointer){.cdecl, locks: 0, tags: [], raises: [], gcsafe.}
const
SQLITE_STATIC* = nil
SQLITE_TRANSIENT* = cast[Tbind_destructor_func](-1)
proc newSqliteError(db: DbConn, errorCode: int32): ref SqliteError =
## Raises a SqliteError exception.
(ref SqliteError)(
@ -52,10 +60,10 @@ template checkRc(db: DbConn, rc: int32) =
if rc != sqlite.SQLITE_OK:
raise newSqliteError(db, rc)
#[proc prepareSql(db: DbConn, sql: string, params: seq[DbValue]): PreparedSql
proc prepareSql(db: DbConn, sql: string, params: seq[DbValue]): ptr PreparedSql
{.raises: [SqliteError].} =
var tail: cstring
let rc = sqlite.prepare_v2(db, sql.cstring, sql.len.cint, result, tail)
let rc = sqlite.prepare_v2(db, sql.cstring, sql.len.cint, addr result, addr tail)
assert tail.len == 0,
"`exec` and `execMany` can only be used with a single SQL statement. " &
"To execute several SQL statements, use `execScript`"
@ -69,15 +77,15 @@ template checkRc(db: DbConn, rc: int32) =
of sqliteInteger: sqlite.bind_int64(result, idx, value.intval)
of sqliteReal: sqlite.bind_double(result, idx, value.floatVal)
of sqliteText: sqlite.bind_text(result, idx, value.strVal.cstring,
value.strVal.len.int32, sqlite.SQLITE_TRANSIENT)
value.strVal.len.int32, SQLITE_TRANSIENT)
of sqliteBlob: sqlite.bind_blob(result, idx.int32,
cast[string](value.blobVal).cstring,
value.blobVal.len.int32, sqlite.SQLITE_TRANSIENT)
value.blobVal.len.int32, SQLITE_TRANSIENT)
sqlite.db_handle(result).checkRc(rc)
idx.inc
proc next(prepared: PreparedSql): bool =
proc next(prepared: ptr PreparedSql): bool =
## Advance cursor by one row.
## Return ``true`` if there are more rows.
let rc = sqlite.step(prepared)
@ -88,11 +96,13 @@ proc next(prepared: PreparedSql): bool =
else:
raise newSqliteError(sqlite.db_handle(prepared), rc)
proc finalize(prepared: PreparedSql) =
proc finalize(prepared: ptr PreparedSql) =
## Finalize statement or raise SqliteError if not successful.
let rc = sqlite.finalize(prepared)
sqlite.db_handle(prepared).checkRc(rc)
proc toDbValue*[T: Ordinal](val: T): DbValue =
DbValue(kind: sqliteInteger, intVal: val.int64)
@ -136,6 +146,9 @@ proc fromDbValue*[T](val: DbValue, _: typedesc[Option[T]]): Option[T] =
else:
some(val.fromDbValue(T))
# TODO: uncomment and test
#[
proc unpack*[T: tuple](row: openArray[DbValue], _: typedesc[T]): T =
## Call ``fromDbValue`` on each element of ``row`` and return it
## as a tuple.
@ -161,6 +174,7 @@ proc exec*(db: DbConn, sql: string, params: varargs[DbValue, toDbValue]) =
defer: prepared.finalize()
discard prepared.next
# TODO: uncomment and test
proc execMany*(db: DbConn, sql: string, params: seq[seq[DbValue]]) =
## Executes ``sql`` repeatedly using each element of ``params`` as parameters.
assert (not db.isNil), "Database is nil"
@ -174,6 +188,7 @@ proc execScript*(db: DbConn, sql: string) =
let rc = sqlite.exec(db, sql.cstring, nil, nil, nil)
db.checkRc(rc)
# TODO: uncomment and test
#[
template transaction*(db: DbConn, body: untyped) =
db.exec("BEGIN")
@ -188,8 +203,9 @@ template transaction*(db: DbConn, body: untyped) =
finally:
if ok:
db.exec("COMMIT")
]#
proc readColumn(prepared: PreparedSql, col: int32): DbValue =
proc readColumn(prepared: ptr PreparedSql, col: int32): DbValue =
let columnType = sqlite.column_type(prepared, col)
case columnType
of sqlite.SQLITE_INTEGER:
@ -228,7 +244,7 @@ proc rows*(db: DbConn, sql: string,
## Executes the query and returns the resulting rows.
for row in db.rows(sql, params):
result.add row
]#
proc openDatabase*(path: string, mode = dbReadWrite): DbConn =
## Open a new database connection to a database file. To create a
## in-memory database the special path `":memory:"` can be used.
@ -260,7 +276,10 @@ proc close*(db: DbConn) =
## Closes the database connection.
let rc = sqlite.close(db)
db.checkRc(rc)
# TODO: test
#[
proc lastInsertRowId*(db: DbConn): int64 =
## Get the row id of the last inserted row.
## For tables with an integer primary key,
@ -281,5 +300,4 @@ proc changes*(db: DbConn): int32 =
proc isReadonly*(db: DbConn): bool =
## Returns true if ``db`` is in readonly mode.
sqlite.db_readonly(db, "main") == 1
]#
]#

View File

@ -1,5 +1,5 @@
import nimterop/[cimport, build, paths]
import os, strutils
import nimterop/[cimport, build]
import os
const
baseDir = getProjectCacheDir("nim-sqlcipher")
@ -23,7 +23,6 @@ static:
"sqlite3_vmprintf",
"sqlite3_vsnprintf",
"sqlite3_str_vappendf",
"sqlite3_destructor_type"
])
@ -35,8 +34,7 @@ cPlugin:
import strutils
# Symbol renaming examples
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
# Remove prefixes or suffixes from procs
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} = # Remove prefixes or suffixes from procs
if sym.kind == nskProc and sym.name.contains("sqlite3_"):
sym.name = sym.name.replace("sqlite3_", "")

View File

@ -1,7 +1,6 @@
import sqlcipher
import times
import strformat
import os
when isMainModule:
let db: DbConn = openDatabase("./myDatabase")
@ -17,4 +16,4 @@ when isMainModule:
execScript(db, &"""insert into Log values("{date}:{time}")""")
#echo rows(db, "select * from Log")
echo rows(db, "select * from Log")