Files
Baradb/clients/nim/ormin/ormin/ormin_sqlite.nim
T
dimgigov 4e54075078
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
Add Ormin ORM support for BaraDB (Nim client)
2026-05-15 21:49:08 +03:00

296 lines
9.5 KiB
Nim

{.deadCodeElim: on.}
import json, times
import db_connector/db_common
import db_connector/sqlite3
import query_hooks
export db_common
type
DbConn* = PSqlite3 ## encapsulates a database connection
varcharType* = string
blobType* = seq[byte]
intType* = int
floatType* = float
boolType* = bool
timestampType* = DateTime
jsonType* = JsonNode
var jsonTimeFormat* = "yyyy-MM-dd HH:mm:ss"
proc dbError*(db: DbConn) {.noreturn.} =
## raises a DbError exception.
var e: ref DbError
new(e)
e.msg = $sqlite3.errmsg(db)
raise e
proc prepareStmt*(db: DbConn; q: string): PStmt =
when defined(debugOrminTrace):
echo "[[Ormin Executing]]: ", q
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK:
dbError(db)
template startBindings*(s: PStmt; n: int) =
if clear_bindings(s) != SQLITE_OK: dbError(db)
template bindParam*(db: DbConn; s: PStmt; idx: int; x, t: untyped) =
#when not (x is t):
# {.error: "type mismatch for query argument at position " & $idx.}
when t is blobType:
let xs = x
let blobPtr = if xs.len == 0:
cast[pointer](nil)
else:
cast[pointer](unsafeAddr(xs[0]))
if bind_blob(s, idx.cint, blobPtr, xs.len.cint, SQLITE_TRANSIENT) != SQLITE_OK:
dbError(db)
elif t is int or t is int64 or t is bool:
if bind_int64(s, idx.cint, x.int64) != SQLITE_OK: dbError(db)
elif t is string:
if bind_text(s, idx.cint, cstring(x), x.len.cint, SQLITE_TRANSIENT) != SQLITE_OK:
dbError(db)
elif t is float64:
if bind_double(s, idx.cint, x) != SQLITE_OK:
dbError(db)
elif t is DateTime:
let xx = if x.nanosecond > 0:
x.utc().format("yyyy-MM-dd HH:mm:ss\'.\'fff")
else:
x.utc().format("yyyy-MM-dd HH:mm:ss")
if bind_text(s, idx.cint, cstring(xx), xx.len.cint, SQLITE_TRANSIENT) != SQLITE_OK:
dbError(db)
elif t is JsonNode:
let xx = $x
if bind_text(s, idx.cint, cstring(xx), xx.len.cint, SQLITE_TRANSIENT) != SQLITE_OK:
dbError(db)
else:
{.error: "type mismatch for query argument at position " & $idx.}
template bindNullParam*(db: DbConn; s: PStmt; idx: int) =
if bind_null(s, idx.cint) != SQLITE_OK:
dbError(db)
template bindParamJson*(db: DbConn; s: PStmt; idx: int; xx: JsonNode;
t: typedesc) =
let x = xx
if x.kind == JNull:
if bind_null(s, idx.cint) != SQLITE_OK: dbError(db)
else:
bindFromJson(db, s, idx, x, t)
template bindFromJson*(db: DbConn; s: PStmt; idx: int; x: JsonNode;
t: typedesc) =
{.error: "invalid type for JSON object".}
template bindFromJson*(db: DbConn; s: PStmt; idx: int; x: JsonNode;
t: typedesc[string]) =
doAssert x.kind == JString
let xs = x.str
if bind_text(s, idx.cint, cstring(xs), xs.len.cint, SQLITE_TRANSIENT) != SQLITE_OK:
dbError(db)
template bindFromJson*(db: DbConn; s: PStmt; idx: int; x: JsonNode;
t: typedesc[int|int64]) =
doAssert x.kind == JInt
let xi = x.num
if bind_int64(s, idx.cint, xi.int64) != SQLITE_OK: dbError(db)
template bindFromJson*(db: DbConn; s: PStmt; idx: int; x: JsonNode;
t: typedesc[float64]) =
doAssert x.kind == JFloat
let xf = x.fnum
if bind_double(s, idx.cint, xf) != SQLITE_OK:
dbError(db)
template bindFromJson*(db: DbConn; s: PStmt; idx: int; x: JsonNode;
t: typedesc[bool]) =
doAssert x.kind == JBool
let xb = x.bval
if bind_int64(s, idx.cint, xb.int64) != SQLITE_OK: dbError(db)
template bindFromJson*(db: DbConn; s: PStmt; idx: int; x: JsonNode;
t: typedesc[DateTime]) =
doAssert x.kind == JString
let
dtStr = x.str
i = dtStr.find('.')
dt = if i > 0 and dtStr[i+1 .. ^1] == "000":
dtStr[0 ..< i]
else:
dtStr
if bind_text(s, idx.cint, cstring(dt), dt.len.cint, SQLITE_TRANSIENT) != SQLITE_OK:
dbError(db)
template bindResult*(db: DbConn; s: PStmt; idx: int; dest: int;
t: typedesc; name: string) =
dest = int column_int64(s, idx.cint)
template bindResult*(db: DbConn; s: PStmt; idx: int; dest: int64;
t: typedesc; name: string) =
dest = column_int64(s, idx.cint)
proc fillString(dest: var string; src: cstring; srcLen: int) =
when defined(nimNoNilSeqs):
setLen(dest, srcLen)
else:
if dest.isNil: dest = newString(srcLen)
else: setLen(dest, srcLen)
if srcLen > 0:
copyMem(unsafeAddr(dest[0]), src, srcLen)
proc fillBytes(dest: var seq[byte]; src: pointer; srcLen: int) =
when defined(nimNoNilSeqs):
setLen(dest, srcLen)
else:
if dest.isNil: dest = newSeq[byte](srcLen)
else: setLen(dest, srcLen)
if srcLen > 0:
copyMem(unsafeAddr(dest[0]), src, srcLen)
template bindResult*(db: DbConn; s: PStmt; idx: int; dest: var string;
t: typedesc; name: string) =
if column_type(s, idx.cint) == SQLITE_NULL:
when defined(nimNoNilSeqs):
setLen(dest, 0)
else:
dest = nil
else:
let srcLen = column_bytes(s, idx.cint)
let src = column_text(s, idx.cint)
fillString(dest, src, srcLen)
template bindResult*(db: DbConn; s: PStmt; idx: int; dest: var blobType;
t: typedesc; name: string) =
let srcLen = column_bytes(s, idx.cint)
let src = column_blob(s, idx.cint)
fillBytes(dest, src, srcLen)
template bindResult*(db: DbConn; s: PStmt; idx: int; dest: float64;
t: typedesc; name: string) =
dest = column_double(s, idx.cint)
template bindResult*(db: DbConn; s: PStmt; idx: int; dest: bool;
t: typedesc; name: string) =
dest = column_int64(s, idx.cint) != 0
template bindResult*(db: DbConn; s: PStmt; idx: int; dest: DateTime;
t: typedesc; name: string) =
let
src = $column_text(s, idx.cint)
i = src.find('.')
if i < 0:
dest = parse(src, "yyyy-MM-dd HH:mm:ss", utc())
else:
dest = parse(src, "yyyy-MM-dd HH:mm:ss\'.\'fff", utc())
template bindResult*(db: DbConn; s: PStmt; idx: int; dest: JsonNode;
t: typedesc; name: string) =
let src = column_text(s, idx.cint)
dest = parseJson($src)
template bindResult*[T](db: DbConn; s: PStmt; idx: int; dest: var DbValue[T];
t: typedesc; name: string) =
if column_type(s, idx.cint) == SQLITE_NULL:
dest.isNull = true
else:
dest.isNull = false
when T is string:
let srcLen = column_bytes(s, idx.cint)
let src = column_text(s, idx.cint)
fillString(dest.value, src, srcLen)
else:
bindResult(db, s, idx, dest.value, t, name)
template createJObject*(): untyped = newJObject()
template createJArray*(): untyped = newJArray()
template bindResultJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
t: typedesc; name: string) =
let x = obj
doAssert x.kind == JObject
if column_type(s, idx.cint) == SQLITE_NULL:
x[name] = newJNull()
else:
bindToJson(db, s, idx, x, t, name)
template columnIsNull*(db: DbConn; s: PStmt; idx: int): bool =
column_type(s, idx.cint) == SQLITE_NULL
template bindToJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
t: typedesc; name: string) =
{.error: "invalid type for JSON object".}
template bindToJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
t: typedesc[string]; name: string) =
let dest = newJString("")
let srcLen = column_bytes(s, idx.cint)
let src = column_text(s, idx.cint)
fillString(dest.str, src, srcLen)
obj[name] = dest
template bindToJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
t: typedesc[int|int64]; name: string) =
obj[name] = newJInt(column_int64(s, idx.cint))
template bindToJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
t: typedesc[blobType]; name: string) =
let srcLen = column_bytes(s, idx.cint)
let src = column_blob(s, idx.cint)
var data: blobType
fillBytes(data, src, srcLen)
let arr = newJArray()
for b in data:
arr.add newJInt(int(b))
obj[name] = arr
template bindToJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
t: typedesc[float64]; name: string) =
obj[name] = newJFloat(column_double(s, idx.cint))
template bindToJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
t: typedesc[bool]; name: string) =
obj[name] = newJBool(column_int64(s, idx.cint) != 0)
template bindToJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
t: typedesc[DateTime]; name: string) =
var dt: DateTime
bindResult(db, s, idx, dt, t, name)
obj[name] = newJString(format(dt, jsonTimeFormat))
template bindToJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
t: typedesc[JsonNode]; name: string) =
let src = column_text(s, idx.cint)
obj[name] = parseJson($src)
template startQuery*(db: DbConn; s: PStmt) = discard "nothing to do"
template stopQuery*(db: DbConn; s: PStmt) =
if sqlite3.reset(s) != SQLITE_OK: dbError(db)
template stepQuery*(db: DbConn; s: PStmt; returnsData: bool): bool =
when returnsData:
step(s) == SQLITE_ROW
else:
step(s) == SQLITE_DONE
template getLastId*(db: DbConn; s: PStmt): int =
int(last_insert_rowid(db))
template getAffectedRows*(db: DbConn; s: PStmt): int =
int(changes(db))
proc close*(db: DbConn) =
## closes the database connection.
if sqlite3.close(db) != SQLITE_OK: dbError(db)
proc open*(connection, user, password, database: string): DbConn =
## opens a database connection. Raises `EDb` if the connection could not
## be established. Only the ``connection`` parameter is used for ``sqlite``.
if sqlite3.open(connection, result) != SQLITE_OK:
dbError(result)